diff --git a/.deadcode-out b/.deadcode-out index e63e4a3dc3..61c5bcb055 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -13,6 +13,13 @@ forgejo.org/models IsErrSHANotFound IsErrMergeDivergingFastForwardOnly +forgejo.org/models/activities + GetActivityByID + NewFederatedUserActivity + CreateUserActivity + GetFollowingFeeds + FederatedUserActivity.loadActor + forgejo.org/models/auth WebAuthnCredentials @@ -54,9 +61,17 @@ forgejo.org/models/user IsErrExternalLoginUserAlreadyExist IsErrExternalLoginUserNotExist NewFederatedUser + NewFederatedUserFollower IsErrUserSettingIsNotExist GetUserAllSettings DeleteUserSetting + GetFederatedUser + GetFederatedUserByUserID + UpdateFederatedUser + GetFollowersForUser + AddFollower + RemoveFollower + IsFollowingAp forgejo.org/modules/activitypub NewContext diff --git a/.dockerignore b/.dockerignore index 5e7a893014..807c70b000 100644 --- a/.dockerignore +++ b/.dockerignore @@ -37,13 +37,9 @@ coverage.all coverage/ cpu.out -/modules/migration/bindata.go /modules/migration/bindata.go.hash -/modules/options/bindata.go /modules/options/bindata.go.hash -/modules/public/bindata.go /modules/public/bindata.go.hash -/modules/templates/bindata.go /modules/templates/bindata.go.hash *.db diff --git a/.forgejo/issue_template/bug-report-ui.yaml b/.forgejo/issue_template/bug-report-ui.yaml index 57d578b232..8bb7bf1d49 100644 --- a/.forgejo/issue_template/bug-report-ui.yaml +++ b/.forgejo/issue_template/bug-report-ui.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - **NOTE: If your issue is a security concern, please email (GPG: `A4676E79`) instead of opening a public issue.** + **NOTE: If your issue is a security concern, please email ([security.txt](https://forgejo.org/.well-known/security.txt)) instead of opening a public issue.** - type: markdown attributes: value: | diff --git a/.forgejo/issue_template/bug-report.yaml b/.forgejo/issue_template/bug-report.yaml index 6e9b116e60..a2b50dbca2 100644 --- a/.forgejo/issue_template/bug-report.yaml +++ b/.forgejo/issue_template/bug-report.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - **NOTE: If your issue is a security concern, please email (GPG: `A4676E79`) instead of opening a public issue.** + **NOTE: If your issue is a security concern, please email ([security.txt](https://forgejo.org/.well-known/security.txt)) instead of opening a public issue.** - type: markdown attributes: value: | diff --git a/.forgejo/testdata/build-release/Dockerfile b/.forgejo/testdata/build-release/Dockerfile index d10564359e..09cce06c47 100644 --- a/.forgejo/testdata/build-release/Dockerfile +++ b/.forgejo/testdata/build-release/Dockerfile @@ -1,4 +1,4 @@ -FROM data.forgejo.org/oci/alpine:3.21 +FROM data.forgejo.org/oci/alpine:3.22 ARG RELEASE_VERSION=unkown LABEL maintainer="contact@forgejo.org" \ org.opencontainers.image.version="${RELEASE_VERSION}" diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index a34f3533fd..3ab63b0589 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -164,7 +164,7 @@ jobs: - name: build container & release if: ${{ secrets.TOKEN != '' }} - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5 with: forgejo: "${{ env.GITHUB_SERVER_URL }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" @@ -183,7 +183,7 @@ jobs: - name: build rootless container if: ${{ secrets.TOKEN != '' }} - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5 with: forgejo: "${{ env.GITHUB_SERVER_URL }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" diff --git a/.forgejo/workflows/publish-release.yml b/.forgejo/workflows/publish-release.yml index 27d3b9383e..3aec46fb03 100644 --- a/.forgejo/workflows/publish-release.yml +++ b/.forgejo/workflows/publish-release.yml @@ -44,7 +44,7 @@ jobs: - uses: https://data.forgejo.org/actions/checkout@v4 - name: copy & sign - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.4 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.5 with: from-forgejo: ${{ vars.FORGEJO }} to-forgejo: ${{ vars.FORGEJO }} diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index b584f94cf8..5aa6c8cd98 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:40.48.4 + image: data.forgejo.org/renovate/renovate:41.1.4 steps: - name: Load renovate repo cache diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index 4d88d3efb0..7a93bb66a8 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -91,6 +91,7 @@ jobs: RACE_ENABLED: 'true' TAGS: bindata TEST_ELASTICSEARCH_URL: http://elasticsearch:9200 + TEST_MINIO_ENDPOINT: minio:9000 test-e2e: if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker @@ -114,6 +115,11 @@ jobs: run: | su forgejo -c 'make deps-frontend frontend' - uses: ./.forgejo/workflows-composite/build-backend + - name: Decide to run all tests + id: run-all + if: contains(github.event.pull_request.labels.*.name, 'run-all-playwright-tests') || contains(github.event.pull_request.title, 'playwright') + run: | + echo "all=1" >> "$GITHUB_OUTPUT" - name: Get changed files id: changed-files uses: https://data.forgejo.org/tj-actions/changed-files@v46 @@ -126,6 +132,7 @@ jobs: USE_REPO_TEST_DIR: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 CHANGED_FILES: ${{steps.changed-files.outputs.all_changed_files}} + RUN_ALL: ${{steps.run-all.all}} - name: Upload test artifacts on failure if: failure() uses: https://data.forgejo.org/forgejo/upload-artifact@v4 diff --git a/CODEOWNERS b/CODEOWNERS index 03b0d8753d..34cdceca09 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -10,6 +10,7 @@ # Javascript and CSS code. web_src/.* @beowulf @gusted +web_src/css/.* @0ko # HTML templates used by the backend. templates/.* @beowulf @gusted diff --git a/Dockerfile b/Dockerfile index a94f4d2b46..322e2c61a2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx -FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.21 AS build-env +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.22 AS build-env ARG GOPROXY ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct} @@ -33,10 +33,10 @@ RUN apk --no-cache add build-base git nodejs npm COPY . ${GOPATH}/src/forgejo.org WORKDIR ${GOPATH}/src/forgejo.org -RUN make clean +RUN make clean-no-bindata RUN make frontend RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini -RUN LDFLAGS="-buildid=" make RELEASE_VERSION=$RELEASE_VERSION GOFLAGS="-trimpath" go-check generate-backend static-executable && xx-verify gitea +RUN LDFLAGS="-buildid=" make FORGEJO_GENERATE_SKIP_HASH=true RELEASE_VERSION=$RELEASE_VERSION GOFLAGS="-trimpath" go-check generate-backend static-executable && xx-verify gitea # Copy local files COPY docker/root /tmp/local @@ -51,7 +51,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \ /go/src/forgejo.org/environment-to-ini RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete -FROM data.forgejo.org/oci/alpine:3.21 +FROM data.forgejo.org/oci/alpine:3.22 ARG RELEASE_VERSION LABEL maintainer="contact@forgejo.org" \ org.opencontainers.image.authors="Forgejo" \ diff --git a/Dockerfile.rootless b/Dockerfile.rootless index 36df26c042..6a3abaa4b9 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -1,6 +1,6 @@ FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx -FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.21 AS build-env +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.22 AS build-env ARG GOPROXY ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct} @@ -33,10 +33,10 @@ RUN apk --no-cache add build-base git nodejs npm COPY . ${GOPATH}/src/forgejo.org WORKDIR ${GOPATH}/src/forgejo.org -RUN make clean +RUN make clean-no-bindata RUN make frontend RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini -RUN make RELEASE_VERSION=$RELEASE_VERSION go-check generate-backend static-executable && xx-verify gitea +RUN make FORGEJO_GENERATE_SKIP_HASH=true RELEASE_VERSION=$RELEASE_VERSION go-check generate-backend static-executable && xx-verify gitea # Copy local files COPY docker/rootless /tmp/local @@ -49,7 +49,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \ /go/src/forgejo.org/environment-to-ini RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete -FROM data.forgejo.org/oci/alpine:3.21 +FROM data.forgejo.org/oci/alpine:3.22 ARG RELEASE_VERSION LABEL maintainer="contact@forgejo.org" \ org.opencontainers.image.authors="Forgejo" \ diff --git a/Makefile b/Makefile index 9774200b06..e770f2a989 100644 --- a/Makefile +++ b/Makefile @@ -47,8 +47,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go -GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@40.48.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@41.1.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... @@ -129,7 +128,7 @@ WEBPACK_CONFIGS := webpack.config.js tailwind.config.js WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts -BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go +BINDATA_DEST := modules/migration/bindata.go modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST)) GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go @@ -222,7 +221,6 @@ help: @echo " - lint-go lint go files" @echo " - lint-go-fix lint go files and fix issues" @echo " - lint-go-vet lint go files with vet" - @echo " - lint-go-gopls lint go files with gopls" @echo " - lint-js lint js files" @echo " - lint-js-fix lint js files and fix issues" @echo " - lint-css lint css files" @@ -325,8 +323,12 @@ clean-all: clean rm -rf $(WEBPACK_DEST_ENTRIES) node_modules .PHONY: clean -clean: - rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \ +clean: clean-no-bindata + rm -rf $(BINDATA_DEST) $(BINDATA_HASH) + +.PHONY: clean-no-bindata +clean-no-bindata: + rm -rf $(EXECUTABLE) $(DIST) \ integrations*.test \ e2e*.test \ tests/integration/gitea-integration-* \ @@ -483,11 +485,6 @@ lint-go-vet: @echo "Running go vet..." @$(GO) vet ./... -.PHONY: lint-go-gopls -lint-go-gopls: - @echo "Running gopls check..." - @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA) - .PHONY: lint-editorconfig lint-editorconfig: $(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates .forgejo/workflows @@ -928,7 +925,6 @@ deps-tools: $(GO) install $(GO_LICENSES_PACKAGE) $(GO) install $(GOVULNCHECK_PACKAGE) $(GO) install $(GOMOCK_PACKAGE) - $(GO) install $(GOPLS_PACKAGE) node_modules: package-lock.json npm install --no-save diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 0113e77120..c3b261320c 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -289,11 +289,6 @@ "path": "github.com/cloudflare/circl/LICENSE", "licenseText": "Copyright (c) 2019 Cloudflare. 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 Cloudflare 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\n========================================================================\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, - { - "name": "github.com/cpuguy83/go-md2man/v2/md2man", - "path": "github.com/cpuguy83/go-md2man/v2/md2man/LICENSE.md", - "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Brian Goff\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" - }, { "name": "github.com/cyphar/filepath-securejoin", "path": "github.com/cyphar/filepath-securejoin/LICENSE", @@ -600,8 +595,8 @@ "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Huan Du\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" }, { - "name": "github.com/jaytaylor/html2text", - "path": "github.com/jaytaylor/html2text/LICENSE", + "name": "github.com/inbucket/html2text", + "path": "github.com/inbucket/html2text/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Jay Taylor\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" }, { @@ -784,6 +779,16 @@ "path": "github.com/nwaples/rardecode/LICENSE", "licenseText": "Copyright (c) 2015, Nicholas Waples\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, + { + "name": "github.com/olekukonko/errors", + "path": "github.com/olekukonko/errors/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2025 Oleku Konko\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + }, + { + "name": "github.com/olekukonko/ll", + "path": "github.com/olekukonko/ll/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2025 Oleku Konko\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + }, { "name": "github.com/olekukonko/tablewriter", "path": "github.com/olekukonko/tablewriter/LICENSE.md", @@ -889,11 +894,6 @@ "path": "github.com/rs/xid/LICENSE", "licenseText": "Copyright (c) 2015 Olivier Poitrey \u003crs@dailymotion.com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished\nto do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" }, - { - "name": "github.com/russross/blackfriday/v2", - "path": "github.com/russross/blackfriday/v2/LICENSE.txt", - "licenseText": "Blackfriday is distributed under the Simplified BSD License:\n\n\u003e Copyright © 2011 Russ Ross\n\u003e All rights reserved.\n\u003e\n\u003e Redistribution and use in source and binary forms, with or without\n\u003e modification, are permitted provided that the following conditions\n\u003e are met:\n\u003e\n\u003e 1. Redistributions of source code must retain the above copyright\n\u003e notice, this list of conditions and the following disclaimer.\n\u003e\n\u003e 2. Redistributions in binary form must reproduce the above\n\u003e copyright notice, this list of conditions and the following\n\u003e disclaimer in the documentation and/or other materials provided with\n\u003e the distribution.\n\u003e\n\u003e THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\u003e \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n\u003e LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n\u003e FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n\u003e COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n\u003e INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n\u003e BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n\u003e LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n\u003e CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n\u003e LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\n\u003e ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n\u003e POSSIBILITY OF SUCH DAMAGE.\n" - }, { "name": "github.com/santhosh-tekuri/jsonschema/v6", "path": "github.com/santhosh-tekuri/jsonschema/v6/LICENSE", @@ -939,11 +939,6 @@ "path": "github.com/ulikunitz/xz/LICENSE", "licenseText": "Copyright (c) 2014-2022 Ulrich Kunitz\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* My name, Ulrich Kunitz, may not be used to endorse or promote products\n derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, - { - "name": "github.com/urfave/cli/v2", - "path": "github.com/urfave/cli/v2/LICENSE", - "licenseText": "MIT License\n\nCopyright (c) 2022 urfave/cli maintainers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" - }, { "name": "github.com/urfave/cli/v3", "path": "github.com/urfave/cli/v3/LICENSE", @@ -964,11 +959,6 @@ "path": "github.com/xanzy/ssh-agent/LICENSE", "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n" }, - { - "name": "github.com/xrash/smetrics", - "path": "github.com/xrash/smetrics/LICENSE", - "licenseText": "Copyright (C) 2016 Felipe da Cunha Gonçalves\nAll Rights Reserved.\n\nMIT LICENSE\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - }, { "name": "github.com/yohcop/openid-go", "path": "github.com/yohcop/openid-go/LICENSE", diff --git a/build/generate-bindata.go b/build/generate-bindata.go index 2bdfc39574..67d3776847 100644 --- a/build/generate-bindata.go +++ b/build/generate-bindata.go @@ -22,12 +22,19 @@ import ( "github.com/klauspost/compress/zstd" ) -func needsUpdate(dir, filename string) (bool, []byte) { - needRegen := false +func fileExists(filename string) bool { _, err := os.Stat(filename) - if err != nil { - needRegen = true + if err == nil { + return true } + if os.IsNotExist(err) { + return false + } + panic(err) +} + +func needsUpdate(dir, filename string) (bool, []byte) { + needRegen := !fileExists(filename) oldHash, err := os.ReadFile(filename + ".hash") if err != nil { @@ -73,10 +80,14 @@ func main() { useGlobalModTime, _ = strconv.ParseBool(os.Args[4]) } - update, newHash := needsUpdate(dir, filename) + if os.Getenv("FORGEJO_GENERATE_SKIP_HASH") == "true" && fileExists(filename) { + fmt.Printf("bindata %s already exists and FORGEJO_GENERATE_SKIP_HASH=true\n", packageName) + return + } + update, newHash := needsUpdate(dir, filename) if !update { - fmt.Printf("bindata for %s already up-to-date\n", packageName) + fmt.Printf("bindata %s already exists and the checksum is a match\n", packageName) return } diff --git a/build/lint-locale/lint-locale_test.go b/build/lint-locale/lint-locale_test.go index 9e9a931feb..dd146c0d70 100644 --- a/build/lint-locale/lint-locale_test.go +++ b/build/lint-locale/lint-locale_test.go @@ -37,6 +37,7 @@ func TestLocalizationPolicy(t *testing.T) { assert.Empty(t, checkLocaleContent([]byte("teams.specific_repositories_helper = Members will only have access to repositories explicitly added to the team. Selecting this will not automatically remove repositories already added with All repositories."))) assert.Empty(t, checkLocaleContent([]byte("sqlite_helper = File path for the SQLite3 database.
Enter an absolute path if you run Forgejo as a service."))) assert.Empty(t, checkLocaleContent([]byte("hi_user_x = Hi %s,"))) + assert.Empty(t, checkLocaleContent([]byte("key = Press Shift"))) assert.Equal(t, []string{"error404: The page you are trying to reach either does not exist or you are not authorized to view it."}, checkLocaleContent([]byte("error404 = The page you are trying to reach either does not exist or you are not authorized to view it."))) }) diff --git a/contrib/environment-to-ini/README b/contrib/environment-to-ini/README index f1d3f2ae83..e4caf25666 100644 --- a/contrib/environment-to-ini/README +++ b/contrib/environment-to-ini/README @@ -1,45 +1,46 @@ Environment To Ini ================== -Multiple docker users have requested that the Gitea docker is changed -to permit arbitrary configuration via environment variables. +This tool allows defining Forgejo's entire configuration via environment +variables, mostly geared towards usage in Docker. -Gitea needs to use an ini file for configuration because the running -environment that starts the docker may not be the same as that used -by the hooks. An ini file also gives a good default and means that -users do not have to completely provide a full environment. +Forgejo needs to use an INI file for configuration because the running +environment that starts the container may not be the same as the one used +by the hooks. An INI file also gives a good default and means that +users do not have to provide the entire set of environment variables. With those caveats above, this command provides a generic way of converting suitably structured environment variables into any ini value. -To use the command is very simple just run it and the default gitea -app.ini will be rewritten to take account of the variables provided, -however there are various options to give slightly different -behavior and these can be interrogated with the `-h` option. +When run, `environment-to-ini` will write the config files based on the +environment variables provided. +Check with the `-h` flag for several options to alter this behaviour. -The environment variables should be of the form: +Environment variables of the form "FORGEJO__SECTION_NAME__KEY_NAME" +will be mapped to the ini section "[section_name]" and the key +"KEY_NAME" with the value as provided. - GITEA__SECTION_NAME__KEY_NAME - -Note, SECTION_NAME in the notation above is case-insensitive. +Environment variables of the form "FORGEJO__SECTION_NAME__KEY_NAME__FILE" +will be mapped to the ini section "[section_name]" and the key +"KEY_NAME" with the value loaded from the specified file. Environment variables are usually restricted to a reduced character set "0-9A-Z_" - in order to allow the setting of sections with characters outside of that set, they should be escaped as following: -"_0X2E_" for "." and "_0X2D_" for "-". The entire section and key names -can be escaped as a UTF8 byte string if necessary. E.g. to configure: +"_0X2E_" for ".". The entire section and key names can be escaped as +a UTF8 byte string if necessary. E.g. to configure: - """ - ... - [log.console] - COLORIZE=false - STDERR=true - ... - """ + """ + ... + [log.console] + COLORIZE=false + STDERR=true + ... + """ -You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false" -and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found +You would set the environment variables: "FORGEJO__LOG_0x2E_CONSOLE__COLORIZE=false" +and "FORGEJO__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found on the configuration cheat sheet. To build locally, run: diff --git a/eslint.config.mjs b/eslint.config.mjs index 5737bed623..28cfa80089 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,5 +1,5 @@ import eslintCommunityEslintPluginEslintComments from '@eslint-community/eslint-plugin-eslint-comments'; -import stylisticEslintPluginJs from '@stylistic/eslint-plugin-js'; +import stylisticEslintPlugin from '@stylistic/eslint-plugin'; import vitest from '@vitest/eslint-plugin'; import arrayFunc from 'eslint-plugin-array-func'; import eslintPluginImportX from 'eslint-plugin-import-x'; @@ -26,7 +26,7 @@ export default tseslint.config( { plugins: { '@eslint-community/eslint-comments': eslintCommunityEslintPluginEslintComments, - '@stylistic/js': stylisticEslintPluginJs, + '@stylistic': stylisticEslintPlugin, '@vitest': vitest, 'array-func': arrayFunc, 'no-jquery': noJquery, @@ -69,62 +69,62 @@ export default tseslint.config( '@eslint-community/eslint-comments/no-unused-enable': [2], '@eslint-community/eslint-comments/no-use': [0], '@eslint-community/eslint-comments/require-description': [0], - '@stylistic/js/array-bracket-newline': [0], - '@stylistic/js/array-bracket-spacing': [2, 'never'], - '@stylistic/js/array-element-newline': [0], - '@stylistic/js/arrow-parens': [2, 'always'], + '@stylistic/array-bracket-newline': [0], + '@stylistic/array-bracket-spacing': [2, 'never'], + '@stylistic/array-element-newline': [0], + '@stylistic/arrow-parens': [2, 'always'], - '@stylistic/js/arrow-spacing': [2, { + '@stylistic/arrow-spacing': [2, { before: true, after: true, }], - '@stylistic/js/block-spacing': [0], + '@stylistic/block-spacing': [0], - '@stylistic/js/brace-style': [2, '1tbs', { + '@stylistic/brace-style': [2, '1tbs', { allowSingleLine: true, }], - '@stylistic/js/comma-dangle': [2, 'always-multiline'], + '@stylistic/comma-dangle': [2, 'always-multiline'], - '@stylistic/js/comma-spacing': [2, { + '@stylistic/comma-spacing': [2, { before: false, after: true, }], - '@stylistic/js/comma-style': [2, 'last'], - '@stylistic/js/computed-property-spacing': [2, 'never'], - '@stylistic/js/dot-location': [2, 'property'], - '@stylistic/js/eol-last': [2], - '@stylistic/js/function-call-spacing': [2, 'never'], - '@stylistic/js/function-call-argument-newline': [0], - '@stylistic/js/function-paren-newline': [0], - '@stylistic/js/generator-star-spacing': [0], - '@stylistic/js/implicit-arrow-linebreak': [0], + '@stylistic/comma-style': [2, 'last'], + '@stylistic/computed-property-spacing': [2, 'never'], + '@stylistic/dot-location': [2, 'property'], + '@stylistic/eol-last': [2], + '@stylistic/function-call-spacing': [2, 'never'], + '@stylistic/function-call-argument-newline': [0], + '@stylistic/function-paren-newline': [0], + '@stylistic/generator-star-spacing': [0], + '@stylistic/implicit-arrow-linebreak': [0], - '@stylistic/js/indent': [2, 2, { + '@stylistic/indent': [2, 2, { ignoreComments: true, SwitchCase: 1, }], - '@stylistic/js/key-spacing': [2], - '@stylistic/js/keyword-spacing': [2], - '@stylistic/js/linebreak-style': [2, 'unix'], - '@stylistic/js/lines-around-comment': [0], - '@stylistic/js/lines-between-class-members': [0], - '@stylistic/js/max-len': [0], - '@stylistic/js/max-statements-per-line': [0], - '@stylistic/js/multiline-ternary': [0], - '@stylistic/js/new-parens': [2], - '@stylistic/js/newline-per-chained-call': [0], - '@stylistic/js/no-confusing-arrow': [0], - '@stylistic/js/no-extra-parens': [0], - '@stylistic/js/no-extra-semi': [2], - '@stylistic/js/no-floating-decimal': [0], - '@stylistic/js/no-mixed-operators': [0], - '@stylistic/js/no-mixed-spaces-and-tabs': [2], + '@stylistic/key-spacing': [2], + '@stylistic/keyword-spacing': [2], + '@stylistic/linebreak-style': [2, 'unix'], + '@stylistic/lines-around-comment': [0], + '@stylistic/lines-between-class-members': [0], + '@stylistic/max-len': [0], + '@stylistic/max-statements-per-line': [0], + '@stylistic/multiline-ternary': [0], + '@stylistic/new-parens': [2], + '@stylistic/newline-per-chained-call': [0], + '@stylistic/no-confusing-arrow': [0], + '@stylistic/no-extra-parens': [0], + '@stylistic/no-extra-semi': [2], + '@stylistic/no-floating-decimal': [0], + '@stylistic/no-mixed-operators': [0], + '@stylistic/no-mixed-spaces-and-tabs': [2], - '@stylistic/js/no-multi-spaces': [2, { + '@stylistic/no-multi-spaces': [2, { ignoreEOLComments: true, exceptions: { @@ -132,60 +132,60 @@ export default tseslint.config( }, }], - '@stylistic/js/no-multiple-empty-lines': [2, { + '@stylistic/no-multiple-empty-lines': [2, { max: 1, maxEOF: 0, maxBOF: 0, }], - '@stylistic/js/no-tabs': [2], - '@stylistic/js/no-trailing-spaces': [2], - '@stylistic/js/no-whitespace-before-property': [2], - '@stylistic/js/nonblock-statement-body-position': [2], - '@stylistic/js/object-curly-newline': [0], - '@stylistic/js/object-curly-spacing': [2, 'never'], - '@stylistic/js/object-property-newline': [0], - '@stylistic/js/one-var-declaration-per-line': [0], - '@stylistic/js/operator-linebreak': [2, 'after'], - '@stylistic/js/padded-blocks': [2, 'never'], - '@stylistic/js/padding-line-between-statements': [0], - '@stylistic/js/quote-props': [0], + '@stylistic/no-tabs': [2], + '@stylistic/no-trailing-spaces': [2], + '@stylistic/no-whitespace-before-property': [2], + '@stylistic/nonblock-statement-body-position': [2], + '@stylistic/object-curly-newline': [0], + '@stylistic/object-curly-spacing': [2, 'never'], + '@stylistic/object-property-newline': [0], + '@stylistic/one-var-declaration-per-line': [0], + '@stylistic/operator-linebreak': [2, 'after'], + '@stylistic/padded-blocks': [2, 'never'], + '@stylistic/padding-line-between-statements': [0], + '@stylistic/quote-props': [0], - '@stylistic/js/quotes': [2, 'single', { + '@stylistic/quotes': [2, 'single', { avoidEscape: true, allowTemplateLiterals: true, }], - '@stylistic/js/rest-spread-spacing': [2, 'never'], + '@stylistic/rest-spread-spacing': [2, 'never'], - '@stylistic/js/semi': [2, 'always', { + '@stylistic/semi': [2, 'always', { omitLastInOneLineBlock: true, }], - '@stylistic/js/semi-spacing': [2, { + '@stylistic/semi-spacing': [2, { before: false, after: true, }], - '@stylistic/js/semi-style': [2, 'last'], - '@stylistic/js/space-before-blocks': [2, 'always'], + '@stylistic/semi-style': [2, 'last'], + '@stylistic/space-before-blocks': [2, 'always'], - '@stylistic/js/space-before-function-paren': [2, { + '@stylistic/space-before-function-paren': [2, { anonymous: 'ignore', named: 'never', asyncArrow: 'always', }], - '@stylistic/js/space-in-parens': [2, 'never'], - '@stylistic/js/space-infix-ops': [2], - '@stylistic/js/space-unary-ops': [2], - '@stylistic/js/spaced-comment': [2, 'always'], - '@stylistic/js/switch-colon-spacing': [2], - '@stylistic/js/template-curly-spacing': [2, 'never'], - '@stylistic/js/template-tag-spacing': [2, 'never'], - '@stylistic/js/wrap-iife': [2, 'inside'], - '@stylistic/js/wrap-regex': [0], - '@stylistic/js/yield-star-spacing': [2, 'after'], + '@stylistic/space-in-parens': [2, 'never'], + '@stylistic/space-infix-ops': [2], + '@stylistic/space-unary-ops': [2], + '@stylistic/spaced-comment': [2, 'always'], + '@stylistic/switch-colon-spacing': [2], + '@stylistic/template-curly-spacing': [2, 'never'], + '@stylistic/template-tag-spacing': [2, 'never'], + '@stylistic/wrap-iife': [2, 'inside'], + '@stylistic/wrap-regex': [0], + '@stylistic/yield-star-spacing': [2, 'after'], 'accessor-pairs': [2], 'array-callback-return': [2, { diff --git a/go.mod b/go.mod index 0f47839420..510ec9c3ae 100644 --- a/go.mod +++ b/go.mod @@ -41,14 +41,14 @@ require ( github.com/gliderlabs/ssh v0.3.8 github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 - github.com/go-chi/chi/v5 v5.2.1 + github.com/go-chi/chi/v5 v5.2.2 github.com/go-chi/cors v1.2.1 github.com/go-co-op/gocron v1.37.0 github.com/go-enry/go-enry/v2 v2.9.2 github.com/go-git/go-git/v5 v5.13.2 github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-openapi/spec v0.21.0 - github.com/go-sql-driver/mysql v1.9.2 + github.com/go-sql-driver/mysql v1.9.3 github.com/go-webauthn/webauthn v0.13.0 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f @@ -63,8 +63,8 @@ require ( github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/huandu/xstrings v1.5.0 - github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 - github.com/jhillyerd/enmime/v2 v2.1.0 + github.com/inbucket/html2text v0.9.0 + github.com/jhillyerd/enmime/v2 v2.2.0 github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/klauspost/compress v1.18.0 @@ -76,7 +76,7 @@ require ( github.com/meilisearch/meilisearch-go v0.31.0 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/minio/minio-go/v7 v7.0.93 + github.com/minio/minio-go/v7 v7.0.94 github.com/msteinert/pam/v2 v2.1.0 github.com/nektos/act v0.2.52 github.com/niklasfasching/go-org v1.8.0 @@ -92,7 +92,6 @@ require ( github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.0 github.com/ulikunitz/xz v0.5.12 - github.com/urfave/cli/v2 v2.27.6 github.com/urfave/cli/v3 v3.3.3 github.com/valyala/fastjson v1.6.4 github.com/yohcop/openid-go v1.0.1 @@ -152,7 +151,6 @@ require ( github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/cyphar/filepath-securejoin v0.3.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect @@ -160,7 +158,7 @@ require ( github.com/dlclark/regexp2 v1.11.5 // indirect github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/fatih/color v1.16.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect @@ -194,7 +192,7 @@ require ( github.com/libdns/libdns v1.0.0-beta.1 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/markbates/going v1.0.3 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mholt/acmez/v3 v3.1.2 // indirect github.com/miekg/dns v1.1.63 // indirect @@ -207,7 +205,9 @@ require ( github.com/mschoch/smat v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nwaples/rardecode v1.1.3 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/olekukonko/errors v1.1.0 // indirect + github.com/olekukonko/ll v0.0.9 // indirect + github.com/olekukonko/tablewriter v1.0.7 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect @@ -221,7 +221,6 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rs/xid v1.6.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.0 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect @@ -229,7 +228,6 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect - github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/zeebo/assert v1.3.0 // indirect github.com/zeebo/blake3 v0.2.4 // indirect go.etcd.io/bbolt v1.4.0 // indirect @@ -246,7 +244,7 @@ require ( replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 -replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.26.0 +replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.28.0 replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 diff --git a/go.sum b/go.sum index 9a93fa8359..53558fddd7 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ code.forgejo.org/f3/gof3/v3 v3.11.0 h1:f/xToKwqTgxG6PYxvewywjDQyCcyHEEJ6sZqUitFs code.forgejo.org/f3/gof3/v3 v3.11.0/go.mod h1:4FaRUNSQGBiD1M0DuB0yNv+Z2wMtlOeckgygHSSq4KQ= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM= -code.forgejo.org/forgejo/act v1.26.0 h1:6mTmoaw7d/WpYiw/Pw6AaypxFdgJog5OFi/PMEgEbxs= -code.forgejo.org/forgejo/act v1.26.0/go.mod h1:HFDFrXPrqfM9aH2RCnMiBdo/3ThxDmZjp58InPjGOfo= +code.forgejo.org/forgejo/act v1.28.0 h1:96njNC7C1YNyjWq5OWvLZMF/nw0PMthzIA8Nwbnn7jo= +code.forgejo.org/forgejo/act v1.28.0/go.mod h1:dFuiwAmD5vyrzecysHB2kL/GM3wRpoVPl+WdbCTC8Bs= code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE= code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= code.forgejo.org/forgejo/go-rpmutils v1.0.0 h1:RZGGeKt70p/WaIEL97pyT6uiiEIoN8/aLmS5Z6WmX0M= @@ -152,8 +152,6 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= -github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= @@ -194,8 +192,8 @@ github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTe github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -215,8 +213,8 @@ github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5La github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= -github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= +github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618= +github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= @@ -247,8 +245,8 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= -github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= -github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= @@ -343,12 +341,12 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA= -github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= +github.com/inbucket/html2text v0.9.0 h1:ULJmVcBEMAcmLE+/rN815KG1Fx6+a4HhbUxiDiN+qks= +github.com/inbucket/html2text v0.9.0/go.mod h1:QDaumzl+/OzlSVbNohhmg+yAy5pKjUjzCKW2BMvztKE= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jhillyerd/enmime/v2 v2.1.0 h1:c8Qwi5Xq5EdtMN6byQWoZ/8I2RMTo6OJ7Xay+s1oPO0= -github.com/jhillyerd/enmime/v2 v2.1.0/go.mod h1:EJ74dcRbBcqHSP2TBu08XRoy6y3Yx0cevwb1YkGMEmQ= +github.com/jhillyerd/enmime/v2 v2.2.0 h1:Pe35MB96eZK5Q0XjlvPftOgWypQpd1gcbfJKAt7rsB8= +github.com/jhillyerd/enmime/v2 v2.2.0/go.mod h1:SOBXlCemjhiV2DvHhAKnJiWrtJGS/Ffuw4Iy7NjBTaI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -391,12 +389,10 @@ github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o= github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8= github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= @@ -413,8 +409,8 @@ github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.93 h1:lAB4QJp8Nq3vDMOU0eKgMuyBiEGMNlXQ5Glc8qAxqSU= -github.com/minio/minio-go/v7 v7.0.93/go.mod h1:71t2CqDt3ThzESgZUlU1rBN54mksGGlkLcFgguDnnAc= +github.com/minio/minio-go/v7 v7.0.94 h1:1ZoksIKPyaSt64AVOyaQvhDOgVC3MfZsWM6mZXRUGtM= +github.com/minio/minio-go/v7 v7.0.94/go.mod h1:71t2CqDt3ThzESgZUlU1rBN54mksGGlkLcFgguDnnAc= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -438,8 +434,12 @@ github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWk github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= +github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI= +github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= +github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw= +github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs= github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E= github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -497,8 +497,6 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= @@ -533,8 +531,6 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= -github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/urfave/cli/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I= github.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= @@ -545,8 +541,6 @@ github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js= @@ -653,7 +647,6 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/main.go b/main.go index 3f0283db7f..ade43881cf 100644 --- a/main.go +++ b/main.go @@ -21,7 +21,7 @@ import ( _ "forgejo.org/modules/markup/markdown" _ "forgejo.org/modules/markup/orgmode" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // these flags will be set by the build flags diff --git a/models/actions/run.go b/models/actions/run.go index 48756b7a08..55def805ed 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -55,6 +55,7 @@ type ActionRun struct { PreviousDuration time.Duration Created timeutil.TimeStamp `xorm:"created"` Updated timeutil.TimeStamp `xorm:"updated"` + NotifyEmail bool } func init() { diff --git a/models/activities/action.go b/models/activities/action.go index a309637d04..8592f81414 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -442,6 +442,12 @@ func (a *Action) GetIssueContent(ctx context.Context) string { return a.Issue.Content } +func GetActivityByID(ctx context.Context, id int64) (*Action, error) { + var act Action + _, err := db.GetEngine(ctx).ID(id).Get(&act) + return &act, err +} + // GetFeedsOptions options for retrieving feeds type GetFeedsOptions struct { db.ListOptions @@ -467,11 +473,8 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err return nil, 0, err } - sess := db.GetEngine(ctx).Where(cond). - Select("`action`.*"). // this line will avoid select other joined table's columns - Join("INNER", "repository", "`repository`.id = `action`.repo_id") - opts.SetDefaultValues() + sess := db.GetEngine(ctx).Where(cond) sess = db.SetSessionPagination(sess, &opts) actions := make([]*Action, 0, opts.PageSize) @@ -598,13 +601,14 @@ func DeleteOldActions(ctx context.Context, olderThan time.Duration) (err error) } // NotifyWatchers creates batch of actions for every watcher. -func NotifyWatchers(ctx context.Context, actions ...*Action) error { +func NotifyWatchers(ctx context.Context, actions ...*Action) ([]Action, error) { var watchers []*repo_model.Watch var repo *repo_model.Repository var err error var permCode []bool var permIssue []bool var permPR []bool + var out []Action e := db.GetEngine(ctx) @@ -615,14 +619,14 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { // Add feeds for user self and all watchers. watchers, err = repo_model.GetWatchers(ctx, act.RepoID) if err != nil { - return fmt.Errorf("get watchers: %w", err) + return nil, fmt.Errorf("get watchers: %w", err) } // Be aware that optimizing this correctly into the `GetWatchers` SQL // query is for most cases less performant than doing this. blockedDoerUserIDs, err := user_model.ListBlockedByUsersID(ctx, act.ActUserID) if err != nil { - return fmt.Errorf("user_model.ListBlockedByUsersID: %w", err) + return nil, fmt.Errorf("user_model.ListBlockedByUsersID: %w", err) } if len(blockedDoerUserIDs) > 0 { @@ -637,8 +641,9 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { // Add feed for actioner. act.UserID = act.ActUserID if _, err = e.Insert(act); err != nil { - return fmt.Errorf("insert new actioner: %w", err) + return nil, fmt.Errorf("insert new actioner: %w", err) } + out = append(out, *act) if repoChanged { act.loadRepo(ctx) @@ -646,7 +651,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { // check repo owner exist. if err := act.Repo.LoadOwner(ctx); err != nil { - return fmt.Errorf("can't get repo owner: %w", err) + return nil, fmt.Errorf("can't get repo owner: %w", err) } } else if act.Repo == nil { act.Repo = repo @@ -657,7 +662,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { act.ID = 0 act.UserID = act.Repo.Owner.ID if err = db.Insert(ctx, act); err != nil { - return fmt.Errorf("insert new actioner: %w", err) + return nil, fmt.Errorf("insert new actioner: %w", err) } } @@ -710,26 +715,29 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { } if err = db.Insert(ctx, act); err != nil { - return fmt.Errorf("insert new action: %w", err) + return nil, fmt.Errorf("insert new action: %w", err) } } } - return nil + return out, nil } // NotifyWatchersActions creates batch of actions for every watcher. -func NotifyWatchersActions(ctx context.Context, acts []*Action) error { +func NotifyWatchersActions(ctx context.Context, acts []*Action) ([]Action, error) { ctx, committer, err := db.TxContext(ctx) if err != nil { - return err + return nil, err } defer committer.Close() + var out []Action for _, act := range acts { - if err := NotifyWatchers(ctx, act); err != nil { - return err + as, err := NotifyWatchers(ctx, act) + if err != nil { + return nil, err } + out = append(out, as...) } - return committer.Commit() + return out, committer.Commit() } // DeleteIssueActions delete all actions related with issueID diff --git a/models/activities/action_test.go b/models/activities/action_test.go index 8b9b2f6929..47dbd8ac2d 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -197,7 +197,8 @@ func TestNotifyWatchers(t *testing.T) { RepoID: 1, OpType: activities_model.ActionStarRepo, } - require.NoError(t, activities_model.NotifyWatchers(db.DefaultContext, action)) + _, err := activities_model.NotifyWatchers(db.DefaultContext, action) + require.NoError(t, err) // One watchers are inactive, thus action is only created for user 8, 1, 4, 11 unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ @@ -226,24 +227,6 @@ func TestNotifyWatchers(t *testing.T) { }) } -func TestGetFeedsCorrupted(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ - ID: 8, - RepoID: 1700, - }) - - actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ - RequestedUser: user, - Actor: user, - IncludePrivate: true, - }) - require.NoError(t, err) - assert.Empty(t, actions) - assert.Equal(t, int64(0), count) -} - func TestConsistencyUpdateAction(t *testing.T) { if !setting.Database.Type.IsSQLite3() { t.Skip("Test is only for SQLite database.") diff --git a/models/activities/federated_user_activity.go b/models/activities/federated_user_activity.go new file mode 100644 index 0000000000..1ff3a855d0 --- /dev/null +++ b/models/activities/federated_user_activity.go @@ -0,0 +1,106 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package activities + +import ( + "context" + "fmt" + + "forgejo.org/models/db" + user_model "forgejo.org/models/user" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/validation" + + ap "github.com/go-ap/activitypub" +) + +type FederatedUserActivity struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"NOT NULL"` + ActorID int64 + ActorURI string + Actor *user_model.User `xorm:"-"` // transient + NoteContent string `xorm:"TEXT"` + NoteURL string `xorm:"VARCHAR(255)"` + OriginalNote string `xorm:"TEXT"` + Created timeutil.TimeStamp `xorm:"created"` +} + +func init() { + db.RegisterModel(new(FederatedUserActivity)) +} + +func NewFederatedUserActivity(userID, actorID int64, actorURI, noteContent, noteURL string, originalNote ap.Activity) (FederatedUserActivity, error) { + jsonString, err := json.Marshal(originalNote) + if err != nil { + return FederatedUserActivity{}, err + } + result := FederatedUserActivity{ + UserID: userID, + ActorID: actorID, + ActorURI: actorURI, + NoteContent: noteContent, + NoteURL: noteURL, + OriginalNote: string(jsonString), + } + if valid, err := validation.IsValid(result); !valid { + return FederatedUserActivity{}, err + } + return result, nil +} + +func (federatedUserActivity FederatedUserActivity) Validate() []string { + var result []string + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.UserID, "UserID")...) + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.ActorID, "ActorID")...) + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.ActorURI, "ActorURI")...) + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.NoteContent, "NoteContent")...) + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.NoteURL, "NoteURL")...) + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.OriginalNote, "OriginalNote")...) + return result +} + +func CreateUserActivity(ctx context.Context, federatedUserActivity *FederatedUserActivity) error { + if valid, err := validation.IsValid(federatedUserActivity); !valid { + return err + } + _, err := db.GetEngine(ctx).Insert(federatedUserActivity) + return err +} + +type GetFollowingFeedsOptions struct { + db.ListOptions +} + +func GetFollowingFeeds(ctx context.Context, actorID int64, opts GetFollowingFeedsOptions) ([]*FederatedUserActivity, int64, error) { + log.Debug("user_id = %s", actorID) + sess := db.GetEngine(ctx).Where("user_id = ?", actorID) + opts.SetDefaultValues() + sess = db.SetSessionPagination(sess, &opts) + + actions := make([]*FederatedUserActivity, 0, opts.PageSize) + count, err := sess.FindAndCount(&actions) + if err != nil { + return nil, 0, fmt.Errorf("FindAndCount: %w", err) + } + for _, act := range actions { + if err := act.loadActor(ctx); err != nil { + return nil, 0, err + } + } + return actions, count, err +} + +func (federatedUserActivity *FederatedUserActivity) loadActor(ctx context.Context) error { + log.Debug("for activity %s", federatedUserActivity) + actorUser, _, err := user_model.GetFederatedUserByUserID(ctx, federatedUserActivity.ActorID) + if err != nil { + return err + } + federatedUserActivity.Actor = actorUser + + return nil +} diff --git a/models/activities/federated_user_activity_test.go b/models/activities/federated_user_activity_test.go new file mode 100644 index 0000000000..9bf4f77984 --- /dev/null +++ b/models/activities/federated_user_activity_test.go @@ -0,0 +1,24 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package activities + +import ( + "testing" + + "forgejo.org/modules/validation" +) + +func Test_FederatedUserActivityValidation(t *testing.T) { + sut := FederatedUserActivity{} + sut.UserID = 13 + sut.ActorID = 33 + sut.ActorURI = "33" + sut.NoteContent = "Any content!" + sut.NoteURL = "https://example.org/note/17" + sut.OriginalNote = "federatedUserActivityNote-17" + + if res, _ := validation.IsValid(sut); !res { + t.Errorf("sut expected to be valid: %v\n", sut.Validate()) + } +} diff --git a/models/fixtures/action.yml b/models/fixtures/action.yml index f1592d4569..a97e94fbf4 100644 --- a/models/fixtures/action.yml +++ b/models/fixtures/action.yml @@ -59,14 +59,6 @@ created_unix: 1603011540 # grouped with id:7 - id: 8 - user_id: 1 - op_type: 12 # close issue - act_user_id: 1 - repo_id: 1700 # dangling intentional - is_private: false - created_unix: 1603011541 - -- id: 9 user_id: 34 op_type: 12 # close issue act_user_id: 34 diff --git a/models/fixtures/comment.yml b/models/fixtures/comment.yml index f4121284a6..34407d6f81 100644 --- a/models/fixtures/comment.yml +++ b/models/fixtures/comment.yml @@ -113,3 +113,344 @@ review_id: 22 assignee_id: 5 created_unix: 946684817 + +- + id: 13 + type: 29 # push + poster_id: 2 + issue_id: 19 # in repo_id 58 + content: '{"is_force_push":false,"commit_ids":["4ca8bcaf27e28504df7bf996819665986b01c847","96cef4a7b72b3c208340ae6f0cf55a93e9077c93","c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2"]}' + created_unix: 1688672373 + +- + id: 14 + type: 29 # push + poster_id: 2 + issue_id: 19 # in repo_id 58 + content: '{"is_force_push":false,"commit_ids":["23576dd018294e476c06e569b6b0f170d0558705"]}' + created_unix: 1688672374 + +- + id: 15 + type: 29 # push + poster_id: 2 + issue_id: 19 # in repo_id 58 + content: '{"is_force_push":false,"commit_ids":["3e64625bd6eb5bcba69ac97de6c8f507402df861", "c704db5794097441aa2d9dd834d5b7e2f8f08108"]}' + created_unix: 1688672375 + +- + id: 16 + type: 29 # push + poster_id: 2 + issue_id: 19 # in repo_id 58 + content: '{"is_force_push":false,"commit_ids":["811d46c7e518f4f180afb862c0db5cb8c80529ce", "747ddb3506a4fa04a7747808eb56ae16f9e933dc", "837d5c8125633d7d258f93b998e867eab0145520", "1978192d98bb1b65e11c2cf37da854fbf94bffd6"]}' + created_unix: 1688672376 + +- + id: 17 + type: 29 # push + poster_id: 2 + issue_id: 19 # in repo_id 58 + content: '{"is_force_push":true,"commit_ids":["1978192d98bb1b65e11c2cf37da854fbf94bffd6", "9b93963cf6de4dc33f915bb67f192d099c301f43"]}' + created_unix: 1749734240 + +- + id: 2000 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 1 + old_milestone_id: 0 + created_unix: 946684820 + +- + id: 2001 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 2 + old_milestone_id: 1 + created_unix: 946684920 + +- + id: 2002 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 0 + old_milestone_id: 2 + created_unix: 946685020 + +- + id: 2003 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 10 # not exsting milestone + old_milestone_id: 0 + created_unix: 946685080 + +- + id: 2010 + type: 30 # project + poster_id: 1 + issue_id: 1 # in repo_id 1 + project_id: 1 + old_project_id: 0 + created_unix: 946685120 + +- + id: 2011 + type: 30 # project + poster_id: 1 + issue_id: 1 # in repo_id 1 + project_id: 2 + old_project_id: 1 + created_unix: 946685220 + +- + id: 2012 + type: 30 # project + poster_id: 1 + issue_id: 1 # in repo_id 1 + project_id: 0 + old_project_id: 2 + created_unix: 946685320 + +- + id: 2013 + type: 30 # project + poster_id: 1 + issue_id: 1 # in repo_id 1 + project_id: 10 # not existing project + old_project_id: 0 + created_unix: 946685420 + +- + id: 2020 + type: 7 # label + poster_id: 1 + issue_id: 1 # in repo_id 1 + label_id: 1 + content: 1 # add label + created_unix: 946685520 + +- + id: 2021 + type: 7 # label + poster_id: 1 + issue_id: 1 + label_id: 2 + content: 1 # add label + created_unix: 946685620 + +- + id: 2022 + type: 7 # label + poster_id: 2 + issue_id: 1 # in repo_id 1 + label_id: 1 + content: 0 # remove label + created_unix: 946685720 + +- + id: 2023 + type: 7 # label + poster_id: 1 + issue_id: 1 # in repo_id 1 + label_id: 1 + content: 1 # add label + created_unix: 946685720 + +- + id: 2024 + type: 7 # label + poster_id: 1 + issue_id: 1 # in repo_id 1 + label_id: 2 + content: 0 # remove label + created_unix: 946685720 + +- + id: 2025 + type: 7 # label + poster_id: 2 + issue_id: 1 # in repo_id 1 + label_id: 2 + content: 1 # add label + created_unix: 946685820 + +- + id: 2026 + type: 7 # label + poster_id: 1 + issue_id: 1 # in repo_id 1 + label_id: 1 + content: 0 # remove label + created_unix: 946685920 + +- + id: 2027 + type: 7 # label + poster_id: 1 + issue_id: 1 # in repo_id 1 + label_id: 2 + content: 0 # remove label + created_unix: 946685920 + +- id: 2040 + type: 9 # assignee + poster_id: 1 + issue_id: 1 # in repo_id 1 + assignee_id: 1 # self + removed_assignee: false # add assignee + created_unix: 946688020 + +- id: 2041 + type: 9 # assignee + poster_id: 2 + issue_id: 1 # in repo_id 1 + assignee_id: 1 + removed_assignee: true # remove assignee + created_unix: 946688120 + +- id: 2042 + type: 9 # assignee + poster_id: 1 + issue_id: 1 # in repo_id 1 + assignee_id: 2 + removed_assignee: false # add assignee + created_unix: 946688220 + +- id: 2043 + type: 9 # assignee + poster_id: 2 + issue_id: 1 # in repo_id 1 + assignee_id: 2 # self + removed_assignee: true # remove assignee + created_unix: 946688320 + +- id: 2050 + type: 23 # lock + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946688420 + +- id: 2051 + type: 24 # unlock + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946688520 + +- id: 2052 + type: 23 # lock + poster_id: 1 + issue_id: 1 # in repo_id 1 + content: "Too heated" + created_unix: 946688620 + +- id: 2053 + type: 24 # unlock + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946688720 + +- id: 2060 + type: 36 # pin + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946688820 + +- id: 2061 + type: 37 # unpin + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946688920 + +- id: 2070 + type: 2 # close + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946689020 + +- id: 2071 + type: 1 # reopen + poster_id: 2 + issue_id: 1 # in repo_id 1 + created_unix: 946689220 + +- id: 2072 + type: 2 # close + poster_id: 1 + issue_id: 2 # pull in repo_id 1 + created_unix: 946689320 + +- id: 2073 + type: 1 # reopen + poster_id: 2 + issue_id: 2 # pull in repo_id 1 + created_unix: 946689420 + +- id: 2080 + type: 3 # issue reference + poster_id: 1 + issue_id: 1 # issue in repo_id 1 + ref_repo_id: 1 + ref_issue_id: 5 # issue in repo_id 1 + created_unix: 946689500 + +- id: 2081 + type: 3 # issue reference + poster_id: 1 + issue_id: 1 # issue in repo_id 1 + ref_repo_id: 1 + ref_issue_id: 2 # pull in repo_id 1 + created_unix: 946689600 + +- id: 2082 + type: 3 # issue reference + poster_id: 1 + issue_id: 1 # issue in repo_id 1 + ref_repo_id: 32 + ref_issue_id: 16 # issue in repo_id 32 + created_unix: 946689700 + +- id: 2083 + type: 3 # issue reference + poster_id: 1 + issue_id: 1 # issue in repo_id 1 + ref_repo_id: 10 + ref_issue_id: 8 # pull in repo_id 10 + created_unix: 946689800 + +- id: 2090 + type: 6 # pull reference + poster_id: 1 + issue_id: 2 # pull in repo_id 1 + ref_repo_id: 1 + ref_issue_id: 1 # issue in repo_id 1 + created_unix: 946689900 + +- id: 2091 + type: 6 # pull reference + poster_id: 1 + issue_id: 2 # pull in repo_id 1 + ref_repo_id: 1 + ref_issue_id: 2 # pull in repo_id 1 + created_unix: 946690000 + +- id: 2092 + type: 6 # pull reference + poster_id: 1 + issue_id: 2 # pull in repo_id 1 + ref_repo_id: 32 + ref_issue_id: 16 # issue in repo_id 32 + created_unix: 946690050 + +- id: 2093 + type: 6 # pull reference + poster_id: 1 + issue_id: 2 # pull in repo_id 1 + ref_repo_id: 10 + ref_issue_id: 8 # pull in repo_id 10 + created_unix: 946690100 diff --git a/models/fixtures/pull_auto_merge.yml b/models/fixtures/pull_auto_merge.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/pull_auto_merge.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/forgefed/federationhost.go b/models/forgefed/federationhost.go index 29f1b7d28e..978847bd95 100644 --- a/models/forgefed/federationhost.go +++ b/models/forgefed/federationhost.go @@ -6,6 +6,7 @@ package forgefed import ( "database/sql" "fmt" + "net/url" "strings" "time" @@ -17,9 +18,9 @@ import ( // swagger:model type FederationHost struct { ID int64 `xorm:"pk autoincr"` - HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"` + HostFqdn string `xorm:"host_fqdn UNIQUE(federation_host) INDEX VARCHAR(255) NOT NULL"` + HostPort uint16 `xorm:" UNIQUE(federation_host) INDEX NOT NULL DEFAULT 443"` NodeInfo NodeInfo `xorm:"extends NOT NULL"` - HostPort uint16 `xorm:"NOT NULL DEFAULT 443"` HostSchema string `xorm:"NOT NULL DEFAULT 'https'"` LatestActivity time.Time `xorm:"NOT NULL"` KeyID sql.NullString `xorm:"key_id UNIQUE"` @@ -42,6 +43,13 @@ func NewFederationHost(hostFqdn string, nodeInfo NodeInfo, port uint16, schema s return result, nil } +func (host FederationHost) AsURL() url.URL { + return url.URL{ + Scheme: host.HostSchema, + Host: fmt.Sprintf("%v:%v", host.HostFqdn, host.HostPort), + } +} + // Validate collects error strings in a slice and returns this func (host FederationHost) Validate() []string { var result []string diff --git a/models/forgefed/nodeinfo.go b/models/forgefed/nodeinfo.go index 2461b5e499..38f51304c5 100644 --- a/models/forgefed/nodeinfo.go +++ b/models/forgefed/nodeinfo.go @@ -17,12 +17,14 @@ type ( ) const ( - ForgejoSourceType SoftwareNameType = "forgejo" - GiteaSourceType SoftwareNameType = "gitea" + ForgejoSourceType SoftwareNameType = "forgejo" + GiteaSourceType SoftwareNameType = "gitea" + MastodonSourceType SoftwareNameType = "mastodon" + GoToSocialSourceType SoftwareNameType = "gotosocial" ) var KnownSourceTypes = []any{ - ForgejoSourceType, GiteaSourceType, + ForgejoSourceType, GiteaSourceType, MastodonSourceType, GoToSocialSourceType, } // ------------------------------------------------ NodeInfoWellKnown ------------------------------------------------ diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 21a2077d06..737350b019 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -103,6 +103,12 @@ var migrations = []*Migration{ NewMigration("Normalize repository.topics to empty slice instead of null", SetTopicsAsEmptySlice), // v31 -> v32 NewMigration("Migrate maven package name concatenation", ChangeMavenArtifactConcatenation), + // v32 -> v33 + NewMigration("Add federated user activity tables, update the `federated_user` table & add indexes", FederatedUserActivityMigration), + // v33 -> v34 + NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun), + // v34 -> v35 + NewMigration("Add index to `stopped` column in `action_run` table", AddIndexToActionRunStopped), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v33.go b/models/forgejo_migrations/v33.go new file mode 100644 index 0000000000..272035fc23 --- /dev/null +++ b/models/forgejo_migrations/v33.go @@ -0,0 +1,126 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package forgejo_migrations //nolint:revive + +import ( + "fmt" + + "forgejo.org/modules/log" + "forgejo.org/modules/timeutil" + + "xorm.io/xorm" +) + +func dropOldFederationHostIndexes(x *xorm.Engine) { + // drop unique index on HostFqdn + type FederationHost struct { + ID int64 `xorm:"pk autoincr"` + HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"` + } + + err := x.DropIndexes(FederationHost{}) + if err != nil { + log.Warn("migration[33]: There was an issue: %v", err) + return + } +} + +func addFederatedUserActivityTables(x *xorm.Engine) { + type FederatedUserActivity struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"NOT NULL INDEX user_id"` + ActorID int64 + ActorURI string + NoteContent string `xorm:"TEXT"` + NoteURL string `xorm:"VARCHAR(255)"` + OriginalNote string `xorm:"TEXT"` + Created timeutil.TimeStamp `xorm:"created"` + } + + // add unique index on HostFqdn+HostPort + type FederationHost struct { + ID int64 `xorm:"pk autoincr"` + HostFqdn string `xorm:"host_fqdn UNIQUE(federation_host) INDEX VARCHAR(255) NOT NULL"` + HostPort uint16 `xorm:"UNIQUE(federation_host) INDEX NOT NULL DEFAULT 443"` + } + + type FederatedUserFollower struct { + ID int64 `xorm:"pk autoincr"` + + FollowedUserID int64 `xorm:"NOT NULL unique(fuf_rel)"` + FollowingUserID int64 `xorm:"NOT NULL unique(fuf_rel)"` + } + + // Add InboxPath to FederatedUser & add index fo UserID + type FederatedUser struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"NOT NULL INDEX user_id"` + InboxPath string + } + + var err error + + err = x.Sync(&FederationHost{}) + if err != nil { + log.Warn("migration[33]: There was an issue: %v", err) + return + } + + err = x.Sync(&FederatedUserActivity{}) + if err != nil { + log.Warn("migration[33]: There was an issue: %v", err) + return + } + + err = x.Sync(&FederatedUserFollower{}) + if err != nil { + log.Warn("migration[33]: There was an issue: %v", err) + return + } + + err = x.Sync(&FederatedUser{}) + if err != nil { + log.Warn("migration[33]: There was an issue: %v", err) + return + } + + // Migrate + sessMigration := x.NewSession() + defer sessMigration.Close() + if err := sessMigration.Begin(); err != nil { + log.Warn("migration[33]: There was an issue: %v", err) + return + } + federatedUsers := make([]*FederatedUser, 0) + err = sessMigration.OrderBy("id").Find(&federatedUsers) + if err != nil { + log.Warn("migration[33]: There was an issue: %v", err) + return + } + + for _, federatedUser := range federatedUsers { + if federatedUser.InboxPath != "" { + log.Info("migration[33]: This user was already migrated: %v", federatedUser) + } else { + // Migrate User.InboxPath + sql := "UPDATE `federated_user` SET `inbox_path` = ? WHERE `id` = ?" + if _, err := sessMigration.Exec(sql, fmt.Sprintf("/api/v1/activitypub/user-id/%v/inbox", federatedUser.UserID), federatedUser.ID); err != nil { + log.Warn("migration[33]: There was an issue: %v", err) + return + } + } + } + + err = sessMigration.Commit() + if err != nil { + log.Warn("migration[33]: There was an issue: %v", err) + return + } +} + +func FederatedUserActivityMigration(x *xorm.Engine) error { + dropOldFederationHostIndexes(x) + addFederatedUserActivityTables(x) + return nil +} diff --git a/models/forgejo_migrations/v33_test.go b/models/forgejo_migrations/v33_test.go new file mode 100644 index 0000000000..664c704bbc --- /dev/null +++ b/models/forgejo_migrations/v33_test.go @@ -0,0 +1,46 @@ +// Copyright 2025 The Forgejo Authors. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import ( + "testing" + "time" + + migration_tests "forgejo.org/models/migrations/test" + "forgejo.org/modules/log" + ft "forgejo.org/modules/test" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_FederatedUserActivityMigration(t *testing.T) { + lc, cl := ft.NewLogChecker(log.DEFAULT, log.WARN) + lc.Filter("migration[33]") + defer cl() + + // intentionally conflicting definition + type FederatedUser struct { + ID int64 `xorm:"pk autoincr"` + UserID string + } + + // Prepare TestEnv + x, deferable := migration_tests.PrepareTestEnv(t, 0, + new(FederatedUser), + ) + sessTest := x.NewSession() + sessTest.Insert(FederatedUser{UserID: "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + + "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"}) + sessTest.Commit() + defer deferable() + if x == nil || t.Failed() { + return + } + + require.NoError(t, FederatedUserActivityMigration(x)) + logFiltered, _ := lc.Check(5 * time.Second) + assert.NotEmpty(t, logFiltered) +} diff --git a/models/forgejo_migrations/v34.go b/models/forgejo_migrations/v34.go new file mode 100644 index 0000000000..9e958b934f --- /dev/null +++ b/models/forgejo_migrations/v34.go @@ -0,0 +1,14 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import "xorm.io/xorm" + +func AddNotifyEmailToActionRun(x *xorm.Engine) error { + type ActionRun struct { + ID int64 + NotifyEmail bool + } + return x.Sync(new(ActionRun)) +} diff --git a/models/forgejo_migrations/v35.go b/models/forgejo_migrations/v35.go new file mode 100644 index 0000000000..0fb3b43e2c --- /dev/null +++ b/models/forgejo_migrations/v35.go @@ -0,0 +1,19 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import ( + "forgejo.org/modules/timeutil" + + "xorm.io/xorm" +) + +func AddIndexToActionRunStopped(x *xorm.Engine) error { + type ActionRun struct { + ID int64 + Stopped timeutil.TimeStamp `xorm:"index"` + } + + return x.Sync(&ActionRun{}) +} diff --git a/models/git/commit_status.go b/models/git/commit_status.go index a679703ffd..60a0aa5a4f 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -179,25 +179,6 @@ func (status *CommitStatus) LocaleString(lang translation.Locale) string { return lang.TrString("repo.commitstatus." + status.State.String()) } -// HideActionsURL set `TargetURL` to an empty string if the status comes from Gitea Actions -func (status *CommitStatus) HideActionsURL(ctx context.Context) { - if status.RepoID == 0 { - return - } - - if status.Repo == nil { - if err := status.loadRepository(ctx); err != nil { - log.Error("loadRepository: %v", err) - return - } - } - - prefix := fmt.Sprintf("%s/actions", status.Repo.Link()) - if strings.HasPrefix(status.TargetURL, prefix) { - status.TargetURL = "" - } -} - // CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus { if len(statuses) == 0 { @@ -453,11 +434,19 @@ type SignCommitWithStatuses struct { *asymkey_model.SignCommit } -// ParseCommitsWithStatus checks commits latest statuses and calculates its worst status state -func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.SignCommit, repo *repo_model.Repository) []*SignCommitWithStatuses { - newCommits := make([]*SignCommitWithStatuses, 0, len(oldCommits)) +// ParseCommitsWithStatus converts git commits into SignCommitWithStatuses (checks signature and calculates its worst status state) +func ParseCommitsWithStatus(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses { + commitsWithSignature := asymkey_model.ParseCommitsWithSignature( + ctx, + user_model.ValidateCommitsWithEmails(ctx, commits), + repo.GetTrustModel(), + func(user *user_model.User) (bool, error) { + return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID) + }, + ) - for _, c := range oldCommits { + commitsWithStatus := make([]*SignCommitWithStatuses, 0, len(commitsWithSignature)) + for _, c := range commitsWithSignature { commit := &SignCommitWithStatuses{ SignCommit: c, } @@ -469,43 +458,12 @@ func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.Sig commit.Status = CalcCommitStatus(statuses) } - newCommits = append(newCommits, commit) + commitsWithStatus = append(commitsWithStatus, commit) } - return newCommits + return commitsWithStatus } // hashCommitStatusContext hash context func hashCommitStatusContext(context string) string { return fmt.Sprintf("%x", sha1.Sum([]byte(context))) } - -// ConvertFromGitCommit converts git commits into SignCommitWithStatuses -func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses { - return ParseCommitsWithStatus(ctx, - asymkey_model.ParseCommitsWithSignature( - ctx, - user_model.ValidateCommitsWithEmails(ctx, commits), - repo.GetTrustModel(), - func(user *user_model.User) (bool, error) { - return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID) - }, - ), - repo, - ) -} - -// CommitStatusesHideActionsURL hide Gitea Actions urls -func CommitStatusesHideActionsURL(ctx context.Context, statuses []*CommitStatus) { - idToRepos := make(map[int64]*repo_model.Repository) - for _, status := range statuses { - if status == nil { - continue - } - - if status.Repo == nil { - status.Repo = idToRepos[status.RepoID] - } - status.HideActionsURL(ctx) - idToRepos[status.RepoID] = status.Repo - } -} diff --git a/models/git/commit_status_test.go b/models/git/commit_status_test.go index c062bbbbb9..ce6c0d4673 100644 --- a/models/git/commit_status_test.go +++ b/models/git/commit_status_test.go @@ -4,11 +4,9 @@ package git_test import ( - "fmt" "testing" "time" - actions_model "forgejo.org/models/actions" "forgejo.org/models/db" git_model "forgejo.org/models/git" repo_model "forgejo.org/models/repo" @@ -246,26 +244,3 @@ func TestFindRepoRecentCommitStatusContexts(t *testing.T) { assert.Equal(t, "compliance/lint-backend", contexts[0]) } } - -func TestCommitStatusesHideActionsURL(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) - run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 791, RepoID: repo.ID}) - require.NoError(t, run.LoadAttributes(db.DefaultContext)) - - statuses := []*git_model.CommitStatus{ - { - RepoID: repo.ID, - TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), run.Index), - }, - { - RepoID: repo.ID, - TargetURL: "https://mycicd.org/1", - }, - } - - git_model.CommitStatusesHideActionsURL(db.DefaultContext, statuses) - assert.Empty(t, statuses[0].TargetURL) - assert.Equal(t, "https://mycicd.org/1", statuses[1].TargetURL) -} diff --git a/models/issues/comment.go b/models/issues/comment.go index c44f65e29c..a81221caf4 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -802,7 +802,7 @@ func (c *Comment) LoadPushCommits(ctx context.Context) (err error) { } defer closer.Close() - c.Commits = git_model.ConvertFromGitCommit(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo) + c.Commits = git_model.ParseCommitsWithStatus(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo) c.CommitsNum = int64(len(c.Commits)) } diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index bf4b89ee0b..91a69c26a7 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -66,6 +66,8 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) { sess.Asc("issue.created_unix").Asc("issue.id") case "recentupdate": sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id") + case "recentclose": + sess.Desc("issue.closed_unix").Desc("issue.created_unix").Desc("issue.id") case "leastupdate": sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id") case "mostcomment": diff --git a/models/issues/milestone.go b/models/issues/milestone.go index 52433e735d..67a23246cf 100644 --- a/models/issues/milestone.go +++ b/models/issues/milestone.go @@ -67,6 +67,13 @@ type Milestone struct { TotalTrackedTime int64 `xorm:"-"` } +// Ghost milestone is a milestone which has been deleted +const GhostMilestoneID = -1 + +func (m *Milestone) IsGhost() bool { + return m.ID == GhostMilestoneID +} + func init() { db.RegisterModel(new(Milestone)) } diff --git a/models/issues/pull.go b/models/issues/pull.go index 0781fd0a2d..188ef00814 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -5,6 +5,7 @@ package issues import ( + "bufio" "context" "errors" "fmt" @@ -923,31 +924,30 @@ func MergeBlockedByOutdatedBranch(protectBranch *git_model.ProtectedBranch, pr * return protectBranch.BlockOnOutdatedBranch && pr.CommitsBehind > 0 } -// GetCodeOwnersFromContent returns the code owners configuration -// Return empty slice if files missing +// GetCodeOwnersFromReader returns the code owners configuration // Return warning messages on parsing errors // We're trying to do the best we can when parsing a file. // Invalid lines are skipped. Non-existent users and teams too. -func GetCodeOwnersFromContent(ctx context.Context, data string) ([]*CodeOwnerRule, []string) { - if len(data) == 0 { - return nil, nil - } +func GetCodeOwnersFromReader(ctx context.Context, rc io.ReadCloser, truncated bool) ([]*CodeOwnerRule, []string) { + defer rc.Close() + scanner := bufio.NewScanner(rc) - rules := make([]*CodeOwnerRule, 0) - lines := strings.Split(data, "\n") - warnings := make([]string, 0) + var rules []*CodeOwnerRule + var warnings []string + line := 0 + for scanner.Scan() { + line++ - for i, line := range lines { - tokens := TokenizeCodeOwnersLine(line) + tokens := TokenizeCodeOwnersLine(scanner.Text()) if len(tokens) == 0 { continue } else if len(tokens) < 2 { - warnings = append(warnings, fmt.Sprintf("Line: %d: incorrect format", i+1)) + warnings = append(warnings, fmt.Sprintf("Line: %d: incorrect format", line)) continue } rule, wr := ParseCodeOwnersLine(ctx, tokens) for _, w := range wr { - warnings = append(warnings, fmt.Sprintf("Line: %d: %s", i+1, w)) + warnings = append(warnings, fmt.Sprintf("Line: %d: %s", line, w)) } if rule == nil { continue @@ -955,6 +955,12 @@ func GetCodeOwnersFromContent(ctx context.Context, data string) ([]*CodeOwnerRul rules = append(rules, rule) } + if err := scanner.Err(); err != nil { + warnings = append(warnings, err.Error()) + } + if truncated { + warnings = append(warnings, fmt.Sprintf("File too big: truncated while on line %d", line)) + } return rules, warnings } diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index a448673454..8fc0491026 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -152,7 +152,8 @@ func PullRequests(ctx context.Context, baseRepoID int64, opts *PullRequestsOptio applySorts(findSession, opts.SortType, 0) findSession = db.SetSessionPagination(findSession, opts) prs := make([]*PullRequest, 0, opts.PageSize) - return prs, maxResults, findSession.Find(&prs) + found := findSession.Find(&prs) + return prs, maxResults, found } // PullRequestList defines a list of pull requests diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go index 5a8e8d8aaf..3682f6fd25 100644 --- a/models/issues/pull_test.go +++ b/models/issues/pull_test.go @@ -79,6 +79,47 @@ func TestPullRequestsNewest(t *testing.T) { } } +func TestPullRequests_Closed_RecentSortType(t *testing.T) { + // Issue ID | Closed At. | Updated At + // 2 | 1707270001 | 1707270001 + // 3 | 1707271000 | 1707279999 + // 11 | 1707279999 | 1707275555 + tests := []struct { + sortType string + expectedIssueIDOrder []int64 + }{ + {"recentupdate", []int64{3, 11, 2}}, + {"recentclose", []int64{11, 3, 2}}, + } + + require.NoError(t, unittest.PrepareTestDatabase()) + _, err := db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707270001, updated_unix = 1707270001, is_closed = true WHERE id = 2") + require.NoError(t, err) + _, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707271000, updated_unix = 1707279999, is_closed = true WHERE id = 3") + require.NoError(t, err) + _, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707279999, updated_unix = 1707275555, is_closed = true WHERE id = 11") + require.NoError(t, err) + + for _, test := range tests { + t.Run(test.sortType, func(t *testing.T) { + prs, _, err := issues_model.PullRequests(db.DefaultContext, 1, &issues_model.PullRequestsOptions{ + ListOptions: db.ListOptions{ + Page: 1, + }, + State: "closed", + SortType: test.sortType, + }) + require.NoError(t, err) + + if assert.Len(t, prs, len(test.expectedIssueIDOrder)) { + for i := range test.expectedIssueIDOrder { + assert.Equal(t, test.expectedIssueIDOrder[i], prs[i].IssueID) + } + } + }) + } +} + func TestLoadRequestedReviewers(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) diff --git a/models/pull/automerge.go b/models/pull/automerge.go index 63f572309b..dcc1f39271 100644 --- a/models/pull/automerge.go +++ b/models/pull/automerge.go @@ -10,6 +10,7 @@ import ( "forgejo.org/models/db" repo_model "forgejo.org/models/repo" user_model "forgejo.org/models/user" + "forgejo.org/modules/log" "forgejo.org/modules/timeutil" ) @@ -58,13 +59,15 @@ func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, return ErrAlreadyScheduledToAutoMerge{PullID: pullID} } - _, err := db.GetEngine(ctx).Insert(&AutoMerge{ + scheduledPRM, err := db.GetEngine(ctx).Insert(&AutoMerge{ DoerID: doer.ID, PullID: pullID, MergeStyle: style, Message: message, DeleteBranchAfterMerge: deleteBranch, }) + log.Trace("ScheduleAutoMerge %+v for PR %d", scheduledPRM, pullID) + return err } @@ -81,6 +84,8 @@ func GetScheduledMergeByPullID(ctx context.Context, pullID int64) (bool, *AutoMe return false, nil, err } + log.Trace("GetScheduledMergeByPullID found %+v for PR %d", scheduledPRM, pullID) + scheduledPRM.Doer = doer return true, scheduledPRM, nil } @@ -94,6 +99,8 @@ func DeleteScheduledAutoMerge(ctx context.Context, pullID int64) error { return db.ErrNotExist{Resource: "auto_merge", ID: pullID} } + log.Trace("DeleteScheduledAutoMerge %+v for PR %d", scheduledPRM, pullID) + _, err = db.GetEngine(ctx).ID(scheduledPRM.ID).Delete(&AutoMerge{}) return err } diff --git a/models/user/federated_user.go b/models/user/federated_user.go index c1833c7de3..d2a9c34c9e 100644 --- a/models/user/federated_user.go +++ b/models/user/federated_user.go @@ -11,19 +11,21 @@ import ( type FederatedUser struct { ID int64 `xorm:"pk autoincr"` - UserID int64 `xorm:"NOT NULL"` + UserID int64 `xorm:"NOT NULL INDEX user_id"` ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` KeyID sql.NullString `xorm:"key_id UNIQUE"` PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"` - NormalizedOriginalURL string // This field is just to keep original information. Pls. do not use for search or as ID! + InboxPath string + NormalizedOriginalURL string // This field is just to keep original information. Pls. do not use for search or as ID! } -func NewFederatedUser(userID int64, externalID string, federationHostID int64, normalizedOriginalURL string) (FederatedUser, error) { +func NewFederatedUser(userID int64, externalID string, federationHostID int64, inboxPath, normalizedOriginalURL string) (FederatedUser, error) { result := FederatedUser{ UserID: userID, ExternalID: externalID, FederationHostID: federationHostID, + InboxPath: inboxPath, NormalizedOriginalURL: normalizedOriginalURL, } if valid, err := validation.IsValid(result); !valid { @@ -32,10 +34,11 @@ func NewFederatedUser(userID int64, externalID string, federationHostID int64, n return result, nil } -func (user FederatedUser) Validate() []string { +func (federatedUser FederatedUser) Validate() []string { var result []string - result = append(result, validation.ValidateNotEmpty(user.UserID, "UserID")...) - result = append(result, validation.ValidateNotEmpty(user.ExternalID, "ExternalID")...) - result = append(result, validation.ValidateNotEmpty(user.FederationHostID, "FederationHostID")...) + result = append(result, validation.ValidateNotEmpty(federatedUser.UserID, "UserID")...) + result = append(result, validation.ValidateNotEmpty(federatedUser.ExternalID, "ExternalID")...) + result = append(result, validation.ValidateNotEmpty(federatedUser.FederationHostID, "FederationHostID")...) + result = append(result, validation.ValidateNotEmpty(federatedUser.InboxPath, "InboxPath")...) return result } diff --git a/models/user/federated_user_follower.go b/models/user/federated_user_follower.go new file mode 100644 index 0000000000..db72c9b5ce --- /dev/null +++ b/models/user/federated_user_follower.go @@ -0,0 +1,30 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package user + +import "forgejo.org/modules/validation" + +type FederatedUserFollower struct { + ID int64 `xorm:"pk autoincr"` + FollowedUserID int64 `xorm:"NOT NULL unique(fuf_rel)"` + FollowingUserID int64 `xorm:"NOT NULL unique(fuf_rel)"` +} + +func NewFederatedUserFollower(followedUserID, federatedUserID int64) (FederatedUserFollower, error) { + result := FederatedUserFollower{ + FollowedUserID: followedUserID, + FollowingUserID: federatedUserID, + } + if valid, err := validation.IsValid(result); !valid { + return FederatedUserFollower{}, err + } + return result, nil +} + +func (user FederatedUserFollower) Validate() []string { + var result []string + result = append(result, validation.ValidateNotEmpty(user.FollowedUserID, "FollowedUserID")...) + result = append(result, validation.ValidateNotEmpty(user.FollowingUserID, "FollowingUserID")...) + return result +} diff --git a/models/user/federated_user_follower_test.go b/models/user/federated_user_follower_test.go new file mode 100644 index 0000000000..e57ba01308 --- /dev/null +++ b/models/user/federated_user_follower_test.go @@ -0,0 +1,27 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package user + +import ( + "testing" + + "forgejo.org/modules/validation" + + "github.com/stretchr/testify/assert" +) + +func Test_FederatedUserFollowerValidation(t *testing.T) { + sut := FederatedUserFollower{ + FollowedUserID: 12, + FollowingUserID: 1, + } + res, err := validation.IsValid(sut) + assert.Truef(t, res, "sut should be valid but was %q", err) + + sut = FederatedUserFollower{ + FollowedUserID: 1, + } + res, _ = validation.IsValid(sut) + assert.False(t, res, "sut should be invalid") +} diff --git a/models/user/federated_user_test.go b/models/user/federated_user_test.go index 542798c9bc..be18339670 100644 --- a/models/user/federated_user_test.go +++ b/models/user/federated_user_test.go @@ -14,6 +14,7 @@ func Test_FederatedUserValidation(t *testing.T) { UserID: 12, ExternalID: "12", FederationHostID: 1, + InboxPath: "/api/v1/activitypub/user-id/12/inbox", } if res, err := validation.IsValid(sut); !res { t.Errorf("sut should be valid but was %q", err) @@ -22,6 +23,7 @@ func Test_FederatedUserValidation(t *testing.T) { sut = FederatedUser{ ExternalID: "12", FederationHostID: 1, + InboxPath: "/api/v1/activitypub/user-id/12/inbox", } if res, _ := validation.IsValid(sut); res { t.Error("sut should be invalid") diff --git a/models/user/follow.go b/models/user/follow.go index 5be0f73c35..e32c226385 100644 --- a/models/user/follow.go +++ b/models/user/follow.go @@ -11,6 +11,7 @@ import ( ) // Follow represents relations of user and their followers. +// TODO: We should unify Activity-pub-following and classical following (see models/user/user_repository.go#IsFollowingAp) type Follow struct { ID int64 `xorm:"pk autoincr"` UserID int64 `xorm:"UNIQUE(follow)"` diff --git a/models/user/user_repository.go b/models/user/user_repository.go index 299d3af64a..3f24efb1fb 100644 --- a/models/user/user_repository.go +++ b/models/user/user_repository.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. +// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package user @@ -8,12 +8,14 @@ import ( "fmt" "forgejo.org/models/db" + "forgejo.org/modules/log" "forgejo.org/modules/optional" "forgejo.org/modules/validation" ) func init() { db.RegisterModel(new(FederatedUser)) + db.RegisterModel(new(FederatedUserFollower)) } func CreateFederatedUser(ctx context.Context, user *User, federatedUser *FederatedUser) error { @@ -30,7 +32,12 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat if err != nil { return err } - defer committer.Close() + defer func() { + err := committer.Close() + if err != nil { + log.Error("Error closing committer: %v", err) + } + }() if err := CreateUser(ctx, user, &overwrite); err != nil { return err @@ -50,6 +57,14 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat return committer.Commit() } +func (federatedUser *FederatedUser) UpdateFederatedUser(ctx context.Context) error { + if _, err := validation.IsValid(federatedUser); err != nil { + return err + } + _, err := db.GetEngine(ctx).ID(federatedUser.ID).Cols("inbox_path").Update(federatedUser) + return err +} + func FindFederatedUser(ctx context.Context, externalID string, federationHostID int64) (*User, *FederatedUser, error) { federatedUser := new(FederatedUser) user := new(User) @@ -75,6 +90,41 @@ func FindFederatedUser(ctx context.Context, externalID string, federationHostID return user, federatedUser, nil } +func GetFederatedUser(ctx context.Context, externalID string, federationHostID int64) (*User, *FederatedUser, error) { + user, federatedUser, err := FindFederatedUser(ctx, externalID, federationHostID) + if err != nil { + return nil, nil, err + } else if federatedUser == nil { + return nil, nil, fmt.Errorf("FederatedUser for externalId = %v and federationHostId = %v does not exist", externalID, federationHostID) + } + return user, federatedUser, nil +} + +func GetFederatedUserByUserID(ctx context.Context, userID int64) (*User, *FederatedUser, error) { + federatedUser := new(FederatedUser) + user := new(User) + has, err := db.GetEngine(ctx).Where("user_id=?", userID).Get(federatedUser) + if err != nil { + return nil, nil, err + } else if !has { + return nil, nil, fmt.Errorf("Federated user %v does not exist", federatedUser.UserID) + } + has, err = db.GetEngine(ctx).ID(federatedUser.UserID).Get(user) + if err != nil { + return nil, nil, err + } else if !has { + return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) + } + + if res, err := validation.IsValid(*user); !res { + return nil, nil, err + } + if res, err := validation.IsValid(*federatedUser); !res { + return nil, nil, err + } + return user, federatedUser, nil +} + func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *FederatedUser, error) { federatedUser := new(FederatedUser) user := new(User) @@ -101,7 +151,85 @@ func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *Federa return user, federatedUser, nil } +func UpdateFederatedUser(ctx context.Context, federatedUser *FederatedUser) error { + if res, err := validation.IsValid(federatedUser); !res { + return err + } + _, err := db.GetEngine(ctx).ID(federatedUser.ID).Update(federatedUser) + return err +} + func DeleteFederatedUser(ctx context.Context, userID int64) error { _, err := db.GetEngine(ctx).Delete(&FederatedUser{UserID: userID}) return err } + +func GetFollowersForUser(ctx context.Context, user *User) ([]*FederatedUserFollower, error) { + if res, err := validation.IsValid(user); !res { + return nil, err + } + followers := make([]*FederatedUserFollower, 0, 8) + + err := db.GetEngine(ctx). + Where("followed_user_id = ?", user.ID). + Find(&followers) + if err != nil { + return nil, err + } + for _, element := range followers { + if res, err := validation.IsValid(*element); !res { + return nil, err + } + } + return followers, nil +} + +func AddFollower(ctx context.Context, followedUser *User, followingUser *FederatedUser) (*FederatedUserFollower, error) { + if res, err := validation.IsValid(followedUser); !res { + return nil, err + } + if res, err := validation.IsValid(followingUser); !res { + return nil, err + } + + federatedUserFollower, err := NewFederatedUserFollower(followedUser.ID, followingUser.UserID) + if err != nil { + return nil, err + } + _, err = db.GetEngine(ctx).Insert(&federatedUserFollower) + if err != nil { + return nil, err + } + + return &federatedUserFollower, err +} + +func RemoveFollower(ctx context.Context, followedUser *User, followingUser *FederatedUser) error { + if res, err := validation.IsValid(followedUser); !res { + return err + } + if res, err := validation.IsValid(followingUser); !res { + return err + } + + _, err := db.GetEngine(ctx).Delete(&FederatedUserFollower{ + FollowedUserID: followedUser.ID, + FollowingUserID: followingUser.UserID, + }) + return err +} + +// TODO: We should unify Activity-pub-following and classical following (see models/user/follow.go) +func IsFollowingAp(ctx context.Context, followedUser *User, followingUser *FederatedUser) (bool, error) { + if res, err := validation.IsValid(followedUser); !res { + return false, err + } + if res, err := validation.IsValid(followingUser); !res { + return false, err + } + + return db.GetEngine(ctx).Get(&FederatedUserFollower{ + FollowedUserID: followedUser.ID, + FollowingUserID: followingUser.UserID, + }) +} diff --git a/models/user/user_system.go b/models/user/user_system.go index 82805cc8ee..11f54591b7 100644 --- a/models/user/user_system.go +++ b/models/user/user_system.go @@ -12,6 +12,13 @@ import ( "forgejo.org/modules/structs" ) +// IsSystem returns true if the user has a fixed +// negative ID, is never stored in the database and +// is generated on the fly when needed. +func (u *User) IsSystem() bool { + return u.IsGhost() || u.IsActions() +} + const ( GhostUserID = -1 GhostUserName = "Ghost" diff --git a/models/user/user_test.go b/models/user/user_test.go index 2a9e652a35..fd9d05653f 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -148,7 +148,7 @@ func TestAPActorID_APActorID(t *testing.T) { assert.Equal(t, expected, url) } -func TestAPActorKeyID(t *testing.T) { +func TestKeyID(t *testing.T) { user := user_model.User{ID: 1} url := user.APActorKeyID() expected := "https://try.gitea.io/api/v1/activitypub/user-id/1#main-key" diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index daf453fb47..7ae4557ed6 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -323,6 +323,10 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa matchTimes++ } case "paths": + if refName.IsTag() { + matchTimes++ + break + } filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before) if err != nil { log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err) @@ -336,6 +340,10 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa } } case "paths-ignore": + if refName.IsTag() { + matchTimes++ + break + } filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before) if err != nil { log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err) diff --git a/modules/actions/workflows_test.go b/modules/actions/workflows_test.go index b85ed7fd56..9068ce31c3 100644 --- a/modules/actions/workflows_test.go +++ b/modules/actions/workflows_test.go @@ -150,6 +150,24 @@ func TestDetectMatched(t *testing.T) { yamlOn: "on: workflow_dispatch", expected: true, }, + { + desc: "push to tag matches workflow with paths condition (should skip paths check)", + triggeredEvent: webhook_module.HookEventPush, + payload: &api.PushPayload{ + Ref: "refs/tags/v1.0.0", + Before: "0000000", + Commits: []*api.PayloadCommit{ + { + ID: "abcdef123456", + Added: []string{"src/main.go"}, + Message: "Release v1.0.0", + }, + }, + }, + commit: nil, + yamlOn: "on:\n push:\n paths:\n - src/**", + expected: true, + }, } for _, tc := range testCases { diff --git a/modules/git/blob.go b/modules/git/blob.go index 3fda358938..30615afe32 100644 --- a/modules/git/blob.go +++ b/modules/git/blob.go @@ -12,7 +12,6 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/typesniffer" - "forgejo.org/modules/util" ) // Blob represents a Git object. @@ -25,42 +24,25 @@ type Blob struct { repo *Repository } -// DataAsync gets a ReadCloser for the contents of a blob without reading it all. -// Calling the Close function on the result will discard all unread output. -func (b *Blob) DataAsync() (io.ReadCloser, error) { +func (b *Blob) newReader() (*bufio.Reader, int64, func(), error) { wr, rd, cancel, err := b.repo.CatFileBatch(b.repo.Ctx) if err != nil { - return nil, err + return nil, 0, nil, err } _, err = wr.Write([]byte(b.ID.String() + "\n")) if err != nil { cancel() - return nil, err + return nil, 0, nil, err } _, _, size, err := ReadBatchLine(rd) if err != nil { cancel() - return nil, err + return nil, 0, nil, err } b.gotSize = true b.size = size - - if size < 4096 { - bs, err := io.ReadAll(io.LimitReader(rd, size)) - defer cancel() - if err != nil { - return nil, err - } - _, err = rd.Discard(1) - return io.NopCloser(bytes.NewReader(bs)), err - } - - return &blobReader{ - rd: rd, - n: size, - cancel: cancel, - }, nil + return rd, size, cancel, err } // Size returns the uncompressed size of the blob @@ -91,10 +73,36 @@ func (b *Blob) Size() int64 { return b.size } +// DataAsync gets a ReadCloser for the contents of a blob without reading it all. +// Calling the Close function on the result will discard all unread output. +func (b *Blob) DataAsync() (io.ReadCloser, error) { + rd, size, cancel, err := b.newReader() + if err != nil { + return nil, err + } + + if size < 4096 { + bs, err := io.ReadAll(io.LimitReader(rd, size)) + defer cancel() + if err != nil { + return nil, err + } + _, err = rd.Discard(1) + return io.NopCloser(bytes.NewReader(bs)), err + } + + return &blobReader{ + rd: rd, + n: size, + cancel: cancel, + }, nil +} + type blobReader struct { - rd *bufio.Reader - n int64 - cancel func() + rd *bufio.Reader + n int64 // number of bytes to read + additionalDiscard int64 // additional number of bytes to discard + cancel func() } func (b *blobReader) Read(p []byte) (n int, err error) { @@ -117,7 +125,8 @@ func (b *blobReader) Close() error { defer b.cancel() - if err := DiscardFull(b.rd, b.n+1); err != nil { + // discard the unread bytes, the truncated bytes and the trailing newline + if err := DiscardFull(b.rd, b.n+b.additionalDiscard+1); err != nil { return err } @@ -131,47 +140,38 @@ func (b *Blob) Name() string { return b.name } -// GetBlobContent Gets the limited content of the blob as raw text +// NewTruncatedReader return a blob-reader which silently truncates when the limit is reached (io.EOF will be returned) +func (b *Blob) NewTruncatedReader(limit int64) (rc io.ReadCloser, fullSize int64, err error) { + r, fullSize, cancel, err := b.newReader() + if err != nil { + return nil, fullSize, err + } + + limit = min(limit, fullSize) + return &blobReader{ + rd: r, + n: limit, + additionalDiscard: fullSize - limit, + cancel: cancel, + }, fullSize, nil +} + +// GetBlobContent Gets the truncated content of the blob as raw text func (b *Blob) GetBlobContent(limit int64) (string, error) { if limit <= 0 { return "", nil } - dataRc, err := b.DataAsync() + rc, fullSize, err := b.NewTruncatedReader(limit) if err != nil { return "", err } - defer dataRc.Close() - buf, err := util.ReadWithLimit(dataRc, int(limit)) + defer rc.Close() + + buf := make([]byte, min(fullSize, limit)) + _, err = io.ReadFull(rc, buf) return string(buf), err } -// GetBlobLineCount gets line count of the blob -func (b *Blob) GetBlobLineCount() (int, error) { - reader, err := b.DataAsync() - if err != nil { - return 0, err - } - defer reader.Close() - buf := make([]byte, 32*1024) - count := 1 - lineSep := []byte{'\n'} - - c, err := reader.Read(buf) - if c == 0 && err == io.EOF { - return 0, nil - } - for { - count += bytes.Count(buf[:c], lineSep) - switch { - case err == io.EOF: - return count, nil - case err != nil: - return count, err - } - c, err = reader.Read(buf) - } -} - // GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string func (b *Blob) GetBlobContentBase64() (string, error) { dataRc, err := b.DataAsync() diff --git a/modules/git/blob_test.go b/modules/git/blob_test.go index 810964b33d..54115013d3 100644 --- a/modules/git/blob_test.go +++ b/modules/git/blob_test.go @@ -35,6 +35,106 @@ func TestBlob_Data(t *testing.T) { assert.Equal(t, output, string(data)) } +func TestBlob(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") + repo, err := openRepositoryWithDefaultContext(bareRepo1Path) + require.NoError(t, err) + + defer repo.Close() + + testBlob, err := repo.GetBlob("6c493ff740f9380390d5c9ddef4af18697ac9375") + require.NoError(t, err) + + t.Run("GetBlobContent", func(t *testing.T) { + r, err := testBlob.GetBlobContent(100) + require.NoError(t, err) + require.Equal(t, "file2\n", r) + + r, err = testBlob.GetBlobContent(-1) + require.NoError(t, err) + require.Empty(t, r) + + r, err = testBlob.GetBlobContent(4) + require.NoError(t, err) + require.Equal(t, "file", r) + + r, err = testBlob.GetBlobContent(6) + require.NoError(t, err) + require.Equal(t, "file2\n", r) + }) + + t.Run("NewTruncatedReader", func(t *testing.T) { + // read fewer than available + rc, size, err := testBlob.NewTruncatedReader(100) + require.NoError(t, err) + require.Equal(t, int64(6), size) + + buf := make([]byte, 1) + n, err := rc.Read(buf) + require.NoError(t, err) + require.Equal(t, 1, n) + require.Equal(t, "f", string(buf)) + n, err = rc.Read(buf) + require.NoError(t, err) + require.Equal(t, 1, n) + require.Equal(t, "i", string(buf)) + + require.NoError(t, rc.Close()) + + // read more than available + rc, size, err = testBlob.NewTruncatedReader(100) + require.NoError(t, err) + require.Equal(t, int64(6), size) + + buf = make([]byte, 100) + n, err = rc.Read(buf) + require.NoError(t, err) + require.Equal(t, 6, n) + require.Equal(t, "file2\n", string(buf[:n])) + + n, err = rc.Read(buf) + require.Error(t, err) + require.Equal(t, io.EOF, err) + require.Equal(t, 0, n) + + require.NoError(t, rc.Close()) + + // read more than truncated + rc, size, err = testBlob.NewTruncatedReader(4) + require.NoError(t, err) + require.Equal(t, int64(6), size) + + buf = make([]byte, 10) + n, err = rc.Read(buf) + require.NoError(t, err) + require.Equal(t, 4, n) + require.Equal(t, "file", string(buf[:n])) + + n, err = rc.Read(buf) + require.Error(t, err) + require.Equal(t, io.EOF, err) + require.Equal(t, 0, n) + + require.NoError(t, rc.Close()) + }) + + t.Run("NonExisting", func(t *testing.T) { + nonExistingBlob, err := repo.GetBlob("00003ff740f9380390d5c9ddef4af18690000000") + require.NoError(t, err) + + r, err := nonExistingBlob.GetBlobContent(100) + require.Error(t, err) + require.IsType(t, ErrNotExist{}, err) + require.Empty(t, r) + + rc, size, err := nonExistingBlob.NewTruncatedReader(100) + require.Error(t, err) + require.IsType(t, ErrNotExist{}, err) + require.Empty(t, rc) + require.Empty(t, size) + }) +} + func Benchmark_Blob_Data(b *testing.B) { bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") repo, err := openRepositoryWithDefaultContext(bareRepo1Path) diff --git a/modules/git/git.go b/modules/git/git.go index 743ff59ddd..1dfd0b5134 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -38,6 +38,7 @@ var ( InvertedGitFlushEnv bool // 2.43.1 SupportCheckAttrOnBare bool // >= 2.40 SupportGitMergeTree bool // >= 2.38 + SupportGrepMaxCount bool // >= 2.38 HasSSHExecutable bool @@ -191,6 +192,7 @@ func InitFull(ctx context.Context) (err error) { InvertedGitFlushEnv = CheckGitVersionEqual("2.43.1") == nil SupportGitMergeTree = CheckGitVersionAtLeast("2.38") == nil + SupportGrepMaxCount = CheckGitVersionAtLeast("2.38") == nil if setting.LFS.StartServer { if CheckGitVersionAtLeast("2.1.2") != nil { diff --git a/modules/git/grep.go b/modules/git/grep.go index 117b09fc83..3703b13660 100644 --- a/modules/git/grep.go +++ b/modules/git/grep.go @@ -98,8 +98,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO cmd.AddOptionValues("--context", fmt.Sprint(opts.ContextLineNumber)) - // --max-count requires at least git 2.38 - if CheckGitVersionAtLeast("2.38.0") == nil { + if SupportGrepMaxCount { cmd.AddOptionValues("--max-count", fmt.Sprint(opts.MatchesPerFile)) } else { log.Warn("git-grep: --max-count requires at least git 2.38") diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go index 534468e268..83ddb766af 100644 --- a/modules/git/grep_test.go +++ b/modules/git/grep_test.go @@ -59,48 +59,55 @@ func TestGrepSearch(t *testing.T) { }, }, res) - res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{MatchesPerFile: 1}) - require.NoError(t, err) - assert.Equal(t, []*GrepResult{ - { - Filename: "i-am-a-python.p", - LineNumbers: []int{1}, - LineCodes: []string{"## This is a simple file to do a hello world"}, - HighlightedRanges: [][3]int{{0, 39, 44}}, - }, - { - Filename: "java-hello/main.java", - LineNumbers: []int{1}, - LineCodes: []string{"public class HelloWorld"}, - HighlightedRanges: [][3]int{{0, 18, 23}}, - }, - { - Filename: "main.vendor.java", - LineNumbers: []int{1}, - LineCodes: []string{"public class HelloWorld"}, - HighlightedRanges: [][3]int{{0, 18, 23}}, - }, - { - Filename: "python-hello/hello.py", - LineNumbers: []int{1}, - LineCodes: []string{"## This is a simple file to do a hello world"}, - HighlightedRanges: [][3]int{{0, 39, 44}}, - }, - }, res) + t.Run("Max count", func(t *testing.T) { + if !SupportGrepMaxCount { + t.Skip("Skipping, git grep --max-count is not supported") + return + } - res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{ - MatchesPerFile: 1, - Filename: "java-hello/", + res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{MatchesPerFile: 1}) + require.NoError(t, err) + assert.Equal(t, []*GrepResult{ + { + Filename: "i-am-a-python.p", + LineNumbers: []int{1}, + LineCodes: []string{"## This is a simple file to do a hello world"}, + HighlightedRanges: [][3]int{{0, 39, 44}}, + }, + { + Filename: "java-hello/main.java", + LineNumbers: []int{1}, + LineCodes: []string{"public class HelloWorld"}, + HighlightedRanges: [][3]int{{0, 18, 23}}, + }, + { + Filename: "main.vendor.java", + LineNumbers: []int{1}, + LineCodes: []string{"public class HelloWorld"}, + HighlightedRanges: [][3]int{{0, 18, 23}}, + }, + { + Filename: "python-hello/hello.py", + LineNumbers: []int{1}, + LineCodes: []string{"## This is a simple file to do a hello world"}, + HighlightedRanges: [][3]int{{0, 39, 44}}, + }, + }, res) + + res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{ + MatchesPerFile: 1, + Filename: "java-hello/", + }) + require.NoError(t, err) + assert.Equal(t, []*GrepResult{ + { + Filename: "java-hello/main.java", + LineNumbers: []int{1}, + LineCodes: []string{"public class HelloWorld"}, + HighlightedRanges: [][3]int{{0, 18, 23}}, + }, + }, res) }) - require.NoError(t, err) - assert.Equal(t, []*GrepResult{ - { - Filename: "java-hello/main.java", - LineNumbers: []int{1}, - LineCodes: []string{"public class HelloWorld"}, - HighlightedRanges: [][3]int{{0, 18, 23}}, - }, - }, res) res, err = GrepSearch(t.Context(), repo, "no-such-content", GrepOptions{}) require.NoError(t, err) diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go index 0006559709..c69382e245 100644 --- a/modules/git/repo_attribute_test.go +++ b/modules/git/repo_attribute_test.go @@ -251,10 +251,14 @@ func TestGitAttributeCheckerError(t *testing.T) { cancel() ac, err := gitRepo.GitAttributeChecker("8fee858da5796dfb37704761701bb8e800ad9ef3", "linguist-language") - require.NoError(t, err) + if SupportCheckAttrOnBare { + require.NoError(t, err) - _, err = ac.CheckPath("i-am-a-python.p") - require.Error(t, err) + _, err = ac.CheckPath("i-am-a-python.p") + require.Error(t, err) + } else { + require.Error(t, err) + } }) t.Run("Cancelled/DuringRun", func(t *testing.T) { diff --git a/modules/indexer/issues/elasticsearch/elasticsearch_test.go b/modules/indexer/issues/elasticsearch/elasticsearch_test.go index 0c2635fd0c..1ad01352d3 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch_test.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch_test.go @@ -16,16 +16,10 @@ import ( ) func TestElasticsearchIndexer(t *testing.T) { - // The elasticsearch instance started by testing.yml > test-unit > services > elasticsearch - url := "http://elastic:changeme@elasticsearch:9200" - - if os.Getenv("CI") == "" { - // Make it possible to run tests against a local elasticsearch instance - url = os.Getenv("TEST_ELASTICSEARCH_URL") - if url == "" { - t.Skip("TEST_ELASTICSEARCH_URL not set and not running in CI") - return - } + url := os.Getenv("TEST_ELASTICSEARCH_URL") + if url == "" { + t.Skip("TEST_ELASTICSEARCH_URL not set") + return } require.Eventually(t, func() bool { diff --git a/modules/indexer/issues/internal/qstring.go b/modules/indexer/issues/internal/qstring.go index 8115fc904f..6b60b4c5f6 100644 --- a/modules/indexer/issues/internal/qstring.go +++ b/modules/indexer/issues/internal/qstring.go @@ -25,7 +25,7 @@ type Token struct { func (tk *Token) ParseIssueReference() (int64, error) { term := tk.Term - if term[0] == '#' || term[0] == '!' { + if len(term) > 1 && (term[0] == '#' || term[0] == '!') { term = term[1:] } return strconv.ParseInt(term, 10, 64) diff --git a/modules/indexer/issues/internal/qstring_test.go b/modules/indexer/issues/internal/qstring_test.go index a911b86e2f..835491707c 100644 --- a/modules/indexer/issues/internal/qstring_test.go +++ b/modules/indexer/issues/internal/qstring_test.go @@ -169,3 +169,35 @@ func TestIssueQueryString(t *testing.T) { }) } } + +func TestToken_ParseIssueReference(t *testing.T) { + var tk Token + { + tk.Term = "123" + id, err := tk.ParseIssueReference() + require.NoError(t, err) + assert.Equal(t, int64(123), id) + } + { + tk.Term = "#123" + id, err := tk.ParseIssueReference() + require.NoError(t, err) + assert.Equal(t, int64(123), id) + } + { + tk.Term = "!123" + id, err := tk.ParseIssueReference() + require.NoError(t, err) + assert.Equal(t, int64(123), id) + } + { + tk.Term = "text" + _, err := tk.ParseIssueReference() + require.Error(t, err) + } + { + tk.Term = "" + _, err := tk.ParseIssueReference() + require.Error(t, err) + } +} diff --git a/modules/markup/html.go b/modules/markup/html.go index c13ebab98a..7961c5c930 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -69,6 +69,10 @@ var ( // https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type%3Demail) emailRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)(?:\\s|$|\\)|\\]|;|,|\\?|!|\\.(\\s|$))") + // Fediverse handle regex (same as emailRegex but with additonal @ or ! + // at start) + fediRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([@!]([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+)@([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+))(?:\\s|$|\\)|\\]|;|,|\\?|!|\\.(\\s|$))") + // blackfriday extensions create IDs like fn:user-content-footnote blackfridayExtRegex = regexp.MustCompile(`[^:]*:user-content-`) @@ -153,6 +157,7 @@ var defaultProcessors = []processor{ issueIndexPatternProcessor, commitCrossReferencePatternProcessor, hashCurrentPatternProcessor, + fediAddressProcessor, emailAddressProcessor, emojiProcessor, emojiShortCodeProcessor, @@ -1237,6 +1242,21 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) { } } +// fediAddressProcessor replaces raw fediverse handles with toolforge links +func fediAddressProcessor(ctx *RenderContext, node *html.Node) { + next := node.NextSibling + for node != nil && node != next { + m := fediRegex.FindStringSubmatchIndex(node.Data) + if m == nil { + return + } + + fedihandle := node.Data[m[2]:m[3]] + replaceContent(node, m[2], m[3], createLink("https://fedirect.toolforge.org/?id="+url.QueryEscape(fedihandle), fedihandle, "fedihandle")) + node = node.NextSibling.NextSibling + } +} + // emailAddressProcessor replaces raw email addresses with a mailto: link. func emailAddressProcessor(ctx *RenderContext, node *html.Node) { next := node.NextSibling diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 9d0c40c9e8..2bc929bb04 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -307,6 +307,19 @@ func TestRender_email(t *testing.T) { test( "email@domain..com", `

email@domain..com

`) + + // Test fediverse handle + test( + "@forgejo@floss.social", + `

@forgejo@floss.social

`) + + test( + "!forgejo@programming.dev", + `

!forgejo@programming.dev

`) + + test( + "@#&@forgejo.org", + `

@#&@forgejo.org

`) } func TestRender_emoji(t *testing.T) { diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index 717da464b9..e811d29994 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -267,8 +267,13 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error // RenderString renders Markdown string to HTML with all specific handling stuff and return string func RenderString(ctx *markup.RenderContext, content string) (template.HTML, error) { + return RenderReader(ctx, strings.NewReader(content)) +} + +// RenderReader renders Markdown io.Reader to HTML with all specific handling stuff and return string +func RenderReader(ctx *markup.RenderContext, input io.Reader) (template.HTML, error) { var buf strings.Builder - if err := Render(ctx, strings.NewReader(content), &buf); err != nil { + if err := Render(ctx, input, &buf); err != nil { return "", err } return template.HTML(buf.String()), nil diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index e229ee4c65..f7955115e0 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -104,7 +104,7 @@ func TestRender_Images(t *testing.T) { test( "!["+title+"]("+url+")", - `

`+title+`

`) + `

`+title+`

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

`+title+`

`) + `

`+title+`

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

image1
-image2

+ expected := `

image1
+image2

` res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase) require.NoError(t, err) @@ -845,10 +845,10 @@ mail@domain.com remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go index 0f9c69cae6..b86c9e3d41 100644 --- a/modules/markup/markdown/transform_image.go +++ b/modules/markup/markdown/transform_image.go @@ -44,6 +44,7 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image) for _, attr := range v.Attributes() { image.SetAttribute(attr.Name, attr.Value) } + image.SetAttributeString("loading", []byte("lazy")) for child := v.FirstChild(); child != nil; { next := child.NextSibling() image.AppendChild(image, child) diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index 384dd1fe94..aacc2536bf 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -108,6 +108,9 @@ func createDefaultPolicy() *bluemonday.Policy { // Allow classes for emojis policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img") + // Allow attributes for images + policy.AllowAttrs("loading").Matching(regexp.MustCompile(`^lazy$`)).OnElements("img") + // Allow icons, emojis, chroma syntax and keyword markup on span policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span") policy.AllowAttrs("data-alias").Matching(regexp.MustCompile(`^[a-zA-Z0-9-_+]+$`)).OnElements("span") diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go index 9805a34910..a0faff0494 100644 --- a/modules/markup/sanitizer_test.go +++ b/modules/markup/sanitizer_test.go @@ -75,6 +75,10 @@ func Test_Sanitizer(t *testing.T) { // Emoji `THUMBS UP`, `THUMBS UP`, `THUMBS UP`, `THUMBS UP`, + + // Images lazy loading + `image1`, `image1`, + `image1`, `image1`, } for i := 0; i < len(testCases); i += 2 { diff --git a/modules/public/mime_types.go b/modules/public/mime_types.go index 32bdf3bfa2..87ee2854ae 100644 --- a/modules/public/mime_types.go +++ b/modules/public/mime_types.go @@ -23,6 +23,11 @@ var wellKnownMimeTypesLower = map[string]string{ ".wasm": "application/wasm", ".webp": "image/webp", ".xml": "text/xml; charset=utf-8", + ".glb": "model/gltf-binary", + ".gltf": "model/gltf+json", + ".obj": "model/obj", + ".stl": "model/stl", + ".3mf": "model/3mf", // well, there are some types missing from the builtin list ".txt": "text/plain; charset=utf-8", diff --git a/modules/storage/minio_test.go b/modules/storage/minio_test.go index e168a2efbb..ec1b2fc77a 100644 --- a/modules/storage/minio_test.go +++ b/modules/storage/minio_test.go @@ -18,13 +18,14 @@ import ( ) func TestMinioStorageIterator(t *testing.T) { - if os.Getenv("CI") == "" { - t.Skip("minioStorage not present outside of CI") + endpoint := os.Getenv("TEST_MINIO_ENDPOINT") + if endpoint == "" { + t.Skip("TEST_MINIO_ENDPOINT not set") return } testStorageIterator(t, setting.MinioStorageType, &setting.Storage{ MinioConfig: setting.MinioStorageConfig{ - Endpoint: "minio:9000", + Endpoint: endpoint, AccessKeyID: "123456", SecretAccessKey: "12345678", Bucket: "gitea", @@ -34,13 +35,14 @@ func TestMinioStorageIterator(t *testing.T) { } func TestVirtualHostMinioStorage(t *testing.T) { - if os.Getenv("CI") == "" { - t.Skip("minioStorage not present outside of CI") + endpoint := os.Getenv("TEST_MINIO_ENDPOINT") + if endpoint == "" { + t.Skip("TEST_MINIO_ENDPOINT not set") return } testStorageIterator(t, setting.MinioStorageType, &setting.Storage{ MinioConfig: setting.MinioStorageConfig{ - Endpoint: "minio:9000", + Endpoint: endpoint, AccessKeyID: "123456", SecretAccessKey: "12345678", Bucket: "gitea", @@ -85,13 +87,14 @@ func TestMinioStoragePath(t *testing.T) { } func TestS3StorageBadRequest(t *testing.T) { - if os.Getenv("CI") == "" { - t.Skip("S3Storage not present outside of CI") + endpoint := os.Getenv("TEST_MINIO_ENDPOINT") + if endpoint == "" { + t.Skip("TEST_MINIO_ENDPOINT not set") return } cfg := &setting.Storage{ MinioConfig: setting.MinioStorageConfig{ - Endpoint: "minio:9000", + Endpoint: endpoint, AccessKeyID: "123456", SecretAccessKey: "12345678", Bucket: "bucket", diff --git a/modules/structs/action.go b/modules/structs/action.go index 2c42365c19..f47b228d75 100644 --- a/modules/structs/action.go +++ b/modules/structs/action.go @@ -78,3 +78,9 @@ type ActionRun struct { // the url of this action run HTMLURL string `json:"html_url"` } + +// ListActionRunResponse return a list of ActionRun +type ListActionRunResponse struct { + Entries []*ActionRun `json:"workflow_runs"` + TotalCount int64 `json:"total_count"` +} diff --git a/modules/structs/git_blob.go b/modules/structs/git_blob.go index 96c7a271a9..ef06693905 100644 --- a/modules/structs/git_blob.go +++ b/modules/structs/git_blob.go @@ -3,8 +3,8 @@ package structs -// GitBlobResponse represents a git blob -type GitBlobResponse struct { +// GitBlob represents a git blob +type GitBlob struct { Content string `json:"content"` Encoding string `json:"encoding"` URL string `json:"url"` diff --git a/modules/structs/hook.go b/modules/structs/hook.go index 5adcad0881..11372ca6e1 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -53,8 +53,7 @@ type CreateHookOption struct { BranchFilter string `json:"branch_filter" binding:"GlobPattern"` AuthorizationHeader string `json:"authorization_header"` // default: false - Active bool `json:"active"` - IsSystemWebhook bool `json:"is_system_webhook"` + Active bool `json:"active"` } // EditHookOption options when modify one hook diff --git a/modules/structs/repo_actions.go b/modules/structs/repo_actions.go index 505367336c..b13f344738 100644 --- a/modules/structs/repo_actions.go +++ b/modules/structs/repo_actions.go @@ -32,23 +32,3 @@ type ActionTaskResponse struct { Entries []*ActionTask `json:"workflow_runs"` TotalCount int64 `json:"total_count"` } - -// ActionRun represents an ActionRun -type RepoActionRun struct { - ID int64 `json:"id"` - Name string `json:"name"` - RunNumber int64 `json:"run_number"` - Event string `json:"event"` - Status string `json:"status"` - HeadBranch string `json:"head_branch"` - HeadSHA string `json:"head_sha"` - WorkflowID string `json:"workflow_id"` - URL string `json:"url"` - TriggeringActor *User `json:"triggering_actor"` -} - -// ListActionRunResponse return a list of ActionRun -type ListRepoActionRunResponse struct { - Entries []*RepoActionRun `json:"workflow_runs"` - TotalCount int64 `json:"total_count"` -} diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index b75b061218..62e063213c 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -192,8 +192,8 @@ func TestRenderMarkdownToHtml(t *testing.T) { remote link local link remote link -local image -remote image +local image +remote image 88fc37a3c0...12fc37a3c0 (hash) diff --git a/modules/typesniffer/typesniffer.go b/modules/typesniffer/typesniffer.go index a8fc70e54c..262feb2b05 100644 --- a/modules/typesniffer/typesniffer.go +++ b/modules/typesniffer/typesniffer.go @@ -24,6 +24,16 @@ const ( AvifMimeType = "image/avif" // ApplicationOctetStream MIME type of binary files. ApplicationOctetStream = "application/octet-stream" + // GLTFMimeType MIME type of GLTF files. + GLTFMimeType = "model/gltf+json" + // GLBMimeType MIME type of GLB files. + GLBMimeType = "model/gltf-binary" + // OBJMimeType MIME type of OBJ files. + OBJMimeType = "model/obj" + // STLMimeType MIME type of STL files. + STLMimeType = "model/stl" + // 3MFMimeType MIME type of 3MF files. + ThreeMFMimeType = "model/3mf" ) var ( @@ -67,6 +77,36 @@ func (ct SniffedType) IsAudio() bool { return strings.Contains(ct.contentType, "audio/") } +// Is3DModel detects if data is a 3D format +func (ct SniffedType) Is3DModel() bool { + return strings.Contains(ct.contentType, "model/") +} + +// IsGLTFFile detects if data is an SVG image format +func (ct SniffedType) IsGLTF() bool { + return strings.Contains(ct.contentType, GLTFMimeType) +} + +// IsGLBFile detects if data is an GLB image format +func (ct SniffedType) IsGLB() bool { + return strings.Contains(ct.contentType, GLBMimeType) +} + +// IsOBJFile detects if data is an OBJ image format +func (ct SniffedType) IsOBJ() bool { + return strings.Contains(ct.contentType, OBJMimeType) +} + +// IsSTLTextFile detects if data is an STL text format +func (ct SniffedType) IsSTL() bool { + return strings.Contains(ct.contentType, STLMimeType) +} + +// Is3MFFile detects if data is an 3MF image format +func (ct SniffedType) Is3MF() bool { + return strings.Contains(ct.contentType, ThreeMFMimeType) +} + // IsRepresentableAsText returns true if file content can be represented as // plain text or is empty. func (ct SniffedType) IsRepresentableAsText() bool { @@ -75,7 +115,7 @@ func (ct SniffedType) IsRepresentableAsText() bool { // IsBrowsableBinaryType returns whether a non-text type can be displayed in a browser func (ct SniffedType) IsBrowsableBinaryType() bool { - return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio() + return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio() || ct.Is3DModel() } // GetMimeType returns the mime type @@ -135,6 +175,13 @@ func DetectContentType(data []byte) SniffedType { ct = "audio/ogg" // for most cases, it is used as an audio container } } + + // GLTF is unsupported by http.DetectContentType + // hexdump -n 4 -C glTF.glb + if bytes.HasPrefix(data, []byte("glTF")) { + ct = GLBMimeType + } + return SniffedType{ct} } diff --git a/modules/typesniffer/typesniffer_test.go b/modules/typesniffer/typesniffer_test.go index 8d80b4ddb4..176d3658bb 100644 --- a/modules/typesniffer/typesniffer_test.go +++ b/modules/typesniffer/typesniffer_test.go @@ -117,6 +117,14 @@ func TestIsAudio(t *testing.T) { assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."+"🌛"[0:2])).IsText()) // test ID3 tag with incomplete UTF8 char } +func TestIsGLB(t *testing.T) { + glb, _ := hex.DecodeString("676c5446") + assert.True(t, DetectContentType(glb).IsGLB()) + assert.True(t, DetectContentType(glb).Is3DModel()) + assert.False(t, DetectContentType([]byte("plain text")).IsGLB()) + assert.False(t, DetectContentType([]byte("plain text")).Is3DModel()) +} + func TestDetectContentTypeFromReader(t *testing.T) { mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl") st, err := DetectContentTypeFromReader(bytes.NewReader(mp3)) @@ -145,3 +153,15 @@ func TestDetectContentTypeAvif(t *testing.T) { assert.True(t, st.IsImage()) } + +func TestDetectContentTypeModelGLB(t *testing.T) { + glb, err := hex.DecodeString("676c5446") + require.NoError(t, err) + + st, err := DetectContentTypeFromReader(bytes.NewReader(glb)) + require.NoError(t, err) + + // print st for debugging + assert.Equal(t, "model/gltf-binary", st.GetMimeType()) + assert.True(t, st.IsGLB()) +} diff --git a/modules/util/io.go b/modules/util/io.go index 1559b019a0..4c99004c0c 100644 --- a/modules/util/io.go +++ b/modules/util/io.go @@ -4,7 +4,6 @@ package util import ( - "bytes" "errors" "io" ) @@ -20,42 +19,6 @@ func ReadAtMost(r io.Reader, buf []byte) (n int, err error) { return n, err } -// ReadWithLimit reads at most "limit" bytes from r into buf. -// If EOF or ErrUnexpectedEOF occurs while reading, err will be nil. -func ReadWithLimit(r io.Reader, n int) (buf []byte, err error) { - return readWithLimit(r, 1024, n) -} - -func readWithLimit(r io.Reader, batch, limit int) ([]byte, error) { - if limit <= batch { - buf := make([]byte, limit) - n, err := ReadAtMost(r, buf) - if err != nil { - return nil, err - } - return buf[:n], nil - } - res := bytes.NewBuffer(make([]byte, 0, batch)) - bufFix := make([]byte, batch) - eof := false - for res.Len() < limit && !eof { - bufTmp := bufFix - if res.Len()+batch > limit { - bufTmp = bufFix[:limit-res.Len()] - } - n, err := io.ReadFull(r, bufTmp) - if err == io.EOF || err == io.ErrUnexpectedEOF { - eof = true - } else if err != nil { - return nil, err - } - if _, err = res.Write(bufTmp[:n]); err != nil { - return nil, err - } - } - return res.Bytes(), nil -} - // ErrNotEmpty is an error reported when there is a non-empty reader var ErrNotEmpty = errors.New("not-empty") diff --git a/modules/util/io_test.go b/modules/util/io_test.go deleted file mode 100644 index 870e713646..0000000000 --- a/modules/util/io_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package util - -import ( - "bytes" - "errors" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -type readerWithError struct { - buf *bytes.Buffer -} - -func (r *readerWithError) Read(p []byte) (n int, err error) { - if r.buf.Len() < 2 { - return 0, errors.New("test error") - } - return r.buf.Read(p) -} - -func TestReadWithLimit(t *testing.T) { - bs := []byte("0123456789abcdef") - - // normal test - buf, err := readWithLimit(bytes.NewBuffer(bs), 5, 2) - require.NoError(t, err) - assert.Equal(t, []byte("01"), buf) - - buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 5) - require.NoError(t, err) - assert.Equal(t, []byte("01234"), buf) - - buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 6) - require.NoError(t, err) - assert.Equal(t, []byte("012345"), buf) - - buf, err = readWithLimit(bytes.NewBuffer(bs), 5, len(bs)) - require.NoError(t, err) - assert.Equal(t, []byte("0123456789abcdef"), buf) - - buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 100) - require.NoError(t, err) - assert.Equal(t, []byte("0123456789abcdef"), buf) - - // test with error - buf, err = readWithLimit(&readerWithError{bytes.NewBuffer(bs)}, 5, 10) - require.NoError(t, err) - assert.Equal(t, []byte("0123456789"), buf) - - buf, err = readWithLimit(&readerWithError{bytes.NewBuffer(bs)}, 5, 100) - require.ErrorContains(t, err, "test error") - assert.Empty(t, buf) - - // test public function - buf, err = ReadWithLimit(bytes.NewBuffer(bs), 2) - require.NoError(t, err) - assert.Equal(t, []byte("01"), buf) - - buf, err = ReadWithLimit(bytes.NewBuffer(bs), 9999999) - require.NoError(t, err) - assert.Equal(t, []byte("0123456789abcdef"), buf) -} diff --git a/modules/util/truncate.go b/modules/util/truncate.go index f2edbdc673..7207a89177 100644 --- a/modules/util/truncate.go +++ b/modules/util/truncate.go @@ -54,3 +54,12 @@ func SplitTrimSpace(input, sep string) []string { return stringList } + +// TruncateRunes returns a truncated string with given rune limit, +// it returns input string if its rune length doesn't exceed the limit. +func TruncateRunes(str string, limit int) string { + if utf8.RuneCountInString(str) < limit { + return str + } + return string([]rune(str)[:limit]) +} diff --git a/modules/util/truncate_test.go b/modules/util/truncate_test.go index dfe1230fd4..8187b13eb2 100644 --- a/modules/util/truncate_test.go +++ b/modules/util/truncate_test.go @@ -44,3 +44,18 @@ func TestSplitString(t *testing.T) { } test(tc, SplitStringAtByteN) } + +func TestTruncateRunes(t *testing.T) { + assert.Empty(t, TruncateRunes("", 0)) + assert.Empty(t, TruncateRunes("", 1)) + + assert.Empty(t, TruncateRunes("ab", 0)) + assert.Equal(t, "a", TruncateRunes("ab", 1)) + assert.Equal(t, "ab", TruncateRunes("ab", 2)) + assert.Equal(t, "ab", TruncateRunes("ab", 3)) + + assert.Empty(t, TruncateRunes("测试", 0)) + assert.Equal(t, "测", TruncateRunes("测试", 1)) + assert.Equal(t, "测试", TruncateRunes("测试", 2)) + assert.Equal(t, "测试", TruncateRunes("测试", 3)) +} diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index f4ac1a0e3d..15d614e8bc 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -699,7 +699,7 @@ issues.filter_milestone_all = كل الأهداف issues.unlock.notice_2 = - يمكنك دوما إقفال هذه المسألة من جديد في المستقبل. issues.num_participants_few = %d متحاور release.title = عنوان الإصدار -issues.closed_at = `أغلق هذه المسألة %[2]s` +issues.closed_at = `أغلق هذه المسألة %s` issues.lock.title = إقفال التحاور في هذه المسألة. issues.new.no_label = بلا تصنيف issues.filter_sort.mostforks = الأعلى اشتقاقا @@ -759,7 +759,7 @@ branch.renamed = غُيّر اسم الفرع %s إلى %s. delete_preexisting = احذف الملفات الموجودة سابقا branch.included_desc = هذا الفرع جزء من الفرع المبدئي trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع -issues.reopened_at = `أعاد فتح هذه المسألة %[2]s` +issues.reopened_at = `أعاد فتح هذه المسألة %s` issues.action_milestone = هدف issues.new.assignees = المكلَّفون release.tag_name_protected = اسم الوسم محمي. @@ -1166,7 +1166,7 @@ pulls.status_checking = في انتظار بعض الفحوص pulls.status_checks_failure = بعض الفحوص فشلت pulls.status_checks_success = جميع الفحوص ناجحة pulls.status_checks_warning = بعض الفحوص تعطي تحذيرات -pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %[2]s` +pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %s` pulls.cmd_instruction_hint = `أظهر شرح استخدام سطر الأوامر.` pulls.cmd_instruction_checkout_title = اسحب pulls.cmd_instruction_checkout_desc = من مستودع مشروعك، اسحب (check out) فرعا جديدا واختبر التغييرات. @@ -1257,8 +1257,8 @@ pulls.status_checks_details = تفاصيل pulls.status_checks_hide_all = أخفِ كل الفحوص pulls.status_checks_show_all = أظهر كل الفحوص pulls.close = أغلق طلب الدمج -pulls.closed_at = `أغلق طلب الدمج %[2]s` -pulls.reopened_at = `أعاد فتح طلب الدمج %[2]s` +pulls.closed_at = `أغلق طلب الدمج %s` +pulls.reopened_at = `أعاد فتح طلب الدمج %s` milestones.title = العنوان milestones.desc = الوصف milestones.edit = عدّل الهدف @@ -1302,11 +1302,11 @@ issues.closed_by_fake = من %[2]s أُغلقت %[1]s issues.num_comments_1 = %d تعليق issues.num_comments = %d تعليقا issues.commented_at = `علّق %s` -issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع %[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.commit_ref_at = `أشار إلى هذه المسألة من إيداع %s` +issues.ref_issue_from = `أشار إلى هذه المسألة %[3]s %[1]s` +issues.ref_pull_from = `أشار إلى هذا الطلب %[3]s %[1]s` +issues.ref_closing_from = `أشار إلى طلب دمج %[3]s سيغلق هذه المسألة %[1]s` +issues.ref_reopening_from = `أشار إلى طلب دمج %[3]s سيعيد فتح هذه المسألة %[1]s` issues.ref_closed_from = `أغلق هذه المسألة %[4]s %[2]s` issues.ref_reopened_from = `أعاد فتح هذه المسألة %[4]s %[2]s` issues.reference_issue.body = المحتوى diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index abce4f1133..1b9767f674 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -749,7 +749,7 @@ settings.admin_settings = Администраторски настройки issues.role.owner = Притежател settings.transfer.title = Прехвърляне на притежанието issues.author = Автор -issues.closed_at = `затвори тази задача %[2]s` +issues.closed_at = `затвори тази задача %s` settings.collaborator_deletion_desc = Премахването на сътрудник ще отнеме достъпа му до това хранилище. Продължаване? commits.message = Съобщение issues.due_date_not_set = Няма зададен краен срок. @@ -773,9 +773,9 @@ issues.filter_type.all_issues = Всички задачи issues.filter_poster_no_select = Всички автори issues.opened_by = отворена %[1]s от %[3]s issues.action_open = Отваряне -pulls.closed_at = `затвори тази заявка за сливане %[2]s` -pulls.reopened_at = `отвори наново тази заявка за сливане %[2]s` -issues.reopened_at = `отвори наново тази задача %[2]s` +pulls.closed_at = `затвори тази заявка за сливане %s` +pulls.reopened_at = `отвори наново тази заявка за сливане %s` +issues.reopened_at = `отвори наново тази задача %s` projects.column.edit = Редактиране на колоната issues.close = Затваряне на задачата issues.ref_reopened_from = `отвори наново тази задача %[4]s %[2]s` @@ -1205,7 +1205,7 @@ issues.dependency.cancel = Отказ issues.dependency.add_error_dep_exists = Зависимостта вече съществува. issues.dependency.add_error_dep_not_exist = Зависимостта не съществува. issues.remove_ref_at = `премахна препратката %s %s` -issues.ref_pull_from = `спомена тази заявка за сливане %[4]s %[2]s` +issues.ref_pull_from = `спомена тази заявка за сливане %[3]s %[1]s` issues.dependency.pr_no_dependencies = Няма зададени зависимости. issues.dependency.remove_info = Премахване на тази зависимост issues.dependency.removed_dependency = `премахна зависимостта %s` @@ -1230,11 +1230,11 @@ issues.dependency.title = Зависимости issues.dependency.issue_no_dependencies = Няма зададени зависимости. issues.dependency.pr_close_blocked = Трябва да затворите всички задачи, блокиращи тази заявка за сливане, преди да можете да я слеете. issues.dependency.pr_close_blocks = Тази заявка за сливане блокира затварянето на следните задачи -issues.ref_issue_from = `спомена тази задача %[4]s %[2]s` -issues.commit_ref_at = `спомена тази задача в подаване %[2]s` +issues.ref_issue_from = `спомена тази задача %[3]s %[1]s` +issues.commit_ref_at = `спомена тази задача в подаване %s` issues.add_ref_at = `добави препратка %s %s` pulls.merged_info_text = Клонът %s вече може да бъде изтрит. -pulls.commit_ref_at = `спомена тази заявка за сливане в подаване %[2]s` +pulls.commit_ref_at = `спомена тази заявка за сливане в подаване %s` issues.change_ref_at = `промени препратката от %s на %s %s` diff.review.reject = Поискване на промени diff.bin_not_shown = Двоичният файл не е показан. @@ -1299,9 +1299,9 @@ branch.create_new_branch = Създаване на клон от клон: pulls.status_checks_show_all = Показване на всички проверки size_format = %[1]s: %[2]s; %[3]s: %[4]s pulls.filter_changes_by_commit = Филтриране по подаване -issues.ref_closing_from = `спомена тази задача в заявка за сливане %[4]s, която ще я затвори, %[2]s` +issues.ref_closing_from = `спомена тази задача в заявка за сливане %[3]s, която ще я затвори, %[1]s` issues.ref_from = `от %[1]s` -issues.ref_reopening_from = `спомена тази задача в заявка за сливане %[4]s, която ще я отвори наново , %[2]s` +issues.ref_reopening_from = `спомена тази задача в заявка за сливане %[3]s, която ще я отвори наново , %[1]s` issues.draft_title = Чернова pulls.reopen_to_merge = Моля, отворете наново тази заявка за сливане, за да извършите сливане. pulls.cant_reopen_deleted_branch = Тази заявка за сливане не може да бъде отворена наново, защото клонът е изтрит. diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 5f25cdea43..830065fb64 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1633,13 +1633,13 @@ issues.opened_by_fake=otevřeno %[1]s uživatelem %[2]s issues.closed_by_fake=od %[2]s byl uzavřen %[1]s issues.previous=Předchozí issues.next=Další -issues.open_title=Otevřeno -issues.closed_title=Uzavřeno +issues.open_title=Otevřené +issues.closed_title=Uzavřené issues.draft_title=Koncept issues.num_comments_1=%d komentář issues.num_comments=%d komentářů issues.commented_at=`okomentoval/a %s` -issues.delete_comment_confirm=Jste si jist, že chcete smazat tento komentář? +issues.delete_comment_confirm=Opravdu 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 problému @@ -1653,13 +1653,13 @@ issues.close_comment_issue=Zavřít s komentářem issues.reopen_issue=Znovu otevřít issues.reopen_comment_issue=Znovu otevřít s komentářem issues.create_comment=Komentovat -issues.closed_at=`uzavřel/a tento problém %[2]s` -issues.reopened_at=`znovu otevřel/a tento problém %[2]s` -issues.commit_ref_at=`odkázal/a na tento problém z revize %[2]s` -issues.ref_issue_from=`odkázal/a na tento problém %[4]s %[2]s` -issues.ref_pull_from=`odkázal/a na tuto žádost o sloučení %[4]s %[2]s` -issues.ref_closing_from=`odkazoval/a na tento problém ze žádosti o sloučení %[4]s, která jej uzavře, %[2]s` -issues.ref_reopening_from=`odkazoval/a na tento problém ze žádosti o sloučení %[4]s, která jej znovu otevře, %[2]s` +issues.closed_at=`uzavřel/a tento problém %s` +issues.reopened_at=`znovu otevřel/a tento problém %s` +issues.commit_ref_at=`odkázal/a na tento problém z revize %s` +issues.ref_issue_from=`odkázal/a na tento problém %[3]s %[1]s` +issues.ref_pull_from=`odkázal/a na tuto žádost o sloučení %[3]s %[1]s` +issues.ref_closing_from=`odkázal/a na tento problém ze žádosti o sloučení %[3]s, která jej uzavře, %[1]s` +issues.ref_reopening_from=`odkázal/a na tento problém ze žádosti o sloučení %[3]s, která jej znovu otevře, %[1]s` issues.ref_closed_from=`uzavřel/a tento problém %[4]s %[2]s` issues.ref_reopened_from=`znovu otevřel/a tento problém %[4]s %[2]s` issues.ref_from=`z %[1]s` @@ -1966,8 +1966,8 @@ 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 žádost o sloučení -pulls.closed_at=`uzavřel/a tuto žádost o sloučení %[2]s` -pulls.reopened_at=`znovu otevřel/a tuto žádost o sloučení %[2]s` +pulls.closed_at=`uzavřel/a tuto žádost o sloučení %s` +pulls.reopened_at=`znovu otevřel/a tuto žádost o sloučení %s` pulls.cmd_instruction_hint=Zobrazit instrukce příkazové řádky pulls.cmd_instruction_checkout_desc=Z vašeho repositáře projektu se podívejte na novou větev a vyzkoušejte změny. pulls.cmd_instruction_merge_title=Sloučit @@ -2758,7 +2758,7 @@ settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning = Tuto ak settings.new_owner_blocked_doer = Nový majitel vás zablokoval. settings.mirror_settings.pushed_repository = Odeslaný repozitář settings.add_collaborator_blocked_our = Nepodařilo se přidat spolupracovníka, jelikož byl zablokován majitelem repozitáře. -pulls.commit_ref_at = `se odkázal/a na tuto žádost o sloučení z revize %[2]s` +pulls.commit_ref_at = `odkázal/a na tuto žádost o sloučení z revize %s` settings.wiki_rename_branch_main = Normalizovat název větve wiki settings.wiki_rename_branch_main_desc = Přejmenovat větev interně používanou pro wiki na „%s“. Tato změna je trvalá a nelze ji vrátit. pulls.fast_forward_only_merge_pull_request = Pouze zrychlené @@ -2922,6 +2922,7 @@ settings.event_action_success = Úspěch settings.event_action_success_desc = Běh akce byl úspěšný. settings.event_header_action = Události běhu akce settings.event_action_recover_desc = Běh akce byl úspěšný, předchozí běh akce ve stejném workflow selhal. +issues.filter_type.all_pull_requests = Všechny žádosti o sloučení [graphs] component_loading_info = Tohle může chvíli trvat… diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index 6ec3a10e28..c82779ab60 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -1520,15 +1520,15 @@ issues.add_labels = tilføjede %s etiketterne %s issues.add_remove_labels = tilføjede %s og fjernede %s etiketter %s issues.add_milestone_at = `føjede dette til %s milepælen %s` issues.add_project_at = `føjede dette til %s- projektet %s` -issues.ref_reopening_from = `henviste til dette problem fra en pull-anmodning %[4]s, der vil genåbne den, %[2]s` +issues.ref_reopening_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil genåbne den, %[1]s` issues.ref_closed_from = `lukkede dette problem %[4]s %[2 ]s` issues.ref_reopened_from = `genåbnede dette problem %[4]s %[2 ]s` issues.ref_from = `fra %[1]s` issues.author = Forfatter -issues.commit_ref_at = `henviste til dette problem fra en commit %[2]s` -issues.ref_issue_from = `henviste til dette problem %[4]s %[2 ]s` -issues.ref_pull_from = `henviste til denne pull-anmodning %[4]s %[ 2]s` -issues.ref_closing_from = `henviste til dette problem fra en pull-anmodning %[4]s, der vil lukke det, %[2]s` +issues.commit_ref_at = `henviste til dette problem fra en commit %s` +issues.ref_issue_from = `henviste til dette problem %[3]s %[2 ]s` +issues.ref_pull_from = `henviste til denne pull-anmodning %[3]s %[1]s` +issues.ref_closing_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil lukke det, %[1]s` issues.author.tooltip.issue = Denne bruger er forfatteren til dette problem. issues.author.tooltip.pr = Denne bruger er forfatteren af denne pull-anmodning. issues.role.owner = Ejer @@ -1564,8 +1564,8 @@ issues.reaction.alt_add = Tilføj %[1]s reaktion til kommentar. issues.context.menu = Kommentar menu issues.reopen_comment_issue = Genåbner med kommentar issues.create_comment = Kommentar -issues.closed_at = `lukkede dette problem %[2]s` -issues.reopened_at = `genåbnede dette problem %[2]s` +issues.closed_at = `lukkede dette problem %s` +issues.reopened_at = `genåbnede dette problem %s` issues.remove_label = fjernede %s etiketten %s issues.remove_labels = fjernede %s etiketterne %s issues.change_project_at = `modificerede projektet fra %s til %s %s` @@ -1911,10 +1911,10 @@ pulls.editable_explanation = Denne pull-anmodning tillader redigeringer fra vedl pulls.auto_merge_button_when_succeed = (Når kontroller lykkes) pulls.status_checks_requested = Påkrævet pulls.close = Luk pull anmodning -pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit %[2]s` +pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit %s` pulls.cmd_instruction_hint = Se instruktionerne på kommandolinjen -pulls.reopened_at = `genåbnede denne pull-anmodning %[2]s` -pulls.closed_at = `lukkede denne pull-anmodning %[2]s` +pulls.reopened_at = `genåbnede denne pull-anmodning %s` +pulls.closed_at = `lukkede denne pull-anmodning %s` pulls.cmd_instruction_checkout_desc = Fra dit projektdepot, tjek en ny gren og test ændringerne. pulls.editable = Redigerbar pulls.made_using_agit = AGit @@ -2735,6 +2735,7 @@ settings.event_action_success = Success settings.event_action_recover_desc = Handlingskørsel lykkedes efter at den sidste handlingskørsel i samme arbejdsgang mislykkedes. settings.event_action_failure_desc = Handlingskørsel sluttede som en fejl. settings.event_action_recover = Gendan +issues.filter_type.all_pull_requests = Alle pull-anmodninger [notification] watching = Overvåger diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 4b2e215107..f8bfc9258a 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -1577,7 +1577,7 @@ issues.remove_ref_at=`hat die Referenz %s %s entfernt` issues.add_ref_at=`hat die Referenz %s %s hinzugefügt` issues.delete_branch_at=`löschte den Branch %s %s` issues.filter_label=Label -issues.filter_label_exclude=`Alt + Klick/Enter verwenden, um Labels auszuschließen` +issues.filter_label_exclude=`Verwende Alt + Klick/Enter, um Labels auszuschließen` issues.filter_label_no_select=Alle Labels issues.filter_label_select_no_label=Kein Label issues.filter_milestone=Meilenstein @@ -1651,13 +1651,13 @@ issues.close_comment_issue=Mit Kommentar schließen issues.reopen_issue=Wieder öffnen issues.reopen_comment_issue=Mit Kommentar wieder öffnen issues.create_comment=Kommentieren -issues.closed_at=`hat diesen Issue %[2]s geschlossen` -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` -issues.ref_closing_from=`hat %[2]s in einem Pull-Request %[4]s auf dieses Issue verwiesen, welcher es schließen wird` -issues.ref_reopening_from=`hat %[2]s in einem Pull-Request %[4]s auf dieses Issue verwiesen, welcher es erneut öffnen wird` +issues.closed_at=`hat dieses Issue %s geschlossen` +issues.reopened_at=`hat dieses Issue %s wieder geöffnet` +issues.commit_ref_at=`hat dieses Issue %s aus einem Commit referenziert` +issues.ref_issue_from=`hat %[1]s auf dieses Issue verwiesen %[3]s` +issues.ref_pull_from=`referenzierte diesen Pull-Request %[3]s %[1]s` +issues.ref_closing_from=`referenzierte dieses Issue aus einem Pull-Request %[3]s der es schließen wird, %[1]s` +issues.ref_reopening_from=`referenzierte dieses Issue aus einem Pull-Request %[3]s der es wieder öffnen wird, %[1]s` issues.ref_closed_from=`hat dieses Issue %[4]s geschlossen %[2]s` issues.ref_reopened_from=`hat dieses Issue %[4]s %[2]s wieder geöffnet` issues.ref_from=`von %[1]s` @@ -1962,8 +1962,8 @@ pulls.update_branch_success=Branch-Aktualisierung erfolgreich pulls.update_not_allowed=Du hast keine Berechtigung, den Branch zu updaten pulls.outdated_with_base_branch=Dieser Branch enthält nicht die neusten Commits des Basis-Branches pulls.close=Pull-Request schließen -pulls.closed_at=`hat diesen Pull-Request %[2]s geschlossen` -pulls.reopened_at=`hat diesen Pull-Request %[2]s wieder geöffnet` +pulls.closed_at=`hat diesen Pull-Request %s geschlossen` +pulls.reopened_at=`hat diesen Pull-Request %s wieder geöffnet` pulls.clear_merge_message=Merge-Nachricht löschen pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie „Co-Authored-By …“ erhalten. @@ -2767,7 +2767,7 @@ settings.wiki_globally_editable = Allen erlauben, das Wiki zu bearbeiten settings.protect_branch_name_pattern_desc = Geschützte Branch-Namens-Patterns. Siehe die Dokumentation für Pattern-Syntax. Beispiele: main, release/** settings.ignore_stale_approvals = Abgestandene Genehmigungen ignorieren settings.ignore_stale_approvals_desc = Genehmigungen, welche für ältere Commits gemacht wurden (abgestandene Reviews), nicht in die Gesamtzahl der Genehmigung des PRs mitzählen. Irrelevant, falls abgestandene Reviews bereits verworfen werden. -pulls.commit_ref_at = `hat sich auf diesen Pull-Request von einem Commit %[2]s bezogen` +pulls.commit_ref_at = `referenzierte diesen Pull-Request aus einem Commit %s` pulls.fast_forward_only_merge_pull_request = Nur Fast-forward pulls.cmd_instruction_checkout_desc = Checke einen neuen Branch aus deinem Projekt-Repository aus und teste die Änderungen. pulls.cmd_instruction_merge_title = Zusammenführen @@ -2924,6 +2924,7 @@ settings.event_action_success = Erfolg settings.event_header_action = Action-Run-Ereignisse settings.event_action_recover_desc = Action-Run war erfolgreich, nachdem der letzte Action-Run im selben Arbeitsablauf fehlgeschlagen ist. settings.event_action_recover = Wiederherstellen +issues.filter_type.all_pull_requests = Alle Pull-Requests [graphs] component_loading_failed = Konnte %s nicht laden @@ -3060,8 +3061,8 @@ teams.invite.by=Von %s eingeladen teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten. follow_blocked_user = Du kannst dieser Organisation nicht folgen, weil diese Organisation dich blockiert hat. open_dashboard = Übersicht öffnen -settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Organisationsname ist nach einer Abkühldauer von einem Tag wieder für alle verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen. -settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Organisationsname ist nach einer Abkühldauer von %[1]d Tagen wieder für alle verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen. +settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Organisationsname ist nach einer Schutzzeit von einem Tag wieder für alle verfügbar. Du kannst den alten Namen während dieser Schutzzeit erneut beanspruchen. +settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Organisationsname ist nach einer Schutzzeit von %[1]d Tagen wieder für alle verfügbar. Du kannst den alten Namen während dieser Schutzzeit erneut beanspruchen. [admin] dashboard=Übersicht diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 29085aebf1..398a0d9ce4 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -1626,13 +1626,13 @@ 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.commit_ref_at=`αναφορά σε αυτό το ζήτημα από την παραπομπή %[2]s` -issues.ref_issue_from=`αναφέρθηκε σε αυτό το ζήτημα %[4]s %[2]s` -issues.ref_pull_from=`αναφέρθηκε σε αυτό το pull request %[4]s %[2]s` -issues.ref_closing_from=`ανέφερε αυτό το ζήτημα σε ένα pull request %[4]s που στοχεύει να κλείσει το ζήτημα %[2]s` -issues.ref_reopening_from=`αναφέρθηκε σε αυτό το ζήτημα σε ένα pull request %[4]s που θα ξαναανοίξει αυτό το ζήτημα %[2]s` +issues.closed_at=`αυτό το ζήτημα έκλεισε %s` +issues.reopened_at=`ξανά άνοιξε αυτό το ζήτημα %s` +issues.commit_ref_at=`αναφορά σε αυτό το ζήτημα από την παραπομπή %s` +issues.ref_issue_from=`αναφέρθηκε σε αυτό το ζήτημα %[3]s %[1]s` +issues.ref_pull_from=`αναφέρθηκε σε αυτό το pull request %[3]s %[1]s` +issues.ref_closing_from=`ανέφερε αυτό το ζήτημα σε ένα pull request %[3]s που στοχεύει να κλείσει το ζήτημα %[1]s` +issues.ref_reopening_from=`αναφέρθηκε σε αυτό το ζήτημα σε ένα pull request %[3]s που θα ξαναανοίξει αυτό το ζήτημα %[1]s` issues.ref_closed_from=`έκλεισε αυτό το ζήτημα %[4]s %[2]s` issues.ref_reopened_from=`άνοιξε ξανά αυτό το ζήτημα %[4]s %[2]s` issues.ref_from=`από %[1]s` @@ -1939,8 +1939,8 @@ pulls.update_branch_success=Η ενημέρωση του κλάδου ήταν pulls.update_not_allowed=Δεν επιτρέπεται να ενημερώσετε τον κλάδο pulls.outdated_with_base_branch=Αυτός ο κλάδος δεν είναι ενημερωμένος με τον βασικό κλάδο pulls.close=Κλείσιμο pull request -pulls.closed_at=`έκλεισε αυτό το pull request %[2]s` -pulls.reopened_at=`άνοιξε ξανά αυτό το pull request %[2]s` +pulls.closed_at=`έκλεισε αυτό το pull request %s` +pulls.reopened_at=`άνοιξε ξανά αυτό το pull request %s` pulls.cmd_instruction_hint=Προβολή οδηγιών γραμμής εντολών pulls.cmd_instruction_checkout_title=Έλεγχος pulls.cmd_instruction_checkout_desc=Από το repository του έργου σας, ελέγξτε έναν νέο κλάδο και δοκιμάστε τις αλλαγές. @@ -2720,7 +2720,7 @@ settings.new_owner_blocked_doer = Ο νέος κάτοχος του αποθετ settings.enter_repo_name = Γράψτε το όνομα του κατόχου και του αποθετηρίου ακριβώς όπως το βλέπετε: settings.confirmation_string = Κείμενο επιβεβαίωσης settings.units.overview = Επισκόπηση -pulls.commit_ref_at = `ανέφερε το pull request στο commit %[2]s` +pulls.commit_ref_at = `ανέφερε το pull request στο commit %s` contributors.contribution_type.filter_label = Είδος συνεισφοράς: settings.wiki_rename_branch_main_notices_1 = Αυτή η ενέργεια ΔΕΝ αναιρείται. activity.navbar.contributors = Συνεισφέροντες diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 7479ab80af..5fd2ebd163 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -768,8 +768,8 @@ update_profile_success = Your profile has been updated. change_username = Your username has been changed. change_username_prompt = Note: Changing your username also changes your account URL. change_username_redirect_prompt = The old username will redirect until someone claims it. -change_username_redirect_prompt.with_cooldown.one = The old username will be available to everyone after a cooldown period of %[1]d day, you can still reclaim the old username during the cooldown period. -change_username_redirect_prompt.with_cooldown.few = The old username will be available to everyone after a cooldown period of %[1]d days, you can still reclaim the old username during the cooldown period. +change_username_redirect_prompt.with_cooldown.one = The old username will be available to everyone after a cooldown period of %[1]d day. You can still reclaim the old username during the cooldown period. +change_username_redirect_prompt.with_cooldown.few = The old username will be available to everyone after a cooldown period of %[1]d days. You can still reclaim the old username during the cooldown period. continue = Continue cancel = Cancel language = Language @@ -1610,7 +1610,7 @@ issues.remove_ref_at = `removed reference %s %s` issues.add_ref_at = `added reference %s %s` issues.delete_branch_at = `deleted branch %s %s` issues.filter_label = Label -issues.filter_label_exclude = `Use alt + click/enter to exclude labels` +issues.filter_label_exclude = Use Alt + Click to exclude labels issues.filter_label_no_select = All labels issues.filter_label_select_no_label = No label issues.filter_milestone = Milestone @@ -1628,6 +1628,7 @@ issues.filter_poster = Author issues.filter_poster_no_select = All authors issues.filter_type = Type issues.filter_type.all_issues = All issues +issues.filter_type.all_pull_requests = All pull requests issues.filter_type.assigned_to_you = Assigned to you issues.filter_type.created_by_you = Created by you issues.filter_type.mentioning_you = Mentioning you @@ -1693,15 +1694,13 @@ issues.close_comment_issue = Close with comment issues.reopen_issue = Reopen issues.reopen_comment_issue = Reopen with comment issues.create_comment = Comment -issues.closed_at = `closed this issue %[2]s` -issues.reopened_at = `reopened this issue %[2]s` -issues.commit_ref_at = `referenced this issue from a commit %[2]s` -issues.ref_issue_from = `referenced this issue %[4]s %[2]s` -issues.ref_pull_from = `referenced this pull request %[4]s %[2]s` -issues.ref_closing_from = `referenced this issue from a pull request %[4]s that will close it, %[2]s` -issues.ref_reopening_from = `referenced this issue from a pull request %[4]s that will reopen it, %[2]s` -issues.ref_closed_from = `closed this issue %[4]s %[2]s` -issues.ref_reopened_from = `reopened this issue %[4]s %[2]s` +issues.closed_at = `closed this issue %s` +issues.reopened_at = `reopened this issue %s` +issues.commit_ref_at = `referenced this issue from a commit %s` +issues.ref_issue_from = `referenced this issue %[3]s %[1]s` +issues.ref_pull_from = `referenced this pull request %[3]s %[1]s` +issues.ref_closing_from = `referenced this issue from a pull request %[3]s that will close it, %[1]s` +issues.ref_reopening_from = `referenced this issue from a pull request %[3]s that will reopen it, %[1]s` issues.ref_from = `from %[1]s` issues.author = Author issues.author.tooltip.issue = This user is the author of this issue. @@ -2013,9 +2012,9 @@ 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.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` +pulls.closed_at = `closed this pull request %s` +pulls.reopened_at = `reopened this pull request %s` +pulls.commit_ref_at = `referenced this pull request from a commit %s` pulls.cmd_instruction_hint = View command line instructions pulls.cmd_instruction_checkout_title = Checkout pulls.cmd_instruction_checkout_desc = From your project repository, check out a new branch and test the changes. @@ -2932,8 +2931,8 @@ 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.change_orgname_redirect_prompt.with_cooldown.one = The old organization name will be available to everyone after a cooldown period of %[1]d day, you can still reclaim the old name during the cooldown period. -settings.change_orgname_redirect_prompt.with_cooldown.few = The old organization name will be available to everyone after a cooldown period of %[1]d days, you can still reclaim the old name during the cooldown period. +settings.change_orgname_redirect_prompt.with_cooldown.one = The old organization name will be available to everyone after a cooldown period of %[1]d day. You can still reclaim the old name during the cooldown period. +settings.change_orgname_redirect_prompt.with_cooldown.few = The old organization name will be available to everyone after a cooldown period of %[1]d days. You can still reclaim the old name during the cooldown period. settings.update_avatar_success = The organization's avatar has been updated. settings.delete = Delete organization settings.delete_account = Delete this organization diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index f912409cc9..bdafba93b4 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -1648,13 +1648,13 @@ issues.close_comment_issue=Cerrar con comentario issues.reopen_issue=Reabrir issues.reopen_comment_issue=Reabrir con comentario issues.create_comment=Comentar -issues.closed_at=`cerró esta incidencia %[2]s` -issues.reopened_at=`reabrió esta incidencia %[2]s` -issues.commit_ref_at=`referenció esta incidencia en un commit %[2]s` -issues.ref_issue_from=`referenció esta incidencia %[4]s %[2]s` -issues.ref_pull_from=`referenció este pull request %[4]s %[2]s` -issues.ref_closing_from=`hizo referencia a esta incidencia desde un pull request %[4]s que lo cerrará , %[2]s` -issues.ref_reopening_from=`hizo referencia a esta incidencia desde un pull request %[4]s que lo reabrirá, %[2]s` +issues.closed_at=`cerró esta incidencia %s` +issues.reopened_at=`reabrió esta incidencia %s` +issues.commit_ref_at=`referenció esta incidencia en un commit %s` +issues.ref_issue_from=`referenció esta incidencia %[3]s %[1]s` +issues.ref_pull_from=`referenció este pull request %[3]s %[1]s` +issues.ref_closing_from=`hizo referencia a esta incidencia desde un pull request %[3]s que lo cerrará , %[1]s` +issues.ref_reopening_from=`hizo referencia a esta incidencia desde un pull request %[3]s que lo reabrirá, %[1]s` issues.ref_closed_from=`cerró esta incidencia %[4]s %[2]s` issues.ref_reopened_from=`reabrió esta incidencia %[4]s %[2]s` issues.ref_from=`de %[1]s` @@ -1959,8 +1959,8 @@ pulls.update_branch_success=La actualización de la rama ha finalizado correctam pulls.update_not_allowed=No tiene permisos para actualizar esta rama pulls.outdated_with_base_branch=Esta rama está desactualizada con la rama base pulls.close=Cerrar pull request -pulls.closed_at=`cerró este pull request %[2]s` -pulls.reopened_at=`reabrió este pull request %[2]s` +pulls.closed_at=`cerró este pull request %s` +pulls.reopened_at=`reabrió este pull request %s` pulls.clear_merge_message=Borrar mensaje de fusión pulls.clear_merge_message_hint=Limpiar el mensaje de fusión solo eliminará el contenido del mensaje de commit y mantendrá frases generadas como "Co-Autorizado por …". @@ -2789,7 +2789,7 @@ pulls.status_checks_hide_all = Ocultar todas las verificaciones settings.federation_not_enabled = La federación no está habilitada en tu instancia. wiki.search = Buscar en wiki pulls.status_checks_show_all = Mostrar todas las verificaciones -pulls.commit_ref_at = `hizo referencia a este pull request desde un commit %[2]s` +pulls.commit_ref_at = `hizo referencia a este pull request desde un commit %s` pulls.cmd_instruction_merge_title = Fusionar contributors.contribution_type.deletions = Eliminaciones contributors.contribution_type.filter_label = Tipo de contribución: diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index 804b48b2b2..dae0695495 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -1250,13 +1250,13 @@ 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.commit_ref_at=`ارجاع این مسئله به کامیت %[2]s` -issues.ref_issue_from=` ارجاعات این مسائله %[4] %[2]s` -issues.ref_pull_from=` ارجاعات این تقاضای ادغام %[4] %[2]s` -issues.ref_closing_from=` ارجاعات این تقاضای واکشی %[4] %[2]s` -issues.ref_reopening_from=` تقاضای واکشی ارجاع شده %[4] که مسائله بازگشایی خواهد کرد %[2] ` +issues.closed_at=`%s این موضوع را بست` +issues.reopened_at=`%s این موضوع را دوباره باز کرد` +issues.commit_ref_at=`ارجاع این مسئله به کامیت %s` +issues.ref_issue_from=` ارجاعات این مسائله %[3] %[1]s` +issues.ref_pull_from=` ارجاعات این تقاضای ادغام %[4] %[1]s` +issues.ref_closing_from=` ارجاعات این تقاضای واکشی %[4] %[1]s` +issues.ref_reopening_from=` تقاضای واکشی ارجاع شده %[3]sکه مسائله بازگشایی خواهد کرد %[2] ` issues.ref_closed_from=` بسته شده این مسائله %[4] %[2]s` issues.ref_reopened_from=` بازگشایی این مسائله %[4] %[2]s` issues.ref_from=`از %[1]` @@ -1493,8 +1493,8 @@ pulls.update_branch_rebase=بروزآوری شاخه با بازسازی مجد pulls.update_branch_success=شاخه به موفقیت بروز شد pulls.update_not_allowed=شما اجازه بروزرسانی شاخه را ندارید pulls.outdated_with_base_branch=این شاخه با شاخه پایه منسوخ شده است -pulls.closed_at=`این درخواست pull بسته شده %[2]s` -pulls.reopened_at=`این درخواست pull را بازگشایی کرد %[2]s` +pulls.closed_at=`این درخواست pull بسته شده %s` +pulls.reopened_at=`این درخواست pull را بازگشایی کرد %s` diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index cff940a05a..164a60cc8d 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -1293,9 +1293,9 @@ issues.close_comment_issue=Kommentoi ja sulje issues.reopen_issue=Avaa uudelleen issues.reopen_comment_issue=Kommentoi ja avaa uudelleen issues.create_comment=Kommentoi -issues.closed_at=`sulki tämän ongelman %[2]s` -issues.reopened_at=`uudelleenavasi tämän ongelman %[2]s` -issues.commit_ref_at=`viittasi tähän ongelmaan kommitissa %[2]s` +issues.closed_at=`sulki tämän ongelman %s` +issues.reopened_at=`uudelleenavasi tämän ongelman %s` +issues.commit_ref_at=`viittasi tähän ongelmaan kommitissa %s` issues.author=Tekijä issues.role.owner=Omistaja issues.role.member=Jäsen @@ -2185,7 +2185,7 @@ settings.confirmation_string = Vahvistusteksti settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi tietovaraston %s mukaan lukien koodin, ongelmat, kommentit, wikidatan ja avustaja-asetukset. issues.filter_assginee_no_select = Kaikki käsittelijät issues.new.assign_to_me = Osoita itselle -pulls.closed_at = `sulki tämän vetopyynnön %[2]s` +pulls.closed_at = `sulki tämän vetopyynnön %s` tree_path_not_found_branch = Polkua %[1]s ei ole olemassa haarassa %[2]s transfer.no_permission_to_reject = Sinulla ei ole oikeutta hylätä tätä siirtoa. generate_repo = Luo tietovarasto @@ -2199,8 +2199,8 @@ issues.new.no_reviewers = Ei katselmoijia issues.add_label = lisäsi nimilapun %s %s issues.due_date_added = lisäsi eräpäivän %s %s issues.review.add_review_request = pyysi katselmointia käyttäjältä %[1]s %[2]s -issues.ref_pull_from = `viittasi tähän vetopyyntöön %[4]s %[2]s` -pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista %[2]s` +issues.ref_pull_from = `viittasi tähän vetopyyntöön %[3]s %[1]s` +pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista %s` issues.review.comment = katselmoi %s issues.add_labels = lisäsi nimilaput %s %s issues.review.add_review_requests = pyysi katselmointeja käyttäjiltä %[1]s %[2]s @@ -2381,7 +2381,7 @@ wiki.page_name_desc = Kirjoita tämän wikisivun nimi. Joitain erikoisnimiä ova pulls.blocked_by_changed_protected_files_1 = Tämä vetopyyntö sisältää suojatun tiedoston ja on siksi estetty: pulls.status_checks_warning = Jotkin tarkistukset raportoivat varoituksia pulls.status_checks_error = Jotkin tarkistukset raportoivat virheitä -pulls.reopened_at = `avasi uudelleen tämän vetopyynnön %[2]s` +pulls.reopened_at = `avasi uudelleen tämän vetopyynnön %s` pulls.auto_merge_when_succeed = Yhdistä automaatisesti kun kaikki tarkistukset onnistuvat signing.wont_sign.error = Tapahtui virhe tarkistaessa voiko kommitin allekirjoittaa. signing.wont_sign.twofa = Sinulla tulee olla kaksivaiheinen todennus käytössä, jotta kommitit voi allekirjoittaa. diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 1dbfb588f0..8c9badb04b 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -365,7 +365,7 @@ table_modal.label.columns = Mga Column link_modal.header = Magdagdag ng link link_modal.url = Url link_modal.description = Deskripsyon -link_modal.paste_reminder = Pahiwatig: Kapag may URL sa clipboard, maari mong direktang i-paste sa editor para gumawa ng link. +link_modal.paste_reminder = Pahiwatig: Kapag may URL sa clipboard, maaari mong direktang i-paste sa editor para gumawa ng link. [filter] string.asc = A - Z @@ -432,7 +432,7 @@ openid_connect_desc = Ang piniling OpenID URI ay hindi alam. Iugnay iyan sa bago 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 na pang-kumpirma ay ipinadala sa %s. Para kumpletuhin ang proseso ng pagrehistro, pakisuri ang iyong inbox at sundan ang ibinigay na link sa loob ng %s. Kung mali ang email, maari kang mag-log in, at humingi ng isa pang email pang-kumpirma na ipapadala sa ibang address. +confirmation_mail_sent_prompt = Ang isang bagong email na pang-kumpirma ay ipinadala sa %s. Para kumpletuhin ang proseso ng pagrehistro, pakisuri ang iyong inbox at sundan ang ibinigay na link sa loob ng %s. Kung mali ang email, maaari 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 tagapangangasiwa ng site para kumpletuhin ang pagrehistro. @@ -484,7 +484,7 @@ admin.new_user.text = Mangyaring mag-click dito para ipamahala register_notify = Maligayang Pagdating sa %s 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 +register_notify.text_2 = Maaari 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: @@ -535,7 +535,7 @@ totp_disabled.text_1 = Ngayon lang na-disable ang Time-based one-time password ( totp_disabled.no_2fa = Wala nang mga ibang paraan ng 2FA ang naka-configure, nangangahulugan na hindi na kailangang mag-log in sa iyong account gamit ang 2FA. removed_security_key.subject = May tinanggal na security key removed_security_key.text_1 = Tinanggal ngayon lang ang security key na "%[1]s" sa iyong account. -account_security_caution.text_1 = Kung ikaw ito, maari mong ligtas na huwag pansinin ang mail na ito. +account_security_caution.text_1 = Kung ikaw ito, maaari mong ligtas na huwag pansinin ang mail na ito. account_security_caution.text_2 = Kung hindi ito ikaw, nakompromiso ang iyong account. Mangyaring makipag-ugnayan sa mga tagapangasiwa ng site na ito. totp_enrolled.subject = Nag-activate ka ng TOTP bilang paraan ng 2FA totp_enrolled.text_1.has_webauthn = Na-enable mo lang ang TOTP para sa iyong account. Nangangahulugan ito na para sa lahat ng mga hinaharap na pag-login sa iyong account, kailangan mong gumamit ng TOTP bilang paraan ng 2FA o gamitin ang iyong mga security key. @@ -644,7 +644,7 @@ AccessToken = Token ng pag-access Biography = Byograpya Location = Lokasyon visit_rate_limit = Natugunan ang limitasyon sa rate ng malayuang pagbisita. -username_claiming_cooldown = Hindi ma-claim ang username na ito, dahil hindi pa tapos ang panahon ng cooldown. Maari itong i-claim sa %[1]s. +username_claiming_cooldown = Hindi ma-claim ang username na ito, dahil hindi pa tapos ang panahon ng cooldown. Maaari itong i-claim sa %[1]s. email_domain_is_not_allowed = Sumasalungat ang domain ng email address ng user %s sa EMAIL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Siguraduhing natakda mo ang email address nang tama. [user] @@ -685,7 +685,7 @@ followers.title.few = Mga tagasunod following.title.one = Sinusundan followers.title.one = Tagasunod public_activity.visibility_hint.self_public = Nakikita ng lahat ang iyong aktibidad, maliban sa mga interaksyon sa pribadong espasyo. I-configure. -public_activity.visibility_hint.admin_public = Nakikita ng lahat ang aktibidad na ito, ngunit bilang tagapangasiwa maari mo ring makita ang mga interaksyon sa mga pribadong espasyo. +public_activity.visibility_hint.admin_public = Nakikita ng lahat ang aktibidad na ito, ngunit bilang tagapangasiwa maaari mo ring makita ang mga interaksyon sa mga pribadong espasyo. public_activity.visibility_hint.self_private = Nakikita mo lang at mga tagapangasiwa ng instansya ang iyong aktibidad. I-configure. public_activity.visibility_hint.admin_private = Nakikita mo ang aktibidad na ito dahil isa kang tagapangasiwa, ngunit gusto ng user na panatilihin itong pribado. public_activity.visibility_hint.self_private_profile = Ikaw lang at ang mga tagapangasiwa ng instansya ang makakakita ng iyong aktibidad dahil pribado ang iyong profile. I-configure. @@ -842,7 +842,7 @@ gpg_key_verify = I-verify gpg_invalid_token_signature = Ang ibinigay na GPG key, signature, at token ay hindi tumutugma o luma. gpg_token_required = Kailangan mong magbigay ng signature para sa token sa ibaba gpg_token = Token -gpg_token_help = Maari kang mag-generate ng signature gamit ng: +gpg_token_help = Maaari kang mag-generate ng signature gamit ng: gpg_token_signature = Naka-armor na GPG signature key_signature_gpg_placeholder = Nagsisimula sa "-----BEGIN PGP SIGNATURE-----" verify_gpg_key_success = Na-verify na ang GPG key na "%s". @@ -851,7 +851,7 @@ ssh_key_verify = I-verify ssh_invalid_token_signature = Ang ibinigay na SSH key, signature, o token ay hindi tumutugma o luma. ssh_token_required = Kailangan mong magbigay ng signature para sa token sa ibaba ssh_token = Token -ssh_token_help = Maari kang mag-generate ng signature gamit ng: +ssh_token_help = Maaari kang mag-generate ng signature gamit ng: ssh_token_signature = Naka-armor na SSH signature key_signature_ssh_placeholder = Nagsisimula sa "-----BEGIN SSH SIGNATURE-----" verify_ssh_key_success = Na-verify na ang SSH key na "%s". @@ -912,10 +912,10 @@ create_oauth2_application_success = Matagumpay kang gumawa ang bagong OAuth2 app oauth2_confidential_client = Kumpidensyal na kliyente. Piliin para sa mga app na pinapatilihing kumpidensyal ang sikreto, tulad ng mga web app. Huwag piliin para sa mga web app kasama ang mga desktop at mobile app. twofa_desc = Para protektahin ang iyong account laban sa pagnanakaw ng password, pwede mo gamitin ang iyong smartphone o ibang device para sa pagtanggap ng time-based one-time password ("TOTP"). twofa_scratch_token_regenerated = Ang iyong isang-beses na paggamit na recovery key ngayon ay %s. Ilagay ito sa ligtas na lugar, dahil hindi na ito ipapakita muli. -regenerate_scratch_token_desc = Kapag nawala mo ang iyong recovery key o ginamit mo na oara mag-sign in, maari mong i-reset dito. +regenerate_scratch_token_desc = Kapag nawala mo ang iyong recovery key o ginamit mo na oara mag-sign in, maaari mong i-reset dito. twofa_disable_desc = Ang pag-disable ng authentikasyong two-factor ay gagawing hindi gaanong ligtas ang iyong account. Magpatuloy? twofa_enrolled = Matagumpay na na-enroll ang iyong account. Ilagay ang iyong isang-beses na paggamit na recovery key (%s) sa isang ligtas na lugar, dahil hindi na ito ipapakita muli. -webauthn_desc = Ang mga security key ay isang hardware device na naglalaman ng mga cryptographic key. Maari silang gamitin para sa authentikasyong two-factor. Ang mga security key ay dapat suportahan ang WebAuthn Authenticator na standard. +webauthn_desc = Ang mga security key ay isang hardware device na naglalaman ng mga cryptographic key. Maaari silang gamitin para sa authentikasyong two-factor. Ang mga security key ay dapat suportahan ang WebAuthn Authenticator na standard. remove_oauth2_application = Tanggalin ang OAuth2 Application remove_oauth2_application_desc = Ang pagtanggal ng OAuth2 application ay babawiin ang access sa lahat ng mga naka-sign na access token. Magpatuloy? remove_oauth2_application_success = Binura na ang application. @@ -931,13 +931,13 @@ oauth2_regenerate_secret = I-regenerate ang sikreto oauth2_regenerate_secret_hint = Nawala mo ang iyong sikreto? oauth2_client_secret_hint = Ang sikreto ay hindi ipapakita muli pagkatapos umalis ka o i-refresh ang page na ito. Mangyaring siguraduhin na na-save mo iyan. oauth2_application_edit = I-edit -twofa_recovery_tip = Kapag mawala mo ang iyong device, maari kang gumamit ng isang isang-beses na paggamit na recovery key para makakuha muli ng access sa iyong account. +twofa_recovery_tip = Kapag mawala mo ang iyong device, maaari kang gumamit ng isang isang-beses na paggamit na recovery key para makakuha muli ng access sa iyong account. twofa_is_enrolled = Ang iyong account ay kasalukuyang naka-enroll sa autentikasyong two-factor. twofa_not_enrolled = Kasalukuyang hindi naka-enroll ang iyong account sa authentikasyong two-factor. twofa_disable = I-disable ang authentikasyong two-factor twofa_scratch_token_regenerate = I-regenerate ang isang-beses na paggamit na recovery key twofa_enroll = Mag-enroll sa authentikasyong two-factor -twofa_disable_note = Maari mong i-disable ang authentikasyong two-factor kapag kinakailangan. +twofa_disable_note = Maaari mong i-disable ang authentikasyong two-factor kapag kinakailangan. twofa_disabled = Na-disable na ang authentikasyong two-factor. scan_this_image = I-scan ang image na ito gamit ng iyong aplikasyong pang-authentikasyon: or_enter_secret = O ilagay ang sikreto: %s @@ -1005,8 +1005,8 @@ language.description = Mase-save ang wika sa iyong account at gagamitin bilang d language.localization_project = Tulungan kaming isalin ang Forgejo sa iyong wika! Matuto pa. pronouns_custom_label = Mga pasadyang pronoun user_block_yourself = Hindi mo maaaring harangan ang sarili mo. -change_username_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. -change_username_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. +change_username_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. +change_username_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. keep_pronouns_private = Ipakita lang ang mga panghalip sa mga naka-authenticate na user keep_pronouns_private.description = Itatago nito ang iyong mga panghalip mula sa mga bisita na hindi naka-log in. quota.applies_to_user = Nag-aapply ang mga sumusunod na panuntunan ng quota sa iyong account @@ -1071,7 +1071,7 @@ readme_helper_desc = Ito ang lugar kung saan makakasulat ka ng kumpletong deskri trust_model_helper_collaborator_committer = Katulong+Committer: I-trust ang mga signature batay sa mga katulong na tumutugma sa committer mirror_interval = Interval ng mirror (ang mga wastong unit ng oras ay "h", "m", "s"). 0 para i-disable ang periodic sync. (Pinakamababang interval: %s) transfer.reject_desc = Kanselahin ang pag-transfer mula sa "%s" -mirror_lfs_endpoint_desc = Ang sync ay susubukang gamitin ang clone url upang matukoy ang LFS server. Maari ka rin tumukoy ng isang custom na endpoint kapag ang LFS data ng repositoryo ay nilalagay sa ibang lugar. +mirror_lfs_endpoint_desc = Ang sync ay susubukang gamitin ang clone url upang matukoy ang LFS server. Maaari ka rin tumukoy ng isang custom na endpoint kapag ang LFS data ng repositoryo ay nilalagay sa ibang lugar. adopt_search = Ilagay ang username para maghanap ng mga unadopted na repositoryo… (iwanang walang laman para hanapin lahat) object_format = Format ng object readme_helper = Pumili ng README file template @@ -1164,8 +1164,8 @@ tree_path_not_found_commit = Hindi umiiral ang path na %[1]s sa commit %[2]s tree_path_not_found_branch = Hindi umiiral ang daanang %[1]s sa branch %[2]s migrate_items_pullrequests = Mga hiling sa paghila archive.pull.nocomment = Naka-archive ang repositoryong ito. Hindi ka makakakomento sa mga pull request. -archive.title = Naka-archive ang repositoryong ito. Maari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado ito, tulad ng pagtulak at paggawa ng mga isyu, pull request o mga komento. -archive.title_date = Naka-archive ang repositoryo na ito noong %s. Maari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado nito, tulad ng pagtulak o paggawa ng mga bagong isyu, mga pull request, o komento. +archive.title = Naka-archive ang repositoryong ito. Maaari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado ito, tulad ng pagtulak at paggawa ng mga isyu, pull request o mga komento. +archive.title_date = Naka-archive ang repositoryo na ito noong %s. Maaari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado nito, tulad ng pagtulak o paggawa ng mga bagong isyu, mga pull request, o komento. pulls = Mga hiling sa paghila activity.merged_prs_count_n = Mga naisamang hiling sa paghila wiki.last_updated = Huling binago %s @@ -1183,7 +1183,7 @@ issues.action_open = Buksan issues.closed_title = Sarado issues.reopen_issue = Buksang muli pulls.merged = Naisama na -pulls.merged_info_text = Maari nang burahin ang branch %s. +pulls.merged_info_text = Maaari nang burahin ang branch %s. milestones.update_ago = Binago %s activity.closed_issue_label = Sarado activity.merged_prs_label = Naisama @@ -1205,7 +1205,7 @@ migrate.clone_address_desc = Ang HTTP(S) o Git "clone" URL ng umiiral na reposit need_auth = Awtorisasyon migrate.github_token_desc = Maaari kang maglagay ng isa o higit pang mga token na hinihiwalay ng kuwit dito upang gawing mas-mabilis ang pagmigrate dahil sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaaring magdulot ng pag-block ng account. template.invalid = Kailangang pumili ng kahit isang template na repositoryo -migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang matukoy ang LFS server. Maari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar. +migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang matukoy ang LFS server. Maaari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar. blame.ignore_revs.failed = Nabigong hindi pansinin ang mga rebisyon sa .git-blame-ignore-revs. tree_path_not_found_tag = Hindi umiiral ang path na %[1]s sa tag %[2]s form.reach_limit_of_creation_n = Naabot na ng may-ari ang limitasyon na %d mga repositoryo. @@ -1471,10 +1471,10 @@ activity.new_issue_label = Nabuksan activity.merged_prs_count_1 = Naisamang hiling sa paghila activity.opened_prs_count_1 = Inimungkahing hiling sa paghila activity.opened_prs_label = Inimungkahi -pulls.reopened_at = `nabuksang muli ang hiling sa paghatak na %[2]s` +pulls.reopened_at = `binuksan muli ang hiling sa paghila %s` issues.opened_by_fake = binuksan ang %[1]s ni/ng %[2]s pulls.reopen_failed.base_branch = Hindi mabuksang muli ang hiling sa paghatak na ito dahil hindi na umiiral ang base branch. -issues.reopened_at = `binuksang muli ang isyung ito %[2]s` +issues.reopened_at = `binuksang muli ang isyung ito %s` pulls.reopen_failed.head_branch = Hindi mabubuksan muli ang hiling sa paghila, dahil hindi na umiiral ang head branch. settings.event_pull_request_desc = Binuksan, sinara, muling binuksan, o binago ang hiling sa paghatak. activity.opened_prs_count_n = Mga inimungkahing hiling sa paghila @@ -1500,7 +1500,7 @@ issues.content_history.created = ginawa editor.patching = Pina-patch: editor.fail_to_apply_patch = Hindi malapat ang patch na "%s" settings.danger_zone = Mapanganib na lugar -issues.closed_at = `isinara ang isyung %[2]s` +issues.closed_at = `isinara ang isyung ito %s` settings.collaboration.admin = Tagapangasiwa settings.admin_settings = Mga setting ng tagapangasiwa issues.start_tracking_history = `sinimulan ang trabaho %s` @@ -1627,7 +1627,7 @@ projects.column.edit_title = Pangalan projects.column.new_title = Pangalan projects.card_type.desc = Mga preview ng card commits.desc = I-browse ang history ng pagbabago ng source code. -commits.search.tooltip = Maari kang mag-prefix ng mga keyword gamit ang "author:", "committer:", "after:", o "before:", hal. "revert author:Nijika before:2022-10-09". +commits.search.tooltip = Maaari kang mag-prefix ng mga keyword gamit ang "author:", "committer:", "after:", o "before:", hal. "revert author:Nijika before:2022-10-09". issues.force_push_codes = `puwersahang itinulak ang %[1]s mula %[2]s sa %[4]s %[6]s` issues.push_commit_1 = idinagdag ang %d commit %s issues.push_commits_n = idinagdag ang %d mga commit %s @@ -1707,7 +1707,7 @@ issues.action_milestone = Milestone issues.action_milestone_no_select = Walang milestone issues.delete_branch_at = `binura ang branch na %s %s` issues.filter_label = Label -issues.filter_label_exclude = `Gamitin ang alt + click/enter para hindi isama ang mga label` +issues.filter_label_exclude = `Gamitin ang Alt + Click para hindi isama ang mga label` issues.filter_label_no_select = Lahat ng mga label issues.filter_milestone_closed = Mga nakasarang milestone issues.filter_assignee = Mangangasiwa @@ -1771,7 +1771,7 @@ issues.lock = I-lock ang usapan issues.unlock = I-unlock ang usapan issues.unlock_comment = na-unlock ang usapang ito %s issues.unlock.notice_1 = - Makakakomento muli ang lahat ng mga tao sa isyung ito. -issues.unlock.notice_2 = - Maari mong i-lock muli ang isyung ito sa hinaharap. +issues.unlock.notice_2 = - Maaari mong i-lock muli ang isyung ito sa hinaharap. issues.comment_on_locked = Hindi ka makakakomento sa naka-lock na isyu. issues.closed_by_fake = ni/ng %[2]s ay isinara %[1]s issues.comment_manually_pull_merged_at = manwal na isinama ang commit %[1]s sa %[2]s %[3]s @@ -1787,10 +1787,10 @@ issues.label_archive_tooltip = Ang mga naka-archive na label ay hindi isasama bi issues.is_stale = May mga pagbabago sa PR na ito mula sa pagsuri na ito issues.role.first_time_contributor = Unang-beses na contributor issues.lock.notice_1 = - Hindi makakadagdag ng mga bagong komento ang mga ibang user sa isyu na ito. -issues.lock.notice_3 = - Maari mong i-unlock muli ang isyung ito sa hinaharap. +issues.lock.notice_3 = - Maaari mong i-unlock muli ang isyung ito sa hinaharap. issues.label_deletion_desc = Ang pagbura ng label ay tatanggalin ito sa lahat ng mga isyu. Magpatuloy? -issues.commit_ref_at = `isinangguni ang isyu na ito mula sa commit %[2]s` -issues.ref_issue_from = `isinangguni ang isyu na ito sa %[4]s %[2]s` +issues.commit_ref_at = `isinangguni ang isyu na ito mula sa commit %s` +issues.ref_issue_from = `isinangguni ang isyu na ito sa %[3]s %[1]s` issues.num_participants_one = %d kasali issues.attachment.download = `I-click para i-download ang "%s" ` issues.num_participants_few = %d mga kasali @@ -1815,10 +1815,10 @@ issues.sign_in_require_desc = Mag-sign in upang sumali sa usapa issues.num_comments = %d mga komento issues.role.contributor_helper = Nakaraang nag-commit ang user na ito sa repositoryo na ito. issues.comment_pull_merged_at = isinama ang commit %[1]s sa %[2]s %[3]s -pulls.commit_ref_at = `isinangguni ang hiling sa paghila mula sa isang commit %[2]s` +pulls.commit_ref_at = `isinangguni ang hiling sa paghila mula sa isang commit %s` wiki.last_commit_info = Binago ni %s ang pahinang ito %s issues.content_history.edited = binago -issues.ref_pull_from = `isinangguni ang hiling sa paghila na ito %[4]s %[2]s` +issues.ref_pull_from = `isinangguni ang hiling sa paghila na ito %[3]s %[1]s` pulls.merged_title_desc_few = isinali ang %[1]d mga commit mula sa %[2]s patungong %[3]s %[4]s settings.org_not_allowed_to_be_collaborator = Hindi maaaring idagdag ang mga organisasyon bilang tagatulong. settings.add_collaborator_success = Naidagdag ang tagatulong. @@ -1828,7 +1828,7 @@ pulls.create = Gumawa ng hiling sa paghila issues.dependency.pr_close_blocked = Kailangan mong isara ang lahat ng mga isyu na humaharang sa hiling sa paghila na ito bago mo ito isama. pulls.delete.title = Burahin ang hiling sa paghila na ito? issues.dependency.pr_closing_blockedby = Hinarang ng mga sumusunod na isyu mula sa pagsara ng hiling sa paghila na ito -pulls.closed_at = `isinara ang hiling sa paghila na %[2]s` +pulls.closed_at = `isinara ang hiling sa paghila na ito %s` pulls.close = Isara ang hiling sa paghila pulls.cmd_instruction_hint = Tingnan ang mga panuto para sa command line project = Mga proyekto @@ -1836,8 +1836,8 @@ issues.content_history.deleted = binura pulls.no_results = Walang mga nahanap na resulta. pulls.closed = Sarado ang hiling sa paghila pulls.is_closed = Naisara na ang hiling sa paghila. -issues.ref_closing_from = `nagsangguni ang isyu mula sa hiling sa paghila %[4]s na magsasara sa isyu, %[2]s` -issues.ref_reopening_from = `nagsangguni ang isyu na ito mula sa hiling sa paghila %[4]s na muling bubukas, %[2]s` +issues.ref_closing_from = `nagsangguni ang isyu mula sa hiling sa paghila %[3]s na magsasara sa isyu, %[1]s` +issues.ref_reopening_from = `nagsangguni ang isyu na ito mula sa hiling sa paghila %[3]s na muling bubukas nito, %[1]s` issues.ref_closed_from = `isinara ang isyung ito %[4]s%[2]s` issues.review.wait = hiniling sa pagsuri %s issues.review.reject = hinihiling ang mga pagbago %s @@ -2015,14 +2015,14 @@ wiki.cancel = Kanselahin settings.collaboration.undefined = Hindi Natukoy settings.federation_settings = Mga Setting ng Federation settings = Mga Setting -settings.desc = Ang mga setting ang lugar kung saan maari mong ipamahala ang mga setting para sa repositoryo +settings.desc = Ang mga setting ang lugar kung saan maaari mong ipamahala ang mga setting para sa repositoryo pulls.collapse_files = I-collapse ang lahat ng mga file pulls.add_prefix = Magdagdag ng %s na prefix pulls.still_in_progress = Ginagawa pa? activity.title.prs_1 = %d hiling sa paghila activity.active_issues_count_n = %d mga aktibong isyu pulls.required_status_check_missing = Nawawala ang ilang mga kinakailangang pagsusuri. -pulls.required_status_check_administrator = Bilang tagapangasiwa, maari mo pa ring isama ang hiling sa paghila na ito. +pulls.required_status_check_administrator = Bilang tagapangasiwa, maaari mo pa ring isama ang hiling sa paghila na ito. pulls.blocked_by_approvals = Wala pang sapat na pag-apruba ang hiling sa paghila na ito. %d ng %d na pag-apruba ang ibinigay. settings.options = Repositoryo wiki.back_to_wiki = Bumalik sa pahina ng wiki @@ -2110,7 +2110,7 @@ settings.actions_desc = I-enable ang mga kasamang CI/CD pipeline gamit ang Forge settings.admin_indexer_commit_sha = Huling na-index na commit settings.admin_indexer_unindexed = Hindi naka-index settings.transfer_notices_3 = - Kung pribado ang repositoryo at ilipat sa isang indibidwal na user, ang aksyon na ito ay sinisigurado na ang user ay may pahintulot na basahin (at palitan ang mga pahintulot kung kailangan). -settings.convert_desc = Maari mong i-convert ang repositoryo na ito sa regular na repositoryo. Hindi ito mababawi. +settings.convert_desc = Maaari mong i-convert ang repositoryo na ito sa regular na repositoryo. Hindi ito mababawi. settings.transfer.button = Ilipat ang pagmamay-ari settings.signing_settings = Mga setting sa pagpapatunay ng pag-sign settings.admin_enable_close_issues_via_commit_in_any_branch = Isara ang isyu sa pamamagitan ng commit na ginawa sa hindi default na branch @@ -2137,7 +2137,7 @@ settings.deploy_key_deletion = Tanggalin ang deploy key settings.protect_enable_push = I-enable ang pagtulak settings.discord_icon_url.exceeds_max_length = Kailangang bababa o equal sa 2048 characters ang URL ng icon settings.protected_branch.save_rule = I-save ang rule -settings.mirror_settings.docs.can_still_use = Bagama't na hindi ka makakabago ng mga umiiral na mirror o gumawa ng bago, maari mo pa rin gamitin ang iyong umiiral na mirror. +settings.mirror_settings.docs.can_still_use = Bagama't na hindi ka makakabago ng mga umiiral na mirror o gumawa ng bago, maaari mo pa rin gamitin ang iyong umiiral na mirror. settings.slack_color = Kulay settings.discord_icon_url = URL ng icon settings.convert_fork_confirm = I-convert ang repositoryo @@ -2254,7 +2254,7 @@ settings.pulls.allow_rebase_update = I-enable ang pag-update ng hiling sa paghil settings.admin_enable_health_check = I-enable ang pagsusuri ng kalusugan ng repositoryo (git fsck) settings.new_owner_has_same_repo = Ang bagong may-ari ay may repositoryo na may katulad na pangalan. Mangyaring pumili ng ibang pangalan. settings.convert = I-convert sa regular na repositoryo -settings.convert_fork_desc = Maari mong i-convert ang fork na ito bilang regular na repositoryo. Hindi ito mababawi. +settings.convert_fork_desc = Maaari mong i-convert ang fork na ito bilang regular na repositoryo. Hindi ito mababawi. settings.convert_fork_notices_1 = Ang operasyon na ito ay ico-convert ang fork bilang regular na repositoryo at hindi mababawi. settings.transfer_abort_invalid = Hindi mo makakansela ang isang hindi umiiral na paglipat ng repositoryo. settings.transfer_quota_exceeded = Ang bagong may-ari (%s) ay lumalagpas sa quota. Hindi nailipat ang repositoryo. @@ -2290,8 +2290,8 @@ settings.webhook.headers = Mga header settings.webhook.payload = Nilalaman settings.webhook.body = Katawan settings.webhook.replay.description = I-replay ang webhook na ito. -settings.webhook.delivery.success = May nadagdag na event sa delivery queue. Maari magtagal ng ilang segundo bago makita sa delivery history. -settings.githooks_desc = Pinapagana ng Git ang mga Git hook. Maari mong baguhin ang mga hook file sa ibaba para mag-set up ng mga custom na operasyon. +settings.webhook.delivery.success = May nadagdag na event sa delivery queue. Maaari magtagal ng ilang segundo bago makita sa delivery history. +settings.githooks_desc = Pinapagana ng Git ang mga Git hook. Maaari mong baguhin ang mga hook file sa ibaba para mag-set up ng mga custom na operasyon. settings.githook_name = Pangalan ng hook settings.githook_content = Nilalaman ng hook settings.update_githook = I-update ang hook @@ -2362,7 +2362,7 @@ settings.mirror_settings.docs.pull_mirror_instructions = Para mag-set up ng pull milestones.invalid_due_date_format = Kailangang "yyyy-mm-dd" na format ang takdang petsa. signing.wont_sign.nokey = Walang key ang instansya na ito para i-sign ang commit na ito. activity.title.releases_1 = %d paglabas -settings.mirror_settings.docs.more_information_if_disabled = Maari kang matuto pa tungkol sa mga push at pull na mirror dito: +settings.mirror_settings.docs.more_information_if_disabled = Maaari kang matuto pa tungkol sa mga push at pull na mirror dito: settings.branches.switch_default_branch = Magpalit ng default branch settings.convert_notices_1 = Ang operasyon na ito ay ico-covert ang mirror sa regular na repositoryo at hindi mababawi. settings.convert_fork_succeed = Na-convert na ang fork sa regular na repositoryo. @@ -2732,7 +2732,7 @@ settings.protect_protected_file_patterns = Mga pattern ng nakaprotektang file (h settings.update_protect_branch_success = Binago na ang branch protection rule na "%s". settings.remove_protected_branch_success = Tinanggal ang branch protection rule na "%s". settings.tags.protection.pattern = Pattern ng tag -settings.tags.protection.pattern.description = Maari kang gumamit ng iisang pangalan o glob pattern o regular expression para magtugma ng maraming tag. Magbasa pa sa guide ng mga nakaprotektang tag. +settings.tags.protection.pattern.description = Maaari kang gumamit ng iisang pangalan o glob pattern o regular expression para magtugma ng maraming tag. Magbasa pa sa guide ng mga nakaprotektang tag. settings.thread_id = ID ng thread settings.matrix.room_id = ID ng room diff.has_escaped = May mga nakatagong Unicode character ang linya na ito @@ -2746,7 +2746,7 @@ diff.bin = BIN settings.default_update_style_desc = Ang default na istilio na gagamitin sa pag-update ng mga hiling sa paghila na nalilipas sa base branch. pulls.sign_in_require = Mag-sign in para gumawa ng bagong hiling sa paghila. new_from_template = Gumamit ng template -new_from_template_description = Maari kang pumili ng umiiral na repository template sa instansya na ito at i-apply ang mga setting nito. +new_from_template_description = Maaari kang pumili ng umiiral na repository template sa instansya na ito at i-apply ang mga setting nito. new_advanced = Mga advanced na setting new_advanced_expand = I-click para i-expand auto_init_description = Simulan ang kasaysayan ng Git gamit ang README at opsyonal na magdagdag ng mga lisensya at .gitignore na file. @@ -2777,6 +2777,10 @@ settings.event_header_action = Mga event sa run ng aksyon settings.event_action_failure = Pagkabigo settings.event_action_failure_desc = Natapos ang action run bilang pagkabigo. settings.event_action_recover = I-recover +settings.event_action_success = Matagumpay +settings.event_action_success_desc = Matagumpay na natapos ang Action Run. +settings.event_action_recover_desc = Matagumpay na natapos ang Action Run pagkatapos na nabigo ang huling Action Run sa katulad na workflow. +issues.filter_type.all_pull_requests = Lahat ng mga hiling sa paghila [search] commit_kind = Maghanap ng mga commit… @@ -3202,7 +3206,7 @@ self_check.database_collation_mismatch = Inaasahan ang database na gamitin ang c auths.oauth2_admin_group = Group claim value para sa mga tagapangasiwa. (Opsyonal - kinakailangan ang claim name sa itaas) auths.tip.facebook = Magrehistro ng bagong application sa %s at idagdag ang produktong "Facebook Login" users.restricted.description = Payagan lamang ang interaksyon sa mga repositoryo at organisasyon kung saan ang user ay dinagdag bilang tagatulong. Iniiwasan nito ang pag-access sa publikong repositoryo sa instansya na ito. -users.local_import.description = Payagan ang pag-import ng mga repositoryo mula sa local file system ng user. Maari itong maging isyu sa seguridad. +users.local_import.description = Payagan ang pag-import ng mga repositoryo mula sa local file system ng user. Maaari itong maging isyu sa seguridad. emails.delete = Burahin ang Email emails.deletion_success = Binura na ang email address. auths.oauth2_required_claim_value = Kinakailangan na claim value @@ -3447,8 +3451,8 @@ teams.owners_permission_desc = Ang mga owner ay may punong access sa lah teams.add_nonexistent_repo = Hindi pa umiiral ang repositoryo na sinusubukan mong idagdag. Mangyaring gawin iyan muna. teams.all_repositories = Lahat ng mga repositoryo teams.all_repositories_helper = Ang koponan ay may access sa lahat ng mga repositoryo. Ang pagpili nito ay idadagdag ang lahat ng mga umiiral na repositoryo sa koponan. -settings.change_orgname_redirect_prompt.with_cooldown.few = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang pangalan sa panahon ng cooldown. -settings.change_orgname_redirect_prompt.with_cooldown.one = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang pangalan ng panahon ng cooldown. +settings.change_orgname_redirect_prompt.with_cooldown.few = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang pangalan sa panahon ng cooldown. +settings.change_orgname_redirect_prompt.with_cooldown.one = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang pangalan ng panahon ng cooldown. [packages] diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 9ba91fc3a9..1cb7103bc0 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1062,8 +1062,8 @@ language.localization_project = Aidez-nous à traduire Forgejo dans votre langue language.description = Cette langue sera enregistrée dans votre compte et utilisée comme langue par défaut après votre connexion. user_block_yourself = Vous ne pouvez pas vous bloquer vous même. pronouns_custom_label = Pronoms personnalisés -change_username_redirect_prompt.with_cooldown.one = L'ancien pseudonyme sera disponible pour n'importe qui après une période d'%[1]d jour, vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période. -change_username_redirect_prompt.with_cooldown.few = L'ancien pseudonyme sera disponible pour n'importe qui après une période de %[1]d jours, vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période. +change_username_redirect_prompt.with_cooldown.one = L'ancien pseudonyme sera disponible pour n'importe qui après une période d'%[1]d jour. Vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période. +change_username_redirect_prompt.with_cooldown.few = L'ancien pseudonyme sera disponible pour n'importe qui après une période de %[1]d jours. Vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période. quota.rule.exceeded = Dépassé regenerate_token = Régénérer access_token_regeneration = Régénérer le token d'accès @@ -1653,13 +1653,13 @@ issues.close_comment_issue=Fermer avec le commentaire issues.reopen_issue=Rouvrir issues.reopen_comment_issue=Réouvrir avec le commentaire issues.create_comment=Commenter -issues.closed_at=`a fermé ce ticket %[2]s.` -issues.reopened_at=`a rouvert ce ticket %[2]s.` -issues.commit_ref_at=`a référencé ce ticket depuis une révision %[2]s.` -issues.ref_issue_from=`a fait référence à %[4]s ce ticket %[2]s.` -issues.ref_pull_from=`a fait référence à cette demande d'ajout %[4]s %[2]s.` -issues.ref_closing_from=`a fait référence à une demande d'ajout %[4]s qui clora ce ticket, %[2]s.` -issues.ref_reopening_from=`a référencé une pull request %[4]s qui va ré-ouvrir ce ticket %[2]s` +issues.closed_at=`a fermé ce ticket %s` +issues.reopened_at=`a rouvert ce ticket %s` +issues.commit_ref_at=`a référencé ce ticket depuis une révision %s` +issues.ref_issue_from=`a fait référence à ce ticket %[3]s %[1]s` +issues.ref_pull_from=`a fait référence à cette demande d'ajout %[3]s %[1]s` +issues.ref_closing_from=`a fait référence à une demande d'ajout %[3]s qui clora ce ticket, %[1]s` +issues.ref_reopening_from=`a référencé ce ticket dans une pull request %[3]s qui va ré-ouvrir ce ticket, %[1]s` issues.ref_closed_from=`a fermé ce ticket %[4]s %[2]s` issues.ref_reopened_from=`a rouvert ce ticket %[4]s %[2]s.` issues.ref_from=`de %[1]s` @@ -1967,8 +1967,8 @@ pulls.update_branch_success=La mise à jour de la branche a réussi pulls.update_not_allowed=Vous n'êtes pas autorisé à mettre à jour la branche pulls.outdated_with_base_branch=Cette branche est désynchronisée avec la branche de base pulls.close=Fermer la demande d’ajout -pulls.closed_at=`a fermé cette demande d'ajout %[2]s.` -pulls.reopened_at=`a rouvert cette demande d'ajout %[2]s.` +pulls.closed_at=`a fermé cette demande d'ajout %s` +pulls.reopened_at=`a rouvert cette demande d'ajout %s` pulls.cmd_instruction_hint=Voir les instructions en ligne de commande pulls.cmd_instruction_checkout_title=Basculer pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications. @@ -2762,7 +2762,7 @@ issues.blocked_by_user = Vous ne pouvez pas créer de tickets sur ce dépôt car pulls.blocked_by_user = Vous ne pouvez pas créer une pull request sur ce dépôt car vous êtes bloqué par son propriétaire. wiki.cancel = Annuler settings.wiki_globally_editable = Permettre l'édition du wiki a tout le monde -pulls.commit_ref_at = `a référencé cette pull request depuis le commit %[2]s` +pulls.commit_ref_at = `a référencé cette pull request depuis un commit %s` settings.new_owner_blocked_doer = Le nouveau propriétaire vous a bloqué. settings.enter_repo_name = Confirmez en entrant le propriétaire et le nom du dépôt exactement comme affiché : settings.wiki_rename_branch_main = Normalise le nom de la branche du Wiki @@ -2920,6 +2920,7 @@ settings.event_header_action = Événements d'exécution d'action settings.event_action_success_desc = L'exécution de l'action a réussi. settings.event_action_failure_desc = L'exécution de l'action a échoué. settings.event_action_recover_desc = L'exécution de l'action a réussi après l'échec de la dernière exécution de l'action dans le même workflow. +issues.filter_type.all_pull_requests = Toutes les demandes d'ajout [graphs] component_loading = Chargement %s… @@ -3057,8 +3058,8 @@ teams.invite.by=Invité par %s teams.invite.description=Veuillez cliquer sur le bouton ci-dessous pour rejoindre l’équipe. follow_blocked_user = Vous ne pouvez pas suivre cette organisation car elle vous a bloqué. open_dashboard = Ouvrir le tableau de bord -settings.change_orgname_redirect_prompt.with_cooldown.few = L'ancien nom d'organisation sera disponible pour n'importe qui après une période de %[1]d jours, vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période. -settings.change_orgname_redirect_prompt.with_cooldown.one = L'ancien nom d'organisation sera disponible pour n'importe qui après une période d'%[1]d jour, vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période. +settings.change_orgname_redirect_prompt.with_cooldown.few = L'ancien nom d'organisation sera disponible pour n'importe qui après une période de %[1]d jours. Vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période. +settings.change_orgname_redirect_prompt.with_cooldown.one = L'ancien nom d'organisation sera disponible pour n'importe qui après une période d'%[1]d jour. Vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période. [admin] dashboard=Tableau de bord diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index d2d960b627..3bb06e8c21 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -1219,11 +1219,11 @@ issues.close_comment_issue = Dún le trácht issues.reopen_issue = Athoscail issues.reopen_comment_issue = Athoscail le trácht issues.create_comment = Trácht -issues.closed_at = `dhún an cheist seo %[2]s` -issues.reopened_at = `athoscail an t-eagrán seo %[2]s` -issues.commit_ref_at = `rinne tagairt don cheist seo ó ghealltanas %[2]s` -issues.ref_issue_from = `rinne dagairt don cheist seo %[4]s %[2]s` -issues.ref_pull_from = `rinne dagairt don iarratas tarraingthe seo %[4]s %[ 2]s` +issues.closed_at = `dhún an cheist seo %s` +issues.reopened_at = `athoscail an t-eagrán seo %s` +issues.commit_ref_at = `rinne tagairt don cheist seo ó ghealltanas %s` +issues.ref_issue_from = `rinne dagairt don cheist seo %[3]s %[1]s` +issues.ref_pull_from = `rinne dagairt don iarratas tarraingthe seo %[3]s %[1]s` issues.ref_closed_from = `dhún an cheist seo %[4]s %[2]s` issues.ref_reopened_from = `d'athoscail an eagrán seo %[4]s %[2]s` issues.ref_from = `ó %[1]s` @@ -1456,8 +1456,8 @@ pulls.update_branch_success = Bhí nuashonrú brainse rathúil pulls.update_not_allowed = Ní cheadaítear duit brainse a nuashonrú pulls.outdated_with_base_branch = Tá an brainse seo as dáta leis an mbunbhrainse pulls.close = Dún Iarratas Tarraing -pulls.closed_at = `dhún an t-iarratas tarraingthe seo %[2]s` -pulls.reopened_at = `athoscail an t-iarratas tarraingthe seo %[2]s` +pulls.closed_at = `dhún an t-iarratas tarraingthe seo %s` +pulls.reopened_at = `athoscail an t-iarratas tarraingthe seo %s` pulls.cmd_instruction_checkout_title = Seiceáil pulls.cmd_instruction_checkout_desc = Ó stór tionscadail, seiceáil brainse nua agus déan tástáil ar na hathruithe. pulls.cmd_instruction_merge_title = Cumaisc diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 411bad835a..3e93ee8ba9 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -932,7 +932,7 @@ issues.close_comment_issue=Hozzászólás és lezárás issues.reopen_issue=Újranyitás issues.reopen_comment_issue=Hozzászólás és újranyitás issues.create_comment=Hozzászólás -issues.commit_ref_at=`hivatkozott erre a hibajegyre egy commit-ból %[2]s` +issues.commit_ref_at=`hivatkozott erre a hibajegyre egy commit-ból %s` issues.role.owner=Tulajdonos issues.role.member=Tag issues.re_request_review=Véleményezés újrakérése diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 673d1464b1..f1a392105e 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -796,7 +796,7 @@ issues.close_comment_issue=Komentar dan Tutup issues.reopen_issue=Buka kembali issues.reopen_comment_issue=Komentar dan Buka Kembali issues.create_comment=Komentar -issues.commit_ref_at=`merujuk masalah dari komit %[2]s` +issues.commit_ref_at=`merujuk masalah dari komit %s` issues.role.owner=Pemilik issues.role.member=Anggota issues.sign_in_require_desc=Masuk untuk bergabung dengan percakapan ini. diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index 9b1d56fed9..baf8286923 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -805,8 +805,8 @@ issues.close_comment_issue=Senda ummæli og Loka issues.reopen_issue=Enduropna issues.reopen_comment_issue=Senda ummæli og Enduropna issues.create_comment=Senda Ummæli -issues.closed_at=`lokaði þessu vandamáli %[2]s` -issues.reopened_at=`enduropnaði þetta vandamál %[2]s` +issues.closed_at=`lokaði þessu vandamáli %s` +issues.reopened_at=`enduropnaði þetta vandamál %s` issues.ref_reopened_from=`enduropnaði þetta vandamál %[4]s %[2]s` issues.author=Höfundur issues.role.owner=Eigandi diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 48995e951f..d46f709cde 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -54,7 +54,7 @@ mirror=Mirror new_repo=Nuovo repository new_migrate=Nuova migrazione new_mirror=Nuovo mirror -new_fork=Nuova derivazione +new_fork=Nuova biforcazione new_org=Nuova organizzazione new_project=Nuovo progetto manage_org=Gestisci le organizzazioni @@ -143,12 +143,12 @@ confirm_delete_selected = Confermare l'eliminazione di tutti gli elementi selezi sign_in_with_provider = Accedi con %s new_project_column = Nuova colonna toggle_menu = Mostra/Nascondi menu -filter.not_fork = Non fork +filter.not_fork = Non biforcazioni filter = Filtro filter.clear = Rimuovi filtri filter.is_archived = Archiviato filter.not_archived = Non archiviato -filter.is_fork = Da fork +filter.is_fork = Biforcazioni filter.is_mirror = Mirror filter.not_mirror = Non mirror filter.is_template = Modelli @@ -209,6 +209,7 @@ table_modal.label.columns = Colonne link_modal.header = Aggiungi collegamento link_modal.url = Url link_modal.description = Descrizione +link_modal.paste_reminder = Suggerimento: se hai già copiato un URL negli appunti, puoi incollarlo direttamente nell’editor per creare un collegamento. [filter] string.asc = A - Z @@ -232,6 +233,7 @@ lightweight_desc=Forgejo ha requisiti minimi bassi e può funzionare su un econo license=Open Source license_desc=Ottieni Forgejo! Partecipa per contribuire a rendere questo progetto ancora più bello. Non aver paura di diventare collaborante! install_desc = Semplicemente avvia l'eseguibile per la tua piattaforma, distribuiscilo con Docker, oppure scarica il pacchetto. +platform_desc = È stato verificato che Forgejo è pienamente compatibile con sistemi operativi liberi, come Linux e FreeBSD, nonché con diverse architetture CPU. Scegli liberamente la piattaforma che preferisci! [install] install=Installazione @@ -396,12 +398,12 @@ go_to = Vai a search.type.tooltip = Tipo di ricerca search.fuzzy.tooltip = Includi anche i risultati che corrispondono parzialmente ai termini di ricerca code_search_results = Risultati di ricerca per "%s" -relevant_repositories_tooltip = I repositori derivati o che non hanno argomento, icona, né descrizione sono nascosti. +relevant_repositories_tooltip = I repositori che sono biforcazioni o che non hanno argomento, icona, né descrizione sono nascosti. relevant_repositories = Sono visibili solo i repositori pertinenti, 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 +forks_one = %d biforcazioni +forks_few = %d biforcazioni stars_one = %d stella [auth] @@ -485,6 +487,8 @@ sign_in_openid = Procedi con OpenID hint_login = Hai già un'utenza? Accedi! hint_register = Non hai un'utenza? Registrati ora. sign_up_button = Registrati ora. +unauthorized_credentials = Le credenziali non sono corrette o sono scadute. Controlla il comando o vedi %s per maggiori informazioni +use_onetime_code = Usa un codice monouso [mail] view_it_on=Visualizza su %s @@ -680,6 +684,8 @@ Location = Posizione AccessToken = Token di accesso FullName = Nome e cognome To = Nome del ramo +email_domain_is_not_allowed = Il dominio dell'indirizzo email dell'utente %s è in conflitto con EMAIL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Assicurati di aver inserito correttamente l'indirizzo email. +username_claiming_cooldown = Il nome utente non può essere assegnato, poiché il periodo di attesa non è ancora terminato. Sarà disponibile il %[1]s. [user] @@ -723,6 +729,7 @@ followers.title.one = Seguace followers.title.few = Seguaci following.title.one = Seguito following.title.few = Osservato +public_activity.visibility_hint.self_private_profile = Poiché il tuo profilo è privato, la tua attività è visibile solo a te e agli amministratori dell'istanza. Configura. [settings] @@ -1045,7 +1052,7 @@ added_on = Aggiunto su %s additional_repo_units_hint = Suggerisci l'attivazione di unità aggiuntive nel repositorio update_hints = Aggiorna suggerimenti update_hints_success = I suggerimenti sono stati aggiornati. -additional_repo_units_hint_description = Mostra un pulsante "Aggiungi più sezioni..." per i repositori che non hanno tutte le sezioni disponibili aggiunte. +additional_repo_units_hint_description = Visualizza un suggerimento “Abilita altro” per i repositori che non hanno tutte le unità disponibili abilitate. hints = Suggerimenti pronouns = Pronomi pronouns_custom = Personalizzato @@ -1053,6 +1060,34 @@ pronouns_unspecified = Non specificato language.title = Lingua predefinita language.description = Questa lingua verrà salvata nella tua utenza e verrà usata come predefinita ogni volta che farai l'accesso. language.localization_project = Aiutaci a tradurre Forgejo nella tua lingua! Più informazioni. +quota.sizes.assets.attachments.all = Allegati +quota.rule.no_limit = Illimitato +quota.sizes.assets.attachments.releases = Allegati del rilascio +quota.rule.exceeded = Superato +regenerate_token = Rigenera +access_token_regeneration = Rigenera il token d'accesso +access_token_regeneration_desc = Rigenerare un token comporterà la revoca dell'accesso al tuo account per tutte le applicazioni che lo utilizzano. Questa operazione è irreversibile. Vuoi procedere? +regenerate_token_success = Il token è stato rigenerato. Le applicazioni che lo utilizzano non hanno più accesso alla tua utenza e devono essere aggiornate con il nuovo token. +user_block_yourself = Non puoi bloccare te stesso. +quota.applies_to_user = Le seguenti regole di quota si applicano al tuo account +quota.applies_to_org = Le seguenti regole di quota si applicano a questa organizzazione +quota.rule.exceeded.helper = La dimensione totale degli oggetti per questa regola ha superato la quota. +quota.sizes.all = Tutti +quota.sizes.repos.all = Repositori +quota.sizes.repos.public = Repositori pubblici +quota.sizes.repos.private = Repositori privati +quota.sizes.git.all = Contenuto git +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.all = Risorse +quota.sizes.assets.attachments.issues = Allegati della segnalazione +quota.sizes.assets.artifacts = Artefatti +quota.sizes.assets.packages.all = Pacchetti +quota.sizes.wiki = Wiki +keep_pronouns_private = Mostra i pronomi solo agli utenti che hanno effettuato il login +keep_pronouns_private.description = Questa impostazione nasconderà i tuoi pronomi agli utenti non ancora autenticati. +storage_overview = Panoramica spazio di archiviazione +quota = Quota +change_username_redirect_prompt.with_cooldown.one = Il vecchio nome utente sarà disponibile per tutti dopo un periodo di protezione di %\[1]d giorni. Durante questo periodo di attesa potrai comunque tornare al vecchio nome utente. [repo] owner=Proprietario @@ -1067,10 +1102,10 @@ template_description=I modelli di repositori consentono allɜ 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'amministratorə del sito impone che i nuovi repositori siano privati. -visibility_fork_helper=(Questa modifica influenzerà la visibilità di tutti i fork.) +visibility_fork_helper=(Questa modifica influenzerà la visibilità di tutte le biforcazioni.) clone_helper=Hai bisogno di aiuto per la clonazione? Visita Help. fork_repo=Deriva repositorio -fork_from=Deriva da +fork_from=Biforcazione di 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 repositorio derivato non può essere modificata. @@ -1514,13 +1549,13 @@ issues.close_comment_issue=Commenta e chiudi issues.reopen_issue=Riapri issues.reopen_comment_issue=Commenta e riapri issues.create_comment=Commento -issues.closed_at=`ha chiuso questa segnalazione %[2]s` -issues.reopened_at=`ha riaperto questa segnalazione %[2]s` -issues.commit_ref_at=`ha fatto riferimento a questa segnalazione dal commit %[2]s` -issues.ref_issue_from=`ha fatto riferimento a questa segnalazione %[4]s %[2]s` -issues.ref_pull_from=`ha fatto riferimento a questa richiesta di modifica %[4]s %[2]s` -issues.ref_closing_from=`ha fatto riferimento a questa segnalazione da una richiesta di modifica %[4]s che la chiuderà, %[2]s` -issues.ref_reopening_from=`ha fatto riferimento a questa segnalazione da una richiesta di modifica %[4]s che la riaprirà, %[2]s` +issues.closed_at=`ha chiuso questa segnalazione %s` +issues.reopened_at=`ha riaperto questa segnalazione %s` +issues.commit_ref_at=`ha fatto riferimento a questa segnalazione dal commit %s` +issues.ref_issue_from=`ha fatto riferimento a questa segnalazione %[3]s %[1]s` +issues.ref_pull_from=`ha fatto riferimento a questa richiesta di modifica %[3]s %[1]s` +issues.ref_closing_from=`ha fatto riferimento a questa segnalazione da una richiesta di modifica %[3]s che la chiuderà, %[1]s` +issues.ref_reopening_from=`ha fatto riferimento a questa segnalazione da una richiesta di modifica %[3]s che la riaprirà, %[1]s` issues.ref_closed_from=`chiuso questa segnalazione %[4]s %[2]s` issues.ref_reopened_from=`ha riaperto questa segnalazione %[4]s %[2]s` issues.ref_from=`da %[1]s` @@ -1718,7 +1753,7 @@ pulls.cannot_merge_work_in_progress=Questa richiesta di modifica è contrassegna pulls.still_in_progress=Ancora in corso? pulls.add_prefix=Aggiungi prefisso %s pulls.remove_prefix=Rimuovi il prefisso %s -pulls.data_broken=Questa richiesta di modifica è rovinata a causa di informazioni mancanti riguardo la derivazione. +pulls.data_broken=Questa richiesta di modifica non è valida a causa di informazioni mancanti sulla biforcazione. pulls.files_conflicted=Questa richiesta di modifica va in conflitto con il ramo di destinazione. 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. @@ -1776,8 +1811,8 @@ pulls.update_branch_rebase=Aggiorna il ramo per cambio base pulls.update_branch_success=Ramo aggiornato con successo pulls.update_not_allowed=Non ti è permesso aggiornare il ramo pulls.outdated_with_base_branch=Questo ramo non è aggiornato con il ramo di base -pulls.closed_at=`ha chiuso questa richiesta di modifica %[2]s` -pulls.reopened_at=`ha riaperto questa richiesta di modifica %[2]s` +pulls.closed_at=`ha chiuso questa richiesta di modifica %s` +pulls.reopened_at=`ha riaperto questa richiesta di modifica %s` pulls.auto_merge_button_when_succeed=(Quando i controlli sono superati) pulls.auto_merge_when_succeed=Unione automatica quando tutti i controlli sono superati @@ -2100,7 +2135,7 @@ settings.event_create_desc=Ramo o etichetta creati. settings.event_delete=Elimina settings.event_delete_desc=Ramo o etichetta eliminati. settings.event_fork=Deriva -settings.event_fork_desc=Repository derivato. +settings.event_fork_desc=Creata una biforcazione del repositorio. settings.event_wiki=Wiki settings.event_release=Release settings.event_release_desc=Release pubblicata, aggiornata o rimossa in una repository. @@ -2137,7 +2172,7 @@ settings.event_pull_request_sync_desc=Pull request sincronizzata. settings.event_package=Pacchetto settings.event_package_desc=Pacchetto creato o eliminato in un repository. settings.branch_filter=Filtro rami -settings.branch_filter_desc=Whitelist dei rami per gli eventi di spinta, creazione dei rami e cancellazione dei rami, specificati come modello globo. Se vuoto o *, gli eventi per tutti i rami sono segnalati. Vedi la documentazione %[2]s per la sintassi. Esempi: master, {master,release*}. +settings.branch_filter_desc=Filtro, scritto come pattern glob, da applicare ai rami per gli eventi di tipo immissione, creazione di rami e rimozione di rami. Se vuoto o *, vengono considerati tutti gli eventi di tutti i rami. Maggiori dettagli sulla sintassi presso %[2]s. Esempi: master, {master,release*}. settings.active=Attivo settings.active_helper=Le informazioni sugli eventi innescati saranno inviate a questo URL del webhook. settings.add_hook_success=Il webhook è stato aggiunto. @@ -2167,8 +2202,8 @@ settings.web_hook_name_packagist=Packagist settings.packagist_username=Nome utente Packagist settings.packagist_api_token=API token settings.packagist_package_url=Url pacchetto pacchetti -settings.deploy_keys=Dispiega chiavi -settings.add_deploy_key=Aggiungi chiave di dispiego +settings.deploy_keys=Chiavi di distribuzione +settings.add_deploy_key=Aggiungi chiave di distribuzione settings.deploy_key_desc=Le deploy key possiedono l'accesso solamente alla lettura di un repository. settings.is_writable=Abilita accesso scrittura settings.is_writable_info=Permetti a questa deploy key di pushare nella repository. @@ -2177,7 +2212,7 @@ settings.title=Titolo settings.deploy_key_content=Contenuto settings.key_been_used=Una deploy key con contenuto identico è già in uso. settings.key_name_used=Esiste già una deploy key con questo nome. -settings.deploy_key_deletion=Rimuovi chiave di dispiego +settings.deploy_key_deletion=Rimuovi chiave di distribuzione settings.deploy_key_deletion_desc=Rimuovere una chiave di distribuzione ne revocherà l'accesso a questo repository. Continuare? settings.deploy_key_deletion_success=La chiave di distribuzione è stata rimossa. settings.branches=Rami @@ -2620,7 +2655,7 @@ 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 = Quest*utente è statə invitatə a collaborare al progetto. -pulls.commit_ref_at = `ha fatto riferimento a questa richiesta di modifica da un commit %[2]s` +pulls.commit_ref_at = `ha fatto riferimento a questa richiesta di modifica da un commit %s` settings.thread_id = ID della discussione release.title = Titolo del rilascio visibility_helper = Rendi il repositorio privato @@ -2660,7 +2695,7 @@ wiki.page_title = Titolo della pagina wiki.page_content = Contenuto della pagina settings.mirror_settings.pushed_repository = Repositorio immesso settings.mirror_settings.push_mirror.edit_sync_time = Modifica intervallo di sincronizzazione degli specchi -settings.units.units = Unità della repository +settings.units.units = Sezioni del repositorio 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. @@ -2732,7 +2767,7 @@ pulls.merged_title_desc_one = ha fuso %[1]d commit da %[2]s in Accedi per creare una richiesta di modifica. +settings.mirror_settings.push_mirror.none_ssh = Nessuno +sync_fork.branch_behind_one = Questo ramo è indietro di %[1]d commit rispetto a %[2]s +sync_fork.branch_behind_few = Questo ramo è indietro di %[1]d commit rispetto a %[2]s +no_eol.text = Nessun fine linea +no_eol.tooltip = Questo file non contiene un carattere di fine linea finale. +milestones.filter_sort.name = Nome +settings.protect_new_rule = Crea una nuova regola di protezione dei rami +editor.commit_email = E-mail di commit +mirror_public_key = Chiave SSH pubblica +mirror_denied_combination = Non è possibile utilizzare contemporaneamente l'autenticazione tramite chiave pubblica e password. +release.type_attachment = Allegato +release.invalid_external_url = URL esterno invalido: "%s" +new_from_template = Utilizza un modello +new_from_template_description = Puoi selezionare un modello di repositorio esistente su questa istanza e applicare le sue impostazioni. +new_advanced = Impostazioni avanzate +new_advanced_expand = Clicca per espandere +summary_card_alt = Scheda riepilogativa del repository %s +issues.filter_sort.relevance = Rilevanza +issues.num_reviews_one = %d revisioni +issues.num_reviews_few = %d revisioni +issues.reaction.add = Aggiungi reazione +issues.reaction.alt_many = %[1] e altri %[2]d hanno reagito %[3]s. +issues.reaction.alt_remove = Rimuovi la reazione %[1]s dal commento. +issues.reaction.alt_add = Aggiungi la reazione %[1]s al commento. +issues.review.remove_review_requests = rimosso richieste di revisione per %\[1]s %\[2]s +comment.blocked_by_user = Non è possibile commentare perché sei stato bloccato dal proprietario del repositorio o dall'autore. +issues.summary_card_alt = Scheda riepilogativa di una segnalazione intitolata "%s" nel repositorio %s +pulls.delete_after_merge.head_branch.is_default = Il ramo head che desideri eliminare è il ramo predefinito e non può essere eliminato. +settings.event_action_success = Successo +settings.event_action_success_desc = L'esecuzione dell'azione è andata a buon fine. +diff.git-notes.remove-header = Rimuovi nota +diff.git-notes.remove-body = Questa nota verrà rimossa. +activity.commit = Attività di commit [graphs] contributors.what = contribuzioni @@ -2839,7 +2947,7 @@ 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=Consenti l'accesso a sezioni di progetto +team_unit_desc=Consenti l'accesso alle sezioni del repositorio team_unit_disabled=(Disabilitato) form.create_org_not_allowed=Non disponi dell'autorizzazione per creare un organizzazione. @@ -3493,6 +3601,12 @@ config.cache_test_slow = Successo nel controllo della cache, ma la risposta è l config.app_slogan = Slogan dell'istanza auths.default_domain_name = Nome di dominio predefinito utilizzato per l'indirizzo e-mail users.restricted.description = Permetti di interagire solo con i repositori e le organizzazioni in cui l'utente è aggiuntə come collaborante. Ciò evita l'accesso ai repositori pubblici di quest'istanza. +emails.deletion_success = L'indirizzo e-mail è stato eliminato. +monitor.duration = Durata (s) +emails.delete_desc = Confermare l’eliminazione di questo indirizzo email? +emails.delete_primary_email_error = Non puoi eliminare la e-mail primaria. +emails.delete = Elimina e-mail +users.organization_creation.description = Abilita la creazione di nuove organizzazioni. [action] @@ -3737,6 +3851,31 @@ owner.settings.cargo.initialize.success = L'indice di Cargo è stato creato corr owner.settings.cargo.rebuild.no_index = Impossibile ricostruire, nessun indice è inizializzato. owner.settings.cargo.rebuild.description = La ricostruzione può essere utile se l'indice non è sincronizzato con i pacchetti Cargo conservati. npm.dependencies.bundle = Dipendenze raggruppate +arch.version.groups = Gruppo +arch.version.conflicts = Va in conflitto con +arch.version.depends = Dipende da +arch.version.makedepends = Dipendenze di build +arch.version.checkdepends = Dipendenze di controllo +arch.version.replaces = Sostituisce +arch.version.optdepends = Dipende opzionalmente da +arch.version.backup = Backup +search_in_external_registry = Cerca in %s +arch.version.provides = Fornisce +arch.pacman.conf = Aggiungi il server con la relativa distribuzione e architettura a /etc/pacman.conf: +alt.setup = Aggiungi il repositorio alla lista dei repositori in rete (seleziona l'architettura necessaria al posto di "_arch_"): +container.images.title = Immagini +arch.version.properties = Proprietà della versione +alt.registry.install = Per installare il pacchetto, esegui il comando seguente: +alt.install = Installa pacchetto +alt.registry = Configura questo registro dalla riga di comando: +arch.pacman.helper.gpg = Aggiungi il certificato a pacman: +arch.pacman.repo.multi = %s ha la stessa versione in diverse distribuzioni. +arch.pacman.repo.multi.item = Configurazione per %s +arch.pacman.sync = Sincronizza il paccketto con pacman: +arch.version.description = Descrizione +alt.repository = Informazioni del repositorio +alt.repository.architectures = Architetture +alt.repository.multiple_groups = Questo pacchetto è disponibile per più gruppi. [secrets] secrets = Segreti @@ -3834,7 +3973,7 @@ runs.empty_commit_message = (messaggio di commit vuoto) runs.no_runs = Il flusso di lavoro non è stato ancora eseguito. variables.creation.success = La variabile "%s" è stata aggiunta. variables.description = Le variabili saranno passate a determinate azioni e non possono essere lette altrimenti. -need_approval_desc = È necessaria l'approvazione per eseguire flussi di lavoro per richieste di modifica da derivazioni. +need_approval_desc = È necessaria l'approvazione per eseguire flussi di lavoro per richieste di modifica da biforcazioni. runs.no_workflows.documentation = Per ulteriori informazioni sulle Forgejo Actions vedi la documentazione. runs.no_workflows.quick_start = Non sai come iniziare con le Forgejo Actions? Vedi la guida rapida. runners.delete_runner_notice = Se un'attività è in esecuzione su questo esecutore sarà terminata ed etichettata fallito. Potrebbe rompere flussi di lavoro di costruzione. @@ -3848,6 +3987,8 @@ workflow.dispatch.invalid_input_type = Tipo ingresso "%s" non valido. workflow.dispatch.warn_input_limit = Visualizzati solo i primi %d ingressi. runs.no_job = Il flusso di lavoro deve contenere almeno un incarico workflow.dispatch.use_from = Usa flusso di lavoro da +variables.not_found = Non è stato possibile trovare la variabile. +runs.expire_log_message = I log sono stati eliminati in quanto troppo vecchi. @@ -3856,6 +3997,7 @@ workflow.dispatch.use_from = Usa flusso di lavoro da type-3.display_name = Progetto dell'organizzazione type-1.display_name = Progetto individuale type-2.display_name = Progetto +deleted.display_name = Progetto eliminato [git.filemode] symbolic_link=Link Simbolico @@ -3896,6 +4038,7 @@ milestone_kind = Ricerca tappe... regexp_tooltip = Interpreta i termini di ricerca come un'espressione regolare regexp = Espressione Regolare union_tooltip = Include i risultati che combaciano con una qualsiasi delle parole chiave separata da spazi +union = Parole chiavi [munits.data] gib = GiB @@ -3914,4 +4057,16 @@ filepreview.line = Linea %[1]d in %[2]s [repo.permissions] issues.write = Scrittura: Chiudere segnalazioni e gestire metadati come etichette, traguardi, assegnatarɜ, scadenze e dipendenze. -pulls.write = Scrittura: Chiudere richieste di modifica e gestire metadati come etichette, traguardi, assegnatarɜ, scadenze e dipendenze. \ No newline at end of file +pulls.write = Scrittura: Chiudere richieste di modifica e gestire metadati come etichette, traguardi, assegnatarɜ, scadenze e dipendenze. +releases.write = Scrittura: Può pubblicare, modificare ed eliminare rilasci e le risorse ad essi allegate. +code.write = Scrittura: Può aggiungere commit al repositorio, creare rami ed etichette. +wiki.read = Lettura: Può leggere la wiki integrata e la sua cronologia. +releases.read = Lettura: Può visualizzare e scaricare i rilasci. +projects.read = Lettura: Può accedere alle board di progetto del repositorio. +code.read = Lettura: Può accedere e clonare il codice del repositorio. +wiki.write = Scrittura: Può creare, aggiornare ed eliminare pagine nella wiki integrata. +issues.read = Lettura: Può leggere e creare segnalazioni e commenti. +pulls.read = Lettura: Può leggere e creare richieste di modifica. + +[translation_meta] +test = daje Roma \ No newline at end of file diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index d4d7024f5d..555f5c6a75 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -1610,13 +1610,13 @@ 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.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.closed_at=`がイシューをクローズ %s` +issues.reopened_at=`がイシューを再オープン %s` +issues.commit_ref_at=`がコミットでこのイシューを参照 %s` +issues.ref_issue_from=`が%[3]s、このイシューを参照 %[1]s` +issues.ref_pull_from=`が%[3]s、このプルリクエストを参照 %[1]s` +issues.ref_closing_from=`が%[3]s、プルリクエストがこのイシューをクローズするよう参照 %[1]s` +issues.ref_reopening_from=`が%[3]s、プルリクエストがこのイシューを再オープンするよう参照 %[1]s` issues.ref_closed_from=`が%[4]s、このイシューをクローズ %[2]s` issues.ref_reopened_from=`が%[4]s、このイシューを再オープン %[2]s` issues.ref_from=` %[1]s にて` @@ -1923,8 +1923,8 @@ pulls.update_branch_success=ブランチの更新が成功しました pulls.update_not_allowed=ブランチを更新する権限がありません pulls.outdated_with_base_branch=このブランチはベースブランチに対して最新ではありません pulls.close=プルリクエストをクローズ -pulls.closed_at=`がプルリクエストをクローズ %[2]s` -pulls.reopened_at=`がプルリクエストを再オープン %[2]s` +pulls.closed_at=`がプルリクエストをクローズ %s` +pulls.reopened_at=`がプルリクエストを再オープン %s` pulls.cmd_instruction_hint=コマンドラインの手順を表示 pulls.cmd_instruction_checkout_title=チェックアウト pulls.cmd_instruction_checkout_desc=プロジェクトリポジトリから新しいブランチをチェックアウトし、変更内容をテストします。 @@ -2721,7 +2721,7 @@ 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.commit_ref_at = `このプルリクエストを言及するコミット %s` pulls.fast_forward_only_merge_pull_request = Fast-forwardのみ admin.manage_flags = フラグ管理 admin.update_flags = フラグを更新 diff --git a/options/locale/locale_jbo.ini b/options/locale/locale_jbo.ini index 6124dc4d22..947bb298de 100644 --- a/options/locale/locale_jbo.ini +++ b/options/locale/locale_jbo.ini @@ -2,4 +2,12 @@ [common] -home = zdani \ No newline at end of file +home = zdani +dashboard = jitypalna +explore = sisku +help = se sidju +logo = se'isni +sign_in = co'a nerkla +sign_in_with_provider = co'a nerka sepi'o la .%s. +sign_out = co'a cliva +sign_up = co'a gumri \ No newline at end of file diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index 433ec01828..be0400bea4 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -943,7 +943,7 @@ issues.close_comment_issue=클로즈 및 코멘트 issues.reopen_issue=다시 열기 issues.reopen_comment_issue=다시 오픈 및 코멘트 issues.create_comment=코멘트 -issues.commit_ref_at=` 커밋 %[2]s에서 이 이슈 언급` +issues.commit_ref_at=` 커밋 %s에서 이 이슈 언급` issues.role.owner=소유자 issues.role.member=멤버 issues.sign_in_require_desc=로그인하여 이 대화에 참여하세요. @@ -1378,7 +1378,7 @@ issues.closed_by_fake = %[2]s님이 %[1]s에 닫음 issues.new.closed_projects = 닫힌 프로젝트 pulls.merged_by_fake = %[2]s님이 %[1]s 병합함 issues.closed_by = %[3]s님이 %[1]s에 닫음 -issues.closed_at = `%[2]s`에 이 이슈를 닫음 +issues.closed_at = `%s`에 이 이슈를 닫음 issues.filter_milestone_closed = 닫힌 마일스톤 issues.opened_by_fake = %[2]s님이 %[1]s에 열음 issues.filter_project_none = 프로젝트 없음 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index b53c0faa9b..98baff217b 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1651,13 +1651,13 @@ issues.close_comment_issue=Aizvērt ar piebildi issues.reopen_issue=Atvērt atkārtoti issues.reopen_comment_issue=Atkārtoti atvērt ar piebildi issues.create_comment=Pievienot piebildi -issues.closed_at=`aizvēra šo pieteikumu %[2]s` -issues.reopened_at=`atkārtoti atvēra šo pieteikumu %[2]s` -issues.commit_ref_at=`atsaucās uz šo pieteikumu iesūtījumā %[2]s` -issues.ref_issue_from=`atsaucās uz šo pieteikumu %[4]s %[2]s` -issues.ref_pull_from=`atsaucās uz šo izmaiņu pieprasījumu %[4]s %[2]s` -issues.ref_closing_from=`atsaucās uz šo pieteikumu izmaiņu pieprasījumā %[4]s, kas aizvērs to, %[2]s` -issues.ref_reopening_from=`atsaucās uz šo pieteikumu izmaiņu pieprasījumā %[4]s, kas atkārtoti atvērs to, %[2]s` +issues.closed_at=`aizvēra šo pieteikumu %s` +issues.reopened_at=`atkārtoti atvēra šo pieteikumu %s` +issues.commit_ref_at=`atsaucās uz šo pieteikumu iesūtījumā %s` +issues.ref_issue_from=`atsaucās uz šo pieteikumu %[3]s %[1]s` +issues.ref_pull_from=`atsaucās uz šo izmaiņu pieprasījumu %[3]s %[1]s` +issues.ref_closing_from=`atsaucās uz šo pieteikumu izmaiņu pieprasījumā %[3]s, kas aizvērs to, %[1]s` +issues.ref_reopening_from=`atsaucās uz šo pieteikumu izmaiņu pieprasījumā %[3]s, kas atkārtoti atvērs to, %[1]s` issues.ref_closed_from=`aizvēra pieteikumu %[4]s %[2]s` issues.ref_reopened_from=`atkārtoti atvēra pieteikumu %[4]s %[2]s` issues.ref_from=`no %[1]s` @@ -1964,8 +1964,8 @@ pulls.update_branch_success=Zara atjaunināšana bija sekmīga pulls.update_not_allowed=Nav ļauts atjaunināt zaru pulls.outdated_with_base_branch=Šis zars ir novecojis salīdzinājumā ar pamata zaru pulls.close=Aizvērt izmaiņu pieprasījumu -pulls.closed_at=`aizvēra šo izmaiņu pieprasījumu %[2]s` -pulls.reopened_at=`atkārtoti atvēra šo izmaiņu pieprasījumu %[2]s` +pulls.closed_at=`aizvēra šo izmaiņu pieprasījumu %s` +pulls.reopened_at=`atkārtoti atvēra šo izmaiņu pieprasījumu %s` pulls.cmd_instruction_hint=Apskatīt komandrindas izmantošanas norādes pulls.cmd_instruction_checkout_title=Paņemt pulls.cmd_instruction_checkout_desc=Projekta glabātavā jāizveido jauns zars un jāpārbauda izmaiņas. @@ -2826,7 +2826,7 @@ issues.author.tooltip.pr = Šis lietotājs ir šī izmaiņu pieprasījuma izveid pulls.edit.already_changed = Neizdevās saglabāt izmaiņu pieprasījuma izmaiņas. Izskatās, ka saturu jau ir mainījis kāds cits lietotājs. Lūgums atsvaidzināt lapu un mēģināt labot vēlreiz, lai izvairītos no izmaiņu pārrakstīšanas pulls.blocked_by_user = Tu nevari izveidot izmaiņu pieprasījumu šajā glabātavā, jo tās īpašnieks ir Tevi liedzis. issues.all_title = Visi -pulls.commit_ref_at = ` atsaucāš uz šo izmaiņu pieprasījumu iesūtījumā %[2]s` +pulls.commit_ref_at = ` atsaucās uz šo izmaiņu pieprasījumu iesūtījumā %s` issues.num_participants_one = %d dalībnieks pulls.title_desc_one = vēlas iekļaut %[1]d iesūtījumu no %[2]s %[3]s issues.archived_label_description = (Arhivēts) %s @@ -2920,6 +2920,7 @@ settings.event_action_recover = Atgūt settings.event_action_recover_desc = Darbības izpilde bija sekmīga pēc kļūmes iepriekšējā darbības izpildē tajā pašā darbplūsmā. settings.event_action_success = Sekmīgi settings.event_action_success_desc = Darbības izpilde bija sekmīga. +issues.filter_type.all_pull_requests = Visi izmaiņu pieprasījumi [graphs] component_loading=Ielādē %s… diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index 2175e11b02..68fe899d6e 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -1347,7 +1347,7 @@ issues.change_title_at = `hett %[3]s de Titel vun %[1]s issues.change_ref_at = `hett %[3]s de Nömen vun %[1]s to %[2]s ännert` issues.delete_branch_at = `hett %[2]s de Twieg %[1]s lösket` issues.filter_label = Vermark -issues.filter_label_exclude = `Bruuk Alt+Klick/Enter, um Vermarkens uttosluten` +issues.filter_label_exclude = Bruuk Alt + Klick, um Vermarkens uttosluten issues.filter_label_no_select = All Vermarkens issues.filter_label_select_no_label = Keen Vermark issues.filter_milestone = Marksteen @@ -1434,12 +1434,12 @@ issues.comment_pull_merged_at = hett Kommitteren %[1]s in %[2]s %[3]s tosamenfö issues.close_comment_issue = Mit Kommentaar dichtmaken issues.reopen_comment_issue = Mit Kommentaar weer opmaken issues.create_comment = Kommenteren -issues.reopened_at = `hett deeses Gefall %[2]s weer opmaakt` +issues.reopened_at = `hett deeses Gefall %s weer opmaakt` issues.comment_manually_pull_merged_at = hett Kommitteren %[1]s in %[2]s %[3]s vun Hand tosamenföhrt issues.reopen_issue = Weer opmaken -issues.closed_at = `hett deeses Gefall %[2]s dichtmaakt` -issues.commit_ref_at = `hett deeses Gefall %[2]s vun eenem Kommitteren benöömt` -issues.ref_closing_from = `hett deeses Gefall %[2]s vun eenem Haalvörslag, wat ’t %[4]s dichtmaken word, benöömt` +issues.closed_at = `hett deeses Gefall %s dichtmaakt` +issues.commit_ref_at = `hett deeses Gefall %s vun eenem Kommitteren benöömt` +issues.ref_closing_from = `hett deeses Gefall %[1]s vun eenem Haalvörslag, wat ’t %[3]s dichtmaken word, benöömt` issues.ref_closed_from = `hett deeses Gefall %[4]s %[2]s dichtmaakt` issues.ref_reopened_from = `hett deeses Gefall %[4]s %[2]s weer opmaakt` issues.ref_from = `vun %[1]s` @@ -1477,12 +1477,12 @@ issues.label.filter_sort.reverse_alphabetically = Umdreiht na de Alphabeet issues.label.filter_sort.by_size = Lüttste Grött issues.num_participants_one = %d Mitmaker issues.num_participants_few = %d Mitmakers -issues.ref_pull_from = `hett deesen Haalvörslag %[4]s %[2]s benöömt` +issues.ref_pull_from = `hett deesen Haalvörslag %[3]s %[1]s benöömt` issues.label_title = Naam issues.label_archived_filter = Archiveert Vermarkens wiesen issues.archived_label_description = (Archiveert) %s -issues.ref_issue_from = `hett deeses Gefall %[4]s %[2]s benöömt` -issues.ref_reopening_from = `hett deeses Gefall vun eenem Haalvörslag, wat ’t %[4]s weer opmaken word, %[2]s benöömt` +issues.ref_issue_from = `hett deeses Gefall %[3]s %[1]s benöömt` +issues.ref_reopening_from = `hett deeses Gefall vun eenem Haalvörslag, wat ’t %[3]s weer opmaken word, %[1]s benöömt` issues.author.tooltip.issue = Deeser Bruker is de Autor vun deesem Gefall. issues.role.member_helper = Deeser Bruker is een Liddmaat vun de Vereenigung, wat de Eegner vun deesem Repositorium is. issues.role.collaborator_helper = Deeser Bruuker is inladen worden, in deesem Repositorium mittoarbeiden. @@ -1740,8 +1740,8 @@ pulls.status_checks_show_all = All Överprüfens wiesen pulls.update_branch_rebase = Twieg mit Umbaseren vernejen pulls.outdated_with_base_branch = De Twieg is tegen de Grund-Twieg verollt pulls.close = Haalvörslag dichtmaken -pulls.closed_at = `hett deesen Haalvörslag %[2]s dichtmaakt` -pulls.reopened_at = `hett deesen Haalvörslag %[2]s weer opmaakt` +pulls.closed_at = `hett deesen Haalvörslag %s dichtmaakt` +pulls.reopened_at = `hett deesen Haalvörslag %s weer opmaakt` pulls.cmd_instruction_hint = Wies Oorderreeg-Instruksjes pulls.cmd_instruction_checkout_title = Utchecken pulls.cmd_instruction_merge_title = Tosamenföhren @@ -1771,7 +1771,7 @@ milestones.deletion = Marksteen lösken pulls.has_merged = Fehlslagen: De Haalvörslag is tosamenföhrt worden, du kannst nich noch eenmaal tosamenföhren of de Enn-Twieg ännern. pulls.unrelated_histories = Tosamenföhren fehlslagen: De Tosamenföhrens-Kopp un -Grund hebben keene gemeensame Histoorje. Wenk: Versöök eene anner Tosamenföhrens-Aard pulls.update_not_allowed = Du düürst deesen Twieg nich vernejen -pulls.commit_ref_at = `hett deesen Haalvörslag %[2]s vun eenem Kommitteren benöömt` +pulls.commit_ref_at = `hett deesen Haalvörslag %s vun eenem Kommitteren benöömt` pulls.auto_merge_newly_scheduled = De Haalvörslag weer sett, sik tosamentoföhren, wenn all Överprüfens kumpleet sünd. milestones.clear = Leeg maken pulls.push_rejected_no_message = Schuven fehlslagen: Dat Schuven is sünner feerne Naricht oflehnt worden. Bidde överprüüf de Git-Hakens för deeses Repositorium @@ -2621,6 +2621,7 @@ settings.event_action_recover = Verhaalt settings.event_header_action = Aktioons-Loop-Vörfallen settings.event_action_failure_desc = Aktioons-Loop is as fehlslagen ennt. settings.event_action_recover_desc = Aktioons-Loop is daankregen worden, nadeem de leste Aktioons-Loop in de sülven Warkwies fehlslagen is. +issues.filter_type.all_pull_requests = All Haalvörslagen [repo.permissions] code.read = Lesen: De Quelltext vun deesem Repositorium ankieken un klonen. diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 549718ce23..48442bc39f 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -1554,13 +1554,13 @@ issues.close_comment_issue=Sluit met commentaar issues.reopen_issue=Heropen issues.reopen_comment_issue=Heropen met commentaar issues.create_comment=Reageer -issues.closed_at=`heeft dit probleem gesloten %[2]s` -issues.reopened_at=`heropende dit probleem %[2]s` -issues.commit_ref_at=`verwees naar dit probleem vanuit commit %[2]s'` -issues.ref_issue_from=`refereerde aan dit issue %[4]s %[2]s` -issues.ref_pull_from=`refereerde aan deze pull request %[4]s %[2]s` -issues.ref_closing_from=`verwees naar deze issue van een pull request %[4]s dat het zal sluiten, %[2]s` -issues.ref_reopening_from=`verwees naar een pull request %[4]s dat dit issue heropent %[2]s ` +issues.closed_at=`heeft dit probleem gesloten %s` +issues.reopened_at=`heropende dit probleem %s` +issues.commit_ref_at=`verwees naar dit probleem vanuit commit %s` +issues.ref_issue_from=`refereerde aan dit issue %[3]s %[1]s` +issues.ref_pull_from=`refereerde aan deze pull request %[3]s %[1]s` +issues.ref_closing_from=`verwees naar deze issue van een pull request %[3]s dat het zal sluiten, %[1]s` +issues.ref_reopening_from=`verwees naar een pull request %[3]s dat dit issue heropent %[1]s ` issues.ref_closed_from=`sloot dit issue %[4]s %[2]s` issues.ref_reopened_from=`heropende dit issue %[4]s %[2]s` issues.ref_from=`van %[1]s` @@ -1815,8 +1815,8 @@ pulls.update_branch_rebase=Update branch via herbaseren pulls.update_branch_success=Branch update is geslaagd pulls.update_not_allowed=Je hebt geen toestemming om branch bij te werken pulls.outdated_with_base_branch=Deze branch is verouderd met de basis branch -pulls.closed_at=`heeft deze pull request gesloten %[2]s` -pulls.reopened_at=`heropende deze pull request %[2]s` +pulls.closed_at=`heeft deze pull request gesloten %s` +pulls.reopened_at=`heropende deze pull request %s` pulls.auto_merge_button_when_succeed=(Bij geslaagde controles) pulls.auto_merge_when_succeed=Automatisch samenvoegen wanneer alle controles gelukt zijn @@ -2627,7 +2627,7 @@ projects.column.set_default_desc = Stel deze kolom in als standaard voor ongecat issues.action_check = Aanvinken/uitvinken issues.dependency.issue_batch_close_blocked = Het is niet mogelijk om de issues die u gekozen heeft in bulk te sluiten, omdat issue #%d nog open afhankelijkheden heeft pulls.review_only_possible_for_full_diff = Beoordeling is alleen mogelijk bij het bekijken van de volledige diff -pulls.commit_ref_at = `heeft naar deze pull request verwezen vanuit een commit %[2]s` +pulls.commit_ref_at = `heeft naar deze pull request verwezen vanuit een commit %s` pulls.cmd_instruction_hint = Bekijk opdrachtregelinstructies pulls.cmd_instruction_checkout_desc = Vanuit uw project repository, schakel over naar een nieuwe branch en test de veranderingen. pulls.showing_specified_commit_range = Alleen veranderingen weergeven tussen %[1]s..%[2]s diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 86a333a886..189e663618 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -1460,13 +1460,13 @@ issues.close_comment_issue=Zamknij z komentarzem issues.reopen_issue=Otwórz ponownie issues.reopen_comment_issue=Otwórz ponownie z komentarzem issues.create_comment=Skomentuj -issues.closed_at=`zamknął(-ęła) to zgłoszenie %[2]s` -issues.reopened_at=`otworzył(-a) ponownie to zgłoszenie %[2]s` -issues.commit_ref_at=`wspomniał(-a) to zgłoszenie z commita %[2]s` -issues.ref_issue_from=`odwołał(-a) się do tego zgłoszenia %[4]s %[2]s` -issues.ref_pull_from=`odwołał(-a) się do tego Pull Requesta %[4]s %[2]s` -issues.ref_closing_from=`odwołał(-a) się do pull requesta %[4]s, który zamknie to zgłoszenie %[2]s` -issues.ref_reopening_from=`odwołał(-a) się z pull requesta %[4]s, który otworzy na nowo to zgłoszenie %[2]s` +issues.closed_at=`zamknął(-ęła) to zgłoszenie %s` +issues.reopened_at=`otworzył(-a) ponownie to zgłoszenie %s` +issues.commit_ref_at=`wspomniał(-a) to zgłoszenie z commita %s` +issues.ref_issue_from=`odwołał(-a) się do tego zgłoszenia %[3]s %[1]s` +issues.ref_pull_from=`odwołał(-a) się do tego Pull Requesta %[3]s %[1]s` +issues.ref_closing_from=`odwołał(-a) się do pull requesta %[3]s, który zamknie to zgłoszenie %[1]s` +issues.ref_reopening_from=`odwołał(-a) się z pull requesta %[3]s, który otworzy na nowo to zgłoszenie %[1]s` issues.ref_closed_from=`zamknął(-ęła) to zgłoszenie %[4]s %[2]s` issues.ref_reopened_from=`ponownie otworzył(-a) to zgłoszenie %[4]s %[2]s` issues.ref_from=`z %[1]s` @@ -1679,8 +1679,8 @@ pulls.update_branch_rebase=Aktualizuj branch przez rebase pulls.update_branch_success=Aktualizacja gałęzi powiodła się pulls.update_not_allowed=Nie masz uprawnień do aktualizacji gałęzi pulls.outdated_with_base_branch=Ta gałąź jest przestarzała w stosunku do gałęzi bazowej -pulls.closed_at=`zamknął(-ęła) ten pull request %[2]s` -pulls.reopened_at=`otworzył(-a) ponownie ten Pull Request %[2]s` +pulls.closed_at=`zamknął(-ęła) ten pull request %s` +pulls.reopened_at=`otworzył(-a) ponownie ten Pull Request %s` @@ -2643,7 +2643,7 @@ pulls.closed = Pull request zamknięty pulls.blocked_by_outdated_branch = Ten pull request jest zablokowany ponieważ jest przedawniony. pulls.blocked_by_changed_protected_files_1 = Ten pull request jest zablokowany ponieważ wprowadza zmiany do chronionego pliku: pulls.push_rejected_no_message = Wypchnięcie nie powiodło się: Wypchnięcie zostało odrzucone, ale nie otrzymano zdalnej wiadomości. Sprawdź hooki Git dla tego repozytorium.= -pulls.commit_ref_at = `odniósł się do tego pull requesta z commita %[2]s` +pulls.commit_ref_at = `odniósł się do tego pull requesta z commita %s` pulls.cmd_instruction_checkout_desc = Ze swojego repozytorium projektu, utwórz nową gałąź i przetestuj zmiany. pulls.clear_merge_message_hint = Wyczyszczenie wiadomości scalenia usunie tylko treść wiadomości commitu pozostawiając wygenerowane przez git dopiski takie jak "Co-Authored-By ...". pulls.delete_after_merge.head_branch.insufficient_branch = Nie masz uprawnień by usunąć head gałęzi. diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 26bdd35420..8de0374eb2 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1063,8 +1063,8 @@ language.localization_project = Ajude-nos a traduzir Forgejo para o seu idioma! language.description = Essa língua será salva em sua conta e será usada como padrão após você iniciar a sessão. user_block_yourself = Você não pode se bloquear. pronouns_custom_label = Pronomes personalizados -change_username_redirect_prompt.with_cooldown.one = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dia, você ainda pode recuperar o nome de usuário antigo durante este período de espera. -change_username_redirect_prompt.with_cooldown.few = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dias, você ainda pode recuperar o nome de usuário antigo durante este período de espera. +change_username_redirect_prompt.with_cooldown.one = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de proteção de %[1]d dia. Você ainda pode recuperar o nome de usuário antigo durante este período de proteção. +change_username_redirect_prompt.with_cooldown.few = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de proteção de %[1]d dias. Você ainda pode recuperar o nome de usuário antigo durante este período de proteção. quota.applies_to_user = As seguintes regras de cota se aplicam à sua conta quota.rule.exceeded.helper = O tamanho total de objetos para esta regra excedeu a cota. keep_pronouns_private = Mostrar pronomes apenas para usuários autenticados @@ -1568,7 +1568,7 @@ issues.remove_ref_at=`removeu a referência %s %s` issues.add_ref_at=`adicionou a referência %s %s` issues.delete_branch_at=`excluiu branch %s %s` issues.filter_label=Etiqueta -issues.filter_label_exclude=`Use alt + clique/enter para excluir etiquetas` +issues.filter_label_exclude=Use Alt + Clique para excluir etiquetas issues.filter_label_no_select=Todas as etiquetas issues.filter_label_select_no_label=Sem etiqueta issues.filter_milestone=Marco @@ -1642,13 +1642,13 @@ issues.close_comment_issue=Comentar e fechar issues.reopen_issue=Reabrir issues.reopen_comment_issue=Comentar e reabrir issues.create_comment=Comentar -issues.closed_at=`fechou esta issue %[2]s` -issues.reopened_at=`reabriu esta issue %[2]s` -issues.commit_ref_at=`citou esta issue em um commit %[2]s` -issues.ref_issue_from=`referenciado esta issue %[4]s %[2]s` -issues.ref_pull_from=`referenciado este pull request %[4]s %[2]s` -issues.ref_closing_from=`referenciado esta issue de um pull request %[4]s que a fechará %[2]s` -issues.ref_reopening_from=`referenciado esta issue de um pull request %[4]s que a reabrirá %[2]s` +issues.closed_at=`fechou esta issue %s` +issues.reopened_at=`reabriu esta issue %s` +issues.commit_ref_at=`citou esta issue de um commit %s` +issues.ref_issue_from=`citou esta issue %[3]s %[1]s` +issues.ref_pull_from=`citou este pull request %[3]s %[1]s` +issues.ref_closing_from=`citou esta issue de um pull request %[3]s que a fechará %[1]s` +issues.ref_reopening_from=`citou esta issue de um pull request %[3]s que a reabrirá, %[1]s` issues.ref_closed_from=`fechou esta issue %[4]s %[2]s` issues.ref_reopened_from=`reabriu esta issue %[4]s %[2]s` issues.ref_from=`de %[1]s` @@ -1942,8 +1942,8 @@ pulls.update_branch_success=Atualização do branch foi bem-sucedida pulls.update_not_allowed=Você não tem permissão para atualizar o branch pulls.outdated_with_base_branch=Este branch está desatualizado com o branch base pulls.close=Fechar pull request -pulls.closed_at=`fechou este pull request %[2]s` -pulls.reopened_at=`reabriu este pull request %[2]s` +pulls.closed_at=`fechou este pull request %s` +pulls.reopened_at=`reabriu este pull request %s` pulls.clear_merge_message=Limpar mensagem do merge pulls.clear_merge_message_hint=Limpar a mensagem de merge só irá remover o conteúdo da mensagem de commit e manter trailers git gerados, como "Co-Authored-By …". @@ -2719,7 +2719,7 @@ issues.label_archive_tooltip = Etiquetas arquivadas não serão exibidas nas sug activity.navbar.pulse = Recente settings.units.overview = Geral settings.units.add_more = Habilitar mais -pulls.commit_ref_at = `referenciou este pedido de mesclagem no commit %[2]s` +pulls.commit_ref_at = `citou este pull request de um commit %s` pulls.cmd_instruction_merge_title = Mesclar settings.units.units = Unidades vendored = Externo @@ -2920,6 +2920,7 @@ settings.event_action_recover = Recuperar settings.event_action_recover_desc = A execução da Action teve sucesso após a última execução no mesmo workflow ter falhado. settings.event_action_success = Sucesso settings.event_action_success_desc = A execução da Action foi bem sucedida. +issues.filter_type.all_pull_requests = Todos os pull requests [graphs] component_loading = Carregando %s… @@ -3056,8 +3057,8 @@ open_dashboard = Abrir painel settings.change_orgname_prompt = Obs.: Alterar o nome de uma organização resultará na alteração do URL dela e disponibilizará o nome antigo para uso. follow_blocked_user = Não foi possível seguir esta organização porque ela bloqueou-o(a). form.name_pattern_not_allowed = O padrão "%s" não é permitido no nome de uma organização. -settings.change_orgname_redirect_prompt.with_cooldown.one = O nome de organização antigo ficará disponível para qualquer pessoa após um período de proteção de %[1]d dia, você ainda pode recuperar o nome antigo durante este período de proteção. -settings.change_orgname_redirect_prompt.with_cooldown.few = O nome de organização antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dia, você ainda pode recuperar o nome antigo durante este período de espera. +settings.change_orgname_redirect_prompt.with_cooldown.one = O nome de organização antigo ficará disponível para qualquer pessoa após um período de proteção de %[1]d dia. Você ainda pode recuperar o nome antigo durante este período de proteção. +settings.change_orgname_redirect_prompt.with_cooldown.few = O nome de organização antigo ficará disponível para qualquer pessoa após um período de proteção de %[1]d dia. Você ainda pode recuperar o nome antigo durante este período de proteção. [admin] dashboard=Painel diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 7f36d164b3..0e8f2d485e 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1657,13 +1657,13 @@ issues.close_comment_issue=Fechar com comentário issues.reopen_issue=Reabrir issues.reopen_comment_issue=Reabrir com comentário issues.create_comment=Comentar -issues.closed_at=`encerrou esta questão %[2]s` -issues.reopened_at=`reabriu esta questão %[2]s` -issues.commit_ref_at=`referenciou esta questão num cometimento %[2]s` -issues.ref_issue_from=`referiu esta questão %[4]s %[2]s` -issues.ref_pull_from=`referiu este pedido de integração %[4]s %[2]s` -issues.ref_closing_from=`referiu esta questão a partir de um pedido de integração %[4]s que a fechará %[2]s` -issues.ref_reopening_from=`referiu esta questão a partir de um pedido de integração %[4]s que a reabrirá %[2]s` +issues.closed_at=`encerrou esta questão %s` +issues.reopened_at=`reabriu esta questão %s` +issues.commit_ref_at=`referenciou esta questão num cometimento %s` +issues.ref_issue_from=`referiu esta questão %[3]s %[1]s` +issues.ref_pull_from=`referiu este pedido de integração %[3]s %[1]s` +issues.ref_closing_from=`referiu esta questão a partir de um pedido de integração %[3]s que a fechará %[1]s` +issues.ref_reopening_from=`referiu esta questão a partir de um pedido de integração %[3]s que a reabrirá %[1]s` issues.ref_closed_from=`encerrou esta questão %[4]s %[2]s` issues.ref_reopened_from=`reabriu esta questão %[4]s %[2]s` issues.ref_from=`de %[1]s` @@ -1972,8 +1972,8 @@ pulls.update_branch_success=A sincronização do ramo foi bem sucedida pulls.update_not_allowed=Não tem autorização para sincronizar o ramo pulls.outdated_with_base_branch=Este ramo é obsoleto em relação ao ramo base pulls.close=Encerrar pedido de integração -pulls.closed_at=`fechou este pedido de integração %[2]s` -pulls.reopened_at=`reabriu este pedido de integração %[2]s` +pulls.closed_at=`fechou este pedido de integração %s` +pulls.reopened_at=`reabriu este pedido de integração %s` pulls.cmd_instruction_hint=Ver instruções para a linha de comandos pulls.cmd_instruction_checkout_title=Conferir pulls.cmd_instruction_checkout_desc=No seu repositório, irá criar um novo ramo para que possa testar as modificações. @@ -2785,7 +2785,7 @@ settings.wiki_rename_branch_main_desc = Renomear o ramo usado internamente pelo settings.add_collaborator_blocked_our = Não foi possível adicionar o/a colaborador/a porque o/a proprietário/a do repositório bloqueou-os. settings.add_webhook.invalid_path = A localização não pode conter "." ou ".." ou ficar em branco. Não pode começar ou terminar com uma barra. settings.graphql_url = URL do GraphQL -pulls.commit_ref_at = `referiu este pedido de integração a partir de um cometimento %[2]s` +pulls.commit_ref_at = `referiu este pedido de integração a partir de um cometimento %s` settings.confirm_wiki_branch_rename = Renomear o ramo do wiki settings.wiki_branch_rename_success = O nome do ramo do wiki do repositório foi normalizado com sucesso. settings.wiki_branch_rename_failure = Falhou a normalização do nome do ramo do wiki do repositório. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index bcb62eb23b..2ef1b868d4 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1408,7 +1408,7 @@ editor.fail_to_update_file=Не удалось обновить/создать editor.fail_to_update_file_summary=Ошибка: editor.push_rejected_no_message=Изменение отклонено сервером без сообщения. Пожалуйста, проверьте Git-хуки. editor.push_rejected=Изменение отклонено сервером. Пожалуйста, проверьте Git-хуки. -editor.push_rejected_summary=Причина отклонения: +editor.push_rejected_summary=Полная причина отклонения: editor.add_subdir=Добавить каталог… editor.unable_to_upload_files=Не удалось загрузить файлы в «%s» из-за ошибки: %v editor.upload_file_is_locked=Файл «%s» заблокирован %s. @@ -1545,27 +1545,27 @@ issues.add_labels=добавлены метки %s %s issues.remove_label=удалил(а) метку %s %s issues.remove_labels=удалил(а) метки %s %s issues.add_remove_labels=добавлены метки %s и убраны метки %s %s -issues.add_milestone_at=`добавлено в этап %s %s` -issues.add_project_at=`добавлено в проект %s %s` -issues.change_milestone_at=`изменил(а) целевой этап с %s на %s %s` -issues.change_project_at=`изменил(а) проект с %s на %s %s` -issues.remove_milestone_at=`удалил(а) это из этапа %s %s` -issues.remove_project_at=`удалил(а) это из проекта %s %s` +issues.add_milestone_at=`добавление в этап %s %s` +issues.add_project_at=`добавление в проект %s %s` +issues.change_milestone_at=`этап изменён с %s на %s %s` +issues.change_project_at=`проект изменён с %s на %s %s` +issues.remove_milestone_at=`удаление из этапа %s %s` +issues.remove_project_at=`удаление из проекта %s %s` issues.deleted_milestone=`(удалено)` issues.deleted_project=`(удалено)` -issues.self_assign_at=`назначил(а) на себя %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` -issues.change_ref_at=`изменил(а) ссылку с %s на %s %s` -issues.remove_ref_at=`убрал(а) ссылку %s %s` -issues.add_ref_at=`добавлена ссылка %s %s` +issues.self_assign_at=`назначение себя %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` +issues.change_ref_at=`изменена ссылка с %s на %s %s` +issues.remove_ref_at=`убрана ссылка на %s %s` +issues.add_ref_at=`добавлена ссылка на %s %s` issues.delete_branch_at=`удалена ветвь %s %s` -issues.filter_label=Метка -issues.filter_label_exclude=`Используйте alt + click/enter, чтобы исключить метки` -issues.filter_label_no_select=Все метки -issues.filter_label_select_no_label=Нет метки +issues.filter_label=Метки +issues.filter_label_exclude=Исключайте метки с помощью Alt + ЛКМ +issues.filter_label_no_select=Любые метки +issues.filter_label_select_no_label=Без меток issues.filter_milestone=Этап issues.filter_milestone_all=Все этапы issues.filter_milestone_none=Нет этапов @@ -1637,13 +1637,13 @@ 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.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.closed_at=`задача была закрыта %s` +issues.reopened_at=`задача была открыта снова %s` +issues.commit_ref_at=`упоминание этой задачи в коммите %s` +issues.ref_issue_from=`упоминание этой задачи %[3]s %[1]s` +issues.ref_pull_from=`упоминание этого запроса слияния %[3]s %[1]s` +issues.ref_closing_from=`упоминание из запроса на слияние %[3]s, который закроет эту задачу %[1]s` +issues.ref_reopening_from=`упоминание из запроса на слияние %[3]s, который повторно откроет эту задачу %[1]s` issues.ref_closed_from=`закрыл этот запрос %[4]s %[2]s` issues.ref_reopened_from=`задача была открыта снова %[4]s %[2]s` issues.ref_from=`из %[1]s` @@ -1943,8 +1943,8 @@ pulls.update_branch_success=Ветвь успешно обновлена pulls.update_not_allowed=Недостаточно прав для обновления ветви pulls.outdated_with_base_branch=Эта ветвь отстает от базовой ветви pulls.close=Закрыть запрос слияния -pulls.closed_at=`закрыл этот запрос на слияние %[2]s` -pulls.reopened_at=`переоткрыл этот запрос на слияние %[2]s` +pulls.closed_at=`закрыл этот запрос на слияние %s` +pulls.reopened_at=`переоткрыл этот запрос на слияние %s` pulls.cmd_instruction_hint=Показать инструкции для командной строки pulls.cmd_instruction_merge_title=Слейте изменения pulls.cmd_instruction_merge_desc=Слейте изменения и отправьте их обратно. @@ -2772,7 +2772,7 @@ settings.ignore_stale_approvals = Игнорировать устаревшие contributors.contribution_type.additions = Добавления contributors.contribution_type.deletions = Удаления contributors.contribution_type.filter_label = Вид деятельности: -pulls.commit_ref_at = `упоминание этого запроса слияния в коммите %[2]s` +pulls.commit_ref_at = `сослался на этот запрос слияния в коммите %s` settings.thread_id = ИД обсуждения pulls.made_using_agit = AGit activity.navbar.contributors = Соавторы @@ -2923,6 +2923,7 @@ settings.event_action_recover = Восстановлен settings.event_action_recover_desc = После неудачи повторное выполнение рабочего потока было успешно. settings.event_action_success = Успех settings.event_action_success_desc = Выполнение завершилось успешно. +issues.filter_type.all_pull_requests = Все запросы на слияние [graphs] component_loading_failed = Не удалось загрузить %s diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index d55b238b1c..54b0b246db 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -1100,12 +1100,12 @@ 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.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.closed_at=`මෙම ගැටළුව වසා %s` +issues.reopened_at=`මෙම ගැටළුව නැවත විවෘත කරන ලදි %s` +issues.ref_issue_from=`මෙම නිකුතුව %[3]s හි %[1]s` +issues.ref_pull_from=`මෙම අදින්න ඉල්ලීම%[3]s %[1]s` +issues.ref_closing_from=`මෙම ගැටළුව වසා දමනු ඇත%[3]s මෙම ගැටළුව %[1]s` +issues.ref_reopening_from=`මෙම ගැටළුව නැවත විවෘත කරනු ඇත%[3]s මෙම ගැටළුව %[1]s` issues.ref_closed_from=`මෙම නිකුතුව%[4]s %[2]s` issues.ref_reopened_from=`මෙම නිකුතුව%[4]s %[2]sනැවත විවෘත කරන ලදි` issues.ref_from=`හිම%[1]s` @@ -1342,8 +1342,8 @@ pulls.update_branch_rebase=රිබේස් මගින් ශාඛාව pulls.update_branch_success=ශාඛා යාවත්කාලීන කිරීම සාර්ථක විය pulls.update_not_allowed=ශාඛාව යාවත්කාලීන කිරීමට ඔබට අවසර නැත pulls.outdated_with_base_branch=මෙම ශාඛාව මූලික ශාඛාව සමඟ දිවයයි -pulls.closed_at=`මෙම අදින්න ඉල්ලීම වසා %[2]s` -pulls.reopened_at=`මෙම අදින්න ඉල්ලීම නැවත විවෘත කරන ලදි %[2]s` +pulls.closed_at=`මෙම අදින්න ඉල්ලීම වසා %s` +pulls.reopened_at=`මෙම අදින්න ඉල්ලීම නැවත විවෘත කරන ලදි %s` diff --git a/options/locale/locale_sr-SP.ini b/options/locale/locale_sr-SP.ini index 56c1a7e650..b14fdc1a35 100644 --- a/options/locale/locale_sr-SP.ini +++ b/options/locale/locale_sr-SP.ini @@ -326,7 +326,7 @@ issues.no_content=Још нема садржаја. issues.close_issue=Затвори issues.reopen_issue=Поново отвори issues.create_comment=Коментирај -issues.commit_ref_at=`поменуо овај задатак у комит %[2]s` +issues.commit_ref_at=`поменуо овај задатак у комит %s` issues.poster=Аутор issues.collaborator=Коаутор issues.owner=Власник diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index a4999e9751..8b43cb29b8 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -201,6 +201,9 @@ table_modal.placeholder.content = Innehåll table_modal.label.rows = Rader table_modal.label.columns = Kolumner buttons.switch_to_legacy.tooltip = Använd legacy-redigeraren istället +link_modal.url = Url +link_modal.description = Beskrivning +link_modal.header = Lägg till en länk [filter] string.asc = A - Ö @@ -329,6 +332,7 @@ invalid_app_data_path = Sökvägen för appdata är ogiltig: %v internal_token_failed = Misslyckades att generera intern token: %v password_algorithm = Hashalgoritm för lösenord invalid_password_algorithm = Ogiltig hashalgoritm för lösenord +env_config_keys_prompt = Följande miljövariabler kommer också att tillämpas på din konfigurationsfil: [home] uname_holder=Användarnamn eller e-postadress @@ -462,6 +466,41 @@ reply = eller svara på detta e-postmeddelande direkt hi_user_x = Hej %s, admin.new_user.user_info = Användarinformation admin.new_user.text = Vänligen klicka här för att hantera denna användare från administratörspanelen. +admin.new_user.subject = Ny användare %s har just registrerat sig +totp_disabled.no_2fa = Det finns inga andra 2FA-metoder konfigurerade längre, vilket innebär att det inte längre är nödvändigt att logga in på ditt konto med 2FA. +removed_security_key.text_1 = Säkerhetsnyckeln ”%[1]s” har just tagits bort från ditt konto. +repo.transfer.to_you = dig +repo.transfer.body = För att acceptera eller avvisa det, besök %s eller ignorera det helt enkelt. +removed_security_key.no_2fa = Det finns inga andra 2FA-metoder konfigurerade längre, vilket innebär att det inte längre är nödvändigt att logga in på ditt konto med 2FA. +release.note = Notera: +totp_enrolled.subject = Du har aktiverat TOTP som 2FA-metod +totp_enrolled.text_1.no_webauthn = Du har just aktiverat TOTP för ditt konto. Det innebär att du måste använda TOTP som 2FA-metod vid alla framtida inloggningar på ditt konto. +totp_enrolled.text_1.has_webauthn = Du har just aktiverat TOTP för ditt konto. Det innebär att du vid alla framtida inloggningar på ditt konto kan använda TOTP som 2FA-metod eller någon av dina säkerhetsnycklar. +link_not_working_do_paste = Fungerar inte länken? Prova att kopiera och klistra in den i webbläsarens adressfält. +primary_mail_change.text_1 = Den primära e-postadressen för ditt konto har just ändrats till %[1]s. Det innebär att denna e-postadress inte längre kommer att ta emot e-postmeddelanden för ditt konto. +totp_disabled.subject = TOTP har inaktiverats +totp_disabled.text_1 = Tidsbaserat engångslösenord (TOTP) på ditt konto har just inaktiverats. +account_security_caution.text_2 = Om detta inte var du, har ditt konto blivit kompromitterat. Kontakta administratören för denna webbplats. +account_security_caution.text_1 = Om detta var du, kan du tryggt ignorera detta meddelande. +activate_account.text_2 = Klicka på följande länk för att aktivera ditt konto inom %s: +activate_email.text = Klicka på följande länk för att verifiera din e-postadress inom %s: +register_notify.text_3 = Om någon annan har skapat det här kontot åt dig måste du först ställa in ditt lösenord. +issue.x_mentioned_you = @%s2 nämnde dig: +repo.collaborator.added.subject = %s har lagt till dig som medarbetare i %s +repo.collaborator.added.text = Du har lagts till som medarbetare i förrådet: +team_invite.subject = %[1]s har bjudit in dig att gå med i organisationen %[2]s +register_notify.text_1 = detta är din registreringsbekräftelse via e-post för %s! +release.downloads = Hämtningar: +release.download.zip = Källkod (ZIP) +release.download.targz = Källkod (TAR.GZ) +repo.transfer.subject_to = %s vill överföra förrådet ”%s” till %s +removed_security_key.subject = En säkerhetsnyckel har tagits bort +issue_assigned.pull = @%[1] har tilldelat dig pull-begäran %[2]s i förrådet %[3]s. +issue_assigned.issue = @%[1] har tilldelat dig ärendet %[2] i förrådet %[3]. +register_notify.text_2 = Du kan logga in på ditt konto med ditt användarnamn: %s +reset_password.text = Om detta var du, klicka på följande länk för att återställa ditt konto inom %s: +issue.action.force_push = %[1]s2 gjorde en force-push av %[2]s från %[3]s till %[4]s. +repo.transfer.subject_to_you = %s vill överföra förrådet ”%s” till dig @@ -545,6 +584,12 @@ auth_failed=Autentisering misslyckades: %v target_branch_not_exist=Målgrenen finns inte. +org_still_own_repo = Denna organisation äger fortfarande ett eller flera förråd, ta bort eller överför dem först. +must_use_public_key = Den nyckel du angav är en privat nyckel. Skicka inte upp din privata nyckel någonstans. Använd istället din publika nyckel. +unable_verify_ssh_key = SSH-nyckeln kan inte verifieras. Kontrollera att den är korrekt. +still_own_repo = Ditt konto äger ett eller flera förråd, ta bort eller överför dem först. +still_has_org = Ditt konto är medlem i en eller flera organisationer. Lämna dem först. +still_own_packages = Ditt konto har ett eller flera paket, ta bort dem först. [user] @@ -560,6 +605,13 @@ follow=Följ unfollow=Sluta följa user_bio=Biografi disabled_public_activity=Den här användaren har inaktiverat den publika synligheten av aktiviteten. +code = Kod +watched = Övervakade förråd +unblock = Avblockera +email_visibility.limited = Din e-postadress är synlig för alla autentiserade användare +show_on_map = Visa denna plats på en karta +settings = Användarinställningar +block = Blockera [settings] @@ -760,6 +812,17 @@ email_notifications.submit=Ställ in e-postpreferenser visibility.public=Offentlig visibility.private=Privat change_password = Byt lösenord +user_block_success = Användaren har blockerats. +blocked_since = Blockerad sedan %s +user_unblock_success = Användaren har blivit avblockerad. +visibility.limited = Begränsad +visibility.limited_tooltip = Synlig endast för inloggade användare +visibility.private_tooltip = Synlig endast för medlemmar i organisationer som du har gått med i +select_permissions = Välj behörigheter +permission_no_access = Ingen åtkomst +permission_write = Läs och skriv +user_block_yourself = Du kan inte blockera dig själv. +gpg_token_help = Du kan skapa en signatur med hjälp av: [repo] owner=Ägare @@ -1094,13 +1157,13 @@ issues.close_comment_issue=Stäng med kommentar issues.reopen_issue=Återöppna issues.reopen_comment_issue=Öppna igen med kommentar issues.create_comment=Kommentera -issues.closed_at=`stängde ärendet %[2]s` -issues.reopened_at=`återöppnade detta ärende %[2]s` -issues.commit_ref_at=`refererade till detta ärende från en incheckning %[2]s` -issues.ref_issue_from=`refererade till detta ärende %[4]s %[2]s` -issues.ref_pull_from=`refererade till denna pull-förfrågan %[4]s %[2]s` -issues.ref_closing_from=`hänvisade till detta ärende från en pull-förfrågan %[4]s som kommer att stänga det %[2]s` -issues.ref_reopening_from=`hänvisade till detta ärende från en pull-förfrågan %[4]s som kommer att öppna ärendet på nytt %[2]s` +issues.closed_at=`stängde ärendet %s` +issues.reopened_at=`återöppnade detta ärende %s` +issues.commit_ref_at=`refererade till detta ärende från en incheckning %s` +issues.ref_issue_from=`refererade till detta ärende %[3]s %[1]s` +issues.ref_pull_from=`refererade till denna pull-förfrågan %[3]s %[1]s` +issues.ref_closing_from=`hänvisade till detta ärende från en pull-förfrågan %[3]s som kommer att stänga det %[1]s` +issues.ref_reopening_from=`hänvisade till detta ärende från en pull-förfrågan %[3]s som kommer att öppna ärendet på nytt %[1]s` issues.ref_closed_from=`stängde detta ärende %[4]s %[2]s` issues.ref_reopened_from=`öpnnade detta ärende igen %[4]s %[2]s` issues.ref_from=`från %[1]s` @@ -1693,6 +1756,21 @@ topic.manage_topics=Hantera ämnen topic.done=Klar topic.count_prompt=Du kan inte välja fler än 25 ämnen settings.enter_repo_name = Ange ägar- och utvecklingskatalog-namnet exakt som det visas: +release = Utgåva +commitstatus.success = Lyckades +visibility_helper = Gör förrådet privat +download_bundle = Hämta BUNDLE +download_zip = Hämta ZIP +download_tar = Hämta TAR.GZ +repo_desc_helper = Ange kort beskrivning (valfritt) +all_branches = Alla grenar +fork_no_valid_owners = Detta förråd kan inte förgrenas eftersom det inte finns några giltiga ägare. +fork_to_different_account = Förgrena till ett annat konto +size_format = %[1]s: %[2]s, %[3]s: %[4]s +already_forked = Du har redan förgrenat %s +commitstatus.failure = Fel +ext_issues = Externa fel +open_with_editor = Öppna med %s @@ -2255,7 +2333,7 @@ project_kind = Sök projekt... search = Sök… type_tooltip = Söktyp team_kind = Sök lag... -org_kind = Sök organisationer... +org_kind = Sök organisationer… issue_kind = Sök ärenden... regexp_tooltip = Tolka söktermen som ett reguljärt uttryck code_search_unavailable = Kodsökning är för närvarande inte tillgänglig. Vänligen kontakta webbplatsadministratören. diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 2f749dfd1e..c07cefdab9 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -668,6 +668,7 @@ username_error_no_dots = ` sadece alfanumerik karakterler ("0-9","a-z","A-Z"), t unset_password = Oturum açma kullanıcısı parola belirlemedi. unsupported_login_type = Oturum açma türü hesap silmeyi desteklemiyor. +email_domain_is_not_allowed = Kullanıcı e-posta adresi %s alan adı EMAIL_DOMAIN_ALLOWLIST veya EMAIL_DOMAIN_BLOCKLIST ile çelişiyor. Lütfen işleminizin beklendiğinden emin olun. [user] change_avatar=Profil resmini değiştir… @@ -1593,13 +1594,13 @@ issues.close_comment_issue=Yorum Yap ve Kapat issues.reopen_issue=Yeniden aç issues.reopen_comment_issue=Yorum Yap ve Yeniden Aç issues.create_comment=Yorum yap -issues.closed_at=`%[2]s konusunu kapattı` -issues.reopened_at=`%[2]s konusunu yeniden açtı` -issues.commit_ref_at=`%[2]s işlemesinde bu konuyu işaret etti` -issues.ref_issue_from=`bu konuya referansta bulundu %[4]s %[2]s` -issues.ref_pull_from=`bu değişiklik isteğine referansta bulundu %[4]s %[2]s` -issues.ref_closing_from=`bir değişiklik isteğine referansta bulundu %[4]s bu konu kapatılacak %[2]s` -issues.ref_reopening_from=`bir değişiklik isteğine referansta bulundu %[4]s bu konu yeniden açılacak %[2]s` +issues.closed_at=`%s konusunu kapattı` +issues.reopened_at=`%s konusunu yeniden açtı` +issues.commit_ref_at=`%s işlemesinde bu konuyu işaret etti` +issues.ref_issue_from=`bu konuya referansta bulundu %[3]s %[1]s` +issues.ref_pull_from=`bu değişiklik isteğine referansta bulundu %[3]s %[1]s` +issues.ref_closing_from=`bir değişiklik isteğine referansta bulundu %[3]s bu konu kapatılacak %[1]s` +issues.ref_reopening_from=`bir değişiklik isteğine referansta bulundu %[3]s bu konu yeniden açılacak %[1]s` issues.ref_closed_from=`bu konuyu kapat%[4]s %[2]s` issues.ref_reopened_from=`konuyu yeniden aç%[4]s %[2]s` issues.ref_from=`%[1]s'den` @@ -1906,8 +1907,8 @@ pulls.update_branch_success=Dal güncellemesi başarıyla gerçekleştirildi pulls.update_not_allowed=Dalı güncelleme izniniz yok pulls.outdated_with_base_branch=Bu dal, temel dal ile güncel değil pulls.close=Değişiklik İsteğini Kapat -pulls.closed_at=`%[2]s değişiklik isteğini kapattı` -pulls.reopened_at=`%[2]s değişiklik isteğini yeniden açtı` +pulls.closed_at=`%s değişiklik isteğini kapattı` +pulls.reopened_at=`%s değişiklik isteğini yeniden açtı` pulls.cmd_instruction_hint=`Komut satırı talimatlarını görüntüleyin.` pulls.cmd_instruction_checkout_title=Çekme pulls.cmd_instruction_checkout_desc=Proje deponuzdan yeni bir dalı çekin ve değişiklikleri test edin. @@ -2163,7 +2164,7 @@ settings.pulls.allow_rebase_update=Değişiklik isteği dalının yeniden yapıl settings.pulls.default_delete_branch_after_merge=Varsayılan olarak birleştirmeden sonra değişiklik isteği dalını sil settings.pulls.default_allow_edits_from_maintainers=Bakımcıların düzenlemelerine izin ver settings.releases_desc=Depo Sürümlerini Etkinleştir -settings.packages_desc=Depo Paket Kütüğünü Etkinleştir +settings.packages_desc=Depo paket kütüğünü etkinleştir settings.projects_desc=Depo Projelerini Etkinleştir settings.actions_desc=Depo İşlemlerini Etkinleştir settings.admin_settings=Yönetici Ayarları @@ -3492,7 +3493,7 @@ error.unit_not_allowed=Bu depo bölümüne erişme izniniz yok. title=Paketler desc=Depo paketlerini yönet. empty=Henüz hiçbir paket yok. -empty.documentation=Paket kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz. +empty.documentation=Paket deposu hakkında daha fazla bilgi için, belgeye bakabilirsiniz. empty.repo=Bir paket yüklediniz ama burada gösterilmiyor mu? Paket ayarlarına gidin ve bu depoya bağlantı verin. registry.documentation=%s kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz. filter.type=Tür @@ -3635,9 +3636,9 @@ owner.settings.cleanuprules.remove.days=Şundan eski sürümleri kaldır owner.settings.cleanuprules.remove.pattern=Eşleşen sürümlari kaldır owner.settings.cleanuprules.success.update=Temizleme kuralı güncellendi. owner.settings.cleanuprules.success.delete=Temizleme kuralı silindi. -owner.settings.chef.title=Chef Kütüğü +owner.settings.chef.title=Chef deposu owner.settings.chef.keypair=Anahtar çifti üret -owner.settings.chef.keypair.description=Chef kütüğünde kimlik doğrulaması için bir anahtar çifti gereklidir. Eğer daha önce bir anahtar çifti ürettiyseniz, yeni bir anahtar çifti üretmek eski anahtar çiftini ıskartaya çıkartacaktır. +owner.settings.chef.keypair.description=Chef kayıt defterine gönderilen istekler kimlik doğrulama yöntemi olarak kriptografik olarak imzalanmalıdır. Bir anahtar çifti oluştururken, yalnızca genel anahtar Forgejo'da saklanır. Özel anahtar size knife ile kullanılmak üzere sağlanır. Yeni bir anahtar çifti oluşturmak öncekini geçersiz kılar. npm.dependencies.bundle = Paketlenmiş Bağımlılıklar rpm.repository.multiple_groups = Bu paket birçok grupta mevcut. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index d2d7c58edc..faa3f2a56e 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -694,7 +694,7 @@ disabled_public_activity=Цей користувач вимкнув публіч joined_on = Реєстрація %s email_visibility.private = Ваш email видно лише вам і адміністраторам email_visibility.limited = Вашу е-пошту видно всім авторизованим -settings = Користувацькі параметри +settings = Користувацькі налаштування block_user.detail_3 = Ви не зможете додати один одного в якості співавтора репозиторію. show_on_map = Показати це місце на мапі block_user.detail_2 = Цей користувач не зможе взаємодіяти з репозиторіями, власником яких є ви, а також із задачами та коментарями, які ви створили. @@ -1447,7 +1447,7 @@ issues.remove_ref_at=`видалив посилання %s %s` issues.add_ref_at=`додав посилання %s %s` issues.delete_branch_at=`видалена гілка %s %s` issues.filter_label=Мітка -issues.filter_label_exclude=`Використовуйте Alt + клік/Enter для виключення міток` +issues.filter_label_exclude=Використовуйте Alt + клік для виключення міток issues.filter_label_no_select=Всі мітки issues.filter_milestone=Етап issues.filter_project=Проєкт @@ -1496,17 +1496,17 @@ issues.context.quote_reply=Цитувати відповідь issues.context.reference_issue=Послатися в новій задачі issues.context.edit=Редагувати issues.context.delete=Видалити -issues.close_comment_issue=Прокоментувати і закрити +issues.close_comment_issue=Закрити з коментарем issues.reopen_issue=Відкрити знову -issues.reopen_comment_issue=Прокоментувати та відкрити знову +issues.reopen_comment_issue=Відкрити знову з коментарем issues.create_comment=Коментар -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.closed_at=`закриває цю задачу %s` +issues.reopened_at=`повторно відкриває цю задачу %s` +issues.commit_ref_at=`посилається на цю задачу в коміті %s` +issues.ref_issue_from=`посилається на цю задачу %[3]s %[1]s` +issues.ref_pull_from=`посилається на цей запит злиття %[3]s %[1]s` +issues.ref_closing_from=`посилається в запиті на злиття %[3]s, який закриє цю задачу, %[1]s` +issues.ref_reopening_from=`посилається в запиті на злиття %[3]s, який повторно відкриє цю задачу, %[1]s` issues.ref_closed_from=`закрив цю задачу %[4]s %[2]s` issues.ref_reopened_from=`повторно відкрито цю задачу %[4]s %[2]s` issues.ref_from=`із %[1]s` @@ -1743,8 +1743,8 @@ pulls.update_branch_rebase=Оновити гілку перебазування pulls.update_branch_success=Оновлення гілки пройшло успішно pulls.update_not_allowed=Ви не можете оновити гілку pulls.outdated_with_base_branch=Ця гілка застаріла відносно базової гілки -pulls.closed_at=`закрив цей запит на злиття %[2]s` -pulls.reopened_at=`повторно відкрив цей запит на злиття %[2]s` +pulls.closed_at=`закриває цей запит на злиття %s` +pulls.reopened_at=`повторно відкриває цей запит на злиття %s` @@ -1887,7 +1887,7 @@ settings.collaboration.owner=Власник settings.collaboration.undefined=Не визначено settings.hooks=Веб-хуки settings.githooks=Git хуки -settings.basic_settings=Базові налаштування +settings.basic_settings=Основні налаштування settings.mirror_settings=Налаштування дзеркала settings.mirror_settings.mirrored_repository=Віддзеркалений репозиторій settings.mirror_settings.direction=Напрямок @@ -2055,12 +2055,12 @@ settings.event_issue_assign=Призначення settings.event_issue_assign_desc=Задачу призначено або скасовано. settings.event_issue_label=Мітки settings.event_issue_label_desc=Додавання або видалення міток задач. -settings.event_issue_milestone=Задача з етапом +settings.event_issue_milestone=Етапи settings.event_issue_milestone_desc=Етап призначено, видалено або змінено. settings.event_issue_comment=Коментарі settings.event_issue_comment_desc=Коментар задачі створено, видалено чи відредаговано. settings.event_header_pull_request=Події запиту на злиття -settings.event_pull_request=Запити до злиття +settings.event_pull_request=Зміна settings.event_pull_request_desc=Запит до злиття відкрито, закрито, перевідкрито або відредаговано. settings.event_pull_request_assign=Призначення settings.event_pull_request_assign_desc=Запит про злиття призначено або скасовано. @@ -2485,7 +2485,7 @@ signing.will_sign = Коміт буде підписано ключем «%s». signing.wont_sign.error = Під час перевірки можливості підписати коміт сталася помилка. commits.search_branch = У цій гілці ext_wiki = Зовнішня вікі -pulls.commit_ref_at = `посилається на цей запит на злиття в коміті %[2]s` +pulls.commit_ref_at = `посилається на цей запит на злиття в коміті %s` pulls.cmd_instruction_hint = Переглянути інструкції для командного рядка issues.max_pinned = Неможливо закріпити більше задач issues.unpin_comment = відкріпив %s @@ -2668,6 +2668,7 @@ settings.event_action_success = Успіх settings.event_action_recover = Відновлено commitstatus.success = Успіх commitstatus.failure = Збій +issues.filter_type.all_pull_requests = Усі запити на злиття [graphs] contributors.what = внески @@ -2846,7 +2847,7 @@ dashboard.update_migration_poster_id=Оновити мігровані 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=Пересинхронізувати перед-прийнятні, оновлюючі та пост-прийнятні хуки в усіх репозиторіях +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 таблицю @@ -2905,7 +2906,7 @@ 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 хуки @@ -3231,7 +3232,7 @@ notices.view_detail_header=Переглянути деталі повідомл notices.select_all=Вибрати все notices.deselect_all=Скасувати виділення notices.inverse_selection=Інвертувати виділене -notices.delete_selected=Видалити обране +notices.delete_selected=Видалити вибране notices.delete_all=Видалити всі cповіщення notices.type=Тип notices.type_1=Репозиторій @@ -3304,6 +3305,7 @@ dashboard.cron.cancelled = Cron: %[1]s скасовано: %[3]s defaulthooks.desc = Вебхуки автоматично сповіщають HTTP-сервер POST-запитами, коли в Forgejo відбуваються певні події. Вказані тут вебхуки є типовими і будуть скопійовані до всіх нових репозиторіїв. Докладніше — в посібнику з вебхуків. assets = Ресурси коду auths.invalid_openIdConnectAutoDiscoveryURL = Неправильна URL-адреса автоматичного виявлення (повинна бути дійсна URL-адреса, що починається з http:// або https://) +settings = Налаштування адміністратора [action] diff --git a/options/locale/locale_vi.ini b/options/locale/locale_vi.ini index 57e592a209..28577bc3f7 100644 --- a/options/locale/locale_vi.ini +++ b/options/locale/locale_vi.ini @@ -9,7 +9,7 @@ sign_up = Đăng ký link_account = Liên kết tài khoản register = Đăng ký version = Phiên bản -powered_by = Sử dụng %s +powered_by = Được cung cấp bởi %s page = Trang template = Mẫu language = Ngôn ngữ @@ -25,7 +25,7 @@ access_token = Mã truy cập captcha = CAPTCHA twofa = Xác thực hai lớp webauthn_insert_key = Cắm khóa bảo mật của bạn vào -copy_hash = Chép chuỗi băm +copy_hash = Sao chép chuỗi băm sign_in_with_provider = Đăng nhập bằng %s webauthn_press_button = Hãy nhấn nút trên khóa bảo mật… webauthn_use_twofa = Dùng mã xác thực hai lớp ở trên điện thoại @@ -36,7 +36,7 @@ webauthn_error_insecure = WebAuthn chỉ hỗ trợ kết nối mã hóa. Nếu webauthn_error_unable_to_process = Máy chủ không thể xử lý yêu cầu của bạn. webauthn_error_empty = Bạn phải đặt tên cho khóa này. webauthn_error_timeout = Hết thời gian đọc khóa mất rồi. Hãy tải lại trang và thử lại. -copy_type_unsupported = Không chép được +copy_type_unsupported = Không thể sao chép loại tệp này repository = Kho mã organization = Tổ chức new_fork = Tạo một nhánh mới @@ -55,17 +55,17 @@ all = Tất cả sources = Nguồn forks = Các phân nhánh activities = Hoạt động -pull_requests = Yêu cầu thêm mã +pull_requests = Yêu cầu kéo mã save = Lưu -issues = +issues =Vấn đề enabled = Bật disabled = Tắt -copy = Chép -copy_generic = Chép vào bộ nhớ tạm -copy_url = Chép URL -copy_content = Chép nội dung -copy_success = Đã chép! -copy_error = Không chép được +copy = Sao chép +copy_generic = Sao chép vào bộ nhớ tạm +copy_url = Sao chép URL +copy_content = Sao chép nội dung +copy_success = Đã sao chép! +copy_error = Sao chép thất bại write = Viết preview = Xem trước error = Lỗi @@ -73,7 +73,7 @@ error413 = Bạn đã dùng hết định mức. go_back = Quay lại invalid_data = Dữ liệu không hợp lệ: %v never = Không bao giờ -unknown = Không biết +unknown = Không xác định unpin = Bỏ ghim pin = Ghim archived = Đã lưu trữ @@ -81,6 +81,60 @@ signed_in_as = Đăng nhập bằng re_type = Xác nhận mật khẩu webauthn_sign_in = Nhấn nút trên khóa bảo mật, nếu không có nút thì bạn hãy rút ra rồi cắm lại. new_org.link = Tạo tổ chức -error404 = Trang bạn đang tìm không tồn tại hoặc bạn không có quyền xem. +error404 = Trang bạn đang tìm không tồn tại, đã bị xoá hoặc bạn không có quyền để xem nó. edit = Chỉnh sửa -filter = Lọc \ No newline at end of file +filter = Bộ lọc +dashboard = Trang quản lý +logo = Logo +toc = Mục lục +user_profile_and_more = Hồ sơ và cài đặt… +passcode = Mã xác thực +webauthn_error_duplicated = Khóa bảo mật không được phép cho yêu cầu này. Vui lòng đảm bảo rằng khóa chưa được đăng ký trước đó. +mirror = Bản sao +new_mirror = Tạo bản sao mới +your_starred = Đã đánh sao +mirrors = Các bản sao +concept_system_global = Chung +concept_user_individual = Cá nhân +show_log_seconds = Hiện giây +show_full_screen = Toàn màn hình +download_logs = Tải xuống nhật ký +confirm_delete_selected = Xác nhận xoá tất cả mục được chọn? +name = Tên +filter.clear = Xoá bộ lọc +filter.not_fork = Không phải phân nhánh +filter.not_archived = Không bị lưu trữ +filter.is_archived = Bị lưu trữ +filter.is_fork = Phân nhánh +filter.is_mirror = Bản sao +filter.is_template = Mẫu +filter.not_template = Không phải mẫu +filter.public = Công khai +filter.private = Riêng tư +twofa_scratch = Mã xác thực 2 lớp dự phòng +collaborative = Cộng tác +milestones = Cột mốc +cancel = Huỷ bỏ +retry = Thử lại +rerun = Chạy lại +rerun_all = Chạy lại tất cả +ok = Đồng ý +add = Thêm +add_all = Thêm tất cả +remove = Xoá +remove_all = Xoá tất cả +remove_label_str = Xoá "%s" +locked = Bị khoá +copy_branch = Sao chép tên nhánh +loading = Đang tải… +rss_feed = Nguồn RSS +confirm_delete_artifact = Bạn có chắc muốn xoá "%s" ? +value = Giá trị +copy_path = Sao chép đường dẫn +filter.not_mirror = Không phải bản sao +show_timestamps = Hiện mốc thời gian +concept_code_repository = Kho mã +concept_user_organization = Tổ chức + +[search] +search = Tìm kiếm… \ No newline at end of file diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index acdd4c0ced..c6c534df9f 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1062,8 +1062,8 @@ language.description = 此语言将保存到您的账号中,并在您登录后 language.localization_project = 帮助我们将 Forgejo 翻译成您的语言!了解更多。 user_block_yourself = 您不能屏蔽自己。 pronouns_custom_label = 自定义代词 -change_username_redirect_prompt.with_cooldown.one = 旧的用户名将在%[1]d天的保护期后对所有人可用,您仍可以在此期间重新认领旧的用户名。 -change_username_redirect_prompt.with_cooldown.few = 旧的用户名将在%[1]d天的保护期后对所有人可用,您仍可以在此期间重新认领旧的用户名。 +change_username_redirect_prompt.with_cooldown.one = 旧用户名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧用户名。 +change_username_redirect_prompt.with_cooldown.few = 旧用户名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧用户名。 keep_pronouns_private = 仅向已认证用户显示代词 keep_pronouns_private.description = 这将对未登录的访问者隐藏您的代词。 quota = 配额 @@ -1581,7 +1581,7 @@ issues.remove_ref_at=`删除了引用 %s %s` issues.add_ref_at=`添加了引用 %s %s` issues.delete_branch_at=`于 %[2]s 删除了分支 %[1]s` issues.filter_label=标签筛选 -issues.filter_label_exclude=`使用 alt + 鼠标左键 / 回车 排除标签` +issues.filter_label_exclude=使用 Alt + 单击 排除标签 issues.filter_label_no_select=所有标签 issues.filter_label_select_no_label=无标签 issues.filter_milestone=里程碑筛选 @@ -1655,13 +1655,13 @@ 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.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=`于 %[2]s 从合并请求 %[4]s引用了此议题,将关闭此议题` -issues.ref_reopening_from=`于 %[2]s 引用了合并请求 %[4]s 将重新讨论此议题 ` +issues.closed_at=`于 %s 关闭了此议题` +issues.reopened_at=`于 %s 重新打开了此议题` +issues.commit_ref_at=`于 %s 从提交中引用了此议题` +issues.ref_issue_from=`引用了此议题 %[3]s %[1]s` +issues.ref_pull_from=`引用了此合并请求 %[3]s %[1]s` +issues.ref_closing_from=`于 %[1]s 从合并请求 %[3]s 引用了此议题,将关闭此议题` +issues.ref_reopening_from=`于 %[1]s 从合并请求 %[3]s 引用了此议题,将重新打开此议题 ` issues.ref_closed_from=`关闭了这个议题 %[4]s %[2]s` issues.ref_reopened_from=`重新打开这个议题 %[4]s %[2]s` issues.ref_from=`来自 %[1]s` @@ -1969,8 +1969,8 @@ pulls.update_branch_success=分支更新成功 pulls.update_not_allowed=您无权更新分支 pulls.outdated_with_base_branch=此分支相比基础分支已过期 pulls.close=关闭 -pulls.closed_at=`于%[2]s关闭此合并请求 ` -pulls.reopened_at=`重新打开此合并请求 %[2]s` +pulls.closed_at=`于 %s 关闭了此合并请求 ` +pulls.reopened_at=`于 %s 重新打开了此合并请求` pulls.cmd_instruction_hint=查看命令行说明 pulls.cmd_instruction_checkout_title=检出 pulls.cmd_instruction_checkout_desc=从你的仓库中检出一个新的分支并测试变更。 @@ -2770,7 +2770,7 @@ settings.wiki_rename_branch_main = 标准化百科分支名称 settings.wiki_rename_branch_main_notices_1 = 此操作无法撤消。 settings.wiki_branch_rename_success = 百科仓库的分支名称已成功规范化。 settings.confirm_wiki_branch_rename = 重命名百科分支 -pulls.commit_ref_at = `在提交 %[2]s 中引用了此合并请求` +pulls.commit_ref_at = `于 %s 从提交中引用了此合并请求` settings.wiki_rename_branch_main_notices_2 = 这将永久重命名 %s 的仓库百科的内部分支。现存的检出方式需要更新。 settings.wiki_branch_rename_failure = 无法标准化仓库百科的分支名称。 settings.add_collaborator_blocked_our = 因仓库所有者已将其拉黑,不能添加该用户为协作者。 @@ -2922,6 +2922,7 @@ settings.event_action_success = 成功 settings.event_action_success_desc = Action运行以成功结束。 settings.event_action_failure_desc = Action运行以失败结束。 settings.event_header_action = Action运行事件 +issues.filter_type.all_pull_requests = 所有合并请求 [graphs] component_loading=正在加载 %s… @@ -3057,8 +3058,8 @@ teams.invite.by=邀请人 %s teams.invite.description=请点击下面的按钮加入团队。 follow_blocked_user = 你无法关注此组织,因为此组织已屏蔽你。 open_dashboard = 打开仪表盘 -settings.change_orgname_redirect_prompt.with_cooldown.one = 旧的组织名将在%[1]d天的保护期后对所有人可用,您仍可以在此期间重新认领旧的名字。 -settings.change_orgname_redirect_prompt.with_cooldown.few = 旧的组织名将在%[1]d天的保护期后对所有人可用,您仍可以在此期间重新认领旧名字。 +settings.change_orgname_redirect_prompt.with_cooldown.one = 旧组织名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧名称。 +settings.change_orgname_redirect_prompt.with_cooldown.few = 旧组织名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧名称。 [admin] dashboard=管理面板 diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini index e2cb0d8b2c..45534801de 100644 --- a/options/locale/locale_zh-HK.ini +++ b/options/locale/locale_zh-HK.ini @@ -574,7 +574,7 @@ issues.delete_comment_confirm=您確定要刪除該條評論嗎? issues.context.edit=編輯 issues.reopen_issue=重新開啟 issues.create_comment=評論 -issues.commit_ref_at=`在代碼提交 %[2]s 中引用了該問題` +issues.commit_ref_at=`在代碼提交 %s 中引用了該問題` issues.role.owner=管理員 issues.role.member=普通成員 issues.sign_in_require_desc= 登入 才能加入這對話。 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index b21e4c8f79..fba51a391e 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -1604,13 +1604,13 @@ 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.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.closed_at=`關閉了這個問題 %s` +issues.reopened_at=`重新開放了這個問題 %s` +issues.commit_ref_at=`在提交中關聯了這個問題 %s` +issues.ref_issue_from=`關聯了這個問題 %[3]s %[1]s` +issues.ref_pull_from=`關聯了這個合併請求 %[3]s %[1]s` +issues.ref_closing_from=從將關閉此問題的拉取請求 %[3]s 中提及了此問題,%[1]s +issues.ref_reopening_from=從將重新開啟此問題的拉取請求 %[3]s 中提及了此問題,%[1]s issues.ref_closed_from=`關閉了這個問題 %[4]s %[2]s` issues.ref_reopened_from=`重新開放了這個問題 %[4]s %[2]s` issues.ref_from=`自 %[1]s` @@ -1879,8 +1879,8 @@ pulls.update_branch_success=分支更新成功 pulls.update_not_allowed=您無權更新分支 pulls.outdated_with_base_branch=相對於基底分支,此分支已過時 pulls.close=關閉合併請求 -pulls.closed_at=`關閉了這個合併請求 %[2]s` -pulls.reopened_at=`重新開放了這個合併請求 %[2]s` +pulls.closed_at=`關閉了這個合併請求 %s` +pulls.reopened_at=`重新開放了這個合併請求 %s` pulls.clear_merge_message=清除合併訊息 pulls.clear_merge_message_hint=清除合併訊息將僅移除提交訊息內容,留下產生的 git 結尾,如「Co-Authored-By …」。 @@ -2634,7 +2634,7 @@ commits.search_branch = 此分支 commits.browse_further = 進一步瀏覽 commits.renamed_from = 自 %s 重新命名 issues.filter_milestone_none = 沒有里程碑 -issues.num_comments_1 = %s 則留言 +issues.num_comments_1 = %d 則留言 issues.no_content = 沒有提供敘述。 settings.new_owner_blocked_doer = 新的所有者已封鎖您。 new_repo_helper = 一個儲存庫包含專案的所有檔案和它們的修訂歷史。在別處已經有儲存庫了嗎?遷移儲存庫。 @@ -2693,7 +2693,7 @@ signing.wont_sign.never = 永不簽署提交。 editor.push_out_of_date = 該推送似乎過期了。 issues.cancel_tracking_history = `已取消時間追蹤 %s` issues.due_date_not_writer = 您需要有寫入這個儲存庫的權限才能更新其問題的到期日。 -pulls.commit_ref_at = `在提交 %[2]s 引用了這個合併請求` +pulls.commit_ref_at = `在提交 %s 引用了這個合併請求` pulls.cmd_instruction_checkout_desc = 從您的專案儲存庫中,建立並切換到一個新分支以測試這些變更。 pulls.cmd_instruction_merge_title = 合併 pulls.ready_for_review = 可以開始審閱了嗎? diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 0e3ea260de..97a8536d4f 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -102,5 +102,8 @@ "editor.textarea.tab_hint": "Řádek je již odsazen. Pro opuštění editoru stiskněte znovu Tab nebo Escape.", "editor.textarea.shift_tab_hint": "Na tomto řádku není žádné odsazení. Pro opuštění editoru stiskněte znovu Shift + Tab nebo Escape.", "admin.dashboard.cleanup_offline_runners": "Vymazat offline runnery", - "settings.visibility.description": "Viditelnost profilu ovlivňuje možnost ostatních přistupovat k vašim veřejným repozitářům. Zjistit více" + "settings.visibility.description": "Viditelnost profilu ovlivňuje možnost ostatních přistupovat k vašim veřejným repozitářům. Zjistit více", + "avatar.constraints_hint": "Velikost vlastního avataru nesmí překročit %[1]s nebo být větší než %[2]dx%[3]d pixelů", + "repo.diff.commit.next-short": "Další", + "repo.diff.commit.previous-short": "Předchozí" } diff --git a/options/locale_next/locale_da.json b/options/locale_next/locale_da.json index fe3c25ab2e..8315e06bcc 100644 --- a/options/locale_next/locale_da.json +++ b/options/locale_next/locale_da.json @@ -92,5 +92,10 @@ "followers.outgoing.list.self.none": "Du følger ikke nogen.", "followers.outgoing.list.none": "%s følger ikke nogen.", "editor.textarea.tab_hint": "Linjen er allerede indrykket. Tryk på Tab igen eller Escape for at forlade editoren.", - "editor.textarea.shift_tab_hint": "Ingen indrykning på denne linje. Tryk på Shift + Tab igen eller Escape for at forlade editoren." + "editor.textarea.shift_tab_hint": "Ingen indrykning på denne linje. Tryk på Shift + Tab igen eller Escape for at forlade editoren.", + "admin.dashboard.cleanup_offline_runners": "Ryd op offline runners", + "settings.visibility.description": "Profilsynlighed påvirker andres adgang til dine ikke-private depoter. Læs mere", + "avatar.constraints_hint": "Brugerdefineret avatar må ikke overstige %[1]s i størrelse eller være større end %[2]dx%[3]d pixels", + "repo.diff.commit.next-short": "Næste", + "repo.diff.commit.previous-short": "Forrige" } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index 846e85d094..3847de2b43 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -93,5 +93,14 @@ "followers.incoming.list.none": "Niemand folgt diesem Benutzer.", "editor.textarea.tab_hint": "Zeile bereits eingerückt. Drücke nochmals Tab oder Escape um den Editor zu verlassen.", "editor.textarea.shift_tab_hint": "Keine Einrückung auf dieser Zeile. Drücke nochmals Shift + Tab oder Escape um den Editor zu verlassen.", - "admin.dashboard.cleanup_offline_runners": "Aufräumen der offline Runner" + "admin.dashboard.cleanup_offline_runners": "Aufräumen der offline Runner", + "settings.visibility.description": "Die Profilsichtbarkeit beeinflusst die Möglichkeit anderer, auf deine nicht-privaten Repositorys zuzugreifen. Erfahre mehr", + "avatar.constraints_hint": "Individuelles Profilbild darf %[1]s in der Größe nicht überschreiten, und nicht größer als %[2]dx%[3]d Pixel sein", + "repo.diff.commit.next-short": "Nächste", + "repo.diff.commit.previous-short": "Vorherige", + "profile.edit.link": "Profil bearbeiten", + "feed.atom.link": "Atom-Feed", + "keys.ssh.link": "SSH-Schlüssel", + "keys.gpg.link": "GPG-Schlüssel", + "profile.actions.tooltip": "Mehr Aktionen" } diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index c7e9f65a68..b1c98e4551 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -63,6 +63,11 @@ "alert.asset_load_failed": "Failed to load asset files from {path}. Please make sure the asset files can be accessed.", "alert.range_error": " must be a number between %[1]s and %[2]s.", "install.invalid_lfs_path": "Unable to create the LFS root at the specified path: %[1]s", + "profile.actions.tooltip": "More actions", + "profile.edit.link": "Edit profile", + "feed.atom.link": "Atom feed", + "keys.ssh.link": "SSH keys", + "keys.gpg.link": "GPG keys", "admin.config.moderation_config": "Moderation configuration", "moderation.report_abuse": "Report abuse", "moderation.report_content": "Report content", @@ -89,10 +94,13 @@ "mail.actions.run_info_previous_status": "Previous Run's Status: %[1]s", "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Triggered because: %[1]s by: %[2]s", + "repo.diff.commit.next-short": "Next", + "repo.diff.commit.previous-short": "Prev", "discussion.locked": "This discussion has been locked. Commenting is limited to contributors.", "editor.textarea.tab_hint": "Line already indented. Press Tab again or Escape to leave the editor.", "editor.textarea.shift_tab_hint": "No indentation on this line. Press Shift + Tab again or Escape to leave the editor.", "admin.dashboard.cleanup_offline_runners": "Cleanup offline runners", "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more", + "avatar.constraints_hint": "Custom avatar may not exceed %[1]s in size or be larger than %[2]dx%[3]d pixels", "meta.last_line": "Thank you for translating Forgejo! This line isn't seen by the users but it serves other purposes in the translation management. You can place a fun fact in the translation instead of translating it." } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 1f1e535dad..884a7b44eb 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -21,7 +21,7 @@ "alert.asset_load_failed": "Nabigong i-load ang mga asset file mula sa {path}. Siguraduhin na maa-access ang mga asset file.", "install.invalid_lfs_path": "Nabigong gawin ang LFS root sa tinakdang path: %[1]s", "alert.range_error": " dapat ay numero sa pagitan ng %[1]s at %[2]s.", - "meta.last_line": "Sayori... I love you. — MC from Doki Doki Literature Club", + "meta.last_line": "Every day, I imagine a future where I can be with you. In my hand is a pen that will write a poem of me and you. The ink flows down into a dark puddle... Just move your hand, write the way into his heart. But in this world of infinite choices. What will it take just to find that special day? Have I found everybody a fun assignment to do today? When you're here, everything that we do is fun for them anyway... When I can't even read my own feelings. What good are words when a smile says it all? And if this world won't write me an ending... What will it take just for me to have it all? Does my pen only write bitter words for those who are dear to me? Is it love if I take you, or is it love if I set you free? The ink flows down into a dark puddle... How can I write love into reality? If I can't hear the sound of your heartbeat What do you call love in your reality? And in your reality, if I don't know how to love you... I'll leave you be.", "mail.actions.successful_run_after_failure": "Na-recover ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.not_successful_run": "Nabigo ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.run_info_previous_status": "Nakaraang Status ng Run: %[1]s", @@ -94,5 +94,13 @@ "editor.textarea.tab_hint": "Naka-indent na ang linya. Pindutin ulit ang Tab o Escape para umalis sa editor.", "editor.textarea.shift_tab_hint": "Walang indentation sa linyang ito. Pindutin ang Shift + Tab ulit o Escape para umalis sa editor.", "admin.dashboard.cleanup_offline_runners": "Linisin ang mga offline na runner", - "settings.visibility.description": "Maaapektuhan ng visibility ng profile ang kakayahan ng iba na i-access ang iyong mga hindi pribadong repositoryo. Matuto pa" + "settings.visibility.description": "Maaapektuhan ng visibility ng profile ang kakayahan ng iba na i-access ang iyong mga hindi pribadong repositoryo. Matuto pa", + "avatar.constraints_hint": "Hindi maaaring lumagpas sa laking %[1]s o mas malaki sa %[2]dx%[3]d pixel ang custom na avatar", + "repo.diff.commit.next-short": "Susunod", + "repo.diff.commit.previous-short": "Nakaraan", + "profile.edit.link": "I-edit ang profile", + "feed.atom.link": "Atom feed", + "keys.ssh.link": "Mga SSH key", + "keys.gpg.link": "Mga GPG key", + "profile.actions.tooltip": "Higit pang mga aksyon" } diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index 0792804bf2..da26d56107 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -6,7 +6,7 @@ }, "repo.pulls.title_desc": { "one": "veut fusionner %[1]d commit depuis %[2]s vers %[3]s", - "many": "souhaite fusionner %[1]d révision(s) depuis %[2]s vers %[3]s", + "many": "veut fusionner %[1]d commits depuis %[2]s vers %[3]s", "other": "" }, "search.milestone_kind": "Recherche dans les jalons…", @@ -97,5 +97,11 @@ "repo.issue_indexer.title": "Indexeur de problèmes", "stars.list.none": "Personne n'a mis d'étoiles sur ce dépôt.", "watch.list.none": "Personne ne consulte ce dépôt.", - "repo.form.cannot_create": "Tous les espaces dans lesquels vous pouvez créer des dépôts ont atteint la limite de dépôts." + "repo.form.cannot_create": "Tous les espaces dans lesquels vous pouvez créer des dépôts ont atteint la limite de dépôts.", + "admin.dashboard.cleanup_offline_runners": "Nettoyer les exécuteurs hors ligne", + "mail.actions.run_info_trigger": "Déclenché parce que : %[1]s par : %[2]s", + "settings.visibility.description": "La visibilité du profil affecte la capacité des autres à accéder à vos dépôts non-privés. Voir plus", + "editor.textarea.shift_tab_hint": "Pas d'indentation sur cette ligne. Appuyez sur Maj + Tab une nouvelle fois ou sur Échap pour quitter l'éditeur.", + "avatar.constraints_hint": "L'avatar personnalisé ne doit pas dépasser une taille de %[1]s ou être plus grand que %[2]dx%[3]d pixels", + "editor.textarea.tab_hint": "Ligne déjà indentée. Appuyez sur Tab une nouvelle fois ou sur Échap pour quitter l'éditeur." } diff --git a/options/locale_next/locale_it-IT.json b/options/locale_next/locale_it-IT.json index 8bd6d811a0..8464d6244e 100644 --- a/options/locale_next/locale_it-IT.json +++ b/options/locale_next/locale_it-IT.json @@ -15,5 +15,83 @@ "home.welcome.no_activity": "Nessun'attività", "home.explore_repos": "Esplora i repositori", "home.explore_users": "Esplora l'utenza", - "home.explore_orgs": "Esplora le organizzazioni" + "home.explore_orgs": "Esplora le organizzazioni", + "mail.actions.successful_run_after_failure_subject": "Il flusso di lavoro %[1] ha ripreso a funzionare nel repositorio %[2]s", + "mail.actions.not_successful_run_subject": "Il flusso di lavoro %[1]s è fallito nel repositorio %[2]s", + "relativetime.future": "nel futuro", + "relativetime.days": { + "one": "ieri", + "many": "%d giorni fa", + "other": "%d giorni fa" + }, + "relativetime.1day": "ieri", + "repo.form.cannot_create": "Tutti gli spazi in cui puoi creare repositori hanno raggiunto il limite di repositori.", + "discussion.locked": "Questa discussione è stata bloccata. Solo i contributori possono commentare.", + "relativetime.hours": { + "one": "un'ora fa", + "many": "%d ore fa", + "other": "%d ore fa" + }, + "relativetime.2years": "due anni fa", + "relativetime.now": "adesso", + "relativetime.weeks": { + "one": "una settimana fa", + "many": "%d settimane fa", + "other": "%d settimane fa" + }, + "relativetime.months": { + "one": "un mese fa", + "many": "%d mesi fa", + "other": "%d mesi fa" + }, + "relativetime.years": { + "one": "un anno fa", + "many": "%d anni fa", + "other": "%d anni fa" + }, + "repo.issue_indexer.title": "Indicizzatore delle segnalazioni", + "admin.config.moderation_config": "Impostazioni di moderazione", + "moderation.report_abuse": "Segnala abuso", + "moderation.report_content": "Segnala contenuto", + "moderation.report_abuse_form.already_reported": "Hai già segnalato questo contenuto", + "moderation.abuse_category": "Categoria", + "moderation.abuse_category.placeholder": "Seleziona una categoria", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.illegal_content": "Contenuti illegali", + "moderation.abuse_category.other_violations": "Altre violazioni delle regole della piattaforma", + "moderation.report_remarks": "Note aggiuntive", + "moderation.report_remarks.placeholder": "Aggiungi dettagli riguardanti l'abuso che stai segnalando.", + "moderation.submit_report": "Invia segnalazione", + "error.not_found.title": "Pagina non trovata", + "themes.names.forgejo-auto": "Forgejo (segui le impostazioni di sistema)", + "stars.list.none": "Nessuno ha messo una stella a questo repo.", + "watch.list.none": "Nessuno sta osservando questo repo.", + "followers.incoming.list.self.none": "Nessuno sta seguendo il tuo profilo.", + "followers.incoming.list.none": "Nessuno sta seguendo questo utente.", + "followers.outgoing.list.self.none": "Non segui nessuno.", + "followers.outgoing.list.none": "%s non sta seguendo nessuno.", + "relativetime.2days": "due giorni fa", + "relativetime.2weeks": "due settimane fa", + "relativetime.1week": "la settimana scorsa", + "relativetime.1month": "il mese scorso", + "relativetime.2months": "due mesi fa", + "relativetime.1year": "l'anno scorso", + "moderation.report_abuse_form.header": "Segnala abuso all'amministratore", + "moderation.report_abuse_form.details": "Questo modulo dovrebbe essere utilizzato per segnalare utenti che creano profili, repositori, segnalazioni o commenti spam o che si comportano in modo non adeguato.", + "moderation.report_abuse_form.invalid": "Argomenti non validi", + "moderation.reporting_failed": "Impossibile inviare segnalazione: %v", + "moderation.reported_thank_you": "Grazie per la segnalazione. L'amministratore è stato avvertito.", + "mail.actions.run_info_ref": "Ramo: %[1]s (%[2]s)", + "alert.asset_load_failed": "Impossibile caricare i file di risorsa da {path}. Controlla che i file di risorsa siano accessibili.", + "install.invalid_lfs_path": "Non è possibile creare una root LFS nel percorso specificato: %[1]s", + "home.welcome.activity_hint": "Non c'è nulla nel tuo feed. Le tue azioni e le attività dei repositori che segui verranno mostrate qui.", + "relativetime.mins": { + "one": "un minuto fa", + "many": "%d minuti fa", + "other": "%d minuti fa" + }, + "editor.textarea.tab_hint": "Linea già indentata. Premi di nuovo Tab o Esc per uscire dall'editor.", + "repo.diff.commit.previous-short": "Precedente", + "meta.last_line": "Ambaraba cicci cocco." } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index 6386f2163b..f71cfa227d 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -102,5 +102,8 @@ "editor.textarea.tab_hint": "Rinda jau ir ar atkāpi. Spied Tab vēlreiz vai Escape, lai izietu no redaktora!", "editor.textarea.shift_tab_hint": "Šajā rindā nav atkāpes. Spied Shift + Tab vēlreiz vai Escape, lai izietu no redaktora!", "admin.dashboard.cleanup_offline_runners": "Notīrīt bezsaistes izpildītājus", - "settings.visibility.description": "Profila redzamība ietekmē iespēju citiem piekļūt Tavām glabātavām, kas nav privātas. Uzzināt vairāk" + "settings.visibility.description": "Profila redzamība ietekmē iespēju citiem piekļūt Tavām glabātavām, kas nav privātas. Uzzināt vairāk", + "avatar.constraints_hint": "Pielāgots profila attēls nevar pārsniegt %[1]s vai būt lielāks par %[2]dx%[3]d pikseļiem", + "repo.diff.commit.next-short": "Nāk.", + "repo.diff.commit.previous-short": "Iepr." } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index ba8d66a2da..24268e2082 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -94,5 +94,13 @@ "editor.textarea.tab_hint": "Rieg al inschuven. Drück weer Tab of Esc, um de Bewarker to verlaten.", "editor.textarea.shift_tab_hint": "Keen Inschuuv in deeser Rieg. Drück weer Umschalt+Tab of Esc, um de Bewarker to verlaten.", "admin.dashboard.cleanup_offline_runners": "Nich verbunnen Lopers uprümen", - "settings.visibility.description": "De Profil-Sichtbaarkeid maakt daar wat an, of un wo anner Lüü diene nich-privaaten Repositoriums ankieken könen. Mehr unnerhören" + "settings.visibility.description": "De Profil-Sichtbaarkeid maakt daar wat an, of un wo anner Lüü diene nich-privaaten Repositoriums ankieken könen. Mehr unnerhören", + "avatar.constraints_hint": "Dat eegene Kontobill düür nich groter as %[1]s wesen of groter as %[2]d×%[3]d Billtüttels wesen", + "repo.diff.commit.next-short": "Anner", + "repo.diff.commit.previous-short": "Vörig", + "feed.atom.link": "Atom-Schuuv", + "keys.ssh.link": "SSH-Slötels", + "keys.gpg.link": "GPG-Slötels", + "profile.actions.tooltip": "Mehr Aktioonen", + "profile.edit.link": "Profil bewarken" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 909c9339d3..1a5eca6d34 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -100,5 +100,10 @@ "stars.list.none": "Ninguém favoritou este repositório.", "followers.outgoing.list.self.none": "Você não está seguindo ninguém.", "editor.textarea.tab_hint": "Linha já indentada. Pressione Tab novamente ou Esc para sair do editor.", - "editor.textarea.shift_tab_hint": "Sem indentação nesta linha. Pressione Shift + Tab novamente ou Esc para sair do editor." + "editor.textarea.shift_tab_hint": "Sem indentação nesta linha. Pressione Shift + Tab novamente ou Esc para sair do editor.", + "admin.dashboard.cleanup_offline_runners": "Limpar runners desconectados", + "avatar.constraints_hint": "Imagem de perfil personalizada não pode exceder %[1]s em tamanho ou ser maior que %[2]dx%[3]d pixels", + "settings.visibility.description": "A visibilidade do perfil afeta a habilidade de acessarem seus repositórios não-privados. Saiba mais", + "repo.diff.commit.next-short": "Próximo", + "repo.diff.commit.previous-short": "Anterior" } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index da27faa2ce..78e6dc4493 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -101,5 +101,7 @@ "editor.textarea.tab_hint": "Linha já indentada. Pressione Tab novamente ou Escape para sair do editor.", "editor.textarea.shift_tab_hint": "Sem indentação nesta linha. Pressione Shift + Tab novamente ou Escape para sair do editor.", "stars.list.none": "Ninguém juntou este repositório aos favoritos.", - "admin.dashboard.cleanup_offline_runners": "Limpeza de executores offline" + "admin.dashboard.cleanup_offline_runners": "Limpeza de executores offline", + "settings.visibility.description": "A visibilidade do perfil afecta a capacidade de outros acederem aos seus repositórios não privados. Ler mais", + "avatar.constraints_hint": "O avatar personalizado não pode exceder %[1]s de tamanho ou ser maior do que %[2]dx%[3]d pixéis" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index 6dd2b6fb7e..922e2612af 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -23,7 +23,7 @@ "alert.asset_load_failed": "Не удалось получить ресурсы из {path}. Убедитесь, что файлы ресурсов доступны.", "install.invalid_lfs_path": "Не удалось расположить корень LFS по указанному пути: %[1]s", "alert.range_error": " - число должно быть в диапазоне от %[1]s-%[2]s.", - "meta.last_line": "Unskip.", + "meta.last_line": "Unskip..", "mail.actions.not_successful_run_subject": "Провал раб. потока %[1]s в репозитории %[2]s", "mail.actions.successful_run_after_failure_subject": "Возобновление раб. потока %[1]s в репозитории %[2]s", "mail.actions.run_info_ref": "Ветвь: %[1]s (%[2]s)", @@ -101,5 +101,14 @@ "moderation.report_abuse_form.header": "Жалоба администрации", "editor.textarea.tab_hint": "Отступ уже добавлен. Нажмите Tab снова или Escape, чтобы покинуть редактор.", "editor.textarea.shift_tab_hint": "В строке нет отступов. Нажмите Shift + Tab снова или Escape, чтобы покинуть редактор.", - "admin.dashboard.cleanup_offline_runners": "Удалить недоступных исполнителей" + "admin.dashboard.cleanup_offline_runners": "Удалить недоступных исполнителей", + "avatar.constraints_hint": "Изображение профиля не может быть более %[1]s и крупнее %[2]dx%[3]d пикселей", + "settings.visibility.description": "Видимость профиля влияет на доступ других до ваших не частных репозиториев. Подробнее", + "repo.diff.commit.previous-short": "Пред.", + "repo.diff.commit.next-short": "След.", + "profile.actions.tooltip": "Показать действия", + "feed.atom.link": "Atom-лента", + "keys.ssh.link": "Ключи SSH", + "keys.gpg.link": "Ключи GPG", + "profile.edit.link": "Изменить профиль" } diff --git a/options/locale_next/locale_sv-SE.json b/options/locale_next/locale_sv-SE.json index 53b25dffcb..9a8762212c 100644 --- a/options/locale_next/locale_sv-SE.json +++ b/options/locale_next/locale_sv-SE.json @@ -56,7 +56,7 @@ "mail.actions.successful_run_after_failure": "Arbetsflöde %[1]s återställdes i förrådet %[2]s", "mail.actions.not_successful_run": "Arbetsflödet %[1]s misslyckades i förrådet %[2]s", "editor.textarea.shift_tab_hint": "Ingen indragning på den här raden. Tryck på Shift + Tab igen eller Escape för att lämna redigeringsläget.", - "meta.last_line": "Tack för att du översatte Forgejo! Daniel Nylander heter jag. https://www.danielnylander.se", + "meta.last_line": "Daniel Nylander heter jag och har översatt Forgejo. Mer information om mig på https://www.danielnylander.se", "editor.textarea.tab_hint": "Raden är redan indragen. Tryck på Tab igen eller Escape för att lämna redigeringsläget.", "home.welcome.no_activity": "Ingen aktivitet", "home.welcome.activity_hint": "Det finns inget i ditt flöde ännu. Dina åtgärder och aktivitet från förråd som du bevakar kommer att visas här.", @@ -93,5 +93,7 @@ "moderation.abuse_category": "Kategori", "moderation.abuse_category.placeholder": "Välj en kategori", "moderation.abuse_category.spam": "Skräppost", - "moderation.abuse_category.malware": "Skadlig kod" + "moderation.abuse_category.malware": "Skadlig kod", + "settings.visibility.description": "Profilens synlighet påverkar andras möjlighet att komma åt dina icke-privata förråd. Läs mer", + "avatar.constraints_hint": "Anpassade avatarer får inte vara större än %[1] eller %[2]dx%[3] bildpunkter" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index a00d368f4a..33cb5a41a3 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -88,10 +88,10 @@ "moderation.report_abuse_form.details": "Використовуйте цю форму, щоб повідомити про користувачів, які створюють спам-профілі, репозиторії, задачі, коментарі або поводяться неналежним чином.", "moderation.abuse_category": "Категорія", "moderation.abuse_category.other_violations": "Інші порушення правил платформи", - "moderation.reporting_failed": "Не вдалося надіслати нове повідомлення про порушення: %v", + "moderation.reporting_failed": "Не вдалося надіслати повідомлення про порушення: %v", "moderation.report_remarks": "Подробиці", "moderation.reported_thank_you": "Дякуємо за ваше повідомлення. Адміністрацію поінформовано про нього.", - "repo.form.cannot_create": "Усі простори, в яких ви можете створювати репозиторії, досягли максимальної кількості репозиторіїв.", + "repo.form.cannot_create": "У всіх просторах, де ви можете створювати репозиторії, досягнуто обмеження кількості репозиторіїв.", "repo.issue_indexer.title": "Індексатор задач", "followers.incoming.list.self.none": "Ніхто не стежить за вашим профілем.", "followers.incoming.list.none": "Ніхто не стежить за цим користувачем.", @@ -102,5 +102,13 @@ "editor.textarea.tab_hint": "У рядку вже є відступ. Натисніть Tab ще раз або Esc, щоб вийти з редактора.", "editor.textarea.shift_tab_hint": "У цьому рядку немає відступів. Натисніть Shift + Tab ще раз або Esc, щоб вийти з редактора.", "admin.dashboard.cleanup_offline_runners": "Очистити неактивні раннери", - "settings.visibility.description": "Видимість профілю впливає на можливість інших користувачів отримати доступ до ваших неприватних репозиторіїв. Дізнатися більше" + "settings.visibility.description": "Видимість профілю впливає на можливість інших користувачів отримати доступ до ваших неприватних репозиторіїв. Дізнатися більше", + "avatar.constraints_hint": "Розмір користувацького аватара не може перевищувати %[1]s або бути більшим за %[2]d×%[3]d пікселів", + "repo.diff.commit.next-short": "Наступний", + "repo.diff.commit.previous-short": "Попередній", + "keys.ssh.link": "Ключі SSH", + "keys.gpg.link": "Ключі GPG", + "profile.edit.link": "Редагувати профіль", + "feed.atom.link": "Стрічка Atom", + "profile.actions.tooltip": "Більше дій" } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 721e2ad839..0f408997bf 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -69,5 +69,14 @@ "followers.incoming.list.self.none": "没有人关注你的个人资料。", "editor.textarea.tab_hint": "此行已缩进。再次按 Tab 或按 Escape 退出编辑器。", "editor.textarea.shift_tab_hint": "此行无缩进。再次按 Shift + Tab 或按 Escape 退出编辑器。", - "admin.dashboard.cleanup_offline_runners": "清理离线运行器" + "admin.dashboard.cleanup_offline_runners": "清理离线运行器", + "settings.visibility.description": "个人资料可见性设置会影响他人对您的非私有仓库的访问。了解更多", + "avatar.constraints_hint": "自定义头像大小不得超过 %[1]s,或大于 %[2]d×%[3]d 像素", + "keys.ssh.link": "SSH 密钥", + "keys.gpg.link": "GPG 密钥", + "profile.actions.tooltip": "更多操作", + "repo.diff.commit.next-short": "下个", + "repo.diff.commit.previous-short": "上个", + "feed.atom.link": "Atom 订阅源", + "profile.edit.link": "编辑个人资料" } diff --git a/options/locale_next/locale_zh-TW.json b/options/locale_next/locale_zh-TW.json index 5e3e43f66e..3ae0b00d2b 100644 --- a/options/locale_next/locale_zh-TW.json +++ b/options/locale_next/locale_zh-TW.json @@ -68,5 +68,10 @@ "moderation.report_abuse_form.details": "這個表單是用來檢舉用戶建立垃圾帳號、儲存庫、問題、留言,或其他不當行為。", "moderation.report_abuse_form.invalid": "無效參數", "moderation.report_abuse_form.already_reported": "您已檢舉此內容", - "meta.last_line": "Rubi-chan? Hai! Nani ga suki? Choko minto yori mo a・na・ta♡ Ayumu-chan? Hai! Nani ga suki? Sutoroberii fureibaa yori mo a・na・ta♡ Shiki-chan! Hai! Nani ga suki? Kukkii and kuriimu yori mo a・na・ta♡ Minna? Hai! Nani ga suki? Mochiron daisuki AiScReam." + "meta.last_line": "Rubi-chan? Hai! Nani ga suki? Choko minto yori mo a・na・ta♡ Ayumu-chan? Hai! Nani ga suki? Sutoroberii fureibaa yori mo a・na・ta♡ Shiki-chan! Hai! Nani ga suki? Kukkii and kuriimu yori mo a・na・ta♡ Minna? Hai! Nani ga suki? Mochiron daisuki AiScReam.", + "admin.dashboard.cleanup_offline_runners": "清理離線 runners", + "settings.visibility.description": "個人資料的可見度會影響他人存取您非私人儲存庫的能力。了解更多", + "avatar.constraints_hint": "自定義大頭貼的大小不得超過 %[1]s,且解析度不得大於 %[2]d×%[3]d 像素", + "repo.diff.commit.next-short": "下一個", + "repo.diff.commit.previous-short": "上一個" } diff --git a/package-lock.json b/package-lock.json index b73bc9099b..604ff38c18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,11 +12,12 @@ "@github/markdown-toolbar-element": "2.2.3", "@github/quote-selection": "2.1.0", "@github/text-expander-element": "2.8.0", + "@google/model-viewer": "4.1.0", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.14.0", "ansi_up": "6.0.5", "asciinema-player": "3.8.2", - "chart.js": "4.4.9", + "chart.js": "4.5.0", "chartjs-adapter-dayjs-4": "1.0.4", "chartjs-plugin-zoom": "2.2.0", "clippie": "4.1.7", @@ -31,13 +32,13 @@ "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.6.0", + "mermaid": "11.7.0", "mini-css-extract-plugin": "2.9.2", - "minimatch": "10.0.2", + "minimatch": "10.0.3", "monaco-editor": "0.52.2", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", - "postcss": "8.5.4", + "postcss": "8.5.5", "postcss-loader": "8.1.1", "postcss-nesting": "13.0.2", "pretty-ms": "9.0.0", @@ -62,9 +63,9 @@ "devDependencies": { "@axe-core/playwright": "4.10.2", "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", - "@playwright/test": "1.53.0", + "@playwright/test": "1.52.0", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin-js": "4.4.1", + "@stylistic/eslint-plugin": "4.4.1", "@stylistic/stylelint-plugin": "3.1.2", "@vitejs/plugin-vue": "5.2.4", "@vitest/coverage-v8": "3.2.3", @@ -84,7 +85,7 @@ "eslint-plugin-vitest-globals": "1.5.0", "eslint-plugin-vue": "10.2.0", "eslint-plugin-vue-scoped-css": "2.10.0", - "eslint-plugin-wc": "2.2.1", + "eslint-plugin-wc": "3.0.1", "globals": "16.1.0", "happy-dom": "18.0.0", "license-checker-rseidelsohn": "4.4.2", @@ -1020,9 +1021,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", + "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1034,17 +1035,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/config-array/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1059,9 +1049,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", - "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", + "integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1122,17 +1112,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -1190,19 +1169,32 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", + "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", + "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@github/combobox-nav": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@github/combobox-nav/-/combobox-nav-2.3.1.tgz", @@ -1231,6 +1223,22 @@ "dom-input-range": "^1.2.0" } }, + "node_modules/@google/model-viewer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@google/model-viewer/-/model-viewer-4.1.0.tgz", + "integrity": "sha512-7WB/jS6wfBfRl/tWhsUUvDMKFE1KlKME97coDLlZQfvJD0nCwjhES1lJ+k7wnmf7T3zMvCfn9mIjM/mgZapuig==", + "license": "Apache-2.0", + "dependencies": { + "@monogrid/gainmap-js": "^3.1.0", + "lit": "^3.2.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "three": "^0.172.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1748,6 +1756,27 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1992,6 +2021,21 @@ "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", "license": "MIT" }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.3.0.tgz", + "integrity": "sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.0.tgz", + "integrity": "sha512-L2qyoZSQClcBmq0qajBVbhYEcG6iK0XfLn66ifLe/RfC0/ihpc+pl0Wdn8bJ8o+hj38cG0fGXRgSS20MuXn7qA==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0" + } + }, "node_modules/@mcaptcha/core-glue": { "version": "0.1.0-alpha-5", "resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-5.tgz", @@ -2044,14 +2088,26 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", - "integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.5.0.tgz", + "integrity": "sha512-AiaN7+VjXC+3BYE+GwNezkpjIcCI2qIMB/K4S2/vMWe0q/XJCBbx5+K7iteuz7VyltX9iAK4FmVTvGc9kjOV4w==", "license": "MIT", "dependencies": { "langium": "3.3.1" } }, + "node_modules/@monogrid/gainmap-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.1.0.tgz", + "integrity": "sha512-Obb0/gEd/HReTlg8ttaYk+0m62gQJmCblMOjHSMHRrBP2zdfKMHLCRbh/6ex9fSUJMKdjjIEiohwkbGD3wj2Nw==", + "license": "MIT", + "dependencies": { + "promise-worker-transferable": "^1.0.4" + }, + "peerDependencies": { + "three": ">= 0.159.0" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.11", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", @@ -2131,13 +2187,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.0.tgz", - "integrity": "sha512-15hjKreZDcp7t6TL/7jkAo6Df5STZN09jGiv5dbP9A6vMVncXRqE7/B2SncsyOwrkZRBH2i6/TPOL8BVmm3c7w==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", + "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.53.0" + "playwright": "1.52.0" }, "bin": { "playwright": "cli.js" @@ -2213,9 +2269,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz", - "integrity": "sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz", + "integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==", "cpu": [ "arm" ], @@ -2227,9 +2283,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz", - "integrity": "sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz", + "integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==", "cpu": [ "arm64" ], @@ -2241,9 +2297,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz", - "integrity": "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz", + "integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==", "cpu": [ "arm64" ], @@ -2255,9 +2311,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz", - "integrity": "sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz", + "integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==", "cpu": [ "x64" ], @@ -2269,9 +2325,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz", - "integrity": "sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz", + "integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==", "cpu": [ "arm64" ], @@ -2283,9 +2339,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz", - "integrity": "sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz", + "integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==", "cpu": [ "x64" ], @@ -2297,9 +2353,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz", - "integrity": "sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz", + "integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==", "cpu": [ "arm" ], @@ -2311,9 +2367,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz", - "integrity": "sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz", + "integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==", "cpu": [ "arm" ], @@ -2325,9 +2381,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz", - "integrity": "sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz", + "integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==", "cpu": [ "arm64" ], @@ -2339,9 +2395,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz", - "integrity": "sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz", + "integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==", "cpu": [ "arm64" ], @@ -2353,9 +2409,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz", - "integrity": "sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz", + "integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==", "cpu": [ "loong64" ], @@ -2367,9 +2423,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz", - "integrity": "sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz", + "integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==", "cpu": [ "ppc64" ], @@ -2381,9 +2437,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz", - "integrity": "sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz", + "integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==", "cpu": [ "riscv64" ], @@ -2395,9 +2451,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz", - "integrity": "sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz", + "integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==", "cpu": [ "riscv64" ], @@ -2409,9 +2465,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz", - "integrity": "sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz", + "integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==", "cpu": [ "s390x" ], @@ -2423,9 +2479,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz", - "integrity": "sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz", + "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==", "cpu": [ "x64" ], @@ -2437,9 +2493,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz", - "integrity": "sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz", + "integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==", "cpu": [ "x64" ], @@ -2451,9 +2507,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz", - "integrity": "sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz", + "integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==", "cpu": [ "arm64" ], @@ -2465,9 +2521,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz", - "integrity": "sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz", + "integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==", "cpu": [ "ia32" ], @@ -2479,9 +2535,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz", - "integrity": "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz", + "integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==", "cpu": [ "x64" ], @@ -2701,17 +2757,6 @@ "node": "^12.20 || >=14.13" } }, - "node_modules/@stoplight/spectral-core/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@stoplight/spectral-core/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3010,15 +3055,18 @@ "node": "^12.20 || >=14.13" } }, - "node_modules/@stylistic/eslint-plugin-js": { + "node_modules/@stylistic/eslint-plugin": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-4.4.1.tgz", - "integrity": "sha512-eLisyHvx7Sel8vcFZOEwDEBGmYsYM1SqDn81BWgmbqEXfXRf8oe6Rwp+ryM/8odNjlxtaaxp0Ihmt86CnLAxKg==", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.4.1.tgz", + "integrity": "sha512-CEigAk7eOLyHvdgmpZsKFwtiqS2wFwI1fn4j09IU9GmD4euFM4jEBAViWeCqaNLlbX2k2+A/Fq9cje4HQBXuJQ==", "dev": true, "license": "MIT", "dependencies": { + "@typescript-eslint/utils": "^8.32.1", "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0" + "espree": "^10.3.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3027,6 +3075,19 @@ "eslint": ">=9.0.0" } }, + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@stylistic/stylelint-plugin": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.2.tgz", @@ -3448,9 +3509,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", - "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", + "version": "20.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.1.tgz", + "integrity": "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3476,8 +3537,7 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/@types/unist": { "version": "2.0.11", @@ -3689,6 +3749,23 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -3747,10 +3824,38 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.1.tgz", + "integrity": "sha512-dd7yIp1hfJFX9ZlVLQRrh/Re9WMUHHmF9hrKD1yIvxcyNr2BhQ3xc1upAVhy8NijadnCswAxWQu8MkkSMC1qXQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.1.tgz", + "integrity": "sha512-EzUPcMFtDVlo5yrbzMqUsGq3HnLXw+3ZOhSd7CUaDmbTtnrzM+RO2ntw2dm2wjbbc5djWj3yX0wzbbg8pLhx8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.11.tgz", - "integrity": "sha512-i3/wlWjQJXMh1uiGtiv7k1EYvrrS3L1hdwmWJJiz1D8jWy726YFYPIxQWbEIVPVAgrfRR0XNlLrTQwq17cuCGw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.1.tgz", + "integrity": "sha512-nB+dna3q4kOleKFcSZJ/wDXIsAd1kpMO9XrVAt8tG3RDWJ6vi+Ic6bpz4cmg5tWNeCfHEY4KuqJCB+pKejPEmQ==", "cpu": [ "arm64" ], @@ -3762,9 +3867,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.7.11.tgz", - "integrity": "sha512-8XXyFvc6w6kmMmi6VYchZhjd5CDcp+Lv6Cn1YmUme0ypsZ/0Kzd+9ESrWtDrWibKPTgSteDTxp75cvBOY64FQQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.1.tgz", + "integrity": "sha512-aKWHCrOGaCGwZcekf3TnczQoBxk5w//W3RZ4EQyhux6rKDwBPgDU9Y2yGigCV1Z+8DWqZgVGQi+hdpnlSy3a1w==", "cpu": [ "x64" ], @@ -3776,9 +3881,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.7.11.tgz", - "integrity": "sha512-0qJBYzP8Qk24CZ05RSWDQUjdiQUeIJGfqMMzbtXgCKl/a5xa6thfC0MQkGIr55LCLd6YmMyO640ifYUa53lybQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.1.tgz", + "integrity": "sha512-4dIEMXrXt0UqDVgrsUd1I+NoIzVQWXy/CNhgpfS75rOOMK/4Abn0Mx2M2gWH4Mk9+ds/ASAiCmqoUFynmMY5hA==", "cpu": [ "x64" ], @@ -3790,9 +3895,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.7.11.tgz", - "integrity": "sha512-1sGwpgvx+WZf0GFT6vkkOm6UJ+mlsVnjw+Yv9esK71idWeRAG3bbpkf3AoY8KIqKqmnzJExi0uKxXpakQ5Pcbg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.1.tgz", + "integrity": "sha512-vtvS13IXPs1eE8DuS/soiosqMBeyh50YLRZ+p7EaIKAPPeevRnA9G/wu/KbVt01ZD5qiGjxS+CGIdVC7I6gTOw==", "cpu": [ "arm" ], @@ -3804,9 +3909,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.7.11.tgz", - "integrity": "sha512-D/1F/2lTe+XAl3ohkYj51NjniVly8sIqkA/n1aOND3ZMO418nl2JNU95iVa1/RtpzaKcWEsNTtHRogykrUflJg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.1.tgz", + "integrity": "sha512-BfdnN6aZ7NcX8djW8SR6GOJc+K+sFhWRF4vJueVE0vbUu5N1bLnBpxJg1TGlhSyo+ImC4SR0jcNiKN0jdoxt+A==", "cpu": [ "arm" ], @@ -3818,9 +3923,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.7.11.tgz", - "integrity": "sha512-7vFWHLCCNFLEQlmwKQfVy066ohLLArZl+AV/AdmrD1/pD1FlmqM+FKbtnONnIwbHtgetFUCV/SRi1q4D49aTlw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.1.tgz", + "integrity": "sha512-Jhge7lFtH0QqfRz2PyJjJXWENqywPteITd+nOS0L6AhbZli+UmEyGBd2Sstt1c+l9C+j/YvKTl9wJo9PPmsFNg==", "cpu": [ "arm64" ], @@ -3832,9 +3937,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.7.11.tgz", - "integrity": "sha512-tYkGIx8hjWPh4zcn17jLEHU8YMmdP2obRTGkdaB3BguGHh31VCS3ywqC4QjTODjmhhNyZYkj/1Dz/+0kKvg9YA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.1.tgz", + "integrity": "sha512-ofdK/ow+ZSbSU0pRoB7uBaiRHeaAOYQFU5Spp87LdcPL/P1RhbCTMSIYVb61XWzsVEmYKjHFtoIE0wxP6AFvrA==", "cpu": [ "arm64" ], @@ -3846,9 +3951,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.7.11.tgz", - "integrity": "sha512-6F328QIUev29vcZeRX6v6oqKxfUoGwIIAhWGD8wSysnBYFY0nivp25jdWmAb1GildbCCaQvOKEhCok7YfWkj4Q==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.1.tgz", + "integrity": "sha512-eC8SXVn8de67HacqU7PoGdHA+9tGbqfEdD05AEFRAB81ejeQtNi5Fx7lPcxpLH79DW0BnMAHau3hi4RVkHfSCw==", "cpu": [ "ppc64" ], @@ -3860,9 +3965,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.7.11.tgz", - "integrity": "sha512-NqhWmiGJGdzbZbeucPZIG9Iav4lyYLCarEnxAceguMx9qlpeEF7ENqYKOwB8Zqk7/CeuYMEcLYMaW2li6HyDzQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.1.tgz", + "integrity": "sha512-fIkwvAAQ41kfoGWfzeJ33iLGShl0JEDZHrMnwTHMErUcPkaaZRJYjQjsFhMl315NEQ4mmTlC+2nfK/J2IszDOw==", "cpu": [ "riscv64" ], @@ -3874,9 +3979,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.7.11.tgz", - "integrity": "sha512-J2RPIFKMdTrLtBdfR1cUMKl8Gcy05nlQ+bEs/6al7EdWLk9cs3tnDREHZ7mV9uGbeghpjo4i8neNZNx3PYUY9w==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.1.tgz", + "integrity": "sha512-RAAszxImSOFLk44aLwnSqpcOdce8sBcxASledSzuFAd8Q5ZhhVck472SisspnzHdc7THCvGXiUeZ2hOC7NUoBQ==", "cpu": [ "riscv64" ], @@ -3888,9 +3993,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.7.11.tgz", - "integrity": "sha512-bDpGRerHvvHdhun7MmFUNDpMiYcJSqWckwAVVRTJf8F+RyqYJOp/mx04PDc7DhpNPeWdnTMu91oZRMV+gGaVcQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.1.tgz", + "integrity": "sha512-QoP9vkY+THuQdZi05bA6s6XwFd6HIz3qlx82v9bTOgxeqin/3C12Ye7f7EOD00RQ36OtOPWnhEMMm84sv7d1XQ==", "cpu": [ "s390x" ], @@ -3902,9 +4007,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.11.tgz", - "integrity": "sha512-G9U7bVmylzRLma3cK39RBm3guoD1HOvY4o0NS4JNm37AD0lS7/xyMt7kn0JejYyc0Im8J+rH69/dXGM9DAJcSQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.1.tgz", + "integrity": "sha512-/p77cGN/h9zbsfCseAP5gY7tK+7+DdM8fkPfr9d1ye1fsF6bmtGbtZN6e/8j4jCZ9NEIBBkT0GhdgixSelTK9g==", "cpu": [ "x64" ], @@ -3916,9 +4021,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.11.tgz", - "integrity": "sha512-7qL20SBKomekSunm7M9Fe5L93bFbn+FbHiGJbfTlp0RKhPVoJDP73vOxf1QrmJHyDPECsGWPFnKa/f8fO2FsHw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.1.tgz", + "integrity": "sha512-wInTqT3Bu9u50mDStEig1v8uxEL2Ht+K8pir/YhyyrM5ordJtxoqzsL1vR/CQzOJuDunUTrDkMM0apjW/d7/PA==", "cpu": [ "x64" ], @@ -3930,9 +4035,9 @@ ] }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.7.11.tgz", - "integrity": "sha512-jisvIva8MidjI+B1lFRZZMfCPaCISePgTyR60wNT1MeQvIh5Ksa0G3gvI+Iqyj3jqYbvOHByenpa5eDGcSdoSg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.1.tgz", + "integrity": "sha512-eNwqO5kUa+1k7yFIircwwiniKWA0UFHo2Cfm8LYgkh9km7uMad+0x7X7oXbQonJXlqfitBTSjhA0un+DsHIrhw==", "cpu": [ "wasm32" ], @@ -3940,16 +4045,16 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.10" + "@napi-rs/wasm-runtime": "^0.2.11" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.7.11.tgz", - "integrity": "sha512-G+H5nQZ8sRZ8ebMY6mRGBBvTEzMYEcgVauLsNHpvTUavZoCCRVP1zWkCZgOju2dW3O22+8seTHniTdl1/uLz3g==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.1.tgz", + "integrity": "sha512-Eaz1xMUnoa2mFqh20mPqSdbYl6crnk8HnIXDu6nsla9zpgZJZO8w3c1gvNN/4Eb0RXRq3K9OG6mu8vw14gIqiA==", "cpu": [ "arm64" ], @@ -3961,9 +4066,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.7.11.tgz", - "integrity": "sha512-Hfy46DBfFzyv0wgR0MMOwFFib2W2+Btc8oE5h4XlPhpelnSyA6nFxkVIyTgIXYGTdFaLoZFNn62fmqx3rjEg3A==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.1.tgz", + "integrity": "sha512-H/+d+5BGlnEQif0gnwWmYbYv7HJj563PUKJfn8PlmzF8UmF+8KxdvXdwCsoOqh4HHnENnoLrav9NYBrv76x1wQ==", "cpu": [ "ia32" ], @@ -3975,9 +4080,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.11.tgz", - "integrity": "sha512-7L8NdsQlCJ8T106Gbz/AjzM4QKWVsoQbKpB9bMBGcIZswUuAnJMHpvbqGW3RBqLHCIwX4XZ5fxSBHEFcK2h9wA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.1.tgz", + "integrity": "sha512-rS86wI4R6cknYM3is3grCb/laE8XBEbpWAMSIPjYfmYp75KL5dT87jXF2orDa4tQYg5aajP5G8Fgh34dRyR+Rw==", "cpu": [ "x64" ], @@ -4965,9 +5070,10 @@ } }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -5019,14 +5125,23 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, + "node_modules/brace-expansion/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -5153,9 +5268,9 @@ } }, "node_modules/cacheable/node_modules/keyv": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.3.tgz", - "integrity": "sha512-Rwu4+nXI9fqcxiEHtbkvoes2X+QfkTRo1TMkPfwzipGsJlJO/z69vqB4FNl9xJ3xCpAcbkvmEabZfPzrwN3+gQ==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.4.tgz", + "integrity": "sha512-ypEvQvInNpUe+u+w8BIcPkQvEqXquyyibWE/1NB5T2BTzIpS5cGEV1LZskDzPSTvNAaT4+5FutvzlvnkxOSKlw==", "dev": true, "license": "MIT", "dependencies": { @@ -5231,9 +5346,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001721", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz", - "integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==", + "version": "1.0.30001724", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz", + "integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==", "funding": [ { "type": "opencollective", @@ -5317,9 +5432,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz", - "integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", + "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", "license": "MIT", "dependencies": { "@kurkle/color": "^0.3.0" @@ -5659,13 +5774,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.42.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz", - "integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==", + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", + "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.4" + "browserslist": "^4.25.0" }, "funding": { "type": "opencollective", @@ -6463,9 +6578,9 @@ } }, "node_modules/decode-named-character-reference": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz", - "integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6767,6 +6882,23 @@ "node": ">=14" } }, + "node_modules/editorconfig/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/editorconfig/node_modules/minimatch": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", @@ -6784,9 +6916,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.165", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz", - "integrity": "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==", + "version": "1.5.171", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz", + "integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7423,6 +7555,23 @@ "eslint": "^8.0.0 || ^9.0.0" } }, + "node_modules/eslint-plugin-sonarjs/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-sonarjs/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/eslint-plugin-sonarjs/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -7593,23 +7742,23 @@ } }, "node_modules/eslint-plugin-wc": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.2.1.tgz", - "integrity": "sha512-KstLqGmyQz088DvFlDYHg0sHih+w2QeulreCi1D1ftr357klO2zqHdG/bbnNMmuQdVFDuNkopNIyNhmG0XCT/g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-3.0.1.tgz", + "integrity": "sha512-0p1wkSlA2Ue3FA4qW+5LZ+15sy0p1nUyVl1eyBMLq4rtN1LtE9IdI49BXNWMz8N8bM/y7Ulx8SWGAni5f8XO5g==", "dev": true, "license": "MIT", "dependencies": { "is-valid-element-name": "^1.0.0", - "js-levenshtein-esm": "^1.2.0" + "js-levenshtein-esm": "^2.0.0" }, "peerDependencies": { "eslint": ">=8.40.0" } }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -7624,9 +7773,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -7660,17 +7809,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -7692,15 +7830,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7789,9 +7927,9 @@ } }, "node_modules/exsolve": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", - "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", "license": "MIT" }, "node_modules/fast-deep-equal": { @@ -8271,17 +8409,6 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "license": "BSD-2-Clause" }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -8684,6 +8811,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/immer": { "version": "9.0.21", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", @@ -9223,6 +9356,12 @@ "dev": true, "license": "MIT" }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, "node_modules/is-proto-prop": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/is-proto-prop/-/is-proto-prop-3.0.1.tgz", @@ -9559,6 +9698,23 @@ "node": ">=14" } }, + "node_modules/js-beautify/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-beautify/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/js-beautify/node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -9607,9 +9763,9 @@ } }, "node_modules/js-levenshtein-esm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz", - "integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-2.0.0.tgz", + "integrity": "sha512-1n4LEPOL4wRXY8rOQcuA7Iuaphe5xCMayvufCzlLAi+hRsnBRDbSS6XPuV58CBVJxj5D9ApFLyjQ7KzFToyHBw==", "dev": true, "license": "MIT" }, @@ -9922,6 +10078,15 @@ "npm": ">=8" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -9950,6 +10115,37 @@ "uc.micro": "^2.0.0" } }, + "node_modules/lit": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.0.tgz", + "integrity": "sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.0.tgz", + "integrity": "sha512-MGrXJVAI5x+Bfth/pU9Kst1iWID6GHDLEzFEnyULB/sFiRLgkd8NPK/PeeXxktA3T6EIIaq8U3KcbTU5XFcP2Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.0.tgz", + "integrity": "sha512-RHoswrFAxY2d8Cf2mm4OZ1DgzCoBKUKSPvA1fhtSELxUERq2aQQ2h05pO9j81gS1o7RIRJ+CePLogfyahwmynw==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -10055,9 +10251,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", + "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", "dev": true, "license": "MIT" }, @@ -10207,15 +10403,15 @@ } }, "node_modules/markdownlint-cli/node_modules/glob": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", - "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", "dev": true, "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -10366,14 +10562,14 @@ } }, "node_modules/mermaid": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz", - "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==", + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.7.0.tgz", + "integrity": "sha512-/1/5R0rt0Z1Ak0CuznAnCF3HtQgayRXUz6SguzOwN4L+DuCobz0UxnQ+ZdTSZ3AugKVVh78tiVmsHpHWV25TCw==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.4", "@iconify/utils": "^2.1.33", - "@mermaid-js/parser": "^0.4.0", + "@mermaid-js/parser": "^0.5.0", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", @@ -10382,7 +10578,7 @@ "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", "dayjs": "^1.11.13", - "dompurify": "^3.2.4", + "dompurify": "^3.2.5", "katex": "^0.16.9", "khroma": "^2.1.0", "lodash-es": "^4.17.21", @@ -11012,12 +11208,12 @@ } }, "node_modules/minimatch": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.2.tgz", - "integrity": "sha512-+9TJCIYXgZ2Dm5LxVCFsa8jOm+evMwXHFI0JM1XROmkfkpz8/iLLDh+TwSmyIBrs6C6Xu9294/fq8cBA+P6AqA==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "license": "ISC", "dependencies": { - "brace-expansion": "^4.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { "node": "20 || >=22" @@ -11026,27 +11222,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minimatch/node_modules/balanced-match": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-3.0.1.tgz", - "integrity": "sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==", - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/minimatch/node_modules/brace-expansion": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-4.0.1.tgz", - "integrity": "sha512-YClrbvTCXGe70pU2JiEiPLYXO9gQkyxYeKpJIQHVS/gOs6EWMQP2RYBwjFLNT322Ji8TOC3IMPfsYCedNpzKfA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^3.0.0" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -11779,13 +11954,13 @@ } }, "node_modules/playwright": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.0.tgz", - "integrity": "sha512-ghGNnIEYZC4E+YtclRn4/p6oYbdPiASELBIYkBXfaTVKreQUYbMUYQDwS12a8F0/HtIjr/CkGjtwABeFPGcS4Q==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", + "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.53.0" + "playwright-core": "1.52.0" }, "bin": { "playwright": "cli.js" @@ -11798,9 +11973,9 @@ } }, "node_modules/playwright-core": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.0.tgz", - "integrity": "sha512-mGLg8m0pm4+mmtB7M89Xw/GSqoNC+twivl8ITteqvAndachozYe2ZA7srU6uleV1vEdAHYqjq+SV8SNxRRFYBw==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", + "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -11857,9 +12032,9 @@ } }, "node_modules/postcss": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", - "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz", + "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==", "funding": [ { "type": "opencollective", @@ -12283,6 +12458,16 @@ "dev": true, "license": "Unlicense" }, + "node_modules/promise-worker-transferable": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz", + "integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==", + "license": "Apache-2.0", + "dependencies": { + "is-promise": "^2.1.0", + "lie": "^3.0.2" + } + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -12414,6 +12599,23 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/read-package-json/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/read-package-json/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/read-package-json/node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -13751,13 +13953,6 @@ "postcss-selector-parser": "^7.0.0" } }, - "node_modules/stylelint/node_modules/balanced-match": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", - "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", - "dev": true, - "license": "MIT" - }, "node_modules/stylelint/node_modules/file-entry-cache": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.1.tgz", @@ -13900,6 +14095,21 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/sucrase/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/sucrase/node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -14166,9 +14376,9 @@ } }, "node_modules/terser": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.41.0.tgz", - "integrity": "sha512-H406eLPXpZbAX14+B8psIuvIr8+3c+2hkuYzpMkoE0ij+NdsVATbA78vb8neA/eqrj7rywa2pIkdmWRsXW6wmw==", + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -14238,6 +14448,23 @@ "node": ">=18" } }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/test-exclude/node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -14303,6 +14530,13 @@ "node": ">=0.8" } }, + "node_modules/three": { + "version": "0.172.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.172.0.tgz", + "integrity": "sha512-6HMgMlzU97MsV7D/tY8Va38b83kz8YJX+BefKjspMNAv0Vx6dxMogHOrnRl/sbMIs3BPUKijPqDqJ/+UwJbIow==", + "license": "MIT", + "peer": true + }, "node_modules/throttle-debounce": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.0.tgz", @@ -14349,9 +14583,9 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", - "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "dev": true, "license": "MIT", "peerDependencies": { @@ -14377,9 +14611,9 @@ } }, "node_modules/tinypool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.0.tgz", - "integrity": "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -14721,9 +14955,9 @@ } }, "node_modules/unrs-resolver": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.11.tgz", - "integrity": "sha512-OhuAzBImFPjKNgZ2JwHMfGFUA6NSbRegd1+BPjC1Y0E6X9Y/vJ4zKeGmIMqmlYboj6cMNEwKI+xQisrg4J0HaQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.1.tgz", + "integrity": "sha512-4AZVxP05JGN6DwqIkSP4VKLOcwQa5l37SWHF/ahcuqBMbfxbpN1L1QKafEhWCziHhzKex9H/AR09H0OuVyU+9g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -14734,23 +14968,25 @@ "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-darwin-arm64": "1.7.11", - "@unrs/resolver-binding-darwin-x64": "1.7.11", - "@unrs/resolver-binding-freebsd-x64": "1.7.11", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.11", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.11", - "@unrs/resolver-binding-linux-arm64-gnu": "1.7.11", - "@unrs/resolver-binding-linux-arm64-musl": "1.7.11", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.11", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.11", - "@unrs/resolver-binding-linux-riscv64-musl": "1.7.11", - "@unrs/resolver-binding-linux-s390x-gnu": "1.7.11", - "@unrs/resolver-binding-linux-x64-gnu": "1.7.11", - "@unrs/resolver-binding-linux-x64-musl": "1.7.11", - "@unrs/resolver-binding-wasm32-wasi": "1.7.11", - "@unrs/resolver-binding-win32-arm64-msvc": "1.7.11", - "@unrs/resolver-binding-win32-ia32-msvc": "1.7.11", - "@unrs/resolver-binding-win32-x64-msvc": "1.7.11" + "@unrs/resolver-binding-android-arm-eabi": "1.9.1", + "@unrs/resolver-binding-android-arm64": "1.9.1", + "@unrs/resolver-binding-darwin-arm64": "1.9.1", + "@unrs/resolver-binding-darwin-x64": "1.9.1", + "@unrs/resolver-binding-freebsd-x64": "1.9.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.9.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.9.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.9.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.9.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.9.1", + "@unrs/resolver-binding-linux-x64-musl": "1.9.1", + "@unrs/resolver-binding-wasm32-wasi": "1.9.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.9.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.9.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.9.1" } }, "node_modules/update-browserslist-db": { @@ -14962,16 +15198,16 @@ "license": "BSD-2-Clause" }, "node_modules/vite/node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, "node_modules/vite/node_modules/fdir": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", - "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "dev": true, "license": "MIT", "peerDependencies": { @@ -15012,13 +15248,13 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.42.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz", - "integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz", + "integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -15028,26 +15264,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.42.0", - "@rollup/rollup-android-arm64": "4.42.0", - "@rollup/rollup-darwin-arm64": "4.42.0", - "@rollup/rollup-darwin-x64": "4.42.0", - "@rollup/rollup-freebsd-arm64": "4.42.0", - "@rollup/rollup-freebsd-x64": "4.42.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.42.0", - "@rollup/rollup-linux-arm-musleabihf": "4.42.0", - "@rollup/rollup-linux-arm64-gnu": "4.42.0", - "@rollup/rollup-linux-arm64-musl": "4.42.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.42.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.42.0", - "@rollup/rollup-linux-riscv64-gnu": "4.42.0", - "@rollup/rollup-linux-riscv64-musl": "4.42.0", - "@rollup/rollup-linux-s390x-gnu": "4.42.0", - "@rollup/rollup-linux-x64-gnu": "4.42.0", - "@rollup/rollup-linux-x64-musl": "4.42.0", - "@rollup/rollup-win32-arm64-msvc": "4.42.0", - "@rollup/rollup-win32-ia32-msvc": "4.42.0", - "@rollup/rollup-win32-x64-msvc": "4.42.0", + "@rollup/rollup-android-arm-eabi": "4.44.0", + "@rollup/rollup-android-arm64": "4.44.0", + "@rollup/rollup-darwin-arm64": "4.44.0", + "@rollup/rollup-darwin-x64": "4.44.0", + "@rollup/rollup-freebsd-arm64": "4.44.0", + "@rollup/rollup-freebsd-x64": "4.44.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", + "@rollup/rollup-linux-arm-musleabihf": "4.44.0", + "@rollup/rollup-linux-arm64-gnu": "4.44.0", + "@rollup/rollup-linux-arm64-musl": "4.44.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-musl": "4.44.0", + "@rollup/rollup-linux-s390x-gnu": "4.44.0", + "@rollup/rollup-linux-x64-gnu": "4.44.0", + "@rollup/rollup-linux-x64-musl": "4.44.0", + "@rollup/rollup-win32-arm64-msvc": "4.44.0", + "@rollup/rollup-win32-ia32-msvc": "4.44.0", + "@rollup/rollup-win32-x64-msvc": "4.44.0", "fsevents": "~2.3.2" } }, @@ -15315,6 +15551,12 @@ "node": ">=10.13.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, "node_modules/webpack": { "version": "5.99.9", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", @@ -15466,9 +15708,9 @@ } }, "node_modules/webpack/node_modules/webpack-sources": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.2.tgz", - "integrity": "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "license": "MIT", "engines": { "node": ">=10.13.0" @@ -15494,12 +15736,6 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/whatwg-url/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index d602a2613c..3d71e94cd3 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,12 @@ "@github/markdown-toolbar-element": "2.2.3", "@github/quote-selection": "2.1.0", "@github/text-expander-element": "2.8.0", + "@google/model-viewer": "4.1.0", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.14.0", "ansi_up": "6.0.5", "asciinema-player": "3.8.2", - "chart.js": "4.4.9", + "chart.js": "4.5.0", "chartjs-adapter-dayjs-4": "1.0.4", "chartjs-plugin-zoom": "2.2.0", "clippie": "4.1.7", @@ -30,13 +31,13 @@ "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.6.0", + "mermaid": "11.7.0", "mini-css-extract-plugin": "2.9.2", - "minimatch": "10.0.2", + "minimatch": "10.0.3", "monaco-editor": "0.52.2", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", - "postcss": "8.5.4", + "postcss": "8.5.5", "postcss-loader": "8.1.1", "postcss-nesting": "13.0.2", "pretty-ms": "9.0.0", @@ -61,9 +62,9 @@ "devDependencies": { "@axe-core/playwright": "4.10.2", "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", - "@playwright/test": "1.53.0", + "@playwright/test": "1.52.0", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin-js": "4.4.1", + "@stylistic/eslint-plugin": "4.4.1", "@stylistic/stylelint-plugin": "3.1.2", "@vitejs/plugin-vue": "5.2.4", "@vitest/coverage-v8": "3.2.3", @@ -78,12 +79,12 @@ "eslint-plugin-playwright": "2.2.0", "eslint-plugin-regexp": "2.9.0", "eslint-plugin-sonarjs": "3.0.2", - "eslint-plugin-unicorn": "59.0.1", "eslint-plugin-toml": "0.12.0", + "eslint-plugin-unicorn": "59.0.1", "eslint-plugin-vitest-globals": "1.5.0", "eslint-plugin-vue": "10.2.0", "eslint-plugin-vue-scoped-css": "2.10.0", - "eslint-plugin-wc": "2.2.1", + "eslint-plugin-wc": "3.0.1", "globals": "16.1.0", "happy-dom": "18.0.0", "license-checker-rseidelsohn": "4.4.2", diff --git a/release-notes-published/11.0.2.md b/release-notes-published/11.0.2.md new file mode 100644 index 0000000000..a1a8549984 --- /dev/null +++ b/release-notes-published/11.0.2.md @@ -0,0 +1,33 @@ + + + + +## Release notes + +- Features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7986) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7991)): feat: make Forgejo Actions server logs less noisy +- Bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8155) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8167)): fix: do not fail when release or wiki is set in `/repos/migrate` API + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7976) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7985)): fix: ignore expired artifacts for quota calculation + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7979) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7983)): fix: pull request cross references + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7883) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7886)): fix: quote reply in Chromium + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7775) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7779)): fix: make hash pattern more strict +- Included for completeness but not worth a release note + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8112) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8120)): fix: remove download attribute from external assets + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8110): Update bleve to v2.5.2 with changes made in backport of 2.5.0 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8094) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8095)): fix: show membership of limited orgs + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8059): Update dependency go to v1.24.3 (v11.0/forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8057): chore: drop unused `@typescript-eslint/parser` package + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8021) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8022)): chore(cleanup): suppress non actionable XORM warnings + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7987) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8000)): fix: aggregate deleted team as ghost team + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7925) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7937)): fix(ui): center footer links + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7894) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7903)): fix(ui): fix force-push compare line layout + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7884) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7887)): fix: parse `change-id` in the git commit header + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7885): Update module github.com/blevesearch/bleve/v2 to v2.5.1 (v11.0/forgejo) - abandoned + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7746) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7871)): fix(ui): improve force-push compare line layout + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7640) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7869)): fix: Remove "create branch" button on mirrored repos + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7858): Update module github.com/msteinert/pam/v2 to v2.1.0 (v11.0/forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7817) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7821)): fix: replace ß with ss in normalizeUserName + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7784) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7786)): fix(api): document `is_system_webhook` field + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7773) ([backported](https://codeberg.org/forgejo/forgejo/pulls/7774)): fix: remove artificial delay for PR update + diff --git a/release-notes-published/12.0.0.md b/release-notes-published/12.0.0.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index e371ebb28b..bf08bdd249 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1310,6 +1310,7 @@ func Routes() *web.Route { m.Get("/refs", repo.GetGitAllRefs) m.Get("/refs/*", repo.GetGitRefs) m.Get("/trees/{sha}", repo.GetTree) + m.Get("/blobs", repo.GetBlobs) m.Get("/blobs/{sha}", repo.GetBlob) m.Get("/tags/{sha}", repo.GetAnnotatedTag) m.Group("/notes/{sha}", func() { diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 03089a18d3..dbc4933de6 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -748,7 +748,7 @@ func ListActionRuns(ctx *context.APIContext) { // type: string // responses: // "200": - // "$ref": "#/responses/RepoActionRunList" + // "$ref": "#/responses/ActionRunList" // "400": // "$ref": "#/responses/error" // "403": @@ -779,16 +779,16 @@ func ListActionRuns(ctx *context.APIContext) { return } - res := new(api.ListRepoActionRunResponse) + res := new(api.ListActionRunResponse) res.TotalCount = total - res.Entries = make([]*api.RepoActionRun, len(runs)) + res.Entries = make([]*api.ActionRun, len(runs)) for i, r := range runs { - cr, err := convert.ToRepoActionRun(ctx, r) - if err != nil { - ctx.Error(http.StatusInternalServerError, "ToActionRun", err) + if err := r.LoadAttributes(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } + cr := convert.ToActionRun(ctx, r, ctx.Doer) res.Entries[i] = cr } @@ -821,7 +821,7 @@ func GetActionRun(ctx *context.APIContext) { // required: true // responses: // "200": - // "$ref": "#/responses/RepoActionRun" + // "$ref": "#/responses/ActionRun" // "400": // "$ref": "#/responses/error" // "403": @@ -839,16 +839,17 @@ func GetActionRun(ctx *context.APIContext) { return } + // Action runs lives in its own table, therefore we check that the + // run with the requested ID is owned by the repository if ctx.Repo.Repository.ID != run.RepoID { ctx.Error(http.StatusNotFound, "GetRunById", util.ErrNotExist) return } - res, err := convert.ToRepoActionRun(ctx, run) - if err != nil { - ctx.Error(http.StatusInternalServerError, "ToRepoActionRun", err) + if err := run.LoadAttributes(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, res) + ctx.JSON(http.StatusOK, convert.ToActionRun(ctx, run, ctx.Doer)) } diff --git a/routers/api/v1/repo/blob.go b/routers/api/v1/repo/blob.go index 8ed57d4787..63baec2025 100644 --- a/routers/api/v1/repo/blob.go +++ b/routers/api/v1/repo/blob.go @@ -5,11 +5,54 @@ package repo import ( "net/http" + "strings" "forgejo.org/services/context" files_service "forgejo.org/services/repository/files" ) +// GetBlobs gets multiple blobs of a repository. +func GetBlobs(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/git/blobs repository GetBlobs + // --- + // summary: Gets multiplbe blobs of a repository. + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: shas + // in: query + // description: a comma separated list of blob-sha (mind the overall URL-length limit of ~2,083 chars) + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/GitBlobList" + // "400": + // "$ref": "#/responses/error" + + shas := ctx.FormString("shas") + if len(shas) == 0 { + ctx.Error(http.StatusBadRequest, "", "shas not provided") + return + } + + if blobs, err := files_service.GetBlobsBySHA(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, strings.Split(shas, ",")); err != nil { + ctx.Error(http.StatusBadRequest, "", err) + } else { + ctx.JSON(http.StatusOK, blobs) + } +} + // GetBlob get the blob of a repository file. func GetBlob(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/git/blobs/{sha} repository GetBlob @@ -30,12 +73,12 @@ func GetBlob(ctx *context.APIContext) { // required: true // - name: sha // in: path - // description: sha of the commit + // description: sha of the blob to retrieve // type: string // required: true // responses: // "200": - // "$ref": "#/responses/GitBlobResponse" + // "$ref": "#/responses/GitBlob" // "400": // "$ref": "#/responses/error" // "404": diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 75b4870e51..c9dda124de 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -71,7 +71,7 @@ func ListPullRequests(ctx *context.APIContext) { // in: query // description: Type of sort // type: string - // enum: [oldest, recentupdate, leastupdate, mostcomment, leastcomment, priority] + // enum: [oldest, recentupdate, recentclose, leastupdate, mostcomment, leastcomment, priority] // - name: milestone // in: query // description: ID of the milestone diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index a27e94253b..cd4832e15f 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -231,11 +231,18 @@ type swaggerGitTreeResponse struct { Body api.GitTreeResponse `json:"body"` } -// GitBlobResponse -// swagger:response GitBlobResponse -type swaggerGitBlobResponse struct { +// GitBlob +// swagger:response GitBlob +type swaggerGitBlob struct { // in: body - Body api.GitBlobResponse `json:"body"` + Body api.GitBlob `json:"body"` +} + +// GitBlobList +// swagger:response GitBlobList +type swaggerGitBlobList struct { + // in: body + Body []api.GitBlob `json:"body"` } // Commit @@ -456,16 +463,16 @@ type swaggerSyncForkInfo struct { Body []api.SyncForkInfo `json:"body"` } -// RepoActionRunList -// swagger:response RepoActionRunList -type swaggerRepoActionRunList struct { +// ActionRunList +// swagger:response ActionRunList +type swaggerActionRunList struct { // in:body - Body api.ListRepoActionRunResponse `json:"body"` + Body api.ListActionRunResponse `json:"body"` } -// RepoActionRun -// swagger:response RepoActionRun -type swaggerRepoActionRun struct { +// ActionRun +// swagger:response ActionRun +type swaggerActionRun struct { // in:body - Body api.RepoActionRun `json:"body"` + Body api.ActionRun `json:"body"` } diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 0188df17b4..964326291e 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -313,6 +313,9 @@ func editUserCommon(ctx *context.Context) { ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice() ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx) + ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize + ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth + ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight } // EditUser show editing user page diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go index 01e663a672..7b09c92ee5 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -25,7 +25,7 @@ import ( "forgejo.org/services/context" "github.com/gorilla/feeds" - "github.com/jaytaylor/html2text" + "github.com/inbucket/html2text" ) func toBranchLink(ctx *context.Context, act *activities_model.Action) string { diff --git a/routers/web/org/home.go b/routers/web/org/home.go index a3823565ed..8f14f8899c 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -175,10 +175,12 @@ func prepareOrgProfileReadme(ctx *context.Context, profileGitRepo *git.Repositor return } - if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil { - log.Error("failed to GetBlobContent: %v", err) + if rc, _, err := profileReadme.NewTruncatedReader(setting.UI.MaxDisplayFileSize); err != nil { + log.Error("failed to NewTruncatedReader: %v", err) } else { - if profileContent, err := markdown.RenderString(&markup.RenderContext{ + defer rc.Close() + + if profileContent, err := markdown.RenderReader(&markup.RenderContext{ Ctx: ctx, GitRepo: profileGitRepo, Links: markup.Links{ @@ -188,7 +190,7 @@ func prepareOrgProfileReadme(ctx *context.Context, profileGitRepo *git.Repositor BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)), }, Metas: map[string]string{"mode": "document"}, - }, bytes); err != nil { + }, rc); err != nil { log.Error("failed to RenderString: %v", err) } else { ctx.Data["ProfileReadme"] = profileContent diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 284f406413..c83242754b 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -50,6 +50,9 @@ func Settings(ctx *context.Context) { ctx.Data["RepoAdminChangeTeamAccess"] = ctx.Org.Organization.RepoAdminChangeTeamAccess ctx.Data["ContextUser"] = ctx.ContextUser ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod + ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize + ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth + ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight err := shared_user.LoadHeaderCount(ctx) if err != nil { diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index ccdd59f2dd..f4cc2a2cea 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -82,19 +82,19 @@ func RefBlame(ctx *context.Context) { return } - ctx.Data["NumLinesSet"] = true - ctx.Data["NumLines"], err = blob.GetBlobLineCount() - if err != nil { - ctx.ServerError("GetBlobLineCount", err) - return - } - result, err := performBlame(ctx, ctx.Repo.Commit, ctx.Repo.TreePath, ctx.FormBool("bypass-blame-ignore")) if err != nil { ctx.ServerError("performBlame", err) return } + ctx.Data["NumLinesSet"] = true + numLines := 0 + for _, p := range result.Parts { + numLines += len(p.Lines) + } + ctx.Data["NumLines"] = numLines + ctx.Data["UsesIgnoreRevs"] = result.UsesIgnoreRevs ctx.Data["FaultyIgnoreRevsFile"] = result.FaultyIgnoreRevsFile diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index af8a838fc9..0fe52bfb48 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -70,11 +70,6 @@ func Branches(ctx *context.Context) { ctx.ServerError("LoadBranches", err) return } - if !ctx.Repo.CanRead(unit.TypeActions) { - for key := range commitStatuses { - git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key]) - } - } commitStatus := make(map[string]*git_model.CommitStatus) for commitID, cs := range commitStatuses { diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 89463d9d03..f3192266ad 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -16,7 +16,6 @@ import ( "forgejo.org/models/db" git_model "forgejo.org/models/git" repo_model "forgejo.org/models/repo" - unit_model "forgejo.org/models/unit" user_model "forgejo.org/models/user" "forgejo.org/modules/base" "forgejo.org/modules/charset" @@ -84,7 +83,7 @@ func Commits(ctx *context.Context) { ctx.ServerError("CommitsByRange", err) return } - ctx.Data["Commits"] = processGitCommits(ctx, commits) + ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository) ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name @@ -202,7 +201,7 @@ func SearchCommits(ctx *context.Context) { return } ctx.Data["CommitCount"] = len(commits) - ctx.Data["Commits"] = processGitCommits(ctx, commits) + ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository) ctx.Data["Keyword"] = query if all { @@ -267,7 +266,7 @@ func FileHistory(ctx *context.Context) { } } - ctx.Data["Commits"] = processGitCommits(ctx, commits) + ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository) ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name @@ -375,9 +374,6 @@ func Diff(ctx *context.Context) { if err != nil { log.Error("GetLatestCommitStatus: %v", err) } - if !ctx.Repo.CanRead(unit_model.TypeActions) { - git_model.CommitStatusesHideActionsURL(ctx, statuses) - } ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses) ctx.Data["CommitStatuses"] = statuses @@ -456,20 +452,6 @@ func RawDiff(ctx *context.Context) { } } -func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) []*git_model.SignCommitWithStatuses { - commits := git_model.ConvertFromGitCommit(ctx, gitCommits, ctx.Repo.Repository) - if !ctx.Repo.CanRead(unit_model.TypeActions) { - for _, commit := range commits { - if commit.Status == nil { - continue - } - commit.Status.HideActionsURL(ctx) - git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses) - } - } - return commits -} - func SetCommitNotes(ctx *context.Context) { form := web.GetForm(ctx).(*forms.CommitNotesForm) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index f5826cf249..de2e29ab9f 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -654,7 +654,7 @@ func PrepareCompareDiff( return false } - commits := processGitCommits(ctx, ci.CompareInfo.Commits) + commits := git_model.ParseCommitsWithStatus(ctx, ci.CompareInfo.Commits, ctx.Repo.Repository) ctx.Data["Commits"] = commits ctx.Data["CommitCount"] = len(commits) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b97c268ae2..5e228507c0 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -348,11 +348,6 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt ctx.ServerError("GetIssuesAllCommitStatus", err) return } - if !ctx.Repo.CanRead(unit.TypeActions) { - for key := range commitStatuses { - git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key]) - } - } if err := issues.LoadAttributes(ctx); err != nil { ctx.ServerError("issues.LoadAttributes", err) @@ -1313,7 +1308,7 @@ func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *use } // Special user that can't have associated contributions and permissions in the repo. - if poster.IsGhost() || poster.IsActions() || poster.IsAPServerActor() { + if poster.IsSystem() || poster.IsAPServerActor() { return roleDescriptor, nil } @@ -1698,7 +1693,7 @@ func ViewIssue(ctx *context.Context) { return } ghostMilestone := &issues_model.Milestone{ - ID: -1, + ID: issues_model.GhostMilestoneID, Name: ctx.Locale.TrString("repo.issues.deleted_milestone"), } if comment.OldMilestoneID > 0 && comment.OldMilestone == nil { @@ -1799,15 +1794,6 @@ func ViewIssue(ctx *context.Context) { ctx.ServerError("LoadPushCommits", err) return } - if !ctx.Repo.CanRead(unit.TypeActions) { - for _, commit := range comment.Commits { - if commit.Status == nil { - continue - } - commit.Status.HideActionsURL(ctx) - git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses) - } - } } else if comment.Type == issues_model.CommentTypeAddTimeManual || comment.Type == issues_model.CommentTypeStopTracking || comment.Type == issues_model.CommentTypeDeleteTimeManual { diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index bb89e30d54..fd18646211 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -10,13 +10,16 @@ import ( "errors" "fmt" "html" + "html/template" "net/http" "net/url" + "path" "strconv" "strings" "forgejo.org/models" activities_model "forgejo.org/models/activities" + asymkey_model "forgejo.org/models/asymkey" "forgejo.org/models/db" git_model "forgejo.org/models/git" issues_model "forgejo.org/models/issues" @@ -28,11 +31,13 @@ import ( "forgejo.org/models/unit" user_model "forgejo.org/models/user" "forgejo.org/modules/base" + "forgejo.org/modules/charset" "forgejo.org/modules/emoji" "forgejo.org/modules/git" "forgejo.org/modules/gitrepo" issue_template "forgejo.org/modules/issue/template" "forgejo.org/modules/log" + "forgejo.org/modules/markup" "forgejo.org/modules/optional" "forgejo.org/modules/setting" "forgejo.org/modules/structs" @@ -498,6 +503,7 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) ctx.Data["IsPullRequestBroken"] = true ctx.Data["BaseTarget"] = pull.BaseBranch ctx.Data["NumCommits"] = 0 + ctx.Data["CommitIDs"] = map[string]bool{} ctx.Data["NumFiles"] = 0 return nil } @@ -508,6 +514,12 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) ctx.Data["NumCommits"] = len(compareInfo.Commits) ctx.Data["NumFiles"] = compareInfo.NumFiles + commitIDs := map[string]bool{} + for _, commit := range compareInfo.Commits { + commitIDs[commit.ID.String()] = true + } + ctx.Data["CommitIDs"] = commitIDs + if len(compareInfo.Commits) != 0 { sha := compareInfo.Commits[0].ID.String() commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, sha, db.ListOptionsAll) @@ -515,9 +527,6 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) ctx.ServerError("GetLatestCommitStatus", err) return nil } - if !ctx.Repo.CanRead(unit.TypeActions) { - git_model.CommitStatusesHideActionsURL(ctx, commitStatuses) - } if len(commitStatuses) != 0 { ctx.Data["LatestCommitStatuses"] = commitStatuses @@ -581,9 +590,6 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C ctx.ServerError("GetLatestCommitStatus", err) return nil } - if !ctx.Repo.CanRead(unit.TypeActions) { - git_model.CommitStatusesHideActionsURL(ctx, commitStatuses) - } if len(commitStatuses) > 0 { ctx.Data["LatestCommitStatuses"] = commitStatuses @@ -597,6 +603,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C ctx.Data["IsPullRequestBroken"] = true ctx.Data["BaseTarget"] = pull.BaseBranch ctx.Data["NumCommits"] = 0 + ctx.Data["CommitIDs"] = map[string]bool{} ctx.Data["NumFiles"] = 0 return nil } @@ -607,6 +614,13 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C ctx.Data["NumCommits"] = len(compareInfo.Commits) ctx.Data["NumFiles"] = compareInfo.NumFiles + + commitIDs := map[string]bool{} + for _, commit := range compareInfo.Commits { + commitIDs[commit.ID.String()] = true + } + ctx.Data["CommitIDs"] = commitIDs + return compareInfo } @@ -665,6 +679,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C } ctx.Data["BaseTarget"] = pull.BaseBranch ctx.Data["NumCommits"] = 0 + ctx.Data["CommitIDs"] = map[string]bool{} ctx.Data["NumFiles"] = 0 return nil } @@ -677,9 +692,6 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C ctx.ServerError("GetLatestCommitStatus", err) return nil } - if !ctx.Repo.CanRead(unit.TypeActions) { - git_model.CommitStatusesHideActionsURL(ctx, commitStatuses) - } if len(commitStatuses) > 0 { ctx.Data["LatestCommitStatuses"] = commitStatuses @@ -745,6 +757,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C ctx.Data["IsPullRequestBroken"] = true ctx.Data["BaseTarget"] = pull.BaseBranch ctx.Data["NumCommits"] = 0 + ctx.Data["CommitIDs"] = map[string]bool{} ctx.Data["NumFiles"] = 0 return nil } @@ -769,6 +782,13 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C ctx.Data["NumCommits"] = len(compareInfo.Commits) ctx.Data["NumFiles"] = compareInfo.NumFiles + + commitIDs := map[string]bool{} + for _, commit := range compareInfo.Commits { + commitIDs[commit.ID.String()] = true + } + ctx.Data["CommitIDs"] = commitIDs + return compareInfo } @@ -847,7 +867,7 @@ func ViewPullCommits(ctx *context.Context) { ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name - commits := processGitCommits(ctx, prInfo.Commits) + commits := git_model.ParseCommitsWithStatus(ctx, prInfo.Commits, ctx.Repo.Repository) ctx.Data["Commits"] = commits ctx.Data["CommitCount"] = len(commits) @@ -928,7 +948,78 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi ctx.Data["IsShowingOnlySingleCommit"] = willShowSpecifiedCommit - if willShowSpecifiedCommit || willShowSpecifiedCommitRange { + if willShowSpecifiedCommit { + commitID := specifiedEndCommit + + ctx.Data["CommitID"] = commitID + + var prevCommit, curCommit, nextCommit *git.Commit + + // Iterate in reverse to properly map "previous" and "next" buttons + for i := len(prInfo.Commits) - 1; i >= 0; i-- { + commit := prInfo.Commits[i] + + if curCommit != nil { + nextCommit = commit + break + } + + if commit.ID.String() == commitID { + curCommit = commit + } else { + prevCommit = commit + } + } + + if curCommit == nil { + ctx.ServerError("Repo.GitRepo.viewPullFiles", git.ErrNotExist{ID: commitID}) + return + } + + ctx.Data["Commit"] = curCommit + if prevCommit != nil { + ctx.Data["PrevCommitLink"] = path.Join(ctx.Repo.RepoLink, "pulls", strconv.FormatInt(issue.Index, 10), "commits", prevCommit.ID.String()) + } + if nextCommit != nil { + ctx.Data["NextCommitLink"] = path.Join(ctx.Repo.RepoLink, "pulls", strconv.FormatInt(issue.Index, 10), "commits", nextCommit.ID.String()) + } + + statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll) + if err != nil { + log.Error("GetLatestCommitStatus: %v", err) + } + + ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses) + ctx.Data["CommitStatuses"] = statuses + + verification := asymkey_model.ParseCommitWithSignature(ctx, curCommit) + ctx.Data["Verification"] = verification + ctx.Data["Author"] = user_model.ValidateCommitWithEmail(ctx, curCommit) + + note := &git.Note{} + err = git.GetNote(ctx, ctx.Repo.GitRepo, specifiedEndCommit, note) + if err == nil { + ctx.Data["NoteCommit"] = note.Commit + ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit) + ctx.Data["NoteRendered"], err = markup.RenderCommitMessage(&markup.RenderContext{ + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + BranchPath: path.Join("commit", util.PathEscapeSegments(commitID)), + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, + }, template.HTMLEscapeString(string(charset.ToUTF8WithFallback(note.Message, charset.ConvertOpts{})))) + if err != nil { + ctx.ServerError("RenderCommitMessage", err) + return + } + } + + endCommitID = commitID + startCommitID = prInfo.MergeBase + ctx.Data["IsShowingAllCommits"] = false + } else if willShowSpecifiedCommitRange { if len(specifiedEndCommit) > 0 { endCommitID = specifiedEndCommit } else { @@ -939,6 +1030,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi } else { startCommitID = prInfo.MergeBase } + ctx.Data["IsShowingAllCommits"] = false } else { endCommitID = headCommitID @@ -946,10 +1038,10 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi ctx.Data["IsShowingAllCommits"] = true } - ctx.Data["Username"] = ctx.Repo.Owner.Name - ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["AfterCommitID"] = endCommitID ctx.Data["BeforeCommitID"] = startCommitID + ctx.Data["Username"] = ctx.Repo.Owner.Name + ctx.Data["Reponame"] = ctx.Repo.Repository.Name fileOnly := ctx.FormBool("file-only") diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 3c923c2c5e..493787ad8b 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -693,9 +693,6 @@ func SearchRepo(ctx *context.Context) { ctx.JSON(http.StatusInternalServerError, nil) return } - if !ctx.Repo.CanRead(unit.TypeActions) { - git_model.CommitStatusesHideActionsURL(ctx, latestCommitStatuses) - } results := make([]*repo_service.WebSearchRepository, len(repos)) for i, repo := range repos { diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go index 2e9c34e8a7..b9cb86bd08 100644 --- a/routers/web/repo/setting/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -342,6 +342,20 @@ func LFSFileGet(ctx *context.Context) { ctx.Data["IsVideoFile"] = true case st.IsAudio(): ctx.Data["IsAudioFile"] = true + case st.Is3DModel(): + ctx.Data["Is3DModelFile"] = true + switch { + case st.IsGLB(): + ctx.Data["IsGLBFile"] = true + case st.IsSTL(): + ctx.Data["IsSTLFile"] = true + case st.IsGLTF(): + ctx.Data["IsGLTFFile"] = true + case st.IsOBJ(): + ctx.Data["IsOBJFile"] = true + case st.Is3MF(): + ctx.Data["Is3MFFile"] = true + } case st.IsImage() && (setting.UI.SVG.Enabled || !st.IsSvgImage()): ctx.Data["IsImageFile"] = true } diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index c59225ba49..6f35e19880 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -64,6 +64,9 @@ func SettingsCtxData(ctx *context.Context) { ctx.Data["DisableNewPushMirrors"] = setting.Mirror.DisableNewPush ctx.Data["DefaultMirrorInterval"] = setting.Mirror.DefaultInterval ctx.Data["MinimumMirrorInterval"] = setting.Mirror.MinInterval + ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize + ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth + ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight signing, _ := asymkey_service.SigningKey(ctx, ctx.Repo.Repository.RepoPath()) ctx.Data["SigningKeyAvailable"] = len(signing) > 0 @@ -150,11 +153,9 @@ func UnitsPost(ctx *context.Context) { }) deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki) } else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() { - var wikiPermissions repo_model.UnitAccessMode + wikiPermissions := repo_model.UnitAccessModeUnset if form.GloballyWriteableWiki { wikiPermissions = repo_model.UnitAccessModeWrite - } else { - wikiPermissions = repo_model.UnitAccessModeRead } units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index de508509dc..bb3e1388a8 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -369,9 +369,6 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool { if err != nil { log.Error("GetLatestCommitStatus: %v", err) } - if !ctx.Repo.CanRead(unit_model.TypeActions) { - git_model.CommitStatusesHideActionsURL(ctx, statuses) - } ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(statuses) ctx.Data["LatestCommitStatuses"] = statuses @@ -442,8 +439,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["FileError"] = ctx.Locale.Tr("actions.runs.invalid_workflow_helper", workFlowErr.Error()) } } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}, ctx.Repo.TreePath) { - if data, err := blob.GetBlobContent(setting.UI.MaxDisplayFileSize); err == nil { - _, warnings := issue_model.GetCodeOwnersFromContent(ctx, data) + if rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize); err == nil { + _, warnings := issue_model.GetCodeOwnersFromReader(ctx, rc, size > setting.UI.MaxDisplayFileSize) if len(warnings) > 0 { ctx.Data["FileWarning"] = strings.Join(warnings, "\n") } @@ -627,6 +624,20 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["IsVideoFile"] = true case fInfo.st.IsAudio(): ctx.Data["IsAudioFile"] = true + case fInfo.st.Is3DModel(): + ctx.Data["Is3DModelFile"] = true + switch { + case fInfo.st.IsGLB(): + ctx.Data["IsGLBFile"] = true + case fInfo.st.IsSTL(): + ctx.Data["IsSTLFile"] = true + case fInfo.st.IsGLTF(): + ctx.Data["IsGLTFFile"] = true + case fInfo.st.IsOBJ(): + ctx.Data["IsOBJFile"] = true + case fInfo.st.Is3MF(): + ctx.Data["Is3MFFile"] = true + } case fInfo.st.IsImage() && (setting.UI.SVG.Enabled || !fInfo.st.IsSvgImage()): ctx.Data["IsImageFile"] = true ctx.Data["CanCopyContent"] = true diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 9a21ac21a3..1b5265978a 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -393,7 +393,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) ctx.ServerError("CommitsByFileAndRange", err) return nil, nil } - ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository) + ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commitsHistory, ctx.Repo.Repository) pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5) pager.SetDefaultParams(ctx) diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 2a93221c8f..d980fa393a 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -16,7 +16,6 @@ import ( activities_model "forgejo.org/models/activities" asymkey_model "forgejo.org/models/asymkey" "forgejo.org/models/db" - git_model "forgejo.org/models/git" issues_model "forgejo.org/models/issues" "forgejo.org/models/organization" repo_model "forgejo.org/models/repo" @@ -611,11 +610,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { ctx.ServerError("GetIssuesLastCommitStatus", err) return } - if !ctx.Repo.CanRead(unit.TypeActions) { - for key := range commitStatuses { - git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key]) - } - } // ------------------------------- // Fill stats to post to ctx.Data. diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 9fa71add57..fdca1a2fdd 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -13,10 +13,8 @@ import ( activities_model "forgejo.org/models/activities" "forgejo.org/models/db" - git_model "forgejo.org/models/git" issues_model "forgejo.org/models/issues" repo_model "forgejo.org/models/repo" - "forgejo.org/models/unit" "forgejo.org/modules/base" "forgejo.org/modules/log" "forgejo.org/modules/optional" @@ -311,11 +309,6 @@ func NotificationSubscriptions(ctx *context.Context) { ctx.ServerError("GetIssuesAllCommitStatus", err) return } - if !ctx.Repo.CanRead(unit.TypeActions) { - for key := range commitStatuses { - git_model.CommitStatusesHideActionsURL(ctx, commitStatuses[key]) - } - } ctx.Data["CommitLastStatus"] = lastStatus ctx.Data["CommitStatuses"] = commitStatuses ctx.Data["Issues"] = issues diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 8068c1c6bc..78dd6c5e7c 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -70,17 +70,6 @@ func userProfile(ctx *context.Context) { ctx.Data["OpenGraphURL"] = ctx.ContextUser.HTMLURL() ctx.Data["OpenGraphDescription"] = ctx.ContextUser.Description - // prepare heatmap data - if setting.Service.EnableUserHeatmap { - data, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer) - if err != nil { - ctx.ServerError("GetUserHeatmapDataByUser", err) - return - } - ctx.Data["HeatmapData"] = data - ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data) - } - profileDbRepo, profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer) defer profileClose() @@ -186,6 +175,17 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb ctx.Data["CardsNoneMsg"] = ctx.Tr("followers.outgoing.list.none", ctx.ContextUser.Name) } case "activity": + // prepare heatmap data + if setting.Service.EnableUserHeatmap { + data, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer) + if err != nil { + ctx.ServerError("GetUserHeatmapDataByUser", err) + return + } + ctx.Data["HeatmapData"] = data + ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data) + } + date := ctx.FormString("date") pagingNum = setting.UI.FeedPagingNum items, count, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{ @@ -264,10 +264,12 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb total = int(count) case "overview": - if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil { - log.Error("failed to GetBlobContent: %v", err) + if rc, _, err := profileReadme.NewTruncatedReader(setting.UI.MaxDisplayFileSize); err != nil { + log.Error("failed to NewTruncatedReader: %v", err) } else { - if profileContent, err := markdown.RenderString(&markup.RenderContext{ + defer rc.Close() + + if profileContent, err := markdown.RenderReader(&markup.RenderContext{ Ctx: ctx, GitRepo: profileGitRepo, Links: markup.Links{ @@ -280,7 +282,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)), }, Metas: map[string]string{"mode": "document"}, - }, bytes); err != nil { + }, rc); err != nil { log.Error("failed to RenderString: %v", err) } else { ctx.Data["ProfileReadme"] = profileContent diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index fe6ffb802d..400ee71f08 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -51,6 +51,9 @@ func Profile(ctx *context.Context) { ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx) ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod ctx.Data["CommonPronouns"] = commonPronouns + ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize + ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth + ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight ctx.HTML(http.StatusOK, tplSettingsProfile) } @@ -63,6 +66,9 @@ func ProfilePost(ctx *context.Context) { ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx) ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod ctx.Data["CommonPronouns"] = commonPronouns + ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize + ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth + ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight if ctx.HasError() { ctx.HTML(http.StatusOK, tplSettingsProfile) diff --git a/routers/web/web.go b/routers/web/web.go index 9be0af6fee..ba51b5c3f6 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1511,7 +1511,10 @@ func registerRoutes(m *web.Route) { m.Group("/commits", func() { m.Get("", context.RepoRef(), repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits) m.Get("/list", context.RepoRef(), repo.GetPullCommits) - m.Get("/{sha:[a-f0-9]{4,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit) + m.Group("/{sha:[a-f0-9]{4,40}}", func() { + m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit) + m.Post("/reviews/submit", context.RepoMustNotBeArchived(), web.Bind(forms.SubmitReviewForm{}), repo.SubmitReview) + }) }) m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), context.EnforceQuotaWeb(quota_model.LimitSubjectSizeGitAll, context.QuotaTargetRepo), repo.MergePullRequest) m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 9654186fbb..e240c996b5 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -345,6 +345,14 @@ func handleWorkflows( Status: actions_model.StatusWaiting, } + if workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content)); err == nil { + notifications, err := workflow.Notifications() + if err != nil { + log.Error("Notifications: %w", err) + } + run.NotifyEmail = notifications + } + need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer) if err != nil { log.Error("check if need approval for repo %d with user %d: %v", input.Repo.ID, input.Doer.ID, err) diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index 3ec0807d5f..cf8b29ead7 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -4,6 +4,7 @@ package actions import ( + "bytes" "context" "errors" "fmt" @@ -18,6 +19,7 @@ import ( webhook_module "forgejo.org/modules/webhook" "github.com/nektos/act/pkg/jobparser" + act_model "github.com/nektos/act/pkg/model" "xorm.io/builder" ) @@ -140,6 +142,16 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) return err } + workflow, err := act_model.ReadWorkflow(bytes.NewReader(cron.Content)) + if err != nil { + return err + } + notifications, err := workflow.Notifications() + if err != nil { + return err + } + run.NotifyEmail = notifications + // Parse the workflow specification from the cron schedule workflows, err := jobparser.Parse(cron.Content, jobparser.WithVars(vars)) if err != nil { diff --git a/services/actions/schedule_tasks_test.go b/services/actions/schedule_tasks_test.go new file mode 100644 index 0000000000..7073985252 --- /dev/null +++ b/services/actions/schedule_tasks_test.go @@ -0,0 +1,121 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package actions + +import ( + "testing" + + actions_model "forgejo.org/models/actions" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + webhook_module "forgejo.org/modules/webhook" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCreateScheduleTask(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: 2}) + + assertConstant := func(t *testing.T, cron *actions_model.ActionSchedule, run *actions_model.ActionRun) { + t.Helper() + assert.Equal(t, cron.Title, run.Title) + assert.Equal(t, cron.RepoID, run.RepoID) + assert.Equal(t, cron.OwnerID, run.OwnerID) + assert.Equal(t, cron.WorkflowID, run.WorkflowID) + assert.Equal(t, cron.TriggerUserID, run.TriggerUserID) + assert.Equal(t, cron.Ref, run.Ref) + assert.Equal(t, cron.CommitSHA, run.CommitSHA) + assert.Equal(t, cron.Event, run.Event) + assert.Equal(t, cron.EventPayload, run.EventPayload) + assert.Equal(t, cron.ID, run.ScheduleID) + assert.Equal(t, actions_model.StatusWaiting, run.Status) + } + + assertMutable := func(t *testing.T, expected, run *actions_model.ActionRun) { + t.Helper() + assert.Equal(t, expected.NotifyEmail, run.NotifyEmail) + } + + testCases := []struct { + name string + cron actions_model.ActionSchedule + want []actions_model.ActionRun + }{ + { + name: "simple", + cron: actions_model.ActionSchedule{ + Title: "scheduletitle1", + RepoID: repo.ID, + OwnerID: repo.OwnerID, + WorkflowID: "some.yml", + TriggerUserID: repo.OwnerID, + Ref: "branch", + CommitSHA: "fakeSHA", + Event: webhook_module.HookEventSchedule, + EventPayload: "fakepayload", + Content: []byte( + ` +name: test +on: push +jobs: + job2: + runs-on: ubuntu-latest + steps: + - run: true +`), + }, + want: []actions_model.ActionRun{ + { + Title: "scheduletitle1", + NotifyEmail: false, + }, + }, + }, + { + name: "enable-email-notifications is true", + cron: actions_model.ActionSchedule{ + Title: "scheduletitle2", + RepoID: repo.ID, + OwnerID: repo.OwnerID, + WorkflowID: "some.yml", + TriggerUserID: repo.OwnerID, + Ref: "branch", + CommitSHA: "fakeSHA", + Event: webhook_module.HookEventSchedule, + EventPayload: "fakepayload", + Content: []byte( + ` +name: test +enable-email-notifications: true +on: push +jobs: + job2: + runs-on: ubuntu-latest + steps: + - run: true +`), + }, + want: []actions_model.ActionRun{ + { + Title: "scheduletitle2", + NotifyEmail: true, + }, + }, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + require.NoError(t, CreateScheduleTask(t.Context(), &testCase.cron)) + require.Equal(t, len(testCase.want), unittest.GetCount(t, actions_model.ActionRun{RepoID: repo.ID})) + for _, expected := range testCase.want { + run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{Title: expected.Title}) + assertConstant(t, &testCase.cron, run) + assertMutable(t, &expected, run) + } + unittest.AssertSuccessfulDelete(t, actions_model.ActionRun{RepoID: repo.ID}) + }) + } +} diff --git a/services/actions/workflows.go b/services/actions/workflows.go index 7ec7c3abed..fbba3fd667 100644 --- a/services/actions/workflows.go +++ b/services/actions/workflows.go @@ -111,6 +111,11 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette return nil, nil, err } + notifications, err := wf.Notifications() + if err != nil { + return nil, nil, err + } + run := &actions_model.ActionRun{ Title: title, RepoID: repo.ID, @@ -125,6 +130,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette EventPayload: string(p), TriggerEvent: string(webhook.HookEventWorkflowDispatch), Status: actions_model.StatusWaiting, + NotifyEmail: notifications, } vars, err := actions_model.GetVariablesOfRun(ctx, run) diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go index f183136907..cbfe3bd54e 100644 --- a/services/automerge/automerge.go +++ b/services/automerge/automerge.go @@ -107,6 +107,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) { return } if !exists { + log.Trace("GetScheduledMergeByPullID found nothing for PR %d", pullID) return } @@ -204,6 +205,10 @@ func handlePullRequestAutoMerge(pullID int64, sha string) { return } + if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) { + log.Error("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err) + } + if err := pull_service.Merge(ctx, pr, doer, baseGitRepo, scheduledPRM.MergeStyle, "", scheduledPRM.Message, true); err != nil { log.Error("pull_service.Merge: %v", err) // FIXME: if merge failed, we should display some error message to the pull request page. diff --git a/services/convert/action.go b/services/convert/action.go index 5e17172b45..703c1f1261 100644 --- a/services/convert/action.go +++ b/services/convert/action.go @@ -8,22 +8,17 @@ import ( actions_model "forgejo.org/models/actions" access_model "forgejo.org/models/perm/access" + user_model "forgejo.org/models/user" api "forgejo.org/modules/structs" ) // ToActionRun convert actions_model.User to api.ActionRun // the run needs all attributes loaded -func ToActionRun(ctx context.Context, run *actions_model.ActionRun) *api.ActionRun { +func ToActionRun(ctx context.Context, run *actions_model.ActionRun, doer *user_model.User) *api.ActionRun { if run == nil { return nil } - // The doer is the one whose perspective is used to view this ActionRun. - // In the best case we use the user that created the webhook. - // Unfortunately we don't know who that was. - // So instead we use the repo owner, who is able to create webhooks and allow others to do so by making them repo admins. - // This is pretty close to perfect. - doer := run.Repo.Owner permissionInRepo, _ := access_model.GetUserRepoPermission(ctx, run.Repo, doer) return &api.ActionRun{ diff --git a/services/convert/convert.go b/services/convert/convert.go index 48da9d7623..2ea24a1b51 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -222,29 +222,6 @@ func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.Action }, nil } -// ToRepoActionRun convert a actions_model.ActionRun to an api.RepoActionRun -func ToRepoActionRun(ctx context.Context, r *actions_model.ActionRun) (*api.RepoActionRun, error) { - if err := r.LoadAttributes(ctx); err != nil { - return nil, err - } - - url := strings.TrimSuffix(setting.AppURL, "/") + r.Link() - actor := ToUser(ctx, r.TriggerUser, nil) - - return &api.RepoActionRun{ - ID: r.ID, - Name: r.Title, - HeadBranch: r.PrettyRef(), - HeadSHA: r.CommitSHA, - RunNumber: r.Index, - Event: r.TriggerEvent, - Status: r.Status.String(), - WorkflowID: r.WorkflowID, - URL: url, - TriggeringActor: actor, - }, nil -} - // ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerification { verif := asymkey_model.ParseCommitWithSignature(ctx, c) diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go index 174c175f86..a3b719d1a7 100644 --- a/services/federation/federation_service.go +++ b/services/federation/federation_service.go @@ -211,6 +211,11 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI return nil, nil, err } + inbox, err := url.ParseRequestURI(person.Inbox.GetLink().String()) + if err != nil { + return nil, nil, err + } + newUser := user.User{ LowerName: strings.ToLower(name), Name: name, @@ -227,6 +232,7 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI federatedUser := user.FederatedUser{ ExternalID: personID.ID, FederationHostID: federationHostID, + InboxPath: inbox.Path, NormalizedOriginalURL: personID.AsURI(), } diff --git a/services/feed/action.go b/services/feed/action.go index a2cd0551a3..7d179bd1c8 100644 --- a/services/feed/action.go +++ b/services/feed/action.go @@ -39,6 +39,24 @@ func NewNotifier() notify_service.Notifier { return &actionNotifier{} } +func notifyAll(ctx context.Context, action *activities_model.Action) error { + _, err := activities_model.NotifyWatchers(ctx, action) + if err != nil { + return err + } + return err + // return federation_service.NotifyActivityPubFollowers(ctx, out) +} + +func notifyAllActions(ctx context.Context, acts []*activities_model.Action) error { + _, err := activities_model.NotifyWatchersActions(ctx, acts) + if err != nil { + return err + } + return nil + // return federation_service.NotifyActivityPubFollowers(ctx, out) +} + func (a *actionNotifier) NewIssue(ctx context.Context, issue *issues_model.Issue, mentions []*user_model.User) { if err := issue.LoadPoster(ctx); err != nil { log.Error("issue.LoadPoster: %v", err) @@ -50,7 +68,7 @@ func (a *actionNotifier) NewIssue(ctx context.Context, issue *issues_model.Issue } repo := issue.Repo - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: issue.Poster.ID, ActUser: issue.Poster, OpType: activities_model.ActionCreateIssue, @@ -91,7 +109,7 @@ func (a *actionNotifier) IssueChangeStatus(ctx context.Context, doer *user_model } // Notify watchers for whatever action comes in, ignore if no action type. - if err := activities_model.NotifyWatchers(ctx, act); err != nil { + if err := notifyAll(ctx, act); err != nil { log.Error("NotifyWatchers: %v", err) } } @@ -127,7 +145,7 @@ func (a *actionNotifier) CreateIssueComment(ctx context.Context, doer *user_mode } // Notify watchers for whatever action comes in, ignore if no action type. - if err := activities_model.NotifyWatchers(ctx, act); err != nil { + if err := notifyAll(ctx, act); err != nil { log.Error("NotifyWatchers: %v", err) } } @@ -146,7 +164,7 @@ func (a *actionNotifier) NewPullRequest(ctx context.Context, pull *issues_model. return } - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: pull.Issue.Poster.ID, ActUser: pull.Issue.Poster, OpType: activities_model.ActionCreatePullRequest, @@ -160,7 +178,7 @@ func (a *actionNotifier) NewPullRequest(ctx context.Context, pull *issues_model. } func (a *actionNotifier) RenameRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldRepoName string) { - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: doer.ID, ActUser: doer, OpType: activities_model.ActionRenameRepo, @@ -174,7 +192,7 @@ func (a *actionNotifier) RenameRepository(ctx context.Context, doer *user_model. } func (a *actionNotifier) TransferRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldOwnerName string) { - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: doer.ID, ActUser: doer, OpType: activities_model.ActionTransferRepo, @@ -188,7 +206,7 @@ func (a *actionNotifier) TransferRepository(ctx context.Context, doer *user_mode } func (a *actionNotifier) CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) { - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: doer.ID, ActUser: doer, OpType: activities_model.ActionCreateRepo, @@ -201,7 +219,7 @@ func (a *actionNotifier) CreateRepository(ctx context.Context, doer, u *user_mod } func (a *actionNotifier) ForkRepository(ctx context.Context, doer *user_model.User, oldRepo, repo *repo_model.Repository) { - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: doer.ID, ActUser: doer, OpType: activities_model.ActionCreateRepo, @@ -266,13 +284,13 @@ func (a *actionNotifier) PullRequestReview(ctx context.Context, pr *issues_model actions = append(actions, action) } - if err := activities_model.NotifyWatchersActions(ctx, actions); err != nil { + if err := notifyAllActions(ctx, actions); err != nil { log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err) } } func (*actionNotifier) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: doer.ID, ActUser: doer, OpType: activities_model.ActionMergePullRequest, @@ -286,7 +304,7 @@ func (*actionNotifier) MergePullRequest(ctx context.Context, doer *user_model.Us } func (*actionNotifier) AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: doer.ID, ActUser: doer, OpType: activities_model.ActionAutoMergePullRequest, @@ -304,7 +322,7 @@ func (*actionNotifier) NotifyPullRevieweDismiss(ctx context.Context, doer *user_ if len(review.OriginalAuthor) > 0 { reviewerName = review.OriginalAuthor } - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: doer.ID, ActUser: doer, OpType: activities_model.ActionPullReviewDismissed, @@ -342,7 +360,7 @@ func (a *actionNotifier) PushCommits(ctx context.Context, pusher *user_model.Use opType = activities_model.ActionDeleteBranch } - if err = activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err = notifyAll(ctx, &activities_model.Action{ ActUserID: pusher.ID, ActUser: pusher, OpType: opType, @@ -362,7 +380,7 @@ func (a *actionNotifier) CreateRef(ctx context.Context, doer *user_model.User, r // has sent same action in `PushCommits`, so skip it. return } - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: doer.ID, ActUser: doer, OpType: opType, @@ -381,7 +399,7 @@ func (a *actionNotifier) DeleteRef(ctx context.Context, doer *user_model.User, r // has sent same action in `PushCommits`, so skip it. return } - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: doer.ID, ActUser: doer, OpType: opType, @@ -405,7 +423,7 @@ func (a *actionNotifier) SyncPushCommits(ctx context.Context, pusher *user_model return } - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: repo.OwnerID, ActUser: repo.MustOwner(ctx), OpType: activities_model.ActionMirrorSyncPush, @@ -420,7 +438,7 @@ func (a *actionNotifier) SyncPushCommits(ctx context.Context, pusher *user_model } func (a *actionNotifier) SyncCreateRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) { - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: repo.OwnerID, ActUser: repo.MustOwner(ctx), OpType: activities_model.ActionMirrorSyncCreate, @@ -434,7 +452,7 @@ func (a *actionNotifier) SyncCreateRef(ctx context.Context, doer *user_model.Use } func (a *actionNotifier) SyncDeleteRef(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, refFullName git.RefName) { - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: repo.OwnerID, ActUser: repo.MustOwner(ctx), OpType: activities_model.ActionMirrorSyncDelete, @@ -452,7 +470,7 @@ func (a *actionNotifier) NewRelease(ctx context.Context, rel *repo_model.Release log.Error("LoadAttributes: %v", err) return } - if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{ + if err := notifyAll(ctx, &activities_model.Action{ ActUserID: rel.PublisherID, ActUser: rel.Publisher, OpType: activities_model.ActionPublishRelease, diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 989f69d4f4..6835dfbf36 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -440,11 +440,29 @@ func getCommitFileLineCount(commit *git.Commit, filePath string) int { if err != nil { return 0 } - lineCount, err := blob.GetBlobLineCount() + reader, err := blob.DataAsync() if err != nil { return 0 } - return lineCount + defer reader.Close() + buf := make([]byte, 32*1024) + count := 1 + lineSep := []byte{'\n'} + + c, err := reader.Read(buf) + if c == 0 && err == io.EOF { + return 0 + } + for { + count += bytes.Count(buf[:c], lineSep) + switch { + case err == io.EOF: + return count + case err != nil: + return count + } + c, err = reader.Read(buf) + } } // Diff represents a difference between two git trees. diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index 3d3c8432c4..695b177b8b 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -712,6 +712,8 @@ func TestGetDiffFull(t *testing.T) { assert.Equal(t, ".gitattributes", diff.Files[0].Name) assert.Equal(t, "24139dae656713ba861751fb2c2ac38839349a7a", diff.Files[0].NameHash) + assert.Len(t, diff.Files[0].Sections, 2) + assert.Equal(t, 4, diff.Files[0].Sections[1].Lines[0].SectionInfo.LeftIdx) }) } diff --git a/services/issue/pull.go b/services/issue/pull.go index b0a0c47d88..2eef1fbfa8 100644 --- a/services/issue/pull.go +++ b/services/issue/pull.go @@ -43,8 +43,6 @@ type ReviewRequestNotifier struct { } func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, pr *issues_model.PullRequest) ([]*ReviewRequestNotifier, error) { - files := []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"} - if pr.IsWorkInProgress(ctx) { return nil, nil } @@ -72,18 +70,17 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, return nil, err } - var data string - for _, file := range files { + var rules []*issues_model.CodeOwnerRule + for _, file := range []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"} { if blob, err := commit.GetBlobByPath(file); err == nil { - data, err = blob.GetBlobContent(setting.UI.MaxDisplayFileSize) + rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize) if err == nil { + rules, _ = issues_model.GetCodeOwnersFromReader(ctx, rc, size > setting.UI.MaxDisplayFileSize) break } } } - rules, _ := issues_model.GetCodeOwnersFromContent(ctx, data) - // get the mergebase mergeBase, err := getMergeBase(repo, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName()) if err != nil { diff --git a/services/mailer/mail_actions.go b/services/mailer/mail_actions.go index 7c63603a98..09763e164e 100644 --- a/services/mailer/mail_actions.go +++ b/services/mailer/mail_actions.go @@ -23,19 +23,24 @@ func MailActionRun(run *actions_model.ActionRun, priorStatus actions_model.Statu return nil } - if run.TriggerUser.Email != "" && run.TriggerUser.EmailNotificationsPreference != user_model.EmailNotificationsDisabled { - if err := sendMailActionRun(run.TriggerUser, run, priorStatus, lastRun); err != nil { - return err - } + if !run.NotifyEmail { + return nil } - if run.Repo.Owner.Email != "" && run.Repo.Owner.Email != run.TriggerUser.Email && run.Repo.Owner.EmailNotificationsPreference != user_model.EmailNotificationsDisabled { - if err := sendMailActionRun(run.Repo.Owner, run, priorStatus, lastRun); err != nil { - return err - } + user := run.TriggerUser + // this happens e.g. when this is a scheduled run + if user.IsSystem() { + user = run.Repo.Owner + } + if user.IsSystem() || user.Email == "" { + return nil } - return nil + if user.EmailNotificationsPreference == user_model.EmailNotificationsDisabled { + return nil + } + + return sendMailActionRun(user, run, priorStatus, lastRun) } func sendMailActionRun(to *user_model.User, run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) error { diff --git a/services/mailer/mail_actions_now_done_test.go b/services/mailer/mail_actions_now_done_test.go index 0d832f2b36..6a01ea7631 100644 --- a/services/mailer/mail_actions_now_done_test.go +++ b/services/mailer/mail_actions_now_done_test.go @@ -4,42 +4,53 @@ package mailer import ( + "slices" "testing" actions_model "forgejo.org/models/actions" "forgejo.org/models/db" + organization_model "forgejo.org/models/organization" repo_model "forgejo.org/models/repo" user_model "forgejo.org/models/user" + "forgejo.org/modules/optional" "forgejo.org/modules/setting" + "forgejo.org/modules/test" notify_service "forgejo.org/services/notify" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func getActionsNowDoneTestUsers(t *testing.T) []*user_model.User { +func getActionsNowDoneTestUser(t *testing.T, name, email, notifications string) *user_model.User { t.Helper() - newTriggerUser := new(user_model.User) - newTriggerUser.Name = "new_trigger_user" - newTriggerUser.Language = "en_US" - newTriggerUser.IsAdmin = false - newTriggerUser.Email = "new_trigger_user@example.com" - newTriggerUser.LastLoginUnix = 1693648327 - newTriggerUser.CreatedUnix = 1693648027 - newTriggerUser.EmailNotificationsPreference = user_model.EmailNotificationsEnabled - require.NoError(t, user_model.CreateUser(db.DefaultContext, newTriggerUser)) + user := new(user_model.User) + user.Name = name + user.Language = "en_US" + user.IsAdmin = false + user.Email = email + user.LastLoginUnix = 1693648327 + user.CreatedUnix = 1693648027 + opts := user_model.CreateUserOverwriteOptions{ + AllowCreateOrganization: optional.Some(true), + EmailNotificationsPreference: ¬ifications, + } + require.NoError(t, user_model.AdminCreateUser(db.DefaultContext, user, &opts)) + return user +} - newOwner := new(user_model.User) - newOwner.Name = "new_owner" - newOwner.Language = "en_US" - newOwner.IsAdmin = false - newOwner.Email = "new_owner@example.com" - newOwner.LastLoginUnix = 1693648329 - newOwner.CreatedUnix = 1693648029 - newOwner.EmailNotificationsPreference = user_model.EmailNotificationsEnabled - require.NoError(t, user_model.CreateUser(db.DefaultContext, newOwner)) - - return []*user_model.User{newTriggerUser, newOwner} +func getActionsNowDoneTestOrg(t *testing.T, name, email string, owner *user_model.User) *user_model.User { + t.Helper() + org := new(organization_model.Organization) + org.Name = name + org.Language = "en_US" + org.IsAdmin = false + // contact email for the organization, for display purposes but otherwise not used as of v12 + org.Email = email + org.LastLoginUnix = 1693648327 + org.CreatedUnix = 1693648027 + org.Email = email + require.NoError(t, organization_model.CreateOrganization(db.DefaultContext, org, owner)) + return (*user_model.User)(org) } func assertTranslatedLocaleMailActionsNowDone(t *testing.T, msgBody string) { @@ -49,98 +60,181 @@ func assertTranslatedLocaleMailActionsNowDone(t *testing.T, msgBody string) { func TestActionRunNowDoneNotificationMail(t *testing.T) { ctx := t.Context() - users := getActionsNowDoneTestUsers(t) - defer CleanUpUsers(ctx, users) - triggerUser := users[0] - ownerUser := users[1] + defer test.MockVariableValue(&setting.Admin.DisableRegularOrgCreation, false)() + + actionsUser := user_model.NewActionsUser() + require.NotEmpty(t, actionsUser.Email) repo := repo_model.Repository{ Name: "some repo", Description: "rockets are cool", - Owner: ownerUser, - OwnerID: ownerUser.ID, } // Do some funky stuff with the action run's ids: // The run with the larger ID finished first. // This is odd but something that must work. - run1 := &actions_model.ActionRun{ID: 2, Repo: &repo, RepoID: repo.ID, Title: "some workflow", TriggerUser: triggerUser, TriggerUserID: triggerUser.ID, Status: actions_model.StatusFailure, Stopped: 1745821796, TriggerEvent: "workflow_dispatch"} - run2 := &actions_model.ActionRun{ID: 1, Repo: &repo, RepoID: repo.ID, Title: "some workflow", TriggerUser: triggerUser, TriggerUserID: triggerUser.ID, Status: actions_model.StatusSuccess, Stopped: 1745822796, TriggerEvent: "push"} + run1 := &actions_model.ActionRun{ID: 2, Repo: &repo, RepoID: repo.ID, Title: "some workflow", Status: actions_model.StatusFailure, Stopped: 1745821796, TriggerEvent: "workflow_dispatch"} + run2 := &actions_model.ActionRun{ID: 1, Repo: &repo, RepoID: repo.ID, Title: "some workflow", Status: actions_model.StatusSuccess, Stopped: 1745822796, TriggerEvent: "push"} + + assignUsers := func(triggerUser, owner *user_model.User) { + for _, run := range []*actions_model.ActionRun{run1, run2} { + run.TriggerUser = triggerUser + run.TriggerUserID = triggerUser.ID + run.NotifyEmail = true + } + repo.Owner = owner + repo.OwnerID = owner.ID + } notify_service.RegisterNotifier(NewNotifier()) + orgOwner := getActionsNowDoneTestUser(t, "org_owner", "org_owner@example.com", "disabled") + defer CleanUpUsers(ctx, []*user_model.User{orgOwner}) + t.Run("DontSendNotificationEmailOnFirstActionSuccess", func(t *testing.T) { + user := getActionsNowDoneTestUser(t, "new_user", "new_user@example.com", "enabled") + defer CleanUpUsers(ctx, []*user_model.User{user}) + assignUsers(user, user) defer MockMailSettings(func(msgs ...*Message) { assert.Fail(t, "no mail should be sent") })() notify_service.ActionRunNowDone(ctx, run2, actions_model.StatusRunning, nil) }) - t.Run("SendNotificationEmailOnActionRunFailed", func(t *testing.T) { - mailSentToOwner := false - mailSentToTriggerUser := false + t.Run("WorkflowEnableEmailNotificationIsFalse", func(t *testing.T) { + user := getActionsNowDoneTestUser(t, "new_user1", "new_user1@example.com", "enabled") + defer CleanUpUsers(ctx, []*user_model.User{user}) + assignUsers(user, user) defer MockMailSettings(func(msgs ...*Message) { - assert.LessOrEqual(t, len(msgs), 2) - for _, msg := range msgs { - switch msg.To { - case triggerUser.EmailTo(): - assert.False(t, mailSentToTriggerUser, "sent mail twice") - mailSentToTriggerUser = true - case ownerUser.EmailTo(): - assert.False(t, mailSentToOwner, "sent mail twice") - mailSentToOwner = true - default: - assert.Fail(t, "sent mail to unknown sender", msg.To) - } - assert.Contains(t, msg.Body, triggerUser.HTMLURL()) - assert.Contains(t, msg.Body, triggerUser.Name) - // what happened - assert.Contains(t, msg.Body, "failed") - // new status of run - assert.Contains(t, msg.Body, "failure") - // prior status of this run - assert.Contains(t, msg.Body, "waiting") - assertTranslatedLocaleMailActionsNowDone(t, msg.Body) - } + assert.Fail(t, "no mail should be sent") })() - notify_service.ActionRunNowDone(ctx, run1, actions_model.StatusWaiting, nil) - assert.True(t, mailSentToOwner) - assert.True(t, mailSentToTriggerUser) + run2.NotifyEmail = false + notify_service.ActionRunNowDone(ctx, run2, actions_model.StatusRunning, nil) }) - t.Run("SendNotificationEmailOnActionRunRecovered", func(t *testing.T) { - mailSentToOwner := false - mailSentToTriggerUser := false - defer MockMailSettings(func(msgs ...*Message) { - assert.LessOrEqual(t, len(msgs), 2) - for _, msg := range msgs { - switch msg.To { - case triggerUser.EmailTo(): - assert.False(t, mailSentToTriggerUser, "sent mail twice") - mailSentToTriggerUser = true - case ownerUser.EmailTo(): - assert.False(t, mailSentToOwner, "sent mail twice") - mailSentToOwner = true - default: - assert.Fail(t, "sent mail to unknown sender", msg.To) - } - assert.Contains(t, msg.Body, triggerUser.HTMLURL()) - assert.Contains(t, msg.Body, triggerUser.Name) - // what happened - assert.Contains(t, msg.Body, "recovered") - // old status of run - assert.Contains(t, msg.Body, "failure") - // new status of run - assert.Contains(t, msg.Body, "success") - // prior status of this run - assert.Contains(t, msg.Body, "running") - assertTranslatedLocaleMailActionsNowDone(t, msg.Body) - } - })() - assert.NotNil(t, setting.MailService) + for _, testCase := range []struct { + name string + triggerUser *user_model.User + owner *user_model.User + expected string + expectMail bool + }{ + { + // if the action is assigned a trigger user in a repository + // owned by a regular user, the mail is sent to the trigger user + name: "RegularTriggerUser", + triggerUser: getActionsNowDoneTestUser(t, "new_trigger_user0", "new_trigger_user0@example.com", user_model.EmailNotificationsEnabled), + owner: getActionsNowDoneTestUser(t, "new_owner0", "new_owner0@example.com", user_model.EmailNotificationsEnabled), + expected: "trigger", + expectMail: true, + }, + { + // if the action is assigned to a system user (e.g. ActionsUser) + // in a repository owned by a regular user, the mail is sent to + // the user that owns the repository + name: "SystemTriggerUserAndRegularOwner", + triggerUser: actionsUser, + owner: getActionsNowDoneTestUser(t, "new_owner1", "new_owner1@example.com", user_model.EmailNotificationsEnabled), + expected: "owner", + expectMail: true, + }, + { + // if the action is assigned a trigger user with disabled notifications in a repository + // owned by a regular user, no mail is sent + name: "RegularTriggerUserNotificationsDisabled", + triggerUser: getActionsNowDoneTestUser(t, "new_trigger_user2", "new_trigger_user2@example.com", user_model.EmailNotificationsDisabled), + owner: getActionsNowDoneTestUser(t, "new_owner2", "new_owner2@example.com", user_model.EmailNotificationsEnabled), + expectMail: false, + }, + { + // if the action is assigned to a system user (e.g. ActionsUser) + // owned by a regular user with disabled notifications, no mail is sent + name: "SystemTriggerUserAndRegularOwnerNotificationsDisabled", + triggerUser: actionsUser, + owner: getActionsNowDoneTestUser(t, "new_owner3", "new_owner3@example.com", user_model.EmailNotificationsDisabled), + expectMail: false, + }, + { + // if the action is assigned to a system user (e.g. ActionsUser) + // in a repository owned by an organization with an email contact, the mail is sent to + // this email contact + name: "SystemTriggerUserAndOrgOwner", + triggerUser: actionsUser, + owner: getActionsNowDoneTestOrg(t, "new_org1", "new_org_owner0@example.com", orgOwner), + expected: "owner", + expectMail: true, + }, + { + // if the action is assigned to a system user (e.g. ActionsUser) + // in a repository owned by an organization without an email contact, no mail is sent + name: "SystemTriggerUserAndNoMailOrgOwner", + triggerUser: actionsUser, + owner: getActionsNowDoneTestOrg(t, "new_org2", "", orgOwner), + expectMail: false, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + assignUsers(testCase.triggerUser, testCase.owner) + defer CleanUpUsers(ctx, slices.DeleteFunc([]*user_model.User{testCase.triggerUser, testCase.owner}, func(user *user_model.User) bool { + return user.IsSystem() + })) - notify_service.ActionRunNowDone(ctx, run2, actions_model.StatusRunning, run1) - assert.True(t, mailSentToOwner) - assert.True(t, mailSentToTriggerUser) - }) + t.Run("SendNotificationEmailOnActionRunFailed", func(t *testing.T) { + mailSent := false + defer MockMailSettings(func(msgs ...*Message) { + assert.Len(t, msgs, 1) + msg := msgs[0] + assert.False(t, mailSent, "sent mail twice") + expectedEmail := testCase.triggerUser.Email + if testCase.expected == "owner" { // otherwise "trigger" + expectedEmail = testCase.owner.Email + } + require.Contains(t, msg.To, expectedEmail, "sent mail to unknown sender") + mailSent = true + assert.Contains(t, msg.Body, testCase.triggerUser.HTMLURL()) + assert.Contains(t, msg.Body, testCase.triggerUser.Name) + // what happened + assert.Contains(t, msg.Body, "failed") + // new status of run + assert.Contains(t, msg.Body, "failure") + // prior status of this run + assert.Contains(t, msg.Body, "waiting") + assertTranslatedLocaleMailActionsNowDone(t, msg.Body) + })() + require.NotNil(t, setting.MailService) + + notify_service.ActionRunNowDone(ctx, run1, actions_model.StatusWaiting, nil) + assert.Equal(t, testCase.expectMail, mailSent) + }) + + t.Run("SendNotificationEmailOnActionRunRecovered", func(t *testing.T) { + mailSent := false + defer MockMailSettings(func(msgs ...*Message) { + assert.Len(t, msgs, 1) + msg := msgs[0] + assert.False(t, mailSent, "sent mail twice") + expectedEmail := testCase.triggerUser.Email + if testCase.expected == "owner" { // otherwise "trigger" + expectedEmail = testCase.owner.Email + } + require.Contains(t, msg.To, expectedEmail, "sent mail to unknown sender") + mailSent = true + assert.Contains(t, msg.Body, testCase.triggerUser.HTMLURL()) + assert.Contains(t, msg.Body, testCase.triggerUser.Name) + // what happened + assert.Contains(t, msg.Body, "recovered") + // old status of run + assert.Contains(t, msg.Body, "failure") + // new status of run + assert.Contains(t, msg.Body, "success") + // prior status of this run + assert.Contains(t, msg.Body, "running") + })() + require.NotNil(t, setting.MailService) + + notify_service.ActionRunNowDone(ctx, run2, actions_model.StatusRunning, run1) + assert.Equal(t, testCase.expectMail, mailSent) + }) + }) + } } diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go index ca5c645e0c..d8646d9ddd 100644 --- a/services/mailer/mailer.go +++ b/services/mailer/mailer.go @@ -29,7 +29,7 @@ import ( notify_service "forgejo.org/services/notify" ntlmssp "github.com/Azure/go-ntlmssp" - "github.com/jaytaylor/html2text" + "github.com/inbucket/html2text" "gopkg.in/gomail.v2" ) diff --git a/services/mailer/main_test.go b/services/mailer/main_test.go index 47e5d5d175..5e9cbe3e99 100644 --- a/services/mailer/main_test.go +++ b/services/mailer/main_test.go @@ -8,6 +8,7 @@ import ( "testing" "forgejo.org/models/db" + organization_model "forgejo.org/models/organization" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/setting" @@ -51,6 +52,11 @@ func MockMailSettings(send func(msgs ...*Message)) func() { func CleanUpUsers(ctx context.Context, users []*user_model.User) { for _, u := range users { - db.DeleteByID[user_model.User](ctx, u.ID) + if u.IsOrganization() { + organization_model.DeleteOrganization(ctx, (*organization_model.Organization)(u)) + } else { + db.DeleteByID[user_model.User](ctx, u.ID) + db.DeleteByBean(ctx, &user_model.EmailAddress{UID: u.ID}) + } } } diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 55adad9685..7887dacdb1 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -766,7 +766,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model issue := issues_model.Issue{ RepoID: g.repo.ID, Repo: g.repo, - Title: prTitle, + Title: util.TruncateRunes(prTitle, 255), Index: pr.Number, Content: pr.Content, MilestoneID: milestoneID, diff --git a/services/pull/check.go b/services/pull/check.go index afeb7e675e..6002e2ae26 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -28,6 +28,7 @@ import ( "forgejo.org/modules/timeutil" asymkey_service "forgejo.org/services/asymkey" notify_service "forgejo.org/services/notify" + shared_automerge "forgejo.org/services/shared/automerge" ) // prPatchCheckerQueue represents a queue to handle update pull request tests @@ -170,7 +171,7 @@ func isSignedIfRequired(ctx context.Context, pr *issues_model.PullRequest, doer // checkAndUpdateStatus checks if pull request is possible to leaving checking status, // and set to be either conflict or mergeable. -func checkAndUpdateStatus(ctx context.Context, pr *issues_model.PullRequest) { +func checkAndUpdateStatus(ctx context.Context, pr *issues_model.PullRequest) bool { // If status has not been changed to conflict by testPatch then we are mergeable if pr.Status == issues_model.PullRequestStatusChecking { pr.Status = issues_model.PullRequestStatusMergeable @@ -184,12 +185,15 @@ func checkAndUpdateStatus(ctx context.Context, pr *issues_model.PullRequest) { if has { log.Trace("Not updating status for %-v as it is due to be rechecked", pr) - return + return false } if err := pr.UpdateColsIfNotMerged(ctx, "merge_base", "status", "conflicted_files", "changed_protected_files"); err != nil { log.Error("Update[%-v]: %v", pr, err) + return false } + + return true } // getMergeCommit checks if a pull request has been merged @@ -339,15 +343,22 @@ func handler(items ...string) []string { } func testPR(id int64) { - pullWorkingPool.CheckIn(fmt.Sprint(id)) - defer pullWorkingPool.CheckOut(fmt.Sprint(id)) ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("Test PR[%d] from patch checking queue", id)) defer finished() + if pr, updated := testPRProtected(ctx, id); pr != nil && updated { + shared_automerge.AddToQueueIfMergeable(ctx, pr) + } +} + +func testPRProtected(ctx context.Context, id int64) (*issues_model.PullRequest, bool) { + pullWorkingPool.CheckIn(fmt.Sprint(id)) + defer pullWorkingPool.CheckOut(fmt.Sprint(id)) + pr, err := issues_model.GetPullRequestByID(ctx, id) if err != nil { log.Error("Unable to GetPullRequestByID[%d] for testPR: %v", id, err) - return + return nil, false } log.Trace("Testing %-v", pr) @@ -357,12 +368,12 @@ func testPR(id int64) { if pr.HasMerged { log.Trace("%-v is already merged (status: %s, merge commit: %s)", pr, pr.Status, pr.MergedCommitID) - return + return nil, false } if manuallyMerged(ctx, pr) { log.Trace("%-v is manually merged (status: %s, merge commit: %s)", pr, pr.Status, pr.MergedCommitID) - return + return nil, false } if err := TestPatch(pr); err != nil { @@ -371,9 +382,10 @@ func testPR(id int64) { if err := pr.UpdateCols(ctx, "status"); err != nil { log.Error("update pr [%-v] status to PullRequestStatusError failed: %v", pr, err) } - return + return nil, false } - checkAndUpdateStatus(ctx, pr) + + return pr, checkAndUpdateStatus(ctx, pr) } // CheckPRsForBaseBranch check all pulls with baseBrannch diff --git a/services/repository/files/content.go b/services/repository/files/content.go index 5f7dd38303..3d2217df18 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -250,8 +250,25 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref return contentsResponse, nil } -// GetBlobBySHA get the GitBlobResponse of a repository using a sha hash. -func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string) (*api.GitBlobResponse, error) { +// GetBlobsBySHA gets multiple GitBlobs of a repository by sha hash. +func GetBlobsBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, shas []string) ([]*api.GitBlob, error) { + if len(shas) > setting.API.MaxResponseItems { + shas = shas[:setting.API.MaxResponseItems] + } + + blobs := make([]*api.GitBlob, 0, len(shas)) + for _, sha := range shas { + blob, err := GetBlobBySHA(ctx, repo, gitRepo, sha) + if err != nil { + return nil, err + } + blobs = append(blobs, blob) + } + return blobs, nil +} + +// GetBlobBySHA get the GitBlob of a repository using a sha hash. +func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string) (*api.GitBlob, error) { gitBlob, err := gitRepo.GetBlob(sha) if err != nil { return nil, err @@ -263,7 +280,7 @@ func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git return nil, err } } - return &api.GitBlobResponse{ + return &api.GitBlob{ SHA: gitBlob.ID.String(), URL: repo.APIURL() + "/git/blobs/" + url.PathEscape(gitBlob.ID.String()), Size: gitBlob.Size(), diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index e55b840660..8fc8f56b4f 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -192,7 +192,7 @@ func TestGetBlobBySHA(t *testing.T) { defer gitRepo.Close() gbr, err := GetBlobBySHA(db.DefaultContext, repo, gitRepo, "65f1bf27bc3bf70f64657658635e66094edbcb4d") - expectedGBR := &api.GitBlobResponse{ + expectedGBR := &api.GitBlob{ Content: "dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK", Encoding: "base64", URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/65f1bf27bc3bf70f64657658635e66094edbcb4d", @@ -202,3 +202,43 @@ func TestGetBlobBySHA(t *testing.T) { require.NoError(t, err) assert.Equal(t, expectedGBR, gbr) } + +func TestGetBlobsBySHA(t *testing.T) { + unittest.PrepareTestEnv(t) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + + gitRepo, err := gitrepo.OpenRepository(db.DefaultContext, repo) + require.NoError(t, err) + defer gitRepo.Close() + + gbr, err := GetBlobsBySHA(db.DefaultContext, repo, gitRepo, []string{ + "ea82fc8777a24b07c26b3a4bf4e2742c03733eab", // Home.md + "6395b68e1feebb1e4c657b4f9f6ba2676a283c0b", // line.svg + "26f842bcad37fa40a1bb34cbb5ee219ee35d863d", // test.xml + }) + expectedGBR := []*api.GitBlob{ + { + Content: "IyBIb21lIHBhZ2UKClRoaXMgaXMgdGhlIGhvbWUgcGFnZSEK", + Encoding: "base64", + URL: "https://try.gitea.io/api/v1/repos/user2/repo2/git/blobs/ea82fc8777a24b07c26b3a4bf4e2742c03733eab", + SHA: "ea82fc8777a24b07c26b3a4bf4e2742c03733eab", + Size: 36, + }, + { + Content: "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZwogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHdpZHRoPSIxMjgiCiAgIGhlaWdodD0iMTI4IgogICB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+CgogIDxsaW5lIHgxPSIwIiB5MT0iNyIgeDI9IjEwIiB5Mj0iNyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPC9zdmc+", + Encoding: "base64", + URL: "https://try.gitea.io/api/v1/repos/user2/repo2/git/blobs/6395b68e1feebb1e4c657b4f9f6ba2676a283c0b", + SHA: "6395b68e1feebb1e4c657b4f9f6ba2676a283c0b", + Size: 246, + }, + { + Content: "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHRlc3Q+VGhpcyBpcyBYTUw8L3Rlc3Q+Cg==", + Encoding: "base64", + URL: "https://try.gitea.io/api/v1/repos/user2/repo2/git/blobs/26f842bcad37fa40a1bb34cbb5ee219ee35d863d", + SHA: "26f842bcad37fa40a1bb34cbb5ee219ee35d863d", + Size: 64, + }, + } + require.NoError(t, err) + assert.Equal(t, expectedGBR, gbr) +} diff --git a/services/shared/automerge/automerge.go b/services/shared/automerge/automerge.go index 1dc309f4b3..be7b2f6eb4 100644 --- a/services/shared/automerge/automerge.go +++ b/services/shared/automerge/automerge.go @@ -21,9 +21,9 @@ import ( var PRAutoMergeQueue *queue.WorkerPoolQueue[string] func addToQueue(pr *issues_model.PullRequest, sha string) { - log.Trace("Adding pullID: %d to the pull requests patch checking queue with sha %s", pr.ID, sha) + log.Trace("Adding pullID: %d to the automerge queue with sha %s", pr.ID, sha) if err := PRAutoMergeQueue.Push(fmt.Sprintf("%d_%s", pr.ID, sha)); err != nil { - log.Error("Error adding pullID: %d to the pull requests patch checking queue %v", pr.ID, err) + log.Error("Error adding pullID: %d to the automerge queue %v", pr.ID, err) } } @@ -43,32 +43,29 @@ func StartPRCheckAndAutoMergeBySHA(ctx context.Context, sha string, repo *repo_m return nil } -// StartPRCheckAndAutoMerge start an automerge check and auto merge task for a pull request func StartPRCheckAndAutoMerge(ctx context.Context, pull *issues_model.PullRequest) { if pull == nil || pull.HasMerged || !pull.CanAutoMerge() { return } - if err := pull.LoadBaseRepo(ctx); err != nil { - log.Error("LoadBaseRepo: %v", err) - return + commitID := pull.HeadCommitID + if commitID == "" { + commitID = getCommitIDFromRefName(ctx, pull) } - gitRepo, err := gitrepo.OpenRepository(ctx, pull.BaseRepo) - if err != nil { - log.Error("OpenRepository: %v", err) - return - } - defer gitRepo.Close() - commitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName()) - if err != nil { - log.Error("GetRefCommitID: %v", err) + if commitID == "" { return } addToQueue(pull, commitID) } +var AddToQueueIfMergeable = func(ctx context.Context, pull *issues_model.PullRequest) { + if pull.Status == issues_model.PullRequestStatusMergeable { + StartPRCheckAndAutoMerge(ctx, pull) + } +} + func getPullRequestsByHeadSHA(ctx context.Context, sha string, repo *repo_model.Repository, filter func(*issues_model.PullRequest) bool) (map[int64]*issues_model.PullRequest, error) { gitRepo, err := gitrepo.OpenRepository(ctx, repo) if err != nil { @@ -118,3 +115,24 @@ func getPullRequestsByHeadSHA(ctx context.Context, sha string, repo *repo_model. return pulls, nil } + +func getCommitIDFromRefName(ctx context.Context, pull *issues_model.PullRequest) string { + if err := pull.LoadBaseRepo(ctx); err != nil { + log.Error("LoadBaseRepo: %v", err) + return "" + } + + gitRepo, err := gitrepo.OpenRepository(ctx, pull.BaseRepo) + if err != nil { + log.Error("OpenRepository: %v", err) + return "" + } + defer gitRepo.Close() + commitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName()) + if err != nil { + log.Error("GetRefCommitID: %v", err) + return "" + } + + return commitID +} diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index b3201e5d10..009efc994f 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -894,9 +894,16 @@ func (m *webhookNotifier) ActionRunNowDone(ctx context.Context, run *actions_mod Owner: run.TriggerUser, } + // The doer is the one whose perspective is used to view this ActionRun. + // In the best case we use the user that created the webhook. + // Unfortunately we don't know who that was. + // So instead we use the repo owner, who is able to create webhooks and allow others to do so by making them repo admins. + // This is pretty close to perfect. + doer := run.Repo.Owner + payload := &api.ActionPayload{ - Run: convert.ToActionRun(ctx, run), - LastRun: convert.ToActionRun(ctx, lastRun), + Run: convert.ToActionRun(ctx, run, doer), + LastRun: convert.ToActionRun(ctx, lastRun, doer), PriorStatus: priorStatus.String(), } diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl index 7d004dd903..a1ff3d4117 100644 --- a/templates/admin/user/edit.tmpl +++ b/templates/admin/user/edit.tmpl @@ -208,6 +208,7 @@
+
{{ctx.Locale.Tr "avatar.constraints_hint" (ctx.Locale.TrSize .MaxAvatarFileSize) .MaxAvatarWidth .MaxAvatarHeight}}
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 7ec2ac87b3..6357981549 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -25,7 +25,7 @@ {{template "base/head_style" .}} {{template "custom/header" .}} - + {{template "custom/body_outer_pre" .}}
diff --git a/templates/org/settings/options.tmpl b/templates/org/settings/options.tmpl index 2ef7031aef..562f3f3ae3 100644 --- a/templates/org/settings/options.tmpl +++ b/templates/org/settings/options.tmpl @@ -94,6 +94,7 @@
+
{{ctx.Locale.Tr "avatar.constraints_hint" (ctx.Locale.TrSize .MaxAvatarFileSize) .MaxAvatarWidth .MaxAvatarHeight}}
diff --git a/templates/repo/commit_header.tmpl b/templates/repo/commit_header.tmpl index 8a074e9545..9604daf2b0 100644 --- a/templates/repo/commit_header.tmpl +++ b/templates/repo/commit_header.tmpl @@ -14,10 +14,19 @@ {{end}} {{end}}
-
+

{{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}

- {{if not $.PageIsWiki}} - {{end}} -
- {{end}} + {{end}} +
{{if IsMultilineCommitMessage .Commit.Message}}
{{RenderCommitBody $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}
@@ -185,9 +194,16 @@ {{end}}
{{ctx.Locale.Tr "repo.diff.commit"}} - - {{ShortSha .CommitID}} - + {{if .PageIsPullFiles}} + {{$commitShaLink := (printf "%s/commit/%s" $.RepoLink (PathEscape .CommitID))}} + + {{ShortSha .CommitID}} + + {{else}} + + {{ShortSha .CommitID}} + + {{end}}
diff --git a/templates/repo/commit_load_branches_and_tags.tmpl b/templates/repo/commit_load_branches_and_tags.tmpl index ffa0e530e8..25402ca2f4 100644 --- a/templates/repo/commit_load_branches_and_tags.tmpl +++ b/templates/repo/commit_load_branches_and_tags.tmpl @@ -1,4 +1,4 @@ -{{if not .PageIsWiki}} +{{if not (or .PageIsWiki .PageIsPullFiles)}}
{{if and (not $.Repository.IsArchived) (not .DiffNotAvailable)}} diff --git a/templates/repo/diff/new_review.tmpl b/templates/repo/diff/new_review.tmpl index 13d09babe1..ded7a6c5fc 100644 --- a/templates/repo/diff/new_review.tmpl +++ b/templates/repo/diff/new_review.tmpl @@ -1,17 +1,14 @@
-
- {{if $.IsShowingAllCommits}}
@@ -55,5 +52,4 @@
- {{end}}
diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl index 8646562ca8..6d2f441793 100644 --- a/templates/repo/issue/card.tmpl +++ b/templates/repo/issue/card.tmpl @@ -4,7 +4,7 @@ {{if $attachments}}
{{range $attachments}} - {{.Name}} + {{.Name}} {{end}}
{{end}} diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl index ae50ac4c46..84ba6e5358 100644 --- a/templates/repo/issue/filter_list.tmpl +++ b/templates/repo/issue/filter_list.tmpl @@ -127,7 +127,7 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}