mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-21 09:06:46 +09:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76e88b30c0 | ||
|
|
82536a3ede | ||
|
|
c52e7c0cd2 | ||
|
|
738ff1d65a | ||
|
|
52eb515751 | ||
|
|
d7d658a8e9 | ||
|
|
5265238726 | ||
|
|
35fa0400e9 | ||
|
|
edd0c0e2ce | ||
|
|
e645afbb3f | ||
|
|
7610336d74 | ||
|
|
3701fcec84 | ||
|
|
f8dd2dd4cc | ||
|
|
ba4f8fa246 |
No files matched your search
@@ -10,7 +10,7 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 26
|
||||
- if: ${{ inputs.cache == 'true' }}
|
||||
|
||||
@@ -4,7 +4,7 @@ description: Set up uv and python and restore the uv cache
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
||||
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
||||
with:
|
||||
python-version: 3.14
|
||||
save-cache: ${{ github.workflow == 'cache-seeder' }}
|
||||
@@ -9,7 +9,7 @@ concurrency:
|
||||
group: cron-renovate
|
||||
|
||||
env:
|
||||
RENOVATE_VERSION: 44.155.8 # renovate: datasource=npm depName=renovate
|
||||
RENOVATE_VERSION: 44.143.0 # renovate: datasource=npm depName=renovate
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- uses: renovatebot/github-action@f0f41e5f2e706165f6814b1d0f0d0e319de7d1d9 # v46.3.8
|
||||
- uses: renovatebot/github-action@230ce922b08968d0a4f6f70f295601daff09ef1d # v46.3.7
|
||||
with:
|
||||
renovate-version: ${{ env.RENOVATE_VERSION }}
|
||||
configurationFile: renovate.json5
|
||||
|
||||
@@ -223,7 +223,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mssql:
|
||||
image: mcr.microsoft.com/mssql/server:2019-latest@sha256:07d7dedecdbfcf6776035dc5eefb5bfd9aa1804b2f6b99f7dedb397d8a599069
|
||||
image: mcr.microsoft.com/mssql/server:2019-latest@sha256:ef0b8db33970ecd01bed49c3a84a1d083c435a9891718df619298b67b352e74a
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
MSSQL_PID: Standard
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.sha }}
|
||||
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 26
|
||||
# Labels are only synced after the title lints, so an invalid title never reaches the label diff.
|
||||
|
||||
@@ -9,7 +9,7 @@ EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-che
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.14.0 # renovate: datasource=go
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.17 # renovate: datasource=go
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.36.6 # renovate: datasource=go
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.9.0 # renovate: datasource=go
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.8.0 # renovate: datasource=go
|
||||
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 # renovate: datasource=go
|
||||
SHELLCHECK_IMAGE ?= docker.io/koalaman/shellcheck:v0.11.0@sha256:61862eba1fcf09a484ebcc6feea46f1782532571a34ed51fedf90dd25f925a8d # renovate: datasource=docker
|
||||
|
||||
|
||||
Generated
+7
-2
@@ -5,8 +5,8 @@
|
||||
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under tLine truncated
|
||||
},
|
||||
{
|
||||
"name": "connectrpc.com/connect/v2",
|
||||
"path": "connectrpc.com/connect/v2/LICENSE",
|
||||
"name": "connectrpc.com/connect",
|
||||
"path": "connectrpc.com/connect/LICENSE",
|
||||
"licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under thiLine truncated
|
||||
},
|
||||
{
|
||||
@@ -889,6 +889,11 @@
|
||||
"path": "github.com/shopspring/decimal/LICENSE",
|
||||
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Spring, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n- Based on https://github.com/oguzbilgic/fpd, which has the following license:\n\"\"\"\nThe MIT License (MIT)\n\nCopyright (c) 2013 Oguz Bilgic\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\"\"\"\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/sirupsen/logrus",
|
||||
"path": "github.com/sirupsen/logrus/LICENSE",
|
||||
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Simon Eskildsen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/sorairolake/lzip-go",
|
||||
"path": "github.com/sorairolake/lzip-go/LICENSE-APACHE",
|
||||
|
||||
@@ -23,8 +23,8 @@ import (
|
||||
var rxDeprecated = regexp.MustCompile(`(?i)(?:^|[\n.;])\s*deprecated\b`)
|
||||
|
||||
// Convert parses a Swagger 2.0 spec and returns an OAS3 spec, applying
|
||||
// Gitea-specific post-processing: file-schema fixups, URI formats,
|
||||
// deprecated flags, and shared-enum extraction.
|
||||
// Gitea-specific post-processing: server URL, file-schema fixups, URI
|
||||
// formats, deprecated flags, and shared-enum extraction.
|
||||
//
|
||||
// astEnumMap is a value-set-key → Go-type-name(s) map (built by
|
||||
// ScanSwaggerEnumTypes). When a value set is shared by multiple Go types,
|
||||
@@ -42,6 +42,7 @@ func Convert(swaggerJSON []byte, astEnumMap map[string][]string) (*openapi3.T, e
|
||||
return nil, fmt.Errorf("converting to openapi 3.0: %w", err)
|
||||
}
|
||||
|
||||
oas3.Servers = openapi3.Servers{{URL: swagger2.BasePath}}
|
||||
fixFileSchemas(oas3)
|
||||
addURIFormats(oas3)
|
||||
addDeprecatedFlags(oas3)
|
||||
|
||||
@@ -1218,8 +1218,9 @@ LEVEL = Info
|
||||
;; In default merge messages only include approvers who are official
|
||||
;DEFAULT_MERGE_MESSAGE_OFFICIAL_APPROVERS_ONLY = true
|
||||
;;
|
||||
;; In default squash-merge messages include the commit message of all commits comprising the pull request.
|
||||
;POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES = false
|
||||
;; Set default squash merge commit message body, valid options: PR_BODY (pull request description), COMMIT_MESSAGES (commit messages), BLANK (none)
|
||||
;; Repositories store the current value when created or when their settings are next saved, and can change it there.
|
||||
;DEFAULT_SQUASH_MERGE_MESSAGE = PR_BODY
|
||||
;;
|
||||
;; Add co-authored-by and co-committed-by trailers if committer does not match author
|
||||
;ADD_CO_COMMITTER_TRAILERS = true
|
||||
@@ -1433,11 +1434,14 @@ LEVEL = Info
|
||||
;; The icon theme for folders (basic/material)
|
||||
;FOLDER_ICON_THEME = basic
|
||||
;;
|
||||
;; All available reactions users can choose on issues/prs and comments.
|
||||
;; All available reactions users can choose on issues, pull requests and comments.
|
||||
;; Values can be emoji alias (:smile:) or a unicode emoji.
|
||||
;; For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png
|
||||
;REACTIONS = +1, -1, laugh, hooray, confused, heart, rocket, eyes
|
||||
;;
|
||||
;; Reactions available for releases
|
||||
;;RELEASE_REACTIONS = +1, laugh, hooray, heart, rocket, eyes
|
||||
;;
|
||||
;; Change the number of users that are displayed in reactions tooltip (triggered by mouse hover).
|
||||
;REACTION_MAX_USER_NUM = 10
|
||||
;;
|
||||
|
||||
@@ -851,7 +851,6 @@ export default defineConfig([
|
||||
'unicorn/no-prevent-default-in-passive-listener': [2],
|
||||
'unicorn/no-process-exit': [0],
|
||||
'unicorn/no-redundant-comparison': [2],
|
||||
'unicorn/no-repeated-call-wrappers': [0],
|
||||
'unicorn/no-return-array-push': [2],
|
||||
'unicorn/no-selector-as-dom-name': [2],
|
||||
'unicorn/no-shorthand-property-overrides': [0], // only applies to css language
|
||||
@@ -872,7 +871,6 @@ export default defineConfig([
|
||||
'unicorn/no-unnecessary-array-splice-count': [2],
|
||||
'unicorn/no-unnecessary-await': [2],
|
||||
'unicorn/no-unnecessary-boolean-comparison': [2],
|
||||
'unicorn/no-unnecessary-default-arguments': [0],
|
||||
'unicorn/no-unnecessary-fetch-options': [0],
|
||||
'unicorn/no-unnecessary-global-this': [0],
|
||||
'unicorn/no-unnecessary-nested-ternary': [2],
|
||||
|
||||
@@ -2,13 +2,13 @@ module gitea.dev
|
||||
|
||||
go 1.27
|
||||
|
||||
toolchain go1.27.2
|
||||
toolchain go1.27.3
|
||||
|
||||
require (
|
||||
connectrpc.com/connect/v2 v2.0.1
|
||||
connectrpc.com/connect v1.21.0
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||
gitea.dev/actionslib v1.4.2-0.20261019014634-1fd849368a92
|
||||
gitea.dev/actionslib v1.4.0
|
||||
gitea.dev/sdk v1.3.0
|
||||
github.com/42wim/httpsig v1.3.0
|
||||
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432
|
||||
@@ -27,19 +27,19 @@ require (
|
||||
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20260812203852-971c0284dc33
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
github.com/coder/websocket v1.8.15
|
||||
github.com/dlclark/regexp2/v2 v2.8.4
|
||||
github.com/dlclark/regexp2/v2 v2.8.3
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
|
||||
github.com/dustin/go-humanize v1.1.0
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.5
|
||||
github.com/emersion/go-imap v1.2.1
|
||||
github.com/felixge/fgprof v0.9.5
|
||||
github.com/fsnotify/fsnotify v1.10.1
|
||||
github.com/getkin/kin-openapi v0.150.0
|
||||
github.com/go-chi/chi/v5 v5.4.0
|
||||
github.com/getkin/kin-openapi v0.149.0
|
||||
github.com/go-chi/chi/v5 v5.3.2
|
||||
github.com/go-chi/cors v1.2.2
|
||||
github.com/go-co-op/gocron/v2 v2.22.0
|
||||
github.com/go-enry/go-enry/v2 v2.9.6
|
||||
github.com/go-ldap/ldap/v3 v3.4.15
|
||||
github.com/go-ldap/ldap/v3 v3.4.14
|
||||
github.com/go-redsync/redsync/v4 v4.18.0
|
||||
github.com/go-sql-driver/mysql v1.10.1
|
||||
github.com/go-webauthn/webauthn v0.18.2
|
||||
@@ -56,7 +56,7 @@ require (
|
||||
github.com/huandu/xstrings v1.6.2
|
||||
github.com/jhillyerd/enmime/v2 v2.5.0
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/compress v1.20.3
|
||||
github.com/klauspost/compress v1.20.1
|
||||
github.com/lib/pq v1.12.3
|
||||
github.com/markbates/goth v1.82.0
|
||||
github.com/mattn/go-isatty v0.0.24
|
||||
@@ -65,13 +65,13 @@ require (
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/microsoft/go-mssqldb v1.11.2
|
||||
github.com/minio/minio-go/v7 v7.4.1
|
||||
github.com/minio/minio-go/v7 v7.3.0
|
||||
github.com/msteinert/pam/v2 v2.1.0
|
||||
github.com/niklasfasching/go-org v1.9.1
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/prometheus/client_golang v1.25.0
|
||||
github.com/prometheus/client_golang v1.24.1
|
||||
github.com/quasoft/websspi v1.1.2
|
||||
github.com/redis/go-redis/v9 v9.23.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
@@ -86,16 +86,16 @@ require (
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.8.6
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.19.0
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.16.0
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6.0.20261013184558-04dd63131525
|
||||
golang.org/x/crypto v0.58.0
|
||||
golang.org/x/image v0.47.0
|
||||
golang.org/x/mod v0.42.0
|
||||
golang.org/x/net v0.61.0
|
||||
golang.org/x/crypto v0.57.0
|
||||
golang.org/x/image v0.46.0
|
||||
golang.org/x/mod v0.41.0
|
||||
golang.org/x/net v0.60.0
|
||||
golang.org/x/oauth2 v0.37.0
|
||||
golang.org/x/sync v0.24.0
|
||||
golang.org/x/sys v0.49.0
|
||||
golang.org/x/text v0.43.0
|
||||
golang.org/x/text v0.42.0
|
||||
google.golang.org/grpc v1.84.0
|
||||
google.golang.org/protobuf v1.36.12
|
||||
gopkg.in/ini.v1 v1.67.3
|
||||
@@ -204,13 +204,14 @@ require (
|
||||
github.com/pierrec/lz4/v4 v4.1.27 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.3 // indirect
|
||||
github.com/prometheus/common v0.72.0 // indirect
|
||||
github.com/prometheus/procfs v0.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.70.1 // indirect
|
||||
github.com/prometheus/procfs v0.21.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.10.2 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
|
||||
@@ -2,8 +2,8 @@ cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdB
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE=
|
||||
code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM=
|
||||
connectrpc.com/connect/v2 v2.0.1 h1:WTAycxe87UqxSM5oxS30d1dalZ+C5xz8Ec8O6zVYkHQ=
|
||||
connectrpc.com/connect/v2 v2.0.1/go.mod h1:N32OLrstcPUzkykFynnefFro3+xzSdOrZlK2jHmS/xo=
|
||||
connectrpc.com/connect v1.21.0 h1:LhqSJt7jHf5NJBo9Jq/t/9FjcYAideif0mg+qe2jCUs=
|
||||
connectrpc.com/connect v1.21.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
|
||||
@@ -12,8 +12,8 @@ gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHq
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||
gitea.dev/actionslib v1.4.2-0.20261019014634-1fd849368a92 h1:qjj0mqRCwZ269pJo4LH8e4ePMVK/iWMA0tO6Dxiey+M=
|
||||
gitea.dev/actionslib v1.4.2-0.20261019014634-1fd849368a92/go.mod h1:NWzFSnEG5EEXaq66Ql3moZ31g4H7+sAmSCyBirjj2R4=
|
||||
gitea.dev/actionslib v1.4.0 h1:k31smj22tnUZrrM5kkntXN8lKAe7dkoTEoiP1Fk5KJc=
|
||||
gitea.dev/actionslib v1.4.0/go.mod h1:svCefEsavx4jmn8vJ4wTG7Q0KBcGfrFmGUjrhxuNUAQ=
|
||||
gitea.dev/sdk v1.3.0 h1:4U95+Ou6WHCQiRPDEyAzvl1Q7cxg5RUTWkqmOXr5w4I=
|
||||
gitea.dev/sdk v1.3.0/go.mod h1:85EBqSAqUn1KlFCk40y3sTjWjBsB/KouL3slGXMA/2o=
|
||||
github.com/42wim/httpsig v1.3.0 h1:HK2bieeZ99X9IvOoc1ST6yrhLek0f4MqlEGES2JCP3g=
|
||||
@@ -166,10 +166,10 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2/v2 v2.8.4 h1:yfDDXTn4oxyLDC10/ieOBNDCCdIVWS9WbT6ddVXsPKI=
|
||||
github.com/dlclark/regexp2/v2 v2.8.4/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
|
||||
github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8=
|
||||
github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2/v2 v2.8.3 h1:4iBHVtmyAsQOOthVo+0v0RxktjLPDOTKx9Nuhf+PpD0=
|
||||
github.com/dlclark/regexp2/v2 v2.8.3/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
@@ -194,15 +194,15 @@ github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx5
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/fxamacker/cbor/v2 v2.9.4 h1:xwjVlxEMR3S605oUlgBjKLTTeGFciYPGYCtF/35LKGo=
|
||||
github.com/fxamacker/cbor/v2 v2.9.4/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/getkin/kin-openapi v0.150.0 h1:7+O1HMsrAB4BevQDElbLAeCAWwyJC/RjQJYw4oosUUU=
|
||||
github.com/getkin/kin-openapi v0.150.0/go.mod h1:1+BHDzstro+P5CKtPy1X4PfofnFgmRe6uvMy9+r9fKY=
|
||||
github.com/getkin/kin-openapi v0.149.0 h1:ZbhmVJ4yq5RZDUsyP8lcBcGMsjsaTqXEFt6isdtMDfA=
|
||||
github.com/getkin/kin-openapi v0.149.0/go.mod h1:1+BHDzstro+P5CKtPy1X4PfofnFgmRe6uvMy9+r9fKY=
|
||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
|
||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8/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.4.0 h1:+ufzvFTGKyin26Or5DLrlIDq4ygIDBVcj7oVNeeaYFE=
|
||||
github.com/go-chi/chi/v5 v5.4.0/go.mod h1:d1IG7GtKPSjPyBP365PH8sAGVHk3qfB/IhjFNRH1wWM=
|
||||
github.com/go-chi/chi/v5 v5.3.2 h1:5YQkICvTCSZ25hoRsyJazN0scjzKGiu4VAUc7H1o1nY=
|
||||
github.com/go-chi/chi/v5 v5.3.2/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
|
||||
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
||||
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-co-op/gocron/v2 v2.22.0 h1:uEuH2F7k7VoESb1BYSaffuuV+T0kkpzsC0aXk7/z79I=
|
||||
@@ -213,8 +213,8 @@ github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCc
|
||||
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-ldap/ldap/v3 v3.4.15 h1:eRB4f4Ecn1POL1mO86+uUDlecmyWsdiJwh60bAUS4UY=
|
||||
github.com/go-ldap/ldap/v3 v3.4.15/go.mod h1:jeD0c98EdZQCKKKKe4kTWs5aCf6y+j8KuYsYLznZSQk=
|
||||
github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs=
|
||||
github.com/go-ldap/ldap/v3 v3.4.14/go.mod h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA=
|
||||
github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4=
|
||||
github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY=
|
||||
github.com/go-openapi/swag/jsonname v0.26.1 h1:VReupaV6WxlAsCn0e4DUfgV6bPmINnPpyJDLqSfNPcE=
|
||||
@@ -354,8 +354,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.20.3 h1:zWNoFqq/lSuYQRim3dUXlXeA0IqwuZGicdG5ZTfY5j4=
|
||||
github.com/klauspost/compress v1.20.3/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ=
|
||||
github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||
@@ -413,8 +413,8 @@ github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI
|
||||
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/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.4.1 h1:PtU/zIOfQRWZ+QydXAZysc+xZYHdG+BBR4Z6TUvy73w=
|
||||
github.com/minio/minio-go/v7 v7.4.1/go.mod h1:nQnQi7zTfXR4lREtYXZQWqo5aw0Rqv+ZqCGrTORygRk=
|
||||
github.com/minio/minio-go/v7 v7.3.0 h1:HM4pFCSQq/TK+j0/zmorSh5ddh81iDgRgU0BG0Vz/YU=
|
||||
github.com/minio/minio-go/v7 v7.3.0/go.mod h1:KUPWdecEO1LWyUz+sTGXAuf2jZHrPh5fCsRH86QbPfk=
|
||||
github.com/minio/minlz v1.1.1 h1:OGmft1V6AnI/Wme332U6bhG54nxEan+VFgkD7lat4KM=
|
||||
github.com/minio/minlz v1.1.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -479,14 +479,14 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
||||
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v1.25.0 h1:cDtW1TcFsMi1RJc8khwCKB4VhzwV04aKqbYtNOXuiK0=
|
||||
github.com/prometheus/client_golang v1.25.0/go.mod h1:j2X/4/kaQHLPnmyEU8zjmkndo1T317VpCIQEml8LTKk=
|
||||
github.com/prometheus/client_model v0.6.3 h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo=
|
||||
github.com/prometheus/client_model v0.6.3/go.mod h1:gpN5P9S7Rr6Yr92PiQ+Ixvhf6JZEkF1dnxsYL2aPBEM=
|
||||
github.com/prometheus/common v0.72.0 h1:tAYsE+sPJxIncDAobm4H5aQjmox9ZxEIIqPbiffa8G4=
|
||||
github.com/prometheus/common v0.72.0/go.mod h1:77NWqAQ2tXT7BIK40qjJdw5Acrsrg1TlHAnsQi3i6mk=
|
||||
github.com/prometheus/procfs v0.22.0 h1:6q9+/JL9IKAPbCmBrv9n5O5Ty3NKnciV5X7YGw0oics=
|
||||
github.com/prometheus/procfs v0.22.0/go.mod h1:CvmFr/GVhIjIvWJZW3tgkODBQMRIf0EyWMQLHCHab58=
|
||||
github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU=
|
||||
github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY=
|
||||
github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc=
|
||||
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
|
||||
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
|
||||
github.com/quasoft/websspi v1.1.2 h1:/mA4w0LxWlE3novvsoEL6BBA1WnjJATbjkh1kFrTidw=
|
||||
github.com/quasoft/websspi v1.1.2/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
|
||||
github.com/redis/go-redis/v9 v9.23.0 h1:/TTVdJa8BRNsybwwGGnmiYktHNlUXtbOQxlWpHBPL3s=
|
||||
@@ -499,8 +499,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g=
|
||||
github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/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=
|
||||
@@ -513,6 +513,8 @@ github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo=
|
||||
github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q=
|
||||
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
@@ -575,8 +577,8 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.19.0 h1:Q2odjMtdRLG1HEAlx8mv3A7QoLT6hW7tdttTXLX9Mw0=
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.19.0/go.mod h1:UQl7jIdE6nbYNDtc4XWXeiam0V4MJx459e637POlpnQ=
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.16.0 h1:LyxbaO5Pep+gREHptHoYDmYPs4dw9EzGTx6B29+kB7I=
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.16.0/go.mod h1:975X4id//8WM4sQyh9kqODM3fHPHdIRb4Uag2+3GAo8=
|
||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||
go.uber.org/atomic v1.12.0 h1:BvcXdFKuviU4fTL/f+SxdQ5qJX/Jix8pAkgdUcb3XOE=
|
||||
@@ -604,16 +606,16 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54=
|
||||
golang.org/x/crypto v0.58.0/go.mod h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w=
|
||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
|
||||
golang.org/x/image v0.47.0 h1:za/8gVz++PPZqmZJBNSISICN5Rrj5zixP/8K++fgmqo=
|
||||
golang.org/x/image v0.47.0/go.mod h1:IkN5o4A7vq55vXN8FWjjnhg9Rs2GOtffqsSi9mWAKYE=
|
||||
golang.org/x/image v0.46.0 h1:b1+oYj0Jbp6K5MDT4i4/eZpYlk3V8SJhhDKh6LBHAyQ=
|
||||
golang.org/x/image v0.46.0/go.mod h1:3B3W05VGVQyuXucLINLjXKrqISASfi4Xj+iCVkLMwew=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg=
|
||||
golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs=
|
||||
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
|
||||
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -622,8 +624,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw=
|
||||
golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
|
||||
golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU=
|
||||
golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
|
||||
golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98=
|
||||
golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -655,23 +657,23 @@ golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM=
|
||||
golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY=
|
||||
golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE=
|
||||
golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
|
||||
golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
|
||||
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.51.0 h1:k4Xc/1Om9jwkBJBo4NVLMSARBoWtK10mx+W5BnXCeAI=
|
||||
golang.org/x/tools v0.51.0/go.mod h1:9eEncMayCV6zRMGhR5eZEC2iBx98qWcF1HZ9Z7wJOoA=
|
||||
golang.org/x/tools v0.50.0 h1:c2ifzfcuY7L90lZ2aKd8S4K2NpASF08SZx9ZuJkHmSU=
|
||||
golang.org/x/tools v0.50.0/go.mod h1:7ulVMw3831Mwi5EZD6RomGyffr4VFjuNYXf2BbCEAV0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -439,6 +439,7 @@ func prepareMigrationTasks() []*migration {
|
||||
newMigration(360, "Move branch protection settings to JSON", v29.MoveBranchProtectionConfigToJSON),
|
||||
newMigration(361, "Drop legacy commit_id column from comment", v29.DropCommentCommitID),
|
||||
newMigration(362, "Add reusable_workflow_ref column to action_run_job", v29.AddReusableWorkflowRefToActionRunJob),
|
||||
newMigration(363, "Add release_id to reaction table", v29.AddReleaseIDToReaction),
|
||||
}
|
||||
return preparedMigrations
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
"gitea.dev/modules/timeutil"
|
||||
)
|
||||
|
||||
func AddReleaseIDToReaction(_ctx context.Context, x base.EngineMigration) error {
|
||||
// rows older than v123 can hold NULL, which the unique index doesn't treat as a duplicate of ''
|
||||
if _, err := x.Exec("UPDATE reaction SET original_author = '' WHERE original_author IS NULL"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type Reaction struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
||||
UserID int64 `xorm:"UNIQUE(s) NOT NULL"` // first unique column implies INDEX
|
||||
IssueID int64 `xorm:"UNIQUE(s) INDEX(issue_comment) NOT NULL"`
|
||||
CommentID int64 `xorm:"UNIQUE(s) INDEX(issue_comment) INDEX NOT NULL"`
|
||||
ReleaseID int64 `xorm:"UNIQUE(s) INDEX NOT NULL DEFAULT(0)"`
|
||||
OriginalAuthorID int64 `xorm:"UNIQUE(s) NOT NULL DEFAULT(0)"`
|
||||
OriginalAuthor string `xorm:"UNIQUE(s) NOT NULL DEFAULT('')"`
|
||||
Type string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
}
|
||||
return x.Sync(new(Reaction))
|
||||
}
|
||||
@@ -725,10 +725,7 @@ func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository
|
||||
if c.Reactions != nil {
|
||||
return nil
|
||||
}
|
||||
c.Reactions, _, err = FindReactions(ctx, FindReactionsOptions{
|
||||
IssueID: c.IssueID,
|
||||
CommentID: c.ID,
|
||||
})
|
||||
c.Reactions, _, err = FindReactions(ctx, FindReactionsOptions{CommentID: c.ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1210,7 +1207,7 @@ func DeleteComment(ctx context.Context, comment *Comment) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return DeleteReaction(ctx, &ReactionOptions{CommentID: comment.ID})
|
||||
return DeleteCommentAllReactions(ctx, comment.ID)
|
||||
}
|
||||
|
||||
// UpdateCommentsMigrationsByType updates comments' migrations information via given git service type and original id and poster id
|
||||
|
||||
@@ -225,13 +225,11 @@ func (issue *Issue) loadCommentsByType(ctx context.Context, tp CommentType) (err
|
||||
return err
|
||||
}
|
||||
|
||||
func (issue *Issue) loadReactions(ctx context.Context) (err error) {
|
||||
func (issue *Issue) loadAllReactions(ctx context.Context) (err error) {
|
||||
if issue.Reactions != nil {
|
||||
return nil
|
||||
}
|
||||
reactions, _, err := FindReactions(ctx, FindReactionsOptions{
|
||||
IssueID: issue.ID,
|
||||
})
|
||||
reactions, _, err := FindReactions(ctx, FindReactionsOptions{IssueIDWithComments: issue.ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -340,7 +338,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
return issue.loadReactions(ctx)
|
||||
return issue.loadAllReactions(ctx)
|
||||
}
|
||||
|
||||
// IsPinned returns if a Issue is pinned
|
||||
|
||||
+84
-110
@@ -58,17 +58,20 @@ func (err ErrReactionAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// Reaction represents a reactions on issues and comments.
|
||||
// Reaction represents a reaction on an issue, comment or release.
|
||||
type Reaction struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type string `xorm:"INDEX UNIQUE(s) NOT NULL"`
|
||||
IssueID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
|
||||
CommentID int64 `xorm:"INDEX UNIQUE(s)"`
|
||||
UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
|
||||
OriginalAuthorID int64 `xorm:"INDEX UNIQUE(s) NOT NULL DEFAULT(0)"`
|
||||
OriginalAuthor string `xorm:"INDEX UNIQUE(s)"`
|
||||
User *user_model.User `xorm:"-"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
||||
UserID int64 `xorm:"UNIQUE(s) NOT NULL"` // first unique column implies INDEX
|
||||
IssueID int64 `xorm:"UNIQUE(s) INDEX(issue_comment) NOT NULL"`
|
||||
CommentID int64 `xorm:"UNIQUE(s) INDEX(issue_comment) INDEX NOT NULL"`
|
||||
ReleaseID int64 `xorm:"UNIQUE(s) INDEX NOT NULL DEFAULT(0)"`
|
||||
OriginalAuthorID int64 `xorm:"UNIQUE(s) NOT NULL DEFAULT(0)"`
|
||||
OriginalAuthor string `xorm:"UNIQUE(s) NOT NULL DEFAULT('')"`
|
||||
Type string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
|
||||
User *user_model.User `xorm:"-"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
}
|
||||
|
||||
// LoadUser load user of reaction
|
||||
@@ -108,53 +111,48 @@ func init() {
|
||||
// FindReactionsOptions describes the conditions to Find reactions
|
||||
type FindReactionsOptions struct {
|
||||
db.ListOptions
|
||||
IssueID int64
|
||||
CommentID int64
|
||||
UserID int64
|
||||
Reaction string
|
||||
IssueIDOnly int64
|
||||
IssueIDWithComments int64
|
||||
CommentID int64
|
||||
ReleaseID int64
|
||||
UserID int64
|
||||
}
|
||||
|
||||
func (opts *FindReactionsOptions) toConds() builder.Cond {
|
||||
// If Issue ID is set add to Query
|
||||
cond := builder.NewCond()
|
||||
if opts.IssueID > 0 {
|
||||
cond = cond.And(builder.Eq{"reaction.issue_id": opts.IssueID})
|
||||
if opts.IssueIDOnly > 0 {
|
||||
cond = cond.And(builder.Eq{"reaction.issue_id": opts.IssueIDOnly}).And(builder.Expr("reaction.comment_id = 0"))
|
||||
}
|
||||
if opts.IssueIDWithComments > 0 {
|
||||
cond = cond.And(builder.Eq{"reaction.issue_id": opts.IssueIDWithComments})
|
||||
}
|
||||
// If CommentID is > 0 add to Query
|
||||
// If it is 0 Query ignore CommentID to select
|
||||
// If it is -1 it explicit search of Issue Reactions where CommentID = 0
|
||||
if opts.CommentID > 0 {
|
||||
cond = cond.And(builder.Eq{"reaction.comment_id": opts.CommentID})
|
||||
} else if opts.CommentID == -1 {
|
||||
cond = cond.And(builder.Eq{"reaction.comment_id": 0})
|
||||
}
|
||||
if opts.ReleaseID > 0 {
|
||||
cond = cond.And(builder.Eq{"reaction.release_id": opts.ReleaseID})
|
||||
}
|
||||
if opts.UserID > 0 {
|
||||
cond = cond.And(builder.Eq{
|
||||
"reaction.user_id": opts.UserID,
|
||||
"reaction.original_author_id": 0,
|
||||
})
|
||||
cond = cond.And(builder.Eq{"reaction.user_id": opts.UserID, "reaction.original_author_id": 0})
|
||||
}
|
||||
if opts.Reaction != "" {
|
||||
cond = cond.And(builder.Eq{"reaction.type": opts.Reaction})
|
||||
if !cond.IsValid() {
|
||||
setting.PanicInDevOrTesting("FindReactionsOptions.toConds: no conditions specified")
|
||||
cond = cond.And(builder.Expr("1=0"))
|
||||
}
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
// FindCommentReactions returns a ReactionList of all reactions from an comment
|
||||
func FindCommentReactions(ctx context.Context, issueID, commentID int64) (ReactionList, int64, error) {
|
||||
return FindReactions(ctx, FindReactionsOptions{
|
||||
IssueID: issueID,
|
||||
CommentID: commentID,
|
||||
})
|
||||
// FindCommentReactions returns a ReactionList of all reactions from a comment
|
||||
func FindCommentReactions(ctx context.Context, commentID int64) (ReactionList, int64, error) {
|
||||
return FindReactions(ctx, FindReactionsOptions{CommentID: commentID})
|
||||
}
|
||||
|
||||
// FindIssueReactions returns a ReactionList of all reactions from an issue
|
||||
func FindIssueReactions(ctx context.Context, issueID int64, listOptions db.ListOptions) (ReactionList, int64, error) {
|
||||
return FindReactions(ctx, FindReactionsOptions{
|
||||
ListOptions: listOptions,
|
||||
IssueID: issueID,
|
||||
CommentID: -1,
|
||||
IssueIDOnly: issueID,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -177,37 +175,14 @@ func FindReactions(ctx context.Context, opts FindReactionsOptions) (ReactionList
|
||||
return reactions, count, err
|
||||
}
|
||||
|
||||
func createReaction(ctx context.Context, opts *ReactionOptions) (*Reaction, error) {
|
||||
reaction := &Reaction{
|
||||
Type: opts.Type,
|
||||
UserID: opts.DoerID,
|
||||
IssueID: opts.IssueID,
|
||||
CommentID: opts.CommentID,
|
||||
}
|
||||
findOpts := FindReactionsOptions{
|
||||
IssueID: opts.IssueID,
|
||||
CommentID: opts.CommentID,
|
||||
Reaction: opts.Type,
|
||||
UserID: opts.DoerID,
|
||||
}
|
||||
if findOpts.CommentID == 0 {
|
||||
// explicit search of Issue Reactions where CommentID = 0
|
||||
findOpts.CommentID = -1
|
||||
}
|
||||
|
||||
existingR, _, err := FindReactions(ctx, findOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(existingR) > 0 {
|
||||
return existingR[0], ErrReactionAlreadyExist{Reaction: opts.Type}
|
||||
}
|
||||
|
||||
if err := db.Insert(ctx, reaction); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reaction, nil
|
||||
func FindReleaseReactions(ctx context.Context, releaseIDs ...int64) (ReactionList, error) {
|
||||
reactions := make(ReactionList, 0, 10)
|
||||
err := db.GetEngine(ctx).
|
||||
In("release_id", releaseIDs).
|
||||
In("`type`", setting.UI.ReleaseReactions).
|
||||
Asc("created_unix", "id").
|
||||
Find(&reactions)
|
||||
return reactions, err
|
||||
}
|
||||
|
||||
// ReactionOptions defines options for creating or deleting reactions
|
||||
@@ -216,56 +191,63 @@ type ReactionOptions struct {
|
||||
DoerID int64
|
||||
IssueID int64
|
||||
CommentID int64
|
||||
ReleaseID int64
|
||||
}
|
||||
|
||||
// CreateReaction creates reaction for issue or comment.
|
||||
func CreateReaction(ctx context.Context, opts *ReactionOptions) (*Reaction, error) {
|
||||
if !setting.UI.ReactionsLookup.Contains(opts.Type) {
|
||||
// CreateReaction creates reaction for issue, comment or release.
|
||||
func CreateReaction(ctx context.Context, allowed container.Set[string], opts *ReactionOptions) (*Reaction, error) {
|
||||
if !allowed.Contains(opts.Type) {
|
||||
return nil, ErrForbiddenIssueReaction{opts.Type}
|
||||
}
|
||||
|
||||
return db.WithTx2(ctx, func(ctx context.Context) (*Reaction, error) {
|
||||
return createReaction(ctx, opts)
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteReaction deletes reaction for issue or comment.
|
||||
func DeleteReaction(ctx context.Context, opts *ReactionOptions) error {
|
||||
reaction := &Reaction{
|
||||
Type: opts.Type,
|
||||
UserID: opts.DoerID,
|
||||
IssueID: opts.IssueID,
|
||||
CommentID: opts.CommentID,
|
||||
ReleaseID: opts.ReleaseID,
|
||||
}
|
||||
|
||||
sess := db.GetEngine(ctx).Where("original_author_id = 0")
|
||||
if opts.CommentID == -1 {
|
||||
reaction.CommentID = 0
|
||||
sess.MustCols("comment_id")
|
||||
err := db.Insert(ctx, reaction)
|
||||
if err != nil {
|
||||
if has, _ := db.GetEngine(ctx).Get(reaction); has {
|
||||
return reaction, ErrReactionAlreadyExist{Reaction: opts.Type}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return reaction, nil
|
||||
}
|
||||
|
||||
_, err := sess.Delete(reaction)
|
||||
func DeleteIssueReaction(ctx context.Context, doerID, issueID int64, content string) error {
|
||||
// here and below: the migration reactions have unrelated user_id (might be the doer?)
|
||||
// so skip the deletion if the reaction is from an external user (original_author_id != 0)
|
||||
sess := db.GetEngine(ctx).Where("issue_id = ? AND comment_id = 0 AND user_id = ? AND original_author_id = 0 AND `type` = ?", issueID, doerID, content)
|
||||
_, err := sess.Delete(&Reaction{})
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteIssueReaction deletes a reaction on issue.
|
||||
func DeleteIssueReaction(ctx context.Context, doerID, issueID int64, content string) error {
|
||||
return DeleteReaction(ctx, &ReactionOptions{
|
||||
Type: content,
|
||||
DoerID: doerID,
|
||||
IssueID: issueID,
|
||||
CommentID: -1,
|
||||
})
|
||||
func DeleteCommentReaction(ctx context.Context, doerID, issueID, commentID int64, content string) error {
|
||||
sess := db.GetEngine(ctx).Where("issue_id = ? AND comment_id = ? AND user_id = ? AND original_author_id = 0 AND `type` = ?", issueID, commentID, doerID, content)
|
||||
_, err := sess.Delete(&Reaction{})
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCommentReaction deletes a reaction on comment.
|
||||
func DeleteCommentReaction(ctx context.Context, doerID, issueID, commentID int64, content string) error {
|
||||
return DeleteReaction(ctx, &ReactionOptions{
|
||||
Type: content,
|
||||
DoerID: doerID,
|
||||
IssueID: issueID,
|
||||
CommentID: commentID,
|
||||
})
|
||||
func DeleteCommentAllReactions(ctx context.Context, commentID int64) error {
|
||||
sess := db.GetEngine(ctx).Where("comment_id = ?", commentID)
|
||||
_, err := sess.Delete(&Reaction{})
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteUserReactions(ctx context.Context, userID int64) error {
|
||||
sess := db.GetEngine(ctx).Where("user_id = ?", userID)
|
||||
_, err := sess.Delete(&Reaction{})
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteReleaseReaction(ctx context.Context, doerID, releaseID int64, content string) error {
|
||||
sess := db.GetEngine(ctx).Where("release_id = ? AND user_id = ? AND `type` = ?", releaseID, doerID, content)
|
||||
_, err := sess.Delete(&Reaction{})
|
||||
return err
|
||||
}
|
||||
|
||||
// ReactionList represents list of reactions
|
||||
@@ -302,23 +284,15 @@ func (list ReactionList) getUserIDs() []int64 {
|
||||
})
|
||||
}
|
||||
|
||||
func valuesUser(m map[int64]*user_model.User) []*user_model.User {
|
||||
values := make([]*user_model.User, 0, len(m))
|
||||
for _, v := range m {
|
||||
values = append(values, v)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// newMigrationOriginalUser creates and returns a fake user for external user
|
||||
func newMigrationOriginalUser(name string) *user_model.User {
|
||||
return &user_model.User{ID: 0, Name: name, LowerName: strings.ToLower(name)}
|
||||
}
|
||||
|
||||
// LoadUsers loads reactions' all users
|
||||
func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Repository) ([]*user_model.User, error) {
|
||||
func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Repository) (map[int64]*user_model.User, error) {
|
||||
if len(list) == 0 {
|
||||
return nil, nil
|
||||
return map[int64]*user_model.User{}, nil
|
||||
}
|
||||
|
||||
userIDs := list.getUserIDs()
|
||||
@@ -339,7 +313,7 @@ func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Reposit
|
||||
reaction.User = user_model.NewGhostUser()
|
||||
}
|
||||
}
|
||||
return valuesUser(userMaps), nil
|
||||
return userMaps, nil
|
||||
}
|
||||
|
||||
// GetFirstUsers returns first reacted user display names separated by comma
|
||||
@@ -350,7 +324,7 @@ func (list ReactionList) GetFirstUsers() string {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
buffer.WriteString(reaction.User.Name)
|
||||
buffer.WriteString(reaction.User.DisplayNameWithFullName())
|
||||
if rem--; rem == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -428,9 +428,6 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error
|
||||
|
||||
// GetCurrentReview returns the current pending review of reviewer for given issue
|
||||
func GetCurrentReview(ctx context.Context, reviewer *user_model.User, issue *Issue) (*Review, error) {
|
||||
if reviewer == nil {
|
||||
return nil, nil //nolint:nilnil // return nil when reviewer is nil
|
||||
}
|
||||
reviews, err := FindReviews(ctx, FindReviewOptions{
|
||||
Types: []ReviewType{ReviewTypePending},
|
||||
IssueID: issue.ID,
|
||||
@@ -442,9 +439,10 @@ func GetCurrentReview(ctx context.Context, reviewer *user_model.User, issue *Iss
|
||||
if len(reviews) == 0 {
|
||||
return nil, ErrReviewNotExist{}
|
||||
}
|
||||
reviews[0].Reviewer = reviewer
|
||||
reviews[0].Issue = issue
|
||||
return reviews[0], nil
|
||||
pendingReview := reviews[0]
|
||||
pendingReview.Reviewer = reviewer
|
||||
pendingReview.Issue = issue
|
||||
return pendingReview, nil
|
||||
}
|
||||
|
||||
// ReviewExists returns whether a review exists for a particular line of code in the PR
|
||||
|
||||
@@ -39,6 +39,19 @@ const (
|
||||
UpdateStyleRebase UpdateStyle = "rebase"
|
||||
)
|
||||
|
||||
// SquashMergeMessage is the source of the default squash merge commit message body, values match GitHub's squash_merge_commit_message: https://docs.github.com/en/rest/repos/repos#update-a-repository
|
||||
type SquashMergeMessage string
|
||||
|
||||
const (
|
||||
SquashMergeMessageBlank SquashMergeMessage = "BLANK"
|
||||
SquashMergeMessageCommitMessages SquashMergeMessage = "COMMIT_MESSAGES"
|
||||
SquashMergeMessagePRBody SquashMergeMessage = "PR_BODY"
|
||||
)
|
||||
|
||||
func (m SquashMergeMessage) IsValid() bool {
|
||||
return m == SquashMergeMessageBlank || m == SquashMergeMessageCommitMessages || m == SquashMergeMessagePRBody
|
||||
}
|
||||
|
||||
// UpdateDefaultBranch updates the default branch
|
||||
func UpdateDefaultBranch(ctx context.Context, repo *Repository) error {
|
||||
_, err := db.GetEngine(ctx).ID(repo.ID).Cols("default_branch").Update(repo)
|
||||
|
||||
@@ -131,6 +131,7 @@ type PullRequestsConfig struct {
|
||||
DefaultUpdateStyle UpdateStyle
|
||||
DefaultDeleteBranchAfterMerge bool
|
||||
DefaultMergeStyle MergeStyle
|
||||
DefaultSquashMergeMessage SquashMergeMessage
|
||||
DefaultAllowMaintainerEdit bool
|
||||
DefaultTargetBranch string
|
||||
}
|
||||
@@ -150,6 +151,7 @@ func DefaultPullRequestsConfig() *PullRequestsConfig {
|
||||
cfg.DefaultDeleteBranchAfterMerge = setting.Repository.PullRequest.DefaultDeleteBranchAfterMerge
|
||||
cfg.DefaultMergeStyle = MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle)
|
||||
cfg.DefaultMergeStyle = util.IfZero(cfg.DefaultMergeStyle, MergeStyleMerge)
|
||||
cfg.DefaultSquashMergeMessage = SquashMergeMessage(setting.Repository.PullRequest.DefaultSquashMergeMessage)
|
||||
return cfg
|
||||
}
|
||||
|
||||
|
||||
@@ -112,6 +112,9 @@ func IsWatchingRepo(ctx context.Context, userID, repoID int64) bool {
|
||||
}
|
||||
|
||||
func watchRepoByMode(ctx context.Context, watch *Watch, mode WatchMode) (err error) {
|
||||
if watch.UserID <= 0 {
|
||||
return nil
|
||||
}
|
||||
if watch.Mode == mode {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestGetWatchers(t *testing.T) {
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
watches, err := repo_model.GetWatchers(t.Context(), repo.ID)
|
||||
assert.NoError(t, err)
|
||||
// One watchers are inactive, thus minus 1
|
||||
// One watcher is inactive, thus minus 1
|
||||
assert.Len(t, watches, repo.NumWatches-1)
|
||||
for _, watch := range watches {
|
||||
assert.Equal(t, repo.ID, watch.RepoID)
|
||||
@@ -90,24 +90,30 @@ func TestWatchIfAuto(t *testing.T) {
|
||||
|
||||
setting.Service.AutoWatchOnChanges = true
|
||||
|
||||
// Must not add watch
|
||||
// Must not add watch (user 8 has "don't watch" set)
|
||||
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), 8, 1, true))
|
||||
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, watchers, prevCount)
|
||||
|
||||
// Should not add watch
|
||||
// Should not add watch (read operation)
|
||||
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), 12, 1, false))
|
||||
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, watchers, prevCount)
|
||||
|
||||
// Should add watch
|
||||
// Should add watch (write operation)
|
||||
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), 12, 1, true))
|
||||
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, watchers, prevCount+1)
|
||||
|
||||
// Should not add watch (system user should not be able to watch)
|
||||
assert.NoError(t, repo_model.WatchIfAuto(t.Context(), user_model.ActionsUserID, 1, true))
|
||||
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, watchers, prevCount+1)
|
||||
|
||||
// Should remove watch, inhibit from adding auto
|
||||
assert.NoError(t, repo_model.WatchRepoAuto(t.Context(), user12, repo, false))
|
||||
watchers, err = repo_model.GetRepoWatchers(t.Context(), repo.ID, db.ListOptions{Page: 1})
|
||||
|
||||
@@ -465,6 +465,7 @@ func (u *User) EmailTo() string {
|
||||
}
|
||||
|
||||
// TODO: DefaultShowFullName causes messy logic, there are already too many methods to display a user's "display name", need to refactor them
|
||||
// Search this TODO to see more details
|
||||
// * user.Name / user.FullName: directly used in templates
|
||||
// * user.DisplayName(): always show FullName if it's not empty, otherwise show Name
|
||||
// * user.GetDisplayName(): show FullName if it's not empty and DefaultShowFullName is set, otherwise show Name
|
||||
@@ -491,6 +492,14 @@ func (u *User) GetDisplayName() string {
|
||||
return u.Name
|
||||
}
|
||||
|
||||
// DisplayNameWithFullName returns the username, full name is included if it is not empty
|
||||
func (u *User) DisplayNameWithFullName() string {
|
||||
if fullName := strings.TrimSpace(u.FullName); fullName != "" {
|
||||
return fmt.Sprintf("%s (%s)", u.Name, fullName)
|
||||
}
|
||||
return u.Name
|
||||
}
|
||||
|
||||
// ShortName ellipses username to length (still used by many templates), it calls GetDisplayName and respects DEFAULT_SHOW_FULL_NAME
|
||||
func (u *User) ShortName(length int) string {
|
||||
return util.EllipsisDisplayString(u.GetDisplayName(), length)
|
||||
|
||||
@@ -295,18 +295,13 @@ func TestNewGitSig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDisplayName(t *testing.T) {
|
||||
users := make([]*user_model.User, 0, 20)
|
||||
err := db.GetEngine(t.Context()).Find(&users)
|
||||
assert.NoError(t, err)
|
||||
u := &user_model.User{Name: "name"}
|
||||
assert.Equal(t, "name", u.DisplayName())
|
||||
assert.Equal(t, "name", u.DisplayNameWithFullName())
|
||||
|
||||
for _, user := range users {
|
||||
displayName := user.DisplayName()
|
||||
assert.Equal(t, strings.TrimSpace(displayName), displayName)
|
||||
if len(strings.TrimSpace(user.FullName)) == 0 {
|
||||
assert.Equal(t, user.Name, displayName)
|
||||
}
|
||||
assert.NotEmpty(t, strings.TrimSpace(displayName))
|
||||
}
|
||||
u = &user_model.User{Name: "name", FullName: "Full Name"}
|
||||
assert.Equal(t, "Full Name", u.DisplayName())
|
||||
assert.Equal(t, "name (Full Name)", u.DisplayNameWithFullName())
|
||||
}
|
||||
|
||||
func TestCreateUserEmailAlreadyUsed(t *testing.T) {
|
||||
|
||||
+11
-2
@@ -7,19 +7,28 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/modules/git/gitcmd"
|
||||
"gitea.dev/modules/setting"
|
||||
)
|
||||
|
||||
func LineBlame(ctx context.Context, repo RepositoryFacade, revision, file string, line uint) (string, error) {
|
||||
func LineBlameCommitID(ctx context.Context, repo RepositoryFacade, revision, file string, line uint) (string, error) {
|
||||
stdout, _, err := gitcmd.NewCommand("blame").WithRepo(repo).
|
||||
AddOptionFormat("-L %d,%d", line, line).
|
||||
AddOptionValues("-p", revision).
|
||||
AddDashesAndList(file).
|
||||
RunStdString(ctx)
|
||||
return stdout, err
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
commitID, _, _ := strings.Cut(stdout, " ")
|
||||
if !IsStringValidObjectID(nil, commitID) {
|
||||
return "", fmt.Errorf("LineBlameCommitID: invalid commit ID %q", commitID)
|
||||
}
|
||||
return commitID, nil
|
||||
}
|
||||
|
||||
// BlamePart represents block of blame - continuous lines with one sha
|
||||
|
||||
@@ -11,6 +11,18 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLineBlameCommitID(t *testing.T) {
|
||||
storage := mockRepository("repo5_pulls")
|
||||
const revision = "f32b0a9dfd09a60f616f29158f772cedd89942d2"
|
||||
commitID, err := LineBlameCommitID(t.Context(), storage, revision, "README.md", 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "72866af952e98d02a73003501836074b286a78f6", commitID)
|
||||
|
||||
commitID, err = LineBlameCommitID(t.Context(), storage, revision, "missing.txt", 1)
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, commitID)
|
||||
}
|
||||
|
||||
func TestReadingBlameOutput(t *testing.T) {
|
||||
setting.AppDataPath = t.TempDir()
|
||||
ctx := t.Context()
|
||||
|
||||
+6
-14
@@ -46,21 +46,13 @@ func GetDivergingCommits(ctx context.Context, repo RepositoryFacade, baseBranch,
|
||||
// GetCommitIDsBetweenReverse returns the last commit IDs between two commits in reverse order (from old to new) with limit.
|
||||
// If the result exceeds the limit, the old commits IDs will be ignored
|
||||
func GetCommitIDsBetweenReverse(ctx context.Context, repo RepositoryFacade, startRef, endRef, notRef string, limit int) ([]string, error) {
|
||||
genCmd := func(reversions ...string) *gitcmd.Command {
|
||||
cmd := gitcmd.NewCommand("rev-list", "--reverse").
|
||||
AddArguments("-n").AddDynamicArguments(strconv.Itoa(limit)).
|
||||
AddDynamicArguments(reversions...)
|
||||
if notRef != "" { // --not should be kept as the last parameter of git command, otherwise the result will be wrong
|
||||
cmd.AddOptionValues("--not", notRef)
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
stdout, _, err := genCmd(startRef + ".." + endRef).WithRepo(repo).RunStdString(ctx)
|
||||
if gitcmd.IsStderr(err, gitcmd.StderrNoMergeBase) {
|
||||
// if the start and end are not related (no merge base), just get all commits pushed by "end ref"
|
||||
// previously it would return the results of git rev-list before last so let's try that...
|
||||
stdout, _, err = genCmd(endRef).WithRepo(repo).RunStdString(ctx)
|
||||
cmd := gitcmd.NewCommand("rev-list", "--reverse").
|
||||
AddArguments("-n").AddDynamicArguments(strconv.Itoa(limit)).
|
||||
AddDynamicArguments(startRef + ".." + endRef)
|
||||
if notRef != "" { // --not should be kept as the last parameter of git command, otherwise the result will be wrong
|
||||
cmd.AddOptionValues("--not", notRef)
|
||||
}
|
||||
stdout, _, err := cmd.WithRepo(repo).RunStdString(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+13
-17
@@ -5,6 +5,7 @@ package git
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -200,22 +201,18 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
|
||||
}
|
||||
if old {
|
||||
begin, _ = strconv.ParseInt(groups["beginOld"], 10, 64)
|
||||
end, _ = strconv.ParseInt(groups["endOld"], 10, 64)
|
||||
end, _ = strconv.ParseInt(cmp.Or(groups["endOld"], "1"), 10, 64)
|
||||
// init otherLine with begin of opposite side
|
||||
otherLine, _ = strconv.ParseInt(groups["beginNew"], 10, 64)
|
||||
} else {
|
||||
begin, _ = strconv.ParseInt(groups["beginNew"], 10, 64)
|
||||
if groups["endNew"] != "" {
|
||||
end, _ = strconv.ParseInt(groups["endNew"], 10, 64)
|
||||
} else {
|
||||
end = 0
|
||||
}
|
||||
end, _ = strconv.ParseInt(cmp.Or(groups["endNew"], "1"), 10, 64)
|
||||
// init otherLine with begin of opposite side
|
||||
otherLine, _ = strconv.ParseInt(groups["beginOld"], 10, 64)
|
||||
}
|
||||
end += begin // end is for real only the number of lines in hunk
|
||||
// lof is between begin and end
|
||||
if begin <= line && end >= line {
|
||||
if begin <= line && line < end {
|
||||
hunk = append(hunk, lof)
|
||||
currentLine = begin
|
||||
continue
|
||||
@@ -265,27 +262,26 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
|
||||
oldBegin = otherLine
|
||||
newBegin = currentLine
|
||||
}
|
||||
// headers + hunk header
|
||||
newHunk := make([]string, headerLines)
|
||||
// transfer existing headers
|
||||
copy(newHunk, hunk[:headerLines])
|
||||
// transfer last n lines
|
||||
newHunk = append(newHunk, hunk[len(hunk)-numbersOfLine-1:]...)
|
||||
// calculate newBegin, ... by counting lines
|
||||
for i := len(hunk) - 1; i >= len(hunk)-numbersOfLine; i-- {
|
||||
switch hunk[i][0] {
|
||||
start := len(hunk)
|
||||
for count := 0; count < numbersOfLine && start > headerLines+1; {
|
||||
start--
|
||||
switch hunk[start][0] {
|
||||
case '+':
|
||||
newBegin--
|
||||
newNumOfLines++
|
||||
case '-':
|
||||
oldBegin--
|
||||
oldNumOfLines++
|
||||
case '\\':
|
||||
continue
|
||||
default:
|
||||
oldBegin--
|
||||
newBegin--
|
||||
newNumOfLines++
|
||||
oldNumOfLines++
|
||||
}
|
||||
count++
|
||||
}
|
||||
|
||||
// "git diff" outputs "@@ -1 +1,3 @@" for "OLD" => "A\nB\nC"
|
||||
@@ -294,9 +290,9 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
|
||||
// For example: "L1\nL2" => "A\nB", then the patch shows "L2" as line 1 on the left (deleted part)
|
||||
|
||||
// construct the new hunk header
|
||||
newHunk[headerLines] = fmt.Sprintf("@@ -%d,%d +%d,%d @@",
|
||||
hunk[headerLines] = fmt.Sprintf("@@ -%d,%d +%d,%d @@",
|
||||
oldBegin, oldNumOfLines, newBegin, newNumOfLines)
|
||||
return strings.Join(newHunk, "\n"), nil
|
||||
return strings.Join(append(hunk[:headerLines+1], hunk[start:]...), "\n"), nil
|
||||
}
|
||||
|
||||
// GetAffectedFiles returns the affected files between two commits
|
||||
|
||||
@@ -120,10 +120,18 @@ func TestCutDiffAroundLine(t *testing.T) {
|
||||
assert.Empty(t, emptyResult)
|
||||
|
||||
// Line is out of scope
|
||||
emptyResult, err = CutDiffAroundLine(strings.NewReader(exampleDiff), 434, false, 0)
|
||||
emptyResult, err = CutDiffAroundLine(strings.NewReader(exampleDiff), 7, false, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, emptyResult)
|
||||
|
||||
noNewlineDiff := "diff --git a/a b/a\n--- a/a\n+++ b/a\n@@ -1 +1 @@\n-a\n\\ No newline at end of file\n+a"
|
||||
result, err = CutDiffAroundLine(strings.NewReader(noNewlineDiff), 1, false, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "diff --git a/a b/a\n--- a/a\n+++ b/a\n@@ -1,1 +1,1 @@\n-a\n\\ No newline at end of file\n+a", result)
|
||||
result, err = CutDiffAroundLine(strings.NewReader(noNewlineDiff), 1, true, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "diff --git a/a b/a\n--- a/a\n+++ b/a\n@@ -1 +1 @@\n-a", result)
|
||||
|
||||
// Handle minus diffs properly
|
||||
minusDiff, err := CutDiffAroundLine(strings.NewReader(breakingDiff), 2, false, 4)
|
||||
assert.NoError(t, err)
|
||||
|
||||
+7
-3
@@ -68,12 +68,12 @@ func DefaultFeatures() *Features {
|
||||
func loadGitVersionFeatures() (*Features, error) {
|
||||
stdout, _, runErr := gitcmd.NewCommand("version").RunStdString(context.Background())
|
||||
if runErr != nil {
|
||||
return nil, runErr
|
||||
return nil, fmt.Errorf(`failed to run command "git version": %w`, runErr)
|
||||
}
|
||||
|
||||
ver, err := parseGitVersionLine(strings.TrimSpace(stdout))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf(`failed to parse output of command "git version": %w`, err)
|
||||
}
|
||||
|
||||
features := &Features{gitVersion: ver}
|
||||
@@ -104,7 +104,11 @@ func parseGitVersionLine(s string) (*version.Version, error) {
|
||||
if len(versionFields) > 3 {
|
||||
versionFields = versionFields[:3]
|
||||
}
|
||||
return version.NewVersion(strings.Join(versionFields, "."))
|
||||
ver, err := version.NewVersion(strings.Join(versionFields, "."))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid git version: %q: %w", s, err)
|
||||
}
|
||||
return ver, nil
|
||||
}
|
||||
|
||||
func checkGitVersionCompatibility(gitVer *version.Version) error {
|
||||
|
||||
@@ -93,15 +93,12 @@ const (
|
||||
StderrPathSpec StderrPrefix = "fatal: pathspec"
|
||||
StderrBadRevision StderrPrefix = "fatal: bad revision"
|
||||
StderrNoSuchPath StderrPrefix = "fatal: no such path"
|
||||
|
||||
StderrNoSuchRemote StderrPrefix = "error: no such remote"
|
||||
StderrBadObject StderrPrefix = "fatal: bad object "
|
||||
|
||||
StderrAuthenticationFailed StderrPrefix = "fatal: Authentication failed for"
|
||||
StderrCouldNotReadUsername StderrPrefix = "fatal: could not read Username"
|
||||
|
||||
StderrUnknownRevisionOrPath StderrRegexp = "^fatal: .*: unknown revision or path not in the working tree"
|
||||
StderrNoMergeBase StderrRegexp = "^fatal: .*: no merge base"
|
||||
StderrFileNoEnoughLines StderrRegexp = `^fatal: file .* has only \d+ lines?`
|
||||
StderrFileNoEnoughLines StderrRegexp = `^fatal: file .* has only \d+ lines?`
|
||||
)
|
||||
|
||||
func matchStderrCheck(stderr string, checkIntf StderrCheck) (match bool) {
|
||||
|
||||
@@ -14,8 +14,6 @@ func TestIsStderr(t *testing.T) {
|
||||
check StderrCheck
|
||||
stderr string
|
||||
}{
|
||||
{StderrUnknownRevisionOrPath, "fatal: ambiguous argument 'origin': unknown revision or path not in the working tree...."},
|
||||
{StderrNoMergeBase, "fatal: origin/main..HEAD: no merge base...."},
|
||||
{StderrFileNoEnoughLines, "fatal: file foo/bar has only 1 line"},
|
||||
{StderrAuthenticationFailed, "Cloning into 'repo'...\r\nremote: Invalid username or token.\r\nfatal: Authentication failed for 'https://host/repo.git/'\r\n"},
|
||||
}
|
||||
|
||||
@@ -64,9 +64,9 @@ func (err *ErrInvalidCloneAddr) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// IsRemoteNotExistError checks the prefix of the error message to see whether a remote does not exist.
|
||||
// IsRemoteNotExistError checks whether a "git remote" subcommand failed because the remote does not exist
|
||||
func IsRemoteNotExistError(err error) bool {
|
||||
return gitcmd.IsStderr(err, gitcmd.StderrNoSuchRemote)
|
||||
return gitcmd.IsErrorExitCode(err, 2)
|
||||
}
|
||||
|
||||
// ParseRemoteAddr checks if given remote address is valid,
|
||||
|
||||
@@ -317,28 +317,19 @@ func (repo *Repository) CommitsByFileAndRange(ctx context.Context, opts CommitsB
|
||||
// CommitsBetween returns a list that contains commits between [after, before). After is the first item in the slice.
|
||||
// If "before" and "after" are not related, it returns the all commits for the "after" commit.
|
||||
func (repo *Repository) CommitsBetween(ctx context.Context, afterRef, beforeRef RefName, limit int, optSkip ...int) ([]*Commit, error) {
|
||||
gitCmd := func() *gitcmd.Command {
|
||||
cmd := gitcmd.NewCommand("rev-list").WithRepo(repo)
|
||||
if limit >= 0 {
|
||||
cmd.AddOptionValues("--max-count", strconv.Itoa(limit))
|
||||
}
|
||||
if len(optSkip) > 0 {
|
||||
cmd.AddOptionValues("--skip", strconv.Itoa(optSkip[0]))
|
||||
}
|
||||
return cmd
|
||||
cmd := gitcmd.NewCommand("rev-list").WithRepo(repo)
|
||||
if limit >= 0 {
|
||||
cmd.AddOptionValues("--max-count", strconv.Itoa(limit))
|
||||
}
|
||||
if len(optSkip) > 0 {
|
||||
cmd.AddOptionValues("--skip", strconv.Itoa(optSkip[0]))
|
||||
}
|
||||
var stdout []byte
|
||||
var err error
|
||||
if beforeRef == "" {
|
||||
stdout, _, err = gitCmd().AddDynamicArguments(afterRef.String()).RunStdBytes(ctx)
|
||||
cmd.AddDynamicArguments(afterRef.String())
|
||||
} else {
|
||||
stdout, _, err = gitCmd().AddDynamicArguments(beforeRef.String() + ".." + afterRef.String()).RunStdBytes(ctx)
|
||||
if gitcmd.IsStderr(err, gitcmd.StderrNoMergeBase) {
|
||||
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
|
||||
// if the beforeRef and afterRef are not related (no merge base), just get all commits pushed by afterRef
|
||||
stdout, _, err = gitCmd().AddDynamicArguments(afterRef.String()).RunStdBytes(ctx)
|
||||
}
|
||||
cmd.AddDynamicArguments(beforeRef.String() + ".." + afterRef.String())
|
||||
}
|
||||
stdout, _, err := cmd.RunStdBytes(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -46,11 +46,6 @@ func (repo *Repository) GetDiffNumChangedFiles(ctx context.Context, base, head s
|
||||
WithRepo(repo).
|
||||
WithStdoutCopy(w).
|
||||
RunWithStderr(ctx); err != nil {
|
||||
if gitcmd.IsStderr(err, gitcmd.StderrNoMergeBase) {
|
||||
// git >= 2.28 now returns an error if base and head have become unrelated.
|
||||
// it doesn't make sense to count the changed files in this case because UI won't display such diff
|
||||
return 0, nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
return w.numLines, nil
|
||||
|
||||
+10
-24
@@ -83,7 +83,7 @@ type Tree struct {
|
||||
}
|
||||
|
||||
// ListEntries returns all entries of current tree.
|
||||
func (t *Tree) ListEntries(ctx context.Context, gitRepo *Repository) (Entries, error) {
|
||||
func (t *Tree) ListEntries(_ctx context.Context, gitRepo *Repository) (Entries, error) {
|
||||
if t.entriesParsed {
|
||||
return t.entries, nil
|
||||
}
|
||||
@@ -109,36 +109,22 @@ func (t *Tree) ListEntries(ctx context.Context, gitRepo *Repository) (Entries, e
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if info.Type == "tree" {
|
||||
t.entries, err = catBatchParseTreeEntries(t.ID.Type(), t, rd, info.Size)
|
||||
if info.Type != "tree" { // resolve annotated tags to their tree, a blob yields ErrNotExist
|
||||
if err := DiscardFull(rd, info.Size+1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info, rd, err = batch.QueryContent(t.ID.String() + "^{tree}")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.entriesParsed = true
|
||||
return t.entries, nil
|
||||
}
|
||||
|
||||
// Not a tree just use ls-tree instead
|
||||
if err := DiscardFull(rd, info.Size+1); err != nil {
|
||||
t.entries, err = catBatchParseTreeEntries(t.ID.Type(), t, rd, info.Size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-l").AddDynamicArguments(t.ID.String()).WithRepo(gitRepo).RunStdBytes(ctx)
|
||||
if runErr != nil {
|
||||
if gitcmd.IsStderr(runErr, gitcmd.StderrNotValidObjectName) || gitcmd.IsStderr(runErr, gitcmd.StderrNotTreeObject) {
|
||||
return nil, ErrNotExist{
|
||||
ID: t.ID.String(),
|
||||
}
|
||||
}
|
||||
return nil, runErr
|
||||
}
|
||||
|
||||
t.entries, err = parseTreeEntries(stdout, t)
|
||||
if err == nil {
|
||||
t.entriesParsed = true
|
||||
}
|
||||
|
||||
return t.entries, err
|
||||
t.entriesParsed = true
|
||||
return t.entries, nil
|
||||
}
|
||||
|
||||
// listEntriesRecursive returns all entries of current tree recursively including all subtrees
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSubTree_Issue29101(t *testing.T) {
|
||||
@@ -26,6 +27,24 @@ func TestSubTree_Issue29101(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeListEntriesResolvesTagAndRejectsBlob(t *testing.T) {
|
||||
repo, err := OpenRepositoryLocal(t.Context(), filepath.Join(testReposDir, "repo1_bare"))
|
||||
require.NoError(t, err)
|
||||
defer repo.Close()
|
||||
|
||||
entries, err := newTree(MustIDFromString("3ad28a9149a2864384548f3d17ed7f38014c9e8a")).ListEntries(t.Context(), repo)
|
||||
require.NoError(t, err)
|
||||
names := make([]string, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
names = append(names, entry.Name())
|
||||
}
|
||||
assert.Equal(t, []string{"file1.txt", "file2.txt", "foo"}, names)
|
||||
assert.True(t, entries[2].IsDir())
|
||||
|
||||
_, err = newTree(MustIDFromString("e2129701f1a4d54dc44f03c93bca0a2aec7c5449")).ListEntries(t.Context(), repo)
|
||||
assert.True(t, IsErrNotExist(err))
|
||||
}
|
||||
|
||||
func Test_GetTreePathLatestCommit(t *testing.T) {
|
||||
repo, err := OpenRepositoryLocal(t.Context(), filepath.Join(testReposDir, "repo6_blame"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/packages"
|
||||
@@ -24,6 +26,8 @@ var (
|
||||
ErrInvalidName = util.NewInvalidArgumentErrorf("package name is invalid")
|
||||
// ErrInvalidVersion indicates an invalid version in the metadata.gz file
|
||||
ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
|
||||
// ErrInvalidMetadata indicates metadata that would corrupt the generated index
|
||||
ErrInvalidMetadata = util.NewInvalidArgumentErrorf("package metadata is invalid")
|
||||
)
|
||||
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
@@ -221,6 +225,19 @@ func parseMetadataFile(r io.Reader) (*Package, error) {
|
||||
}
|
||||
}
|
||||
|
||||
values := []string{m.Platform}
|
||||
reqs := slices.Concat(m.RequiredRubyVersion, m.RequiredRubygemsVersion)
|
||||
for _, dep := range slices.Concat(m.RuntimeDependencies, m.DevelopmentDependencies) {
|
||||
values = append(values, dep.Name)
|
||||
reqs = append(reqs, dep.Version...)
|
||||
}
|
||||
for _, req := range reqs {
|
||||
values = append(values, req.Restriction, req.Version)
|
||||
}
|
||||
if slices.ContainsFunc(values, func(s string) bool { return strings.ContainsAny(s, "\r\n") }) {
|
||||
return nil, ErrInvalidMetadata // the compact index is line-based, a line break would forge entries
|
||||
}
|
||||
|
||||
return &Package{
|
||||
Name: spec.Name,
|
||||
Version: spec.Version.Version,
|
||||
|
||||
@@ -43,6 +43,19 @@ version:
|
||||
assert.Nil(t, rp)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("LineBreakInIndexedMetadata", func(t *testing.T) {
|
||||
for _, field := range []string{
|
||||
`platform: "ruby\nx"`,
|
||||
`dependencies: [{name: "d\nx"}]`,
|
||||
`dependencies: [{name: d, requirement: {requirements: [[">=", {version: "1\nx"}]]}}]`,
|
||||
`required_ruby_version: {requirements: [[">=", {version: "1\nx"}]]}`,
|
||||
} {
|
||||
rp, err := parseMetadataFile(test.CompressGzip("name: g\nversion:\n version: 1\n" + field + "\n"))
|
||||
assert.ErrorIs(t, err, ErrInvalidMetadata, field)
|
||||
assert.Nil(t, rp)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseMetadataFile(t *testing.T) {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
type deprecatedKey struct {
|
||||
oldSection, oldKey, newSection, newKey string
|
||||
convert func(old ConfigKey) string
|
||||
}
|
||||
|
||||
func invertBool(old ConfigKey) string {
|
||||
return strconv.FormatBool(!old.MustBool())
|
||||
}
|
||||
|
||||
// keep rows, users upgrading from older versions still need them
|
||||
var deprecatedKeys = []deprecatedKey{
|
||||
{"server", "LFS_CONTENT_PATH", "lfs", "PATH", nil},
|
||||
{"server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME", nil},
|
||||
{"server", "LETSENCRYPT_ACCEPTTOS", "server", "ACME_ACCEPTTOS", nil},
|
||||
{"server", "LETSENCRYPT_DIRECTORY", "server", "ACME_DIRECTORY", nil},
|
||||
{"server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL", nil},
|
||||
{"mailer", "MAILER_TYPE", "mailer", "PROTOCOL", func(old ConfigKey) string { return old.In("", []string{"sendmail"}) }},
|
||||
{"mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL", func(old ConfigKey) string { return util.Iif(old.MustBool(), "smtps", "smtp+starttls") }},
|
||||
{"mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO", invertBool},
|
||||
{"mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT", nil},
|
||||
{"mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT", nil},
|
||||
{"mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE", nil},
|
||||
{"mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE", nil},
|
||||
{"mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT", invertBool},
|
||||
{"repository", "DISABLE_MIRRORS", "mirror", "DISABLE_NEW_PULL", nil},
|
||||
{"git.reflog", "ENABLED", "git.config", "core.logAllRefUpdates", func(old ConfigKey) string { return old.In("true", []string{"true", "false"}) }},
|
||||
{"git.reflog", "EXPIRATION", "git.config", "gc.reflogExpire", func(old ConfigKey) string { return old.String() + ".days" }},
|
||||
{"oauth2", "ENABLE", "oauth2", "ENABLED", nil},
|
||||
{"cors", "X_FRAME_OPTIONS", "security", "X_FRAME_OPTIONS", nil},
|
||||
{"repository.pull-request", "POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES", "repository.pull-request", "DEFAULT_SQUASH_MERGE_MESSAGE", func(old ConfigKey) string { return util.Iif(old.MustBool(), "COMMIT_MESSAGES", "PR_BODY") }},
|
||||
}
|
||||
|
||||
// applyDeprecatedKeys copies each deprecated key of the table to its replacement unless the replacement is present
|
||||
func applyDeprecatedKeys(rootCfg ConfigProvider) {
|
||||
for _, deprecated := range deprecatedKeys {
|
||||
oldSec, _ := rootCfg.GetSection(deprecated.oldSection)
|
||||
if oldSec == nil || !oldSec.HasKey(deprecated.oldKey) {
|
||||
continue
|
||||
}
|
||||
deprecatedSetting(rootCfg, deprecated.oldSection, deprecated.oldKey, deprecated.newSection, deprecated.newKey)
|
||||
if newSec, _ := rootCfg.GetSection(deprecated.newSection); newSec != nil && newSec.HasKey(deprecated.newKey) {
|
||||
continue
|
||||
}
|
||||
oldKey := oldSec.Key(deprecated.oldKey)
|
||||
value := oldKey.String()
|
||||
if deprecated.convert != nil {
|
||||
value = deprecated.convert(oldKey)
|
||||
}
|
||||
if value != "" {
|
||||
_, _ = rootCfg.Section(deprecated.newSection).NewKey(deprecated.newKey, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,9 +392,9 @@ func LogStartupProblem(skip int, level log.Level, format string, args ...any) {
|
||||
StartupProblems = append(StartupProblems, msg)
|
||||
}
|
||||
|
||||
func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
|
||||
func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey string) {
|
||||
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` present, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
|
||||
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` present, please use `[%s].%s` instead", oldSection, oldKey, newSection, newKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,16 +88,6 @@ func loadGitFrom(rootCfg ConfigProvider) {
|
||||
GitConfig.SetOption("diff.algorithm", "histogram")
|
||||
GitConfig.SetOption("core.logAllRefUpdates", "true")
|
||||
|
||||
secGitReflog := rootCfg.Section("git.reflog")
|
||||
if secGitReflog.HasKey("ENABLED") {
|
||||
deprecatedSetting(rootCfg, "git.reflog", "ENABLED", "git.config", "core.logAllRefUpdates", "1.21")
|
||||
GitConfig.SetOption("core.logAllRefUpdates", secGitReflog.Key("ENABLED").In("true", []string{"true", "false"}))
|
||||
}
|
||||
if secGitReflog.HasKey("EXPIRATION") {
|
||||
deprecatedSetting(rootCfg, "git.reflog", "EXPIRATION", "git.config", "gc.reflogExpire", "1.21")
|
||||
GitConfig.SetOption("gc.reflogExpire", secGitReflog.Key("EXPIRATION").String()+".days")
|
||||
}
|
||||
|
||||
for _, key := range secGitConfig.Keys() {
|
||||
GitConfig.SetOption(key.Name(), key.String())
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ ENABLED = false
|
||||
EXPIRATION = 123
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
applyDeprecatedKeys(cfg)
|
||||
loadGitFrom(cfg)
|
||||
|
||||
assert.Equal(t, "false", GitConfig.GetOption("core.logAllRefUpdates"))
|
||||
|
||||
@@ -40,18 +40,6 @@ func loadLFSFrom(rootCfg ConfigProvider) error {
|
||||
|
||||
lfsSec, _ := rootCfg.GetSection("lfs")
|
||||
|
||||
// Specifically default PATH to LFS_CONTENT_PATH
|
||||
// DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
|
||||
// if these are removed, the warning will not be shown
|
||||
deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH", "v1.19.0")
|
||||
|
||||
if val := sec.Key("LFS_CONTENT_PATH").String(); val != "" {
|
||||
if lfsSec == nil {
|
||||
lfsSec = rootCfg.Section("lfs")
|
||||
}
|
||||
lfsSec.Key("PATH").MustString(val) // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
|
||||
var err error
|
||||
LFS.Storage, err = getStorage(rootCfg, "lfs", "", lfsSec)
|
||||
if err != nil {
|
||||
|
||||
@@ -29,6 +29,7 @@ PATH = path_used
|
||||
`
|
||||
cfg, err = NewConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
applyDeprecatedKeys(cfg)
|
||||
assert.NoError(t, loadLFSFrom(cfg))
|
||||
|
||||
assert.EqualValues(t, "local", LFS.Storage.Type)
|
||||
@@ -40,6 +41,7 @@ LFS_CONTENT_PATH = deprecatedpath
|
||||
`
|
||||
cfg, err = NewConfigProviderFromData(iniStr)
|
||||
assert.NoError(t, err)
|
||||
applyDeprecatedKeys(cfg)
|
||||
assert.NoError(t, loadLFSFrom(cfg))
|
||||
|
||||
assert.EqualValues(t, "local", LFS.Storage.Type)
|
||||
|
||||
@@ -59,8 +59,8 @@ func prepareLoggerConfig(rootCfg ConfigProvider) {
|
||||
sec.Key("logger.default.MODE").MustString(",") // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "log", "ACCESS", "log", "logger.access.MODE", "1.21")
|
||||
deprecatedSetting(rootCfg, "log", "ENABLE_ACCESS_LOG", "log", "logger.access.MODE", "1.21")
|
||||
deprecatedSetting(rootCfg, "log", "ACCESS", "log", "logger.access.MODE")
|
||||
deprecatedSetting(rootCfg, "log", "ENABLE_ACCESS_LOG", "log", "logger.access.MODE")
|
||||
if val := sec.Key("ACCESS").String(); val != "" {
|
||||
sec.Key("logger.access.MODE").MustString(val) // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
@@ -68,8 +68,8 @@ func prepareLoggerConfig(rootCfg ConfigProvider) {
|
||||
sec.Key("logger.access.MODE").SetValue("")
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "log", "ROUTER", "log", "logger.router.MODE", "1.21")
|
||||
deprecatedSetting(rootCfg, "log", "DISABLE_ROUTER_LOG", "log", "logger.router.MODE", "1.21")
|
||||
deprecatedSetting(rootCfg, "log", "ROUTER", "log", "logger.router.MODE")
|
||||
deprecatedSetting(rootCfg, "log", "DISABLE_ROUTER_LOG", "log", "logger.router.MODE")
|
||||
if val := sec.Key("ROUTER").String(); val != "" {
|
||||
sec.Key("logger.router.MODE").MustString(val) // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
@@ -81,8 +81,8 @@ func prepareLoggerConfig(rootCfg ConfigProvider) {
|
||||
sec.Key("logger.router.MODE").SetValue("")
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "log", "XORM", "log", "logger.xorm.MODE", "1.21")
|
||||
deprecatedSetting(rootCfg, "log", "ENABLE_XORM_LOG", "log", "logger.xorm.MODE", "1.21")
|
||||
deprecatedSetting(rootCfg, "log", "XORM", "log", "logger.xorm.MODE")
|
||||
deprecatedSetting(rootCfg, "log", "ENABLE_XORM_LOG", "log", "logger.xorm.MODE")
|
||||
if val := sec.Key("XORM").String(); val != "" {
|
||||
sec.Key("logger.xorm.MODE").MustString(val) // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
|
||||
@@ -76,14 +76,7 @@ func loadMailerFrom(rootCfg ConfigProvider) {
|
||||
// Handle Deprecations and map on to new configuration
|
||||
// DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
|
||||
// if these are removed, the warning will not be shown
|
||||
deprecatedSetting(rootCfg, "mailer", "MAILER_TYPE", "mailer", "PROTOCOL", "v1.19.0")
|
||||
if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") {
|
||||
if sec.Key("MAILER_TYPE").String() == "sendmail" {
|
||||
sec.Key("PROTOCOL").MustString("sendmail") // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "mailer", "HOST", "mailer", "SMTP_ADDR", "v1.19.0")
|
||||
deprecatedSetting(rootCfg, "mailer", "HOST", "mailer", "SMTP_ADDR")
|
||||
if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") {
|
||||
givenHost := sec.Key("HOST").String()
|
||||
addr, port, err := net.SplitHostPort(givenHost)
|
||||
@@ -99,47 +92,8 @@ func loadMailerFrom(rootCfg ConfigProvider) {
|
||||
sec.Key("SMTP_PORT").MustString(port) // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL", "v1.19.0")
|
||||
if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") {
|
||||
if sec.Key("IS_TLS_ENABLED").MustBool() {
|
||||
sec.Key("PROTOCOL").MustString("smtps") // FIXME: INI-MUST-SIDE-EFFECT
|
||||
} else {
|
||||
sec.Key("PROTOCOL").MustString("smtp+starttls") // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO", "v1.19.0")
|
||||
if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") {
|
||||
sec.Key("ENABLE_HELO").MustBool(!sec.Key("DISABLE_HELO").MustBool()) // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT", "v1.19.0")
|
||||
if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") {
|
||||
sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(sec.Key("SKIP_VERIFY").MustBool()) // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT", "v1.19.0")
|
||||
if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") {
|
||||
sec.Key("USE_CLIENT_CERT").MustBool(sec.Key("USE_CERTIFICATE").MustBool()) // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE", "v1.19.0")
|
||||
if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") {
|
||||
sec.Key("CERT_FILE").MustString(sec.Key("CERT_FILE").String())
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE", "v1.19.0")
|
||||
if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") {
|
||||
sec.Key("KEY_FILE").MustString(sec.Key("KEY_FILE").String())
|
||||
}
|
||||
|
||||
deprecatedSetting(rootCfg, "mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT", "v1.19.0")
|
||||
if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") {
|
||||
sec.Key("SEND_AS_PLAIN_TEXT").MustBool(!sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)) // FIXME: INI-MUST-SIDE-EFFECT
|
||||
}
|
||||
|
||||
if sec.HasKey("PROTOCOL") && sec.Key("PROTOCOL").String() == "smtp+startls" {
|
||||
log.Error("Deprecated fallback `[mailer]` `PROTOCOL = smtp+startls` present. Use `[mailer]` `PROTOCOL = smtp+starttls`` instead. This fallback will be removed in v1.19.0")
|
||||
log.Error("Deprecated fallback `[mailer]` `PROTOCOL = smtp+startls` present. Use `[mailer]` `PROTOCOL = smtp+starttls` instead.")
|
||||
sec.Key("PROTOCOL").SetValue("smtp+starttls")
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ func loadMigrationsFrom(rootCfg ConfigProvider) {
|
||||
egressModeSet := sec.HasKey("EGRESS_MODE")
|
||||
Migrations.EgressMode = normalizePolicyMode(sec.Key("EGRESS_MODE").String())
|
||||
|
||||
deprecatedSetting(rootCfg, "migrations", "ALLOWED_DOMAINS", "migrations", "ALLOWED_HOST_LIST", "v28.0.0")
|
||||
deprecatedSetting(rootCfg, "migrations", "BLOCKED_DOMAINS", "migrations", "BLOCKED_HOST_LIST", "v28.0.0")
|
||||
deprecatedSetting(rootCfg, "migrations", "ALLOW_LOCALNETWORKS", "migrations", "ALLOWED_HOST_LIST", "v28.0.0")
|
||||
deprecatedSetting(rootCfg, "migrations", "ALLOWED_DOMAINS", "migrations", "ALLOWED_HOST_LIST")
|
||||
deprecatedSetting(rootCfg, "migrations", "BLOCKED_DOMAINS", "migrations", "BLOCKED_HOST_LIST")
|
||||
deprecatedSetting(rootCfg, "migrations", "ALLOW_LOCALNETWORKS", "migrations", "ALLOWED_HOST_LIST")
|
||||
Migrations.AllowedHostList = ConfigSectionKeyString(sec, "ALLOWED_HOST_LIST")
|
||||
if Migrations.AllowedHostList == "" {
|
||||
var hosts []string
|
||||
|
||||
@@ -25,15 +25,6 @@ var Mirror = struct {
|
||||
}
|
||||
|
||||
func loadMirrorFrom(rootCfg ConfigProvider) {
|
||||
// Handle old configuration through `[repository]` `DISABLE_MIRRORS`
|
||||
// - please note this was badly named and only disabled the creation of new pull mirrors
|
||||
// DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
|
||||
// if these are removed, the warning will not be shown
|
||||
deprecatedSetting(rootCfg, "repository", "DISABLE_MIRRORS", "mirror", "ENABLED", "v1.19.0")
|
||||
if ConfigSectionKeyBool(rootCfg.Section("repository"), "DISABLE_MIRRORS") {
|
||||
Mirror.DisableNewPull = true
|
||||
}
|
||||
|
||||
if err := rootCfg.Section("mirror").MapTo(&Mirror); err != nil {
|
||||
log.Fatal("Failed to map Mirror settings: %v", err)
|
||||
}
|
||||
|
||||
@@ -122,12 +122,6 @@ func loadOAuth2From(rootCfg ConfigProvider) {
|
||||
OAuth2.DefaultApplications = nil
|
||||
}
|
||||
|
||||
// Handle the rename of ENABLE to ENABLED
|
||||
deprecatedSetting(rootCfg, "oauth2", "ENABLE", "oauth2", "ENABLED", "v1.23.0")
|
||||
if sec.HasKey("ENABLE") && !sec.HasKey("ENABLED") {
|
||||
OAuth2.Enabled = sec.Key("ENABLE").MustBool(OAuth2.Enabled)
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
|
||||
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package setting
|
||||
import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/modules/log"
|
||||
@@ -94,7 +95,7 @@ var (
|
||||
DefaultMergeMessageAllAuthors bool
|
||||
DefaultMergeMessageMaxApprovers int
|
||||
DefaultMergeMessageOfficialApproversOnly bool
|
||||
PopulateSquashCommentWithCommitMessages bool
|
||||
DefaultSquashMergeMessage string
|
||||
AddCoCommitterTrailers bool
|
||||
RetargetChildrenOnMerge bool
|
||||
DelayCheckForInactiveDays int
|
||||
@@ -188,7 +189,7 @@ var (
|
||||
DefaultMergeMessageAllAuthors bool
|
||||
DefaultMergeMessageMaxApprovers int
|
||||
DefaultMergeMessageOfficialApproversOnly bool
|
||||
PopulateSquashCommentWithCommitMessages bool
|
||||
DefaultSquashMergeMessage string
|
||||
AddCoCommitterTrailers bool
|
||||
RetargetChildrenOnMerge bool
|
||||
DelayCheckForInactiveDays int
|
||||
@@ -206,7 +207,7 @@ var (
|
||||
DefaultMergeMessageAllAuthors: false,
|
||||
DefaultMergeMessageMaxApprovers: 10,
|
||||
DefaultMergeMessageOfficialApproversOnly: true,
|
||||
PopulateSquashCommentWithCommitMessages: false,
|
||||
DefaultSquashMergeMessage: "PR_BODY",
|
||||
AddCoCommitterTrailers: true,
|
||||
RetargetChildrenOnMerge: true,
|
||||
DelayCheckForInactiveDays: 7,
|
||||
@@ -339,6 +340,10 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
|
||||
log.Fatal("Failed to map Repository.PullRequest settings: %v", err)
|
||||
}
|
||||
|
||||
if !slices.Contains([]string{"BLANK", "COMMIT_MESSAGES", "PR_BODY"}, Repository.PullRequest.DefaultSquashMergeMessage) { // uppercase unlike other config values, to match GitHub's squash_merge_commit_message which the API also uses
|
||||
log.Fatal("Invalid repository.pull-request.DEFAULT_SQUASH_MERGE_MESSAGE: %s", Repository.PullRequest.DefaultSquashMergeMessage)
|
||||
}
|
||||
|
||||
if !rootCfg.Section("packages").Key("ENABLED").MustBool(Packages.Enabled) {
|
||||
Repository.DisabledRepoUnits = append(Repository.DisabledRepoUnits, "repo.packages")
|
||||
}
|
||||
|
||||
@@ -180,10 +180,6 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
|
||||
PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false)
|
||||
SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
|
||||
|
||||
deprecatedSetting(rootCfg, "cors", "X_FRAME_OPTIONS", "security", "X_FRAME_OPTIONS", "v1.26.0")
|
||||
if !sec.HasKey("X_FRAME_OPTIONS") {
|
||||
Security.XFrameOptions = rootCfg.Section("cors").Key("X_FRAME_OPTIONS").MustString(Security.XFrameOptions)
|
||||
}
|
||||
if err := sec.MapTo(&Security); err != nil {
|
||||
log.Fatal("Failed to map security settings: %v", err)
|
||||
}
|
||||
|
||||
@@ -151,14 +151,7 @@ func loadServerFrom(rootCfg ConfigProvider) {
|
||||
HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
|
||||
HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
|
||||
|
||||
// DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
|
||||
// if these are removed, the warning will not be shown
|
||||
if sec.HasKey("ENABLE_ACME") {
|
||||
EnableAcme = sec.Key("ENABLE_ACME").MustBool(false)
|
||||
} else {
|
||||
deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME", "v1.19.0")
|
||||
EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false)
|
||||
}
|
||||
EnableAcme = sec.Key("ENABLE_ACME").MustBool(false)
|
||||
|
||||
protocolCfg := sec.Key("PROTOCOL").String()
|
||||
if protocolCfg != "https" && EnableAcme {
|
||||
@@ -175,29 +168,12 @@ func loadServerFrom(rootCfg ConfigProvider) {
|
||||
AcmeProfile = sec.Key("ACME_PROFILE").MustString("")
|
||||
AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("")
|
||||
|
||||
if sec.HasKey("ACME_ACCEPTTOS") {
|
||||
AcmeTOS = sec.Key("ACME_ACCEPTTOS").MustBool(false)
|
||||
} else {
|
||||
deprecatedSetting(rootCfg, "server", "LETSENCRYPT_ACCEPTTOS", "server", "ACME_ACCEPTTOS", "v1.19.0")
|
||||
AcmeTOS = sec.Key("LETSENCRYPT_ACCEPTTOS").MustBool(false)
|
||||
}
|
||||
AcmeTOS = sec.Key("ACME_ACCEPTTOS").MustBool(false)
|
||||
if !AcmeTOS {
|
||||
log.Fatal("ACME TOS is not accepted (ACME_ACCEPTTOS).")
|
||||
}
|
||||
|
||||
if sec.HasKey("ACME_DIRECTORY") {
|
||||
AcmeLiveDirectory = sec.Key("ACME_DIRECTORY").MustString("https")
|
||||
} else {
|
||||
deprecatedSetting(rootCfg, "server", "LETSENCRYPT_DIRECTORY", "server", "ACME_DIRECTORY", "v1.19.0")
|
||||
AcmeLiveDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https")
|
||||
}
|
||||
|
||||
if sec.HasKey("ACME_EMAIL") {
|
||||
AcmeEmail = sec.Key("ACME_EMAIL").MustString("")
|
||||
} else {
|
||||
deprecatedSetting(rootCfg, "server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL", "v1.19.0")
|
||||
AcmeEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("")
|
||||
}
|
||||
AcmeLiveDirectory = sec.Key("ACME_DIRECTORY").MustString("https")
|
||||
AcmeEmail = sec.Key("ACME_EMAIL").MustString("")
|
||||
} else {
|
||||
CertFile = sec.Key("CERT_FILE").String()
|
||||
KeyFile = sec.Key("KEY_FILE").String()
|
||||
|
||||
@@ -158,7 +158,7 @@ func loadServiceFrom(rootCfg ConfigProvider) {
|
||||
Service.RegisterManualConfirm = false
|
||||
}
|
||||
if sec.HasKey("EMAIL_DOMAIN_WHITELIST") {
|
||||
deprecatedSetting(rootCfg, "service", "EMAIL_DOMAIN_WHITELIST", "service", "EMAIL_DOMAIN_ALLOWLIST", "1.21")
|
||||
deprecatedSetting(rootCfg, "service", "EMAIL_DOMAIN_WHITELIST", "service", "EMAIL_DOMAIN_ALLOWLIST")
|
||||
}
|
||||
Service.EmailDomainAllowList = CompileEmailGlobList(sec, "EMAIL_DOMAIN_WHITELIST", "EMAIL_DOMAIN_ALLOWLIST")
|
||||
Service.EmailDomainBlockList = CompileEmailGlobList(sec, "EMAIL_DOMAIN_BLOCKLIST")
|
||||
|
||||
@@ -117,6 +117,7 @@ func LoadCommonSettings() {
|
||||
func loadCommonSettingsFrom(cfg ConfigProvider) error {
|
||||
// a lot of logic depends on InstallLock value, so it must be loaded before any other settings
|
||||
InstallLock = HasInstallLock(cfg)
|
||||
applyDeprecatedKeys(cfg)
|
||||
|
||||
// WARNING: don't change the sequence except you know what you are doing.
|
||||
loadRunModeFrom(cfg)
|
||||
|
||||
@@ -10,9 +10,9 @@ func loadTaskFrom(rootCfg ConfigProvider) {
|
||||
taskSec := rootCfg.Section("task")
|
||||
queueTaskSec := rootCfg.Section("queue.task")
|
||||
|
||||
deprecatedSetting(rootCfg, "task", "QUEUE_TYPE", "queue.task", "TYPE", "v1.19.0")
|
||||
deprecatedSetting(rootCfg, "task", "QUEUE_CONN_STR", "queue.task", "CONN_STR", "v1.19.0")
|
||||
deprecatedSetting(rootCfg, "task", "QUEUE_LENGTH", "queue.task", "LENGTH", "v1.19.0")
|
||||
deprecatedSetting(rootCfg, "task", "QUEUE_TYPE", "queue.task", "TYPE")
|
||||
deprecatedSetting(rootCfg, "task", "QUEUE_CONN_STR", "queue.task", "CONN_STR")
|
||||
deprecatedSetting(rootCfg, "task", "QUEUE_LENGTH", "queue.task", "LENGTH")
|
||||
|
||||
switch taskSec.Key("QUEUE_TYPE").MustString("channel") {
|
||||
case "channel":
|
||||
|
||||
@@ -30,7 +30,9 @@ var UI = struct {
|
||||
FileIconTheme string
|
||||
FolderIconTheme string
|
||||
Reactions []string
|
||||
ReleaseReactions []string
|
||||
ReactionsLookup container.Set[string] `ini:"-"`
|
||||
ReleaseReactionsLookup container.Set[string] `ini:"-"`
|
||||
CustomEmojis []string
|
||||
CustomEmojisMap map[string]string `ini:"-"`
|
||||
EnabledEmojis []string
|
||||
@@ -98,6 +100,7 @@ var UI = struct {
|
||||
FileIconTheme: `material`,
|
||||
FolderIconTheme: `basic`,
|
||||
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
|
||||
ReleaseReactions: []string{`+1`, `laugh`, `hooray`, `heart`, `rocket`, `eyes`},
|
||||
CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
|
||||
CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
|
||||
ExploreDefaultSort: "recentupdate",
|
||||
@@ -170,10 +173,9 @@ func loadUIFrom(rootCfg ConfigProvider) {
|
||||
// because many private repositories do not have "description/topic", users just want to search by their names.
|
||||
UI.OnlyShowRelevantRepos = sec.Key("ONLY_SHOW_RELEVANT_REPOS").MustBool(false)
|
||||
|
||||
UI.ReactionsLookup = make(container.Set[string])
|
||||
for _, reaction := range UI.Reactions {
|
||||
UI.ReactionsLookup.Add(reaction)
|
||||
}
|
||||
UI.ReactionsLookup = container.SetOf(UI.Reactions...)
|
||||
UI.ReleaseReactionsLookup = container.SetOf(UI.ReleaseReactions...)
|
||||
|
||||
UI.CustomEmojisMap = make(map[string]string)
|
||||
for _, emoji := range UI.CustomEmojis {
|
||||
UI.CustomEmojisMap[emoji] = ":" + emoji + ":"
|
||||
|
||||
@@ -35,7 +35,7 @@ func loadWebhookFrom(rootCfg ConfigProvider) {
|
||||
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
|
||||
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
|
||||
|
||||
deprecatedSetting(rootCfg, "webhook", "ALLOWED_HOST_LIST", "security", "ALLOWED_HOST_LIST", "v28.0.0")
|
||||
deprecatedSetting(rootCfg, "webhook", "ALLOWED_HOST_LIST", "security", "ALLOWED_HOST_LIST")
|
||||
Webhook.AllowedHostList = sec.Key("ALLOWED_HOST_LIST").MustString(Security.AllowedHostList)
|
||||
if Webhook.AllowedHostList != Security.AllowedHostList {
|
||||
checkHostList("[webhook] ALLOWED_HOST_LIST", Webhook.AllowedHostList, false)
|
||||
|
||||
@@ -120,6 +120,7 @@ type Repository struct {
|
||||
DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"`
|
||||
DefaultMergeStyle string `json:"default_merge_style"`
|
||||
DefaultUpdateStyle string `json:"default_update_style"`
|
||||
SquashMergeCommitMessage string `json:"squash_merge_commit_message"`
|
||||
DefaultAllowMaintainerEdit bool `json:"default_allow_maintainer_edit"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Internal bool `json:"internal"`
|
||||
@@ -236,6 +237,8 @@ type EditRepoOption struct {
|
||||
DefaultMergeStyle *string `json:"default_merge_style,omitempty"`
|
||||
// set to an update style to be used by this repository: "merge" or "rebase".
|
||||
DefaultUpdateStyle *string `json:"default_update_style,omitempty"`
|
||||
// set the default squash merge commit message body: "PR_BODY", "COMMIT_MESSAGES", or "BLANK".
|
||||
SquashMergeCommitMessage *string `json:"squash_merge_commit_message,omitempty"`
|
||||
// set to `true` to allow edits from maintainers by default
|
||||
DefaultAllowMaintainerEdit *bool `json:"default_allow_maintainer_edit,omitempty"`
|
||||
// set to `true` to archive this repository.
|
||||
|
||||
@@ -93,9 +93,6 @@ func newFuncMapWebPage() template.FuncMap {
|
||||
"ShowFooterPoweredBy": func() bool {
|
||||
return setting.Other.ShowFooterPoweredBy
|
||||
},
|
||||
"AllowedReactions": func() []string {
|
||||
return setting.UI.Reactions
|
||||
},
|
||||
"MetaAuthor": func() string {
|
||||
return setting.UI.Meta.Author
|
||||
},
|
||||
|
||||
@@ -2148,6 +2148,10 @@
|
||||
"repo.settings.pulls.allow_merge_update": "Enable updating pull request branch by merge",
|
||||
"repo.settings.pulls.allow_rebase_update": "Enable updating pull request branch by rebase",
|
||||
"repo.settings.pulls.default_update_style": "Default branch update style",
|
||||
"repo.settings.pulls.default_squash_merge_message": "Default squash merge commit message",
|
||||
"repo.settings.pulls.default_squash_merge_message.title": "Pull request title",
|
||||
"repo.settings.pulls.default_squash_merge_message.title_and_commit_details": "Pull request title and commit details",
|
||||
"repo.settings.pulls.default_squash_merge_message.title_and_description": "Pull request title and description",
|
||||
"repo.settings.pulls.default_target_branch": "Default target branch for new pull requests",
|
||||
"repo.settings.pulls.default_target_branch_default": "Default branch (%s)",
|
||||
"repo.settings.pulls.default_delete_branch_after_merge": "Delete pull request branch after merge by default",
|
||||
|
||||
@@ -602,7 +602,7 @@
|
||||
"user.followers": "abonnés",
|
||||
"user.show_more": "Voir plus",
|
||||
"user.starred": "Dépôts favoris",
|
||||
"user.watched": "Dépôts surveillés",
|
||||
"user.watched": "Dépôts observés",
|
||||
"user.code": "Code",
|
||||
"user.projects": "Projets",
|
||||
"user.overview": "Vue d'ensemble",
|
||||
@@ -929,6 +929,7 @@
|
||||
"settings.delete_account_title": "Supprimer ce compte d’utilisateur",
|
||||
"settings.delete_account_desc": "Êtes-vous sûr de vouloir supprimer définitivement ce compte d’utilisateur ?",
|
||||
"settings.email_notifications.enable": "Notifier par courriel",
|
||||
"settings.email_notifications.onmention_and_custom": "Courriel uniquement sur Mentions et Observations personnalisées",
|
||||
"settings.email_notifications.disable": "Ne pas notifier",
|
||||
"settings.email_notifications.submit": "Définir les préférences de courriel",
|
||||
"settings.email_notifications.andyourown": "Inclure vos propres notifications",
|
||||
@@ -1141,10 +1142,10 @@
|
||||
"repo.generated_from": "généré depuis",
|
||||
"repo.fork_from_self": "Vous ne pouvez pas bifurquer un dépôt que vous possédez.",
|
||||
"repo.fork_guest_user": "Connectez-vous pour bifurquer ce dépôt.",
|
||||
"repo.watch_guest_user": "Connectez-vous pour consulter ce dépôt.",
|
||||
"repo.watch_guest_user": "Connectez-vous pour observer ce dépôt.",
|
||||
"repo.star_guest_user": "Connectez-vous pour ajouter aux favoris ce dépôt.",
|
||||
"repo.watch": "Suivre",
|
||||
"repo.watching": "Suivi",
|
||||
"repo.watch": "Observer",
|
||||
"repo.watching": "Observé",
|
||||
"repo.ignoring": "Ignoré",
|
||||
"repo.watch.options.issues": "Tickets",
|
||||
"repo.watch.options.pull_requests": "Demandes d'ajout",
|
||||
@@ -2511,7 +2512,7 @@
|
||||
"repo.settings.visibility.private.text": "Rendre le dépôt privé rendra non seulement le dépôt visible uniquement aux membres autorisés, mais peut également rompre la relation entre lui et ses bifurcations, observateurs, et favoris.",
|
||||
"repo.settings.visibility.private.bullet_title": "<strong>Changer la visibilité en privé :</strong>",
|
||||
"repo.settings.visibility.private.bullet_one": "Rendra le dépôt visible uniquement aux membres autorisés.",
|
||||
"repo.settings.visibility.private.bullet_two": "Applique la visibilité aux bifurcation et retire les <strong>observateurs</strong> et les <strong>favoris</strong>.",
|
||||
"repo.settings.visibility.private.bullet_two": "Applique la visibilité aux bifurcations et retire les <strong>observateurs</strong> et les <strong>favoris</strong>.",
|
||||
"repo.settings.visibility.private.stats_stars": "Il y a <strong>%d</strong> favori(s) sur ce dépôt qui pourrai(en)t être perdu(s).",
|
||||
"repo.settings.visibility.private.stats_watchers": "Il y a <strong>%d</strong> observateur(s) sur ce dépôt qui pourrai(en)t être perdu(s).",
|
||||
"repo.settings.visibility.private.stats_forks": "Il y a <strong>%d</strong> bifurcation(s) associée(s) à ce dépôt.",
|
||||
@@ -3548,7 +3549,7 @@
|
||||
"notification.mark_all_as_read": "Tout marquer comme lu",
|
||||
"notification.mark_page_as_read": "Marquer cette page comme lue",
|
||||
"notification.subscriptions": "Abonnements",
|
||||
"notification.watching": "Suivi",
|
||||
"notification.watching": "Observé",
|
||||
"notification.no_subscriptions": "Pas d'abonnements",
|
||||
"gpg.default_key": "Signé avec la clé par défaut",
|
||||
"gpg.error.extract_sign": "Impossible d'extraire la signature",
|
||||
|
||||
@@ -926,6 +926,7 @@
|
||||
"settings.delete_account_title": "Scrios Cuntas Úsáide",
|
||||
"settings.delete_account_desc": "An bhfuil tú cinnte gur mhaith leat an cuntas úsáideora seo a scriosadh go buan?",
|
||||
"settings.email_notifications.enable": "Cumasaigh Fógraí Ríomhphoist",
|
||||
"settings.email_notifications.onmention_and_custom": "Ríomhphost amháin nuair a dhéantar tagairt, agus uaireadóirí saincheaptha",
|
||||
"settings.email_notifications.disable": "Díchumasaigh Fógraí Ríomhphoist",
|
||||
"settings.email_notifications.submit": "Socraigh rogha ríomhphoist",
|
||||
"settings.email_notifications.andyourown": "Agus Do Fógraí Féin",
|
||||
@@ -3987,6 +3988,8 @@
|
||||
"actions.general.token_permissions.maximum.description": "Beidh ceadanna éifeachtacha an phoist gníomhartha teoranta ag na ceadanna uasta.",
|
||||
"actions.general.token_permissions.fork_pr_note": "Mura gcuirtear tús le post trí iarratas tarraingthe ó fhorc, ní rachaidh a cheadanna éifeachtacha thar na ceadanna léite amháin.",
|
||||
"actions.general.token_permissions.customize_max_permissions": "Saincheap na ceadanna uasta",
|
||||
"actions.general.token_permissions.id_token": "Comhartha OpenID Connect",
|
||||
"actions.general.token_permissions.id_token.description": "Ceadaigh do phoist a dhearbhaíonn \"id-token: write\" comharthaí OpenID Connect gearrshaolacha a iarraidh chun fíordheimhniú a dhéanamh i gcoinne seirbhísí seachtracha.",
|
||||
"actions.general.cross_repo": "Rochtain Tras-Stórtha",
|
||||
"actions.general.cross_repo_desc": "Ceadaigh rochtain (léamh amháin) a bheith ag na stórtha uile san úinéir seo ar na stórtha roghnaithe le GITEA_TOKEN agus poist Gníomhartha á reáchtáil.",
|
||||
"actions.general.cross_repo_selected": "Stórtha roghnaithe",
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
"add_all": "すべて追加",
|
||||
"dismiss": "閉じる",
|
||||
"remove": "除去",
|
||||
"rename": "名前を変更",
|
||||
"remove_all": "すべて除去",
|
||||
"remove_label_str": "アイテム「%s」を削除",
|
||||
"edit": "編集",
|
||||
@@ -906,6 +907,7 @@
|
||||
"settings.webauthn_desc": "セキュリティキーは暗号化キーを内蔵するハードウェア ・ デバイスです。 2要素認証に使用できます。 セキュリティキーは<a rel=\"noreferrer\" target=\"_blank\" href=\"%s\">WebAuthn Authenticator</a>規格をサポートしている必要があります。",
|
||||
"settings.webauthn_register_key": "セキュリティキーを追加",
|
||||
"settings.webauthn_nickname": "ニックネーム",
|
||||
"settings.webauthn_nickname_been_used": "同じニックネームのシークレットキーが既に存在しています。",
|
||||
"settings.webauthn_delete_key": "セキュリティキーの登録解除",
|
||||
"settings.webauthn_delete_key_desc": "セキュリティキーの登録を解除すると、今後そのセキュリティキーでサインインすることはできなくなります。 続行しますか?",
|
||||
"settings.webauthn_key_loss_warning": "セキュリティキーを紛失すると、アカウントへのアクセスを失います。",
|
||||
@@ -2296,6 +2298,7 @@
|
||||
"repo.settings.event_release_desc": "リポジトリでリリースが作成・更新・削除されたとき。",
|
||||
"repo.settings.event_push": "プッシュ",
|
||||
"repo.settings.event_force_push": "強制プッシュ",
|
||||
"repo.settings.event_branch_delete": "ブランチの抹消",
|
||||
"repo.settings.event_push_desc": "Gitがリポジトリにプッシュを行ったとき。",
|
||||
"repo.settings.event_repository": "リポジトリ",
|
||||
"repo.settings.event_repository_desc": "リポジトリが作成・削除・名前変更されたとき。",
|
||||
@@ -2407,6 +2410,12 @@
|
||||
"repo.settings.protect_enable_force_push_all_desc": "プッシュ権限を持つ人は誰でも、このブランチに強制プッシュを行うことができます。",
|
||||
"repo.settings.protect_enable_force_push_allowlist": "許可リストで強制プッシュを制限",
|
||||
"repo.settings.protect_enable_force_push_allowlist_desc": "許可リストに登録された、プッシュ権限を持つユーザーまたはチームのみが、このブランチに強制プッシュを行うことができます。",
|
||||
"repo.settings.protect_disable_deletion": "ブランチの抹消を無効化",
|
||||
"repo.settings.protect_disable_deletion_desc": "このブランチではブランチ抹消は許可されません。",
|
||||
"repo.settings.protect_enable_deletion_all": "ブランチの抹消を有効化",
|
||||
"repo.settings.protect_enable_deletion_all_desc": "プッシュ権限を持つ人は誰でも、このブランチの抹消を行うことができます。",
|
||||
"repo.settings.protect_enable_deletion_allowlist": "許可リストでブランチ抹消を制限",
|
||||
"repo.settings.protect_enable_deletion_allowlist_desc": "許可リストに登録された、プッシュ権限を持つユーザーまたはチームのみが、このブランチの抹消を行うことができます。",
|
||||
"repo.settings.protect_enable_merge": "マージ有効",
|
||||
"repo.settings.protect_enable_merge_desc": "書き込み権限を持つ人は誰でも、このブランチにプルリクエストをマージすることができます。",
|
||||
"repo.settings.protect_whitelist_committers": "許可リストでプッシュを制限",
|
||||
@@ -2417,6 +2426,8 @@
|
||||
"repo.settings.protect_force_push_allowlist_users": "強制プッシュ許可リストに含むユーザー:",
|
||||
"repo.settings.protect_force_push_allowlist_teams": "強制プッシュ許可リストに含むチーム:",
|
||||
"repo.settings.protect_force_push_allowlist_deploy_keys": "強制プッシュ可能なプッシュ権限を持つデプロイキーを許可リストに含める。",
|
||||
"repo.settings.protect_deletion_allowlist_users": "抹消許可リストに含むユーザー:",
|
||||
"repo.settings.protect_deletion_allowlist_teams": "抹消許可リストに含むチーム:",
|
||||
"repo.settings.protect_merge_whitelist_committers": "マージ許可リストを有効にする",
|
||||
"repo.settings.protect_merge_whitelist_committers_desc": "許可リストに登録したユーザーまたはチームにのみ、このブランチへのプルリクエストのマージを許可します。",
|
||||
"repo.settings.protect_merge_whitelist_users": "マージ許可リストに含むユーザー:",
|
||||
|
||||
@@ -929,6 +929,7 @@
|
||||
"settings.delete_account_title": "Eliminar conta de utilizador",
|
||||
"settings.delete_account_desc": "Tem a certeza que quer eliminar permanentemente esta conta de utilizador?",
|
||||
"settings.email_notifications.enable": "Habilitar notificações por email",
|
||||
"settings.email_notifications.onmention_and_custom": "Email somente em vigilâncias de menções e personalizadas",
|
||||
"settings.email_notifications.disable": "Desabilitar notificações por email",
|
||||
"settings.email_notifications.submit": "Definir preferência do email",
|
||||
"settings.email_notifications.andyourown": "e as suas próprias notificações",
|
||||
@@ -4000,6 +4001,8 @@
|
||||
"actions.general.token_permissions.maximum.description": "As permissões concretas para o trabalho das operações serão limitadas pelas permissões máximas.",
|
||||
"actions.general.token_permissions.fork_pr_note": "Se o trabalho for iniciado por um pedido de integração de uma derivação, as suas permissões concretas não irão exceder as permissões apenas de leitura.",
|
||||
"actions.general.token_permissions.customize_max_permissions": "Personalizar as permissões máximas",
|
||||
"actions.general.token_permissions.id_token": "Código OpenID Connect",
|
||||
"actions.general.token_permissions.id_token.description": "Permitir que trabalhos que declaram \"id-token: write\" solicitem códigos OpenID Connect de curta duração para se autenticarem em serviços externos.",
|
||||
"actions.general.cross_repo": "Acesso entre repositórios",
|
||||
"actions.general.cross_repo_desc": "Permitir que os repositórios escolhidos sejam acedidos (somente leitura) por todos os repositórios deste proprietário com GITEA_TOKEN ao executar os trabalhos das operações.",
|
||||
"actions.general.cross_repo_selected": "Repositórios escolhidos",
|
||||
|
||||
@@ -929,6 +929,7 @@
|
||||
"settings.delete_account_title": "删除当前帐户",
|
||||
"settings.delete_account_desc": "确定要永久删除此用户帐户吗?",
|
||||
"settings.email_notifications.enable": "启用邮件通知",
|
||||
"settings.email_notifications.onmention_and_custom": "仅在被提及或发生自定义关注事件时发送邮件",
|
||||
"settings.email_notifications.disable": "停用邮件通知",
|
||||
"settings.email_notifications.submit": "设置邮件通知",
|
||||
"settings.email_notifications.andyourown": "同时接收由自己操作触发的通知",
|
||||
@@ -2298,6 +2299,7 @@
|
||||
"repo.settings.event_release_desc": "发布、更新或删除版本时。",
|
||||
"repo.settings.event_push": "推送",
|
||||
"repo.settings.event_force_push": "强制推送",
|
||||
"repo.settings.event_branch_delete": "分支删除",
|
||||
"repo.settings.event_push_desc": "Git 仓库推送",
|
||||
"repo.settings.event_repository": "仓库",
|
||||
"repo.settings.event_repository_desc": "仓库被创建、删除或重命名。",
|
||||
@@ -2409,6 +2411,12 @@
|
||||
"repo.settings.protect_enable_force_push_all_desc": "任何拥有推送权限的人都可以对这个分支进行强制推送。",
|
||||
"repo.settings.protect_enable_force_push_allowlist": "强制推送白名单",
|
||||
"repo.settings.protect_enable_force_push_allowlist_desc": "只有具有推送权限且列入白名单的用户或团队,才能强制推送到此分支。",
|
||||
"repo.settings.protect_disable_deletion": "禁止删除分支",
|
||||
"repo.settings.protect_disable_deletion_desc": "不允许删除此分支。",
|
||||
"repo.settings.protect_enable_deletion_all": "允许删除分支",
|
||||
"repo.settings.protect_enable_deletion_all_desc": "任何拥有推送权限的人都可以删除此分支。",
|
||||
"repo.settings.protect_enable_deletion_allowlist": "分支删除白名单",
|
||||
"repo.settings.protect_enable_deletion_allowlist_desc": "只有具有推送权限且列入白名单的用户或团队,才能删除此分支。",
|
||||
"repo.settings.protect_enable_merge": "启用合并",
|
||||
"repo.settings.protect_enable_merge_desc": "任何具有写入权限的人都可以将合并请求合并到此分支中。",
|
||||
"repo.settings.protect_whitelist_committers": "受白名单限制的推送",
|
||||
@@ -2419,6 +2427,8 @@
|
||||
"repo.settings.protect_force_push_allowlist_users": "允许强制推送的白名单用户:",
|
||||
"repo.settings.protect_force_push_allowlist_teams": "允许强制推送的白名单团队:",
|
||||
"repo.settings.protect_force_push_allowlist_deploy_keys": "允许具有推送权限的白名单部署密钥进行强制推送。",
|
||||
"repo.settings.protect_deletion_allowlist_users": "删除白名单用户:",
|
||||
"repo.settings.protect_deletion_allowlist_teams": "删除白名单团队:",
|
||||
"repo.settings.protect_merge_whitelist_committers": "启用合并白名单",
|
||||
"repo.settings.protect_merge_whitelist_committers_desc": "仅允许白名单用户或团队合并合并请求到此分支。",
|
||||
"repo.settings.protect_merge_whitelist_users": "合并白名单用户:",
|
||||
@@ -3991,6 +4001,8 @@
|
||||
"actions.general.token_permissions.maximum.description": "工作流任务的实际权限将受到最大权限的限制。",
|
||||
"actions.general.token_permissions.fork_pr_note": "如果任务是由来自派生仓库的合并请求触发的,那么它的实际权限不会超过只读权限。",
|
||||
"actions.general.token_permissions.customize_max_permissions": "自定义最大权限",
|
||||
"actions.general.token_permissions.id_token": "OpenID Connect 令牌",
|
||||
"actions.general.token_permissions.id_token.description": "允许声明了「id-token: write」的作业请求短期有效的 OpenID Connect 令牌,以向外部服务进行身份验证。",
|
||||
"actions.general.cross_repo": "跨仓库访问",
|
||||
"actions.general.cross_repo_desc": "允许此所有者下的所有仓库在运行工作流任务时,通过 GITEA_TOKEN 以只读方式访问所选仓库。",
|
||||
"actions.general.cross_repo_selected": "选择的仓库",
|
||||
|
||||
+16
-16
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@12.13.0",
|
||||
"packageManager": "pnpm@12.10.1",
|
||||
"engines": {
|
||||
"node": ">= 22.18.0",
|
||||
"pnpm": ">= 11.0.0"
|
||||
@@ -29,7 +29,7 @@
|
||||
"@replit/codemirror-lang-svelte": "6.0.0",
|
||||
"@replit/codemirror-vscode-keymap": "6.0.2",
|
||||
"@resvg/resvg-wasm": "2.6.2",
|
||||
"@vitejs/plugin-vue": "6.0.10",
|
||||
"@vitejs/plugin-vue": "6.0.9",
|
||||
"asciinema-player": "3.17.0",
|
||||
"chart.js": "4.5.1",
|
||||
"chartjs-plugin-zoom": "2.2.0",
|
||||
@@ -46,46 +46,46 @@
|
||||
"jquery": "4.0.0",
|
||||
"js-yaml": "5.4.3",
|
||||
"katex": "0.19.0",
|
||||
"mermaid": "12.2.0",
|
||||
"mermaid": "12.1.0",
|
||||
"online-3d-viewer": "0.18.0",
|
||||
"pdfobject": "2.3.1",
|
||||
"postcss": "8.5.30",
|
||||
"postcss": "8.5.29",
|
||||
"rolldown-license-plugin": "3.0.14",
|
||||
"sortablejs": "1.15.7",
|
||||
"swagger-ui-dist": "5.33.2",
|
||||
"swagger-ui-dist": "5.33.1",
|
||||
"tailwindcss": "3.4.19",
|
||||
"tippy.js": "6.3.7",
|
||||
"toastify-js": "1.12.0",
|
||||
"tributejs": "5.1.3",
|
||||
"vanilla-colorful": "0.7.2",
|
||||
"vite": "8.3.4",
|
||||
"vite": "8.3.3",
|
||||
"vite-string-plugin": "2.0.7",
|
||||
"vue": "3.5.44"
|
||||
"vue": "3.5.43"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "4.8.1",
|
||||
"@eslint/json": "2.1.1",
|
||||
"@playwright/test": "1.64.0",
|
||||
"@playwright/test": "1.63.0",
|
||||
"@stylistic/eslint-plugin": "5.10.0",
|
||||
"@stylistic/stylelint-plugin": "5.3.1",
|
||||
"@types/codemirror": "5.60.18",
|
||||
"@types/jquery": "4.0.1",
|
||||
"@types/katex": "0.16.8",
|
||||
"@types/node": "26.6.5",
|
||||
"@types/node": "26.6.4",
|
||||
"@types/pdfobject": "2.2.5",
|
||||
"@types/sortablejs": "1.15.9",
|
||||
"@types/swagger-ui-dist": "3.30.6",
|
||||
"@types/toastify-js": "1.12.4",
|
||||
"@typescript-eslint/parser": "8.71.2",
|
||||
"@vitejs/plugin-vue": "6.0.10",
|
||||
"@vitest/browser-playwright": "5.0.4",
|
||||
"@typescript-eslint/parser": "8.71.1",
|
||||
"@vitejs/plugin-vue": "6.0.9",
|
||||
"@vitest/browser-playwright": "5.0.3",
|
||||
"@vitest/eslint-plugin": "1.6.27",
|
||||
"eslint": "10.12.0",
|
||||
"eslint-import-resolver-typescript": "4.4.5",
|
||||
"eslint-plugin-import-x": "4.17.1",
|
||||
"eslint-plugin-playwright": "2.12.1",
|
||||
"eslint-plugin-regexp": "3.3.1",
|
||||
"eslint-plugin-unicorn": "78.0.0",
|
||||
"eslint-plugin-unicorn": "77.0.0",
|
||||
"eslint-plugin-vue": "10.11.1",
|
||||
"eslint-plugin-vue-scoped-css": "3.1.3",
|
||||
"eslint-plugin-wc": "3.1.0",
|
||||
@@ -103,9 +103,9 @@
|
||||
"stylelint-value-no-unknown-custom-properties": "6.1.1",
|
||||
"svgo": "4.1.0",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "8.71.2",
|
||||
"vitest": "5.0.4",
|
||||
"typescript-eslint": "8.71.1",
|
||||
"vitest": "5.0.3",
|
||||
"vue-eslint-parser": "10.4.1",
|
||||
"vue-tsc": "3.3.13"
|
||||
"vue-tsc": "3.3.12"
|
||||
}
|
||||
}
|
||||
Generated
+403
-461
File diff suppressed because it is too large.
Load diff
@@ -12,16 +12,11 @@ import (
|
||||
"gitea.dev/actionslib/ping/v1/pingv1connect"
|
||||
"gitea.dev/modules/log"
|
||||
|
||||
"connectrpc.com/connect/v2"
|
||||
"connectrpc.com/connect/v2/connecthttp"
|
||||
"connectrpc.com/connect"
|
||||
)
|
||||
|
||||
func NewPingServiceHandler() (string, http.Handler) {
|
||||
server := connect.NewServer()
|
||||
pingv1connect.RegisterPingServiceHandler(server, &Service{})
|
||||
mux := http.NewServeMux()
|
||||
connecthttp.Mount(mux, server)
|
||||
return "/" + pingv1connect.PingServiceName + "/", mux
|
||||
return pingv1connect.NewPingServiceHandler(&Service{})
|
||||
}
|
||||
|
||||
var _ pingv1connect.PingServiceHandler = (*Service)(nil)
|
||||
@@ -30,13 +25,12 @@ type Service struct{}
|
||||
|
||||
func (s *Service) Ping(
|
||||
ctx context.Context,
|
||||
req *pingv1.PingRequest,
|
||||
) (*pingv1.PingResponse, error) {
|
||||
info, _ := connect.CallInfoForServerContext(ctx)
|
||||
log.Trace("Content-Type: %s", info.RequestHeader().Get("Content-Type"))
|
||||
log.Trace("User-Agent: %s", info.RequestHeader().Get("User-Agent"))
|
||||
res := &pingv1.PingResponse{
|
||||
Data: fmt.Sprintf("Hello, %s!", req.Data),
|
||||
}
|
||||
req *connect.Request[pingv1.PingRequest],
|
||||
) (*connect.Response[pingv1.PingResponse], error) {
|
||||
log.Trace("Content-Type: %s", req.Header().Get("Content-Type"))
|
||||
log.Trace("User-Agent: %s", req.Header().Get("User-Agent"))
|
||||
res := connect.NewResponse(&pingv1.PingResponse{
|
||||
Data: fmt.Sprintf("Hello, %s!", req.Msg.Data),
|
||||
})
|
||||
return res, nil
|
||||
}
|
||||
@@ -11,15 +11,17 @@ import (
|
||||
pingv1 "gitea.dev/actionslib/ping/v1"
|
||||
"gitea.dev/actionslib/ping/v1/pingv1connect"
|
||||
|
||||
"connectrpc.com/connect/v2"
|
||||
"connectrpc.com/connect/v2/connecthttp"
|
||||
"connectrpc.com/connect"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
_, handler := NewPingServiceHandler()
|
||||
MainServiceTest(t, handler)
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle(pingv1connect.NewPingServiceHandler(
|
||||
&Service{},
|
||||
))
|
||||
MainServiceTest(t, mux)
|
||||
}
|
||||
|
||||
func MainServiceTest(t *testing.T, h http.Handler) {
|
||||
@@ -29,31 +31,31 @@ func MainServiceTest(t *testing.T, h http.Handler) {
|
||||
server.StartTLS()
|
||||
defer server.Close()
|
||||
|
||||
connectClient := pingv1connect.NewPingServiceClient(connect.NewClient(connecthttp.NewTransport(
|
||||
connectClient := pingv1connect.NewPingServiceClient(
|
||||
server.Client(),
|
||||
server.URL,
|
||||
)))
|
||||
)
|
||||
|
||||
grpcClient := pingv1connect.NewPingServiceClient(connect.NewClient(connecthttp.NewTransport(
|
||||
grpcClient := pingv1connect.NewPingServiceClient(
|
||||
server.Client(),
|
||||
server.URL,
|
||||
connecthttp.WithGRPC(),
|
||||
)))
|
||||
connect.WithGRPC(),
|
||||
)
|
||||
|
||||
grpcWebClient := pingv1connect.NewPingServiceClient(connect.NewClient(connecthttp.NewTransport(
|
||||
grpcWebClient := pingv1connect.NewPingServiceClient(
|
||||
server.Client(),
|
||||
server.URL,
|
||||
connecthttp.WithGRPCWeb(),
|
||||
)))
|
||||
connect.WithGRPCWeb(),
|
||||
)
|
||||
|
||||
clients := []pingv1connect.PingServiceClient{connectClient, grpcClient, grpcWebClient}
|
||||
t.Run("ping request", func(t *testing.T) {
|
||||
for _, client := range clients {
|
||||
result, err := client.Ping(t.Context(), &pingv1.PingRequest{
|
||||
result, err := client.Ping(t.Context(), connect.NewRequest(&pingv1.PingRequest{
|
||||
Data: "foobar",
|
||||
})
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Hello, foobar!", result.Data)
|
||||
assert.Equal(t, "Hello, foobar!", result.Msg.Data)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"connectrpc.com/connect/v2"
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
@@ -26,39 +26,27 @@ const (
|
||||
tokenHeaderKey = protocol.TokenHeader
|
||||
)
|
||||
|
||||
// withErrorMessages sends plain errors to runners as CodeUnknown with their message, like connect v1 did.
|
||||
func withErrorMessages(next connect.ServerFunc) connect.ServerFunc {
|
||||
return func(ctx context.Context, spec connect.Spec, stream connect.ServerStream) error {
|
||||
err := next(ctx, spec, stream)
|
||||
if _, ok := errors.AsType[*connect.Error](err); err == nil || ok {
|
||||
return err
|
||||
}
|
||||
return connect.NewError(connect.CodeUnknown, err.Error()).WithCause(err)
|
||||
}
|
||||
}
|
||||
|
||||
func withRunner(next connect.ServerFunc) connect.ServerFunc {
|
||||
var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unaryFunc connect.UnaryFunc) connect.UnaryFunc {
|
||||
// A plain gRPC status.Error is treated as unknown by Connect and becomes HTTP 500
|
||||
// To respond an HTTP status code, use connect.Error instead
|
||||
errUnregisteredRunner := connect.NewError(connect.CodeUnauthenticated, "unregistered runner")
|
||||
return func(ctx context.Context, spec connect.Spec, stream connect.ServerStream) error {
|
||||
methodName := getMethodName(spec)
|
||||
errUnregisteredRunner := connect.NewError(connect.CodeUnauthenticated, errors.New("unregistered runner"))
|
||||
return func(ctx context.Context, request connect.AnyRequest) (connect.AnyResponse, error) {
|
||||
methodName := getMethodName(request)
|
||||
if methodName == "Register" {
|
||||
return next(ctx, spec, stream)
|
||||
return unaryFunc(ctx, request)
|
||||
}
|
||||
info, _ := connect.CallInfoForServerContext(ctx)
|
||||
uuid := info.RequestHeader().Get(uuidHeaderKey)
|
||||
token := info.RequestHeader().Get(tokenHeaderKey)
|
||||
uuid := request.Header().Get(uuidHeaderKey)
|
||||
token := request.Header().Get(tokenHeaderKey)
|
||||
|
||||
runner, err := actions_model.GetRunnerByUUID(ctx, uuid)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
return errUnregisteredRunner
|
||||
return nil, errUnregisteredRunner
|
||||
}
|
||||
return status.Error(codes.Internal, err.Error())
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
if !util.CryptoConstTimeEqual(runner.TokenHash, auth_model.HashToken(token, runner.TokenSalt)) {
|
||||
return errUnregisteredRunner
|
||||
return nil, errUnregisteredRunner
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
@@ -84,12 +72,12 @@ func withRunner(next connect.ServerFunc) connect.ServerFunc {
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, runnerCtxKey{}, runner)
|
||||
return next(ctx, spec, stream)
|
||||
return unaryFunc(ctx, request)
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
func getMethodName(spec connect.Spec) string {
|
||||
splits := strings.Split(spec.Procedure, "/")
|
||||
func getMethodName(req connect.AnyRequest) string {
|
||||
splits := strings.Split(req.Spec().Procedure, "/")
|
||||
if len(splits) > 0 {
|
||||
return splits[len(splits)-1]
|
||||
}
|
||||
|
||||
@@ -20,20 +20,18 @@ import (
|
||||
"gitea.dev/modules/util"
|
||||
actions_service "gitea.dev/services/actions"
|
||||
|
||||
"connectrpc.com/connect/v2"
|
||||
"connectrpc.com/connect/v2/connecthttp"
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func NewRunnerServiceHandler() (string, http.Handler) {
|
||||
server := connect.NewServer(withErrorMessages, withRunner)
|
||||
runnerv1connect.RegisterRunnerServiceHandler(server, &Service{})
|
||||
mux := http.NewServeMux()
|
||||
// runners send all pending log rows in one UpdateLog, so keep v1's unlimited message size
|
||||
connecthttp.Mount(mux, server, connecthttp.WithCompressMinBytes(1024), connecthttp.WithReadMaxBytes(0))
|
||||
return "/" + runnerv1connect.RunnerServiceName + "/", mux
|
||||
return runnerv1connect.NewRunnerServiceHandler(
|
||||
&Service{},
|
||||
connect.WithCompressMinBytes(1024),
|
||||
withRunner,
|
||||
)
|
||||
}
|
||||
|
||||
var _ runnerv1connect.RunnerServiceClient = (*Service)(nil)
|
||||
@@ -43,13 +41,13 @@ type Service struct{}
|
||||
// Register for new runner.
|
||||
func (s *Service) Register(
|
||||
ctx context.Context,
|
||||
req *runnerv1.RegisterRequest,
|
||||
) (*runnerv1.RegisterResponse, error) {
|
||||
if req.Token == "" || req.Name == "" {
|
||||
req *connect.Request[runnerv1.RegisterRequest],
|
||||
) (*connect.Response[runnerv1.RegisterResponse], error) {
|
||||
if req.Msg.Token == "" || req.Msg.Name == "" {
|
||||
return nil, errors.New("missing runner token, name")
|
||||
}
|
||||
|
||||
runnerToken, err := actions_model.GetRunnerToken(ctx, req.Token)
|
||||
runnerToken, err := actions_model.GetRunnerToken(ctx, req.Msg.Token)
|
||||
if err != nil {
|
||||
return nil, errors.New("runner registration token not found")
|
||||
}
|
||||
@@ -70,19 +68,19 @@ func (s *Service) Register(
|
||||
}
|
||||
}
|
||||
|
||||
labels := req.Labels
|
||||
hasCancellingSupport := slices.Contains(req.GetCapabilities(), runnerCapabilityCancelling)
|
||||
labels := req.Msg.Labels
|
||||
hasCancellingSupport := slices.Contains(req.Msg.GetCapabilities(), runnerCapabilityCancelling)
|
||||
|
||||
// create new runner
|
||||
name := util.EllipsisDisplayString(req.Name, 255)
|
||||
name := util.EllipsisDisplayString(req.Msg.Name, 255)
|
||||
runner := &actions_model.ActionRunner{
|
||||
UUID: uuid.New().String(),
|
||||
Name: name,
|
||||
OwnerID: runnerToken.OwnerID,
|
||||
RepoID: runnerToken.RepoID,
|
||||
Version: req.Version,
|
||||
Version: req.Msg.Version,
|
||||
AgentLabels: labels,
|
||||
Ephemeral: req.Ephemeral,
|
||||
Ephemeral: req.Msg.Ephemeral,
|
||||
HasCancellingSupport: hasCancellingSupport,
|
||||
}
|
||||
runner.GenerateAndFillToken()
|
||||
@@ -98,7 +96,7 @@ func (s *Service) Register(
|
||||
return nil, errors.New("can't update runner token status")
|
||||
}
|
||||
|
||||
res := &runnerv1.RegisterResponse{
|
||||
res := connect.NewResponse(&runnerv1.RegisterResponse{
|
||||
Runner: &runnerv1.Runner{
|
||||
Id: runner.ID,
|
||||
Uuid: runner.UUID,
|
||||
@@ -108,7 +106,7 @@ func (s *Service) Register(
|
||||
Labels: runner.AgentLabels,
|
||||
Ephemeral: runner.Ephemeral,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
return res, nil
|
||||
}
|
||||
@@ -141,18 +139,14 @@ func applyDeclareRequestToRunner(runner *actions_model.ActionRunner, req declare
|
||||
|
||||
func (s *Service) Declare(
|
||||
ctx context.Context,
|
||||
req *runnerv1.DeclareRequest,
|
||||
) (*runnerv1.DeclareResponse, error) {
|
||||
req *connect.Request[runnerv1.DeclareRequest],
|
||||
) (*connect.Response[runnerv1.DeclareResponse], error) {
|
||||
runner := GetRunner(ctx)
|
||||
if err := actions_model.UpdateRunner(ctx, runner, applyDeclareRequestToRunner(runner, req)...); err != nil {
|
||||
if err := actions_model.UpdateRunner(ctx, runner, applyDeclareRequestToRunner(runner, req.Msg)...); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "update runner: %v", err)
|
||||
}
|
||||
|
||||
// Capabilities are communicated via headers to avoid a hard dependency on a proto bump.
|
||||
// Older runners ignore unknown headers; newer runners can use this for feature negotiation.
|
||||
info, _ := connect.CallInfoForServerContext(ctx)
|
||||
info.ResponseHeader().Set("X-Gitea-Actions-Capabilities", actions_model.RunnerCapabilities())
|
||||
return &runnerv1.DeclareResponse{
|
||||
resp := connect.NewResponse(&runnerv1.DeclareResponse{
|
||||
Runner: &runnerv1.Runner{
|
||||
Id: runner.ID,
|
||||
Uuid: runner.UUID,
|
||||
@@ -161,18 +155,22 @@ func (s *Service) Declare(
|
||||
Version: runner.Version,
|
||||
Labels: runner.AgentLabels,
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
// Capabilities are communicated via headers to avoid a hard dependency on a proto bump.
|
||||
// Older runners ignore unknown headers; newer runners can use this for feature negotiation.
|
||||
resp.Header().Set("X-Gitea-Actions-Capabilities", actions_model.RunnerCapabilities())
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// FetchTask assigns a task to the runner
|
||||
func (s *Service) FetchTask(
|
||||
ctx context.Context,
|
||||
req *runnerv1.FetchTaskRequest,
|
||||
) (*runnerv1.FetchTaskResponse, error) {
|
||||
req *connect.Request[runnerv1.FetchTaskRequest],
|
||||
) (*connect.Response[runnerv1.FetchTaskResponse], error) {
|
||||
runner := GetRunner(ctx)
|
||||
|
||||
var task *runnerv1.Task
|
||||
tasksVersion := req.TasksVersion // task version from runner
|
||||
tasksVersion := req.Msg.TasksVersion // task version from runner
|
||||
latestVersion, err := actions_model.GetTasksVersionByScope(ctx, runner.OwnerID, runner.RepoID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "query tasks version failed: %v", err)
|
||||
@@ -209,26 +207,26 @@ func (s *Service) FetchTask(
|
||||
task = t
|
||||
}
|
||||
}
|
||||
res := &runnerv1.FetchTaskResponse{
|
||||
res := connect.NewResponse(&runnerv1.FetchTaskResponse{
|
||||
Task: task,
|
||||
TasksVersion: latestVersion,
|
||||
}
|
||||
})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// UpdateTask updates the task status.
|
||||
func (s *Service) UpdateTask(
|
||||
ctx context.Context,
|
||||
req *runnerv1.UpdateTaskRequest,
|
||||
) (*runnerv1.UpdateTaskResponse, error) {
|
||||
req *connect.Request[runnerv1.UpdateTaskRequest],
|
||||
) (*connect.Response[runnerv1.UpdateTaskResponse], error) {
|
||||
runner := GetRunner(ctx)
|
||||
|
||||
task, err := actions_model.UpdateTaskByState(ctx, runner.ID, req.State)
|
||||
task, err := actions_model.UpdateTaskByState(ctx, runner.ID, req.Msg.State)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "update task: %v", err)
|
||||
}
|
||||
|
||||
for k, v := range req.Outputs {
|
||||
for k, v := range req.Msg.Outputs {
|
||||
if len(k) > 255 {
|
||||
log.Warn("Ignore the output of task %d because the key is too long: %q", task.ID, k)
|
||||
continue
|
||||
@@ -266,7 +264,7 @@ func (s *Service) UpdateTask(
|
||||
actions_service.NotifyWorkflowJobStatusUpdateWithTask(ctx, task.Job, task)
|
||||
}
|
||||
|
||||
if req.State.Result != runnerv1.Result_RESULT_UNSPECIFIED {
|
||||
if req.Msg.State.Result != runnerv1.Result_RESULT_UNSPECIFIED {
|
||||
if err := actions_service.EmitJobsIfReadyByRun(task.Job.RunID); err != nil {
|
||||
log.Error("Emit ready jobs of run %d: %v", task.Job.RunID, err)
|
||||
}
|
||||
@@ -275,25 +273,25 @@ func (s *Service) UpdateTask(
|
||||
}
|
||||
}
|
||||
|
||||
return &runnerv1.UpdateTaskResponse{
|
||||
return connect.NewResponse(&runnerv1.UpdateTaskResponse{
|
||||
State: &runnerv1.TaskState{
|
||||
Id: req.State.Id,
|
||||
Id: req.Msg.State.Id,
|
||||
Result: task.Status.AsResult(),
|
||||
},
|
||||
SentOutputs: sentOutputs,
|
||||
}, nil
|
||||
}), nil
|
||||
}
|
||||
|
||||
// UpdateLog uploads log of the task.
|
||||
func (s *Service) UpdateLog(
|
||||
ctx context.Context,
|
||||
req *runnerv1.UpdateLogRequest,
|
||||
) (*runnerv1.UpdateLogResponse, error) {
|
||||
req *connect.Request[runnerv1.UpdateLogRequest],
|
||||
) (*connect.Response[runnerv1.UpdateLogResponse], error) {
|
||||
runner := GetRunner(ctx)
|
||||
|
||||
res := &runnerv1.UpdateLogResponse{}
|
||||
res := connect.NewResponse(&runnerv1.UpdateLogResponse{})
|
||||
|
||||
task, err := actions_model.GetTaskByID(ctx, req.TaskId)
|
||||
task, err := actions_model.GetTaskByID(ctx, req.Msg.TaskId)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "get task: %v", err)
|
||||
} else if runner.ID != task.RunnerID {
|
||||
@@ -303,8 +301,8 @@ func (s *Service) UpdateLog(
|
||||
|
||||
// Trim rows the runner already had acked.
|
||||
var rows []*runnerv1.LogRow
|
||||
if req.Index <= ack && int64(len(req.Rows))+req.Index > ack {
|
||||
rows = req.Rows[ack-req.Index:]
|
||||
if req.Msg.Index <= ack && int64(len(req.Msg.Rows))+req.Msg.Index > ack {
|
||||
rows = req.Msg.Rows[ack-req.Msg.Index:]
|
||||
}
|
||||
|
||||
// Ack a re-sent finalize idempotently. Appending new rows past the seal errors.
|
||||
@@ -312,15 +310,15 @@ func (s *Service) UpdateLog(
|
||||
if len(rows) > 0 {
|
||||
return nil, status.Errorf(codes.AlreadyExists, "log file has been archived")
|
||||
}
|
||||
res.AckIndex = ack
|
||||
res.Msg.AckIndex = ack
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Bail unless we have new rows or a NoMore to finalize. Even with
|
||||
// NoMore, bail when the runner has outrun the server — archiving a
|
||||
// log with a gap is worse than asking it to retry.
|
||||
if len(rows) == 0 && (!req.NoMore || req.Index > ack) {
|
||||
res.AckIndex = ack
|
||||
if len(rows) == 0 && (!req.Msg.NoMore || req.Msg.Index > ack) {
|
||||
res.Msg.AckIndex = ack
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -337,10 +335,10 @@ func (s *Service) UpdateLog(
|
||||
task.LogSize += int64(n)
|
||||
}
|
||||
|
||||
res.AckIndex = task.LogLength
|
||||
res.Msg.AckIndex = task.LogLength
|
||||
|
||||
var remove func()
|
||||
if req.NoMore {
|
||||
if req.Msg.NoMore {
|
||||
task.LogInStorage = true
|
||||
remove, err = actions.TransferLogs(ctx, task.LogFilename)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,33 +4,14 @@
|
||||
package runner
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
"gitea.dev/actionslib/runner/v1/runnerv1connect"
|
||||
actions_model "gitea.dev/models/actions"
|
||||
|
||||
"connectrpc.com/connect/v2"
|
||||
"connectrpc.com/connect/v2/connecthttp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPlainErrorReachesRunnerAsUnknownWithMessage(t *testing.T) {
|
||||
_, handler := NewRunnerServiceHandler()
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
client := runnerv1connect.NewRunnerServiceClient(connect.NewClient(connecthttp.NewTransport(server.Client(), server.URL)))
|
||||
_, err := client.Register(t.Context(), &runnerv1.RegisterRequest{})
|
||||
connectErr, ok := errors.AsType[*connect.Error](err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, connect.CodeUnknown, connectErr.Code())
|
||||
assert.Equal(t, "missing runner token, name", connectErr.Message())
|
||||
}
|
||||
|
||||
func TestApplyDeclareRequestToRunnerAdvertisedCapabilityEnablesCancelling(t *testing.T) {
|
||||
runner := &actions_model.ActionRunner{}
|
||||
req := &runnerv1.DeclareRequest{
|
||||
|
||||
@@ -335,8 +335,8 @@ func ListIssues(ctx *context.APIContext) {
|
||||
keyword := ctx.FormTrim("q")
|
||||
|
||||
var labelIDs []int64
|
||||
if splitted := strings.Split(ctx.FormString("labels"), ","); len(splitted) > 0 {
|
||||
labelIDs, err = issues_model.GetLabelIDsInRepoByNames(ctx, ctx.Repo.Repository.ID, splitted)
|
||||
if labelsParam := ctx.FormTrim("labels"); labelsParam != "" {
|
||||
labelIDs, err = issues_model.GetLabelIDsInRepoByNames(ctx, ctx.Repo.Repository.ID, strings.Split(labelsParam, ","))
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
|
||||
@@ -73,7 +73,7 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
reactions, _, err := issues_model.FindCommentReactions(ctx, comment.IssueID, comment.ID)
|
||||
reactions, _, err := issues_model.FindCommentReactions(ctx, comment.ID)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
|
||||
@@ -981,14 +981,23 @@ func MergePullRequest(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
message := strings.TrimSpace(form.MergeTitleField)
|
||||
if len(message) == 0 {
|
||||
message, _, err = pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pr, repo_model.MergeStyle(form.Do))
|
||||
useDefaultSquashBody := !form.HasMergeMessageField && !ctx.Req.Form.Has("merge_message_field") && repo_model.MergeStyle(form.Do) == repo_model.MergeStyleSquash
|
||||
if len(message) == 0 || useDefaultSquashBody {
|
||||
defaultTitle, defaultBody, err := pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pr, repo_model.MergeStyle(form.Do))
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
message = util.IfZero(message, defaultTitle)
|
||||
if useDefaultSquashBody {
|
||||
squashCommitMessages, err := pull_service.GetSquashMergeCommitMessages(ctx, pr)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
form.MergeMessageField = git.CommitMessageMerge(squashCommitMessages, defaultBody)
|
||||
}
|
||||
}
|
||||
|
||||
form.MergeMessageField = strings.TrimSpace(form.MergeMessageField)
|
||||
if len(form.MergeMessageField) > 0 {
|
||||
message += "\n\n" + form.MergeMessageField
|
||||
|
||||
@@ -895,6 +895,10 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||
optional.AssignPtrValue(changed, &config.DefaultAllowMaintainerEdit, opts.DefaultAllowMaintainerEdit)
|
||||
optional.AssignPtrString(changed, &config.DefaultMergeStyle, opts.DefaultMergeStyle)
|
||||
optional.AssignPtrString(changed, &config.DefaultUpdateStyle, opts.DefaultUpdateStyle)
|
||||
optional.AssignPtrString(changed, &config.DefaultSquashMergeMessage, opts.SquashMergeCommitMessage)
|
||||
if opts.SquashMergeCommitMessage != nil && !config.DefaultSquashMergeMessage.IsValid() {
|
||||
return util.NewInvalidArgumentErrorf("invalid squash_merge_commit_message: %s", config.DefaultSquashMergeMessage)
|
||||
}
|
||||
// only validate update-style fields when the caller is actually changing one of them,
|
||||
// so unrelated PATCH calls don't reject historical configs.
|
||||
if opts.AllowMergeUpdate != nil || opts.AllowRebaseUpdate != nil || opts.DefaultUpdateStyle != nil {
|
||||
|
||||
@@ -669,7 +669,6 @@ func (cpi *comparePageInfoType) prepareCreatePullRequestPage(ctx *context.Contex
|
||||
}
|
||||
|
||||
ctx.Data["IsProjectsEnabled"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
|
||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||
upload.AddUploadContext(ctx, "comment")
|
||||
|
||||
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.Permission.CanWrite(unit.TypePullRequests)
|
||||
|
||||
+10
-14
@@ -25,6 +25,7 @@ import (
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/markup/markdown"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/setting"
|
||||
api "gitea.dev/modules/structs"
|
||||
"gitea.dev/modules/templates"
|
||||
"gitea.dev/modules/util"
|
||||
@@ -45,7 +46,7 @@ const (
|
||||
tplIssueView templates.TplName = "repo/issue/view"
|
||||
|
||||
tplPullMergeBox templates.TplName = "repo/issue/view_content/pull_merge_box"
|
||||
tplReactions templates.TplName = "repo/issue/view_content/reactions"
|
||||
tplReactions templates.TplName = "shared/reactions"
|
||||
|
||||
issueTemplateKey = "IssueTemplate"
|
||||
issueTemplateTitleKey = "IssueTemplateTitle"
|
||||
@@ -536,25 +537,20 @@ func ChangeIssueReaction(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(issue.Reactions) == 0 {
|
||||
ctx.JSON(http.StatusOK, map[string]any{
|
||||
"empty": true,
|
||||
"html": "",
|
||||
})
|
||||
return
|
||||
}
|
||||
renderReactions(ctx, fmt.Sprintf("%s/issues/%d/reactions", ctx.Repo.RepoLink, issue.Index), issue.Reactions, setting.UI.Reactions)
|
||||
}
|
||||
|
||||
func renderReactions(ctx *context.Context, actionURL string, reactions issues_model.ReactionList, allowedReactions []string) {
|
||||
html, err := ctx.RenderToHTML(tplReactions, map[string]any{
|
||||
"ActionURL": fmt.Sprintf("%s/issues/%d/reactions", ctx.Repo.RepoLink, issue.Index),
|
||||
"Reactions": issue.Reactions.GroupByType(),
|
||||
"ActionURL": actionURL,
|
||||
"Reactions": reactions.GroupByType(),
|
||||
"AllowedReactions": allowedReactions,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("ChangeIssueReaction.HTMLString", err)
|
||||
ctx.ServerError("RenderToHTML", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, map[string]any{
|
||||
"html": html,
|
||||
})
|
||||
ctx.JSON(http.StatusOK, map[string]any{"html": html})
|
||||
}
|
||||
|
||||
// GetIssueAttachments returns attachments for the issue
|
||||
|
||||
@@ -300,25 +300,7 @@ func ChangeCommentReaction(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(comment.Reactions) == 0 {
|
||||
ctx.JSON(http.StatusOK, map[string]any{
|
||||
"empty": true,
|
||||
"html": "",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
html, err := ctx.RenderToHTML(tplReactions, map[string]any{
|
||||
"ActionURL": fmt.Sprintf("%s/comments/%d/reactions", ctx.Repo.RepoLink, comment.ID),
|
||||
"Reactions": comment.Reactions.GroupByType(),
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("ChangeCommentReaction.HTMLString", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, map[string]any{
|
||||
"html": html,
|
||||
})
|
||||
renderReactions(ctx, fmt.Sprintf("%s/comments/%d/reactions", ctx.Repo.RepoLink, comment.ID), comment.Reactions, setting.UI.Reactions)
|
||||
}
|
||||
|
||||
// GetCommentAttachments returns attachments for the comment
|
||||
|
||||
@@ -111,7 +111,6 @@ func NewIssue(ctx *context.Context) {
|
||||
|
||||
isProjectsEnabled := ctx.Repo.Permission.CanRead(unit.TypeProjects)
|
||||
ctx.Data["IsProjectsEnabled"] = isProjectsEnabled
|
||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||
upload.AddUploadContext(ctx, "comment")
|
||||
|
||||
pageMetaData := retrieveRepoIssueMetaDataWithPrefill(ctx, ctx.Repo.Repository, false)
|
||||
|
||||
@@ -362,7 +362,7 @@ func ViewIssue(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Data["IsProjectsEnabled"] = ctx.Repo.Permission.CanRead(unit.TypeProjects)
|
||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||
ctx.Data["AllowedIssueReactions"] = setting.UI.Reactions
|
||||
upload.AddUploadContext(ctx, "comment")
|
||||
|
||||
if err := issue.LoadAttributes(ctx); err != nil {
|
||||
|
||||
+21
-18
@@ -370,8 +370,7 @@ func (prInfo *pullRequestViewInfo) prepareViewFillCompareInfo(ctx *context.Conte
|
||||
pull := prInfo.issue.PullRequest
|
||||
prInfo.CompareInfo, err = git_service.GetCompareInfo(ctx, ctx.Repo.Repository, ctx.Repo.Repository, ctx.Repo.GitRepo, baseRef, git.RefName(pull.GetGitHeadRefName()), false, false)
|
||||
if err != nil {
|
||||
isKnownErrorForBroken := errors.Is(err, util.ErrNotExist) || gitcmd.IsStderr(err, gitcmd.StderrNotValidObjectName) || gitcmd.IsStderr(err, gitcmd.StderrUnknownRevisionOrPath)
|
||||
if !isKnownErrorForBroken {
|
||||
if !errors.Is(err, util.ErrNotExist) && !gitcmd.IsStderr(err, gitcmd.StderrNotValidObjectName) {
|
||||
log.Error("GetCompareInfo: %v", err)
|
||||
}
|
||||
prInfo.IsPullRequestBroken = true
|
||||
@@ -885,35 +884,39 @@ func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) {
|
||||
}
|
||||
ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
|
||||
|
||||
currentReview, err := issues_model.GetCurrentReview(ctx, ctx.Doer, issue)
|
||||
if err != nil && !issues_model.IsErrReviewNotExist(err) {
|
||||
ctx.ServerError("GetCurrentReview", err)
|
||||
return
|
||||
}
|
||||
numPendingCodeComments := int64(0)
|
||||
if currentReview != nil {
|
||||
numPendingCodeComments, err = issues_model.CountComments(ctx, &issues_model.FindCommentsOptions{
|
||||
Type: issues_model.CommentTypeCode,
|
||||
ReviewID: currentReview.ID,
|
||||
IssueID: issue.ID,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("CountComments", err)
|
||||
var numPendingCodeComments int64
|
||||
var currentPendingReview *issues_model.Review
|
||||
if ctx.Doer != nil {
|
||||
currentPendingReview, err = issues_model.GetCurrentReview(ctx, ctx.Doer, issue)
|
||||
if err != nil && !issues_model.IsErrReviewNotExist(err) {
|
||||
ctx.ServerError("GetCurrentReview", err)
|
||||
return
|
||||
}
|
||||
if currentPendingReview != nil {
|
||||
numPendingCodeComments, err = issues_model.CountComments(ctx, &issues_model.FindCommentsOptions{
|
||||
Type: issues_model.CommentTypeCode,
|
||||
ReviewID: currentPendingReview.ID,
|
||||
IssueID: issue.ID,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("CountComments", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.Data["CurrentReview"] = currentReview
|
||||
ctx.Data["CurrentReview"] = currentPendingReview
|
||||
ctx.Data["PendingCodeCommentNumber"] = numPendingCodeComments
|
||||
|
||||
ctx.Data["IsIssuePoster"] = ctx.Doer != nil && issue.IsPoster(ctx.Doer.ID)
|
||||
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull)
|
||||
|
||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||
// For files changed page
|
||||
PrepareBranchList(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["AllowedIssueReactions"] = setting.UI.Reactions
|
||||
upload.AddUploadContext(ctx, "comment")
|
||||
|
||||
ctx.Data["CanBlockUser"] = func(blocker, blockee *user_model.User) bool {
|
||||
|
||||
@@ -55,7 +55,6 @@ func RenderNewCodeCommentForm(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
ctx.Data["AfterCommitID"] = pullHeadCommitID
|
||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||
upload.AddUploadContext(ctx, "comment")
|
||||
ctx.HTML(http.StatusOK, tplNewComment)
|
||||
}
|
||||
@@ -186,7 +185,7 @@ func renderConversation(ctx *context.Context, comment *issues_model.Comment, ori
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||
ctx.Data["AllowedIssueReactions"] = setting.UI.Reactions
|
||||
upload.AddUploadContext(ctx, "comment")
|
||||
|
||||
ctx.Data["comments"] = comments
|
||||
|
||||
+85
-13
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"gitea.dev/models/db"
|
||||
git_model "gitea.dev/models/git"
|
||||
issues_model "gitea.dev/models/issues"
|
||||
"gitea.dev/models/renderhelper"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
@@ -73,6 +74,7 @@ type ReleaseInfo struct {
|
||||
Release *repo_model.Release
|
||||
CommitStatus *git_model.CommitStatus
|
||||
CommitStatuses []*git_model.CommitStatus
|
||||
Reactions issues_model.ReactionList
|
||||
}
|
||||
|
||||
func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions) ([]*ReleaseInfo, error) {
|
||||
@@ -81,17 +83,31 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
releaseIDs := make([]int64, 0, len(releases))
|
||||
for _, release := range releases {
|
||||
release.Repo = ctx.Repo.Repository
|
||||
releaseIDs = append(releaseIDs, release.ID)
|
||||
}
|
||||
|
||||
if err = repo_model.GetReleaseAttachments(ctx, releases...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reactions, err := issues_model.FindReleaseReactions(ctx, releaseIDs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacheUsers, err := reactions.LoadUsers(ctx, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reactionsByRelease := make(map[int64]issues_model.ReactionList)
|
||||
for _, reaction := range reactions {
|
||||
reactionsByRelease[reaction.ReleaseID] = append(reactionsByRelease[reaction.ReleaseID], reaction)
|
||||
}
|
||||
|
||||
// Temporary cache commits count of used branches to speed up.
|
||||
countCache := make(map[string]int64)
|
||||
cacheUsers := make(map[int64]*user_model.User)
|
||||
if ctx.Doer != nil {
|
||||
cacheUsers[ctx.Doer.ID] = ctx.Doer
|
||||
}
|
||||
@@ -138,7 +154,8 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
|
||||
}
|
||||
|
||||
info := &ReleaseInfo{
|
||||
Release: r,
|
||||
Release: r,
|
||||
Reactions: reactionsByRelease[r.ID],
|
||||
}
|
||||
|
||||
if canReadCode {
|
||||
@@ -154,10 +171,22 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
|
||||
return releaseInfos, nil
|
||||
}
|
||||
|
||||
// Releases render releases list page
|
||||
func Releases(ctx *context.Context) {
|
||||
func prepareReleasePageCommon(ctx *context.Context) (ret struct {
|
||||
writeAccess bool
|
||||
},
|
||||
) {
|
||||
ctx.Data["PageIsReleaseList"] = true
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.releases")
|
||||
ctx.Data["AllowedReleaseReactions"] = release_service.AllowedReleaseReactions()
|
||||
|
||||
ret.writeAccess = ctx.Repo.Permission.CanWrite(unit.TypeReleases)
|
||||
ctx.Data["CanCreateRelease"] = ret.writeAccess && !ctx.Repo.Repository.IsArchived
|
||||
return ret
|
||||
}
|
||||
|
||||
// Releases render releases list page
|
||||
func Releases(ctx *context.Context) {
|
||||
preparedData := prepareReleasePageCommon(ctx)
|
||||
|
||||
listOptions := db.ListOptions{
|
||||
Page: ctx.FormInt("page"),
|
||||
@@ -170,13 +199,10 @@ func Releases(ctx *context.Context) {
|
||||
listOptions.PageSize = setting.API.MaxResponseItems
|
||||
}
|
||||
|
||||
writeAccess := ctx.Repo.Permission.CanWrite(unit.TypeReleases)
|
||||
ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
|
||||
|
||||
releases, err := getReleaseInfos(ctx, &repo_model.FindReleasesOptions{
|
||||
ListOptions: listOptions,
|
||||
// only show draft releases for users who can write, read-only users shouldn't see draft releases.
|
||||
IncludeDrafts: writeAccess,
|
||||
IncludeDrafts: preparedData.writeAccess,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -275,17 +301,14 @@ func releasesOrTagsFeed(ctx *context.Context, isReleasesOnly bool, formatType st
|
||||
|
||||
// SingleRelease renders a single release's page
|
||||
func SingleRelease(ctx *context.Context) {
|
||||
ctx.Data["PageIsReleaseList"] = true
|
||||
|
||||
writeAccess := ctx.Repo.Permission.CanWrite(unit.TypeReleases)
|
||||
ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
|
||||
preparedData := prepareReleasePageCommon(ctx)
|
||||
|
||||
releases, err := getReleaseInfos(ctx, &repo_model.FindReleasesOptions{
|
||||
ListOptions: db.ListOptions{Page: 1, PageSize: 1},
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
TagNames: []string{ctx.PathParam("*")},
|
||||
// only show draft releases for users who can write, read-only users shouldn't see draft releases.
|
||||
IncludeDrafts: writeAccess,
|
||||
IncludeDrafts: preparedData.writeAccess,
|
||||
IncludeTags: true,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -681,3 +704,52 @@ func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) {
|
||||
|
||||
redirect()
|
||||
}
|
||||
|
||||
func ChangeReleaseReaction(ctx *context.Context) {
|
||||
form := context.GetFetchActionForm[*forms.ReactionForm](ctx)
|
||||
if form == nil {
|
||||
return
|
||||
}
|
||||
|
||||
rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
ctx.ServerError("GetReleaseForRepoByID", err)
|
||||
return
|
||||
}
|
||||
if rel.IsTag || rel.IsDraft {
|
||||
ctx.NotFound(nil)
|
||||
return
|
||||
}
|
||||
rel.Repo = ctx.Repo.Repository
|
||||
|
||||
switch ctx.PathParam("action") {
|
||||
case "react":
|
||||
_, err = release_service.CreateReleaseReaction(ctx, ctx.Doer, rel, form.Content)
|
||||
if errors.Is(err, util.ErrPermissionDenied) {
|
||||
ctx.HTTPError(http.StatusForbidden)
|
||||
return
|
||||
} else if err != nil && !errors.Is(err, util.ErrAlreadyExist) {
|
||||
ctx.ServerError("CreateReleaseReaction", err)
|
||||
return
|
||||
}
|
||||
case "unreact":
|
||||
if err = issues_model.DeleteReleaseReaction(ctx, ctx.Doer.ID, rel.ID, form.Content); err != nil {
|
||||
ctx.ServerError("DeleteReleaseReaction", err)
|
||||
return
|
||||
}
|
||||
default:
|
||||
ctx.NotFound(nil)
|
||||
return
|
||||
}
|
||||
|
||||
reactions, err := issues_model.FindReleaseReactions(ctx, rel.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("FindReleaseReactions", err)
|
||||
return
|
||||
}
|
||||
if _, err = reactions.LoadUsers(ctx, ctx.Repo.Repository); err != nil {
|
||||
ctx.ServerError("LoadUsers", err)
|
||||
return
|
||||
}
|
||||
renderReactions(ctx, fmt.Sprintf("%s/releases/%d/reactions", ctx.Repo.RepoLink, rel.ID), reactions, release_service.AllowedReleaseReactions())
|
||||
}
|
||||
@@ -680,6 +680,7 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
|
||||
DefaultUpdateStyle: defaultUpdateStyle,
|
||||
DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge,
|
||||
DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle),
|
||||
DefaultSquashMergeMessage: util.IfZero(repo_model.SquashMergeMessage(form.PullsDefaultSquashMergeMessage), repo_model.DefaultPullRequestsConfig().DefaultSquashMergeMessage),
|
||||
DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit,
|
||||
DefaultTargetBranch: strings.TrimSpace(form.DefaultTargetBranch),
|
||||
}
|
||||
@@ -687,6 +688,10 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
|
||||
ctx.JSONErrorAuto(err)
|
||||
return
|
||||
}
|
||||
if !prConfig.DefaultSquashMergeMessage.IsValid() {
|
||||
ctx.JSONErrorAuto(util.NewInvalidArgumentErrorf("invalid default squash merge message: %s", prConfig.DefaultSquashMergeMessage))
|
||||
return
|
||||
}
|
||||
units = append(units, newRepoUnit(repo, unit_model.TypePullRequests, prConfig))
|
||||
} else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
|
||||
|
||||
+4
-3
@@ -224,13 +224,13 @@ func verifyAuthWithOptionsWeb(options *common.VerifyOptions) func(ctx *context.C
|
||||
}
|
||||
|
||||
if options.SignInRequired && !ctx.IsSigned {
|
||||
ctx.Redirect(middleware.RedirectLinkUserLogin(ctx.Req))
|
||||
ctx.RedirectForLogin(middleware.RedirectLinkUserLogin(ctx.Req))
|
||||
return
|
||||
}
|
||||
|
||||
// Redirect to log in page if auto-signin info is provided and has not signed in.
|
||||
if !options.SignOutRequired && !ctx.IsSigned && ctx.GetSiteCookie(setting.CookieRememberName) != "" {
|
||||
ctx.Redirect(middleware.RedirectLinkUserLogin(ctx.Req))
|
||||
ctx.RedirectForLogin(middleware.RedirectLinkUserLogin(ctx.Req))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1503,6 +1503,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
|
||||
}, ctxDataSet(reqctx.ContextData{"EnableFeed": setting.Other.EnableFeed}))
|
||||
m.Get("/releases/attachments/{uuid}", webAuth.AllowBasic, webAuth.AllowOAuth2, repo.GetAttachment)
|
||||
m.Get("/releases/download/{vTag}/{fileName}", webAuth.AllowBasic, webAuth.AllowOAuth2, repo.RedirectDownload)
|
||||
m.Post("/releases/{id}/reactions/{action}", reqSignIn, context.RepoMustNotBeArchived(), repo.ChangeReleaseReaction)
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/new", repo.NewRelease)
|
||||
m.Post("/new", web.Bind[*forms.NewReleaseForm](), repo.NewReleasePost)
|
||||
@@ -1661,7 +1662,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
|
||||
m.Get("/new_comment", repo.RenderNewCodeCommentForm)
|
||||
m.Post("/comments", web.Bind[*forms.CodeCommentForm](), repo.SetShowOutdatedComments, repo.CreateCodeComment)
|
||||
m.Post("/submit", web.Bind[*forms.SubmitReviewForm](), repo.SubmitReview)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
}, reqSignIn, context.RepoMustNotBeArchived())
|
||||
})
|
||||
})
|
||||
}, optSignIn, context.RepoAssignment, repo.MustAllowPulls, reqUnitPullsReader)
|
||||
|
||||
@@ -148,16 +148,26 @@ func (b *Base) PlainText(status int, text string) {
|
||||
|
||||
// Redirect redirects the request
|
||||
func (b *Base) Redirect(location string, status ...int) {
|
||||
code := util.OptionalArg(status, http.StatusSeeOther)
|
||||
// In case the request is made by "fetch-action" module, make JS redirect to the new location
|
||||
// Otherwise, the JS fetch will follow the redirection and read a "login" page, embed it to the current page, which is not expected.
|
||||
if httplib.IsGiteaFetchActionRequest(b.Req) {
|
||||
b.JSON(http.StatusOK, map[string]any{"redirect": location})
|
||||
return
|
||||
}
|
||||
code := util.OptionalArg(status, http.StatusSeeOther)
|
||||
http.Redirect(b.Resp, b.Req, location, code)
|
||||
}
|
||||
|
||||
func (b *Base) RedirectForLogin(location string) {
|
||||
if httplib.IsGiteaFetchActionRequest(b.Req) {
|
||||
// let fetch-action handles the redirect automatically
|
||||
b.JSON(http.StatusUnauthorized, map[string]any{"redirect": location})
|
||||
} else {
|
||||
// keep the old behaivor, use 303 to redirect to login page
|
||||
http.Redirect(b.Resp, b.Req, location, http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
|
||||
type ServeHeaderOptions = httplib.ServeHeaderOptions
|
||||
|
||||
func (b *Base) SetServeHeaders(opts ServeHeaderOptions) {
|
||||
|
||||
@@ -104,6 +104,7 @@ type uploadOptions struct {
|
||||
|
||||
// AddUploadContext renders template values for dropzone
|
||||
func AddUploadContext(ctx *context.Context, uploadType string) {
|
||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||
switch uploadType {
|
||||
case "release":
|
||||
ctx.Data["UploadOptions"] = uploadOptions{
|
||||
|
||||
@@ -105,6 +105,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||
defaultDeleteBranchAfterMerge := false
|
||||
defaultMergeStyle := repo_model.MergeStyleMerge
|
||||
defaultUpdateStyle := repo_model.UpdateStyleMerge
|
||||
defaultSquashMergeMessage := repo_model.DefaultPullRequestsConfig().DefaultSquashMergeMessage
|
||||
defaultAllowMaintainerEdit := false
|
||||
defaultTargetBranch := ""
|
||||
if unit, err := repo.GetUnit(ctx, unit_model.TypePullRequests); err == nil {
|
||||
@@ -123,6 +124,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||
defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge
|
||||
defaultMergeStyle = config.DefaultMergeStyle
|
||||
defaultUpdateStyle = config.DefaultUpdateStyle
|
||||
defaultSquashMergeMessage = config.DefaultSquashMergeMessage
|
||||
defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit
|
||||
defaultTargetBranch = config.DefaultTargetBranch
|
||||
}
|
||||
@@ -251,6 +253,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
|
||||
DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge,
|
||||
DefaultMergeStyle: string(defaultMergeStyle),
|
||||
DefaultUpdateStyle: string(defaultUpdateStyle),
|
||||
SquashMergeCommitMessage: string(defaultSquashMergeMessage),
|
||||
DefaultAllowMaintainerEdit: defaultAllowMaintainerEdit,
|
||||
DefaultTargetBranch: defaultTargetBranch,
|
||||
AvatarURL: repo.AvatarLink(ctx),
|
||||
|
||||
@@ -133,6 +133,7 @@ type RepoSettingForm struct {
|
||||
PullsAllowMergeUpdate bool
|
||||
PullsAllowRebaseUpdate bool
|
||||
PullsDefaultUpdateStyle string
|
||||
PullsDefaultSquashMergeMessage string
|
||||
DefaultDeleteBranchAfterMerge bool
|
||||
DefaultAllowMaintainerEdit bool
|
||||
DefaultTargetBranch string
|
||||
@@ -424,20 +425,21 @@ type MergePullRequestForm struct {
|
||||
ForceMerge bool `json:"force_merge,omitempty"`
|
||||
MergeWhenChecksSucceed bool `json:"merge_when_checks_succeed,omitempty"`
|
||||
DeleteBranchAfterMerge *bool `json:"delete_branch_after_merge,omitempty"`
|
||||
HasMergeMessageField bool `form:"-" json:"-"`
|
||||
}
|
||||
|
||||
func (f *MergePullRequestForm) UnmarshalJSON(b []byte) error {
|
||||
// This is for backward compatibility, to support both field names like "do" and "Do",
|
||||
// because old code doesn't have "json" tag for these fields
|
||||
type aux struct {
|
||||
Do1 string `json:"do"`
|
||||
Do2 string `json:"Do"`
|
||||
MergeTitleField1 string `json:"merge_title_field"`
|
||||
MergeTitleField2 string `json:"MergeTitleField"`
|
||||
MergeMessageField1 string `json:"merge_message_field"`
|
||||
MergeMessageField2 string `json:"MergeMessageField"`
|
||||
MergeCommitID1 string `json:"merge_commit_id"`
|
||||
MergeCommitID2 string `json:"MergeCommitID"`
|
||||
Do1 string `json:"do"`
|
||||
Do2 string `json:"Do"`
|
||||
MergeTitleField1 string `json:"merge_title_field"`
|
||||
MergeTitleField2 string `json:"MergeTitleField"`
|
||||
MergeMessageField1 *string `json:"merge_message_field"`
|
||||
MergeMessageField2 *string `json:"MergeMessageField"`
|
||||
MergeCommitID1 string `json:"merge_commit_id"`
|
||||
MergeCommitID2 string `json:"MergeCommitID"`
|
||||
|
||||
HeadCommitID string `json:"head_commit_id"`
|
||||
ForceMerge bool `json:"force_merge"`
|
||||
@@ -450,7 +452,10 @@ func (f *MergePullRequestForm) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
f.Do = util.IfZero(a.Do1, a.Do2)
|
||||
f.MergeTitleField = util.IfZero(a.MergeTitleField1, a.MergeTitleField2)
|
||||
f.MergeMessageField = util.IfZero(a.MergeMessageField1, a.MergeMessageField2)
|
||||
f.MergeMessageField, f.HasMergeMessageField = "", false
|
||||
if mergeMessageField := util.IfZero(a.MergeMessageField1, a.MergeMessageField2); mergeMessageField != nil {
|
||||
f.MergeMessageField, f.HasMergeMessageField = *mergeMessageField, true
|
||||
}
|
||||
f.MergeCommitID = util.IfZero(a.MergeCommitID1, a.MergeCommitID2)
|
||||
f.HeadCommitID = a.HeadCommitID
|
||||
f.ForceMerge = a.ForceMerge
|
||||
|
||||
@@ -51,6 +51,7 @@ func TestMergePullRequestForm(t *testing.T) {
|
||||
ForceMerge: true,
|
||||
MergeWhenChecksSucceed: true,
|
||||
DeleteBranchAfterMerge: new(true),
|
||||
HasMergeMessageField: true,
|
||||
}
|
||||
|
||||
t.Run("NewFields", func(t *testing.T) {
|
||||
@@ -84,4 +85,18 @@ func TestMergePullRequestForm(t *testing.T) {
|
||||
require.NoError(t, json.Unmarshal([]byte(input), &m))
|
||||
assert.Equal(t, expected, m)
|
||||
})
|
||||
|
||||
t.Run("OmittedMergeMessageFieldClearsPreviousMessage", func(t *testing.T) {
|
||||
form := &MergePullRequestForm{MergeMessageField: "previous message", HasMergeMessageField: true}
|
||||
require.NoError(t, json.Unmarshal([]byte(`{"do": "squash"}`), &form))
|
||||
assert.Empty(t, form.MergeMessageField)
|
||||
assert.False(t, form.HasMergeMessageField)
|
||||
})
|
||||
|
||||
t.Run("EmptyMergeMessageField", func(t *testing.T) {
|
||||
var form MergePullRequestForm
|
||||
require.NoError(t, json.Unmarshal([]byte(`{"do": "squash", "merge_message_field": ""}`), &form))
|
||||
assert.Empty(t, form.MergeMessageField)
|
||||
assert.True(t, form.HasMergeMessageField)
|
||||
})
|
||||
}
|
||||
@@ -1596,6 +1596,22 @@ func CommentAsDiff(ctx context.Context, c *issues_model.Comment) (*Diff, error)
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
// GetCodeCommentPatch returns the code snippet stored with a code comment
|
||||
func GetCodeCommentPatch(ctx context.Context, gitRepo *git.Repository, mergeBase, headCommitID, treePath string, line int64) (string, error) {
|
||||
patch, err := git.GetFileDiffCutAroundLine(ctx, gitRepo, mergeBase, headCommitID, treePath,
|
||||
int64((&issues_model.Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines,
|
||||
)
|
||||
contextCommitID := util.Iif(line < 0, mergeBase, headCommitID)
|
||||
if err != nil || patch != "" || contextCommitID == "" {
|
||||
return patch, err
|
||||
}
|
||||
patch, err = GeneratePatchForUnchangedLine(ctx, gitRepo, contextCommitID, treePath, line, setting.UI.CodeCommentLines)
|
||||
if err != nil {
|
||||
log.Debug("Unable to generate patch for unchanged line (file=%s, line=%d, commit=%s): %v", treePath, line, contextCommitID, err)
|
||||
}
|
||||
return patch, nil
|
||||
}
|
||||
|
||||
// GeneratePatchForUnchangedLine creates a patch showing code context for an unchanged line
|
||||
func GeneratePatchForUnchangedLine(ctx context.Context, gitRepo *git.Repository, commitID, treePath string, line int64, contextLines int) (string, error) {
|
||||
commit, err := gitRepo.GetCommit(ctx, commitID)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
issues_model "gitea.dev/models/issues"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/setting"
|
||||
)
|
||||
|
||||
// CreateIssueReaction creates a reaction on an issue.
|
||||
@@ -20,7 +21,7 @@ func CreateIssueReaction(ctx context.Context, doer *user_model.User, issue *issu
|
||||
return nil, user_model.ErrBlockedUser
|
||||
}
|
||||
|
||||
return issues_model.CreateReaction(ctx, &issues_model.ReactionOptions{
|
||||
return issues_model.CreateReaction(ctx, setting.UI.ReactionsLookup, &issues_model.ReactionOptions{
|
||||
Type: content,
|
||||
DoerID: doer.ID,
|
||||
IssueID: issue.ID,
|
||||
@@ -41,7 +42,7 @@ func CreateCommentReaction(ctx context.Context, doer *user_model.User, comment *
|
||||
return nil, user_model.ErrBlockedUser
|
||||
}
|
||||
|
||||
return issues_model.CreateReaction(ctx, &issues_model.ReactionOptions{
|
||||
return issues_model.CreateReaction(ctx, setting.UI.ReactionsLookup, &issues_model.ReactionOptions{
|
||||
Type: content,
|
||||
DoerID: doer.ID,
|
||||
IssueID: comment.Issue.ID,
|
||||
|
||||
@@ -90,9 +90,7 @@ func TestIssueReactionCount(t *testing.T) {
|
||||
addReaction(t, user4, issue, nil, "heart")
|
||||
addReaction(t, ghost, issue, nil, "-1")
|
||||
|
||||
reactionsList, _, err := issues_model.FindReactions(t.Context(), issues_model.FindReactionsOptions{
|
||||
IssueID: issue.ID,
|
||||
})
|
||||
reactionsList, _, err := issues_model.FindReactions(t.Context(), issues_model.FindReactionsOptions{IssueIDOnly: issue.ID})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, reactionsList, 7)
|
||||
_, err = reactionsList.LoadUsers(t.Context(), repo)
|
||||
@@ -101,7 +99,7 @@ func TestIssueReactionCount(t *testing.T) {
|
||||
reactions := reactionsList.GroupByType()
|
||||
assert.Len(t, reactions["heart"], 4)
|
||||
assert.Equal(t, 2, reactions["heart"].GetMoreUserCount())
|
||||
assert.Equal(t, user1.Name+", "+user2.Name, reactions["heart"].GetFirstUsers())
|
||||
assert.Equal(t, "user1 (User One), user2 (< U<se>r Tw<o > ><)", reactions["heart"].GetFirstUsers())
|
||||
assert.True(t, reactions["heart"].HasUser(1))
|
||||
assert.False(t, reactions["heart"].HasUser(5))
|
||||
assert.False(t, reactions["heart"].HasUser(0))
|
||||
@@ -136,10 +134,7 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
|
||||
addReaction(t, org3, nil, comment, "heart")
|
||||
addReaction(t, user4, nil, comment, "+1")
|
||||
|
||||
reactionsList, _, err := issues_model.FindReactions(t.Context(), issues_model.FindReactionsOptions{
|
||||
IssueID: comment.IssueID,
|
||||
CommentID: comment.ID,
|
||||
})
|
||||
reactionsList, _, err := issues_model.FindReactions(t.Context(), issues_model.FindReactionsOptions{CommentID: comment.ID})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, reactionsList, 4)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/uri"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/services/gitdiff"
|
||||
"gitea.dev/services/pull"
|
||||
repo_service "gitea.dev/services/repository"
|
||||
)
|
||||
@@ -905,10 +906,7 @@ func (g *GiteaLocalUploader) CreateReviews(ctx context.Context, reviews ...*base
|
||||
// SECURITY: The TreePath must be cleaned! use relative path
|
||||
comment.TreePath = util.PathJoinRel(comment.TreePath)
|
||||
|
||||
patch, _ := git.GetFileDiffCutAroundLine(ctx,
|
||||
g.gitRepo, pr.MergeBase, headCommitID, comment.TreePath,
|
||||
int64((&issues_model.Comment{Line: int64(line + comment.Position - 1)}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines,
|
||||
)
|
||||
patch, _ := gitdiff.GetCodeCommentPatch(ctx, g.gitRepo, pr.MergeBase, headCommitID, comment.TreePath, int64(line+comment.Position-1))
|
||||
|
||||
if comment.CreatedAt.IsZero() {
|
||||
comment.CreatedAt = review.CreatedAt
|
||||
|
||||
@@ -43,7 +43,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
|
||||
}
|
||||
|
||||
err = git.ManagedRemoteAdd(ctx, repo, remoteName, addr, git.RemoteOptionMirrorFetch)
|
||||
if err != nil && !git.IsRemoteNotExistError(err) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
|
||||
}
|
||||
|
||||
err = git.ManagedRemoteAdd(ctx, repo.WikiStorageRepo(), remoteName, wikiRemotePath, git.RemoteOptionMirrorFetch)
|
||||
if err != nil && !git.IsRemoteNotExistError(err) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+12
-26
@@ -358,26 +358,6 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
|
||||
})
|
||||
}
|
||||
|
||||
func checkForInvalidation(ctx context.Context, requests issues_model.PullRequestList, repoID int64, doer *user_model.User, branch string) error {
|
||||
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetRepositoryByIDCtx: %w", err)
|
||||
}
|
||||
gitRepo, err := git.OpenRepository(ctx, repo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gitrepo.OpenRepository: %w", err)
|
||||
}
|
||||
go func() {
|
||||
// FIXME: graceful: We need to tell the manager we're doing something...
|
||||
err := InvalidateCodeComments(ctx, requests, doer, repo, gitRepo, branch)
|
||||
if err != nil {
|
||||
log.Error("PullRequestList.InvalidateCodeComments: %v", err)
|
||||
}
|
||||
gitRepo.Close()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
type TestPullRequestOptions struct {
|
||||
RepoID int64
|
||||
Doer *user_model.User
|
||||
@@ -436,8 +416,8 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) {
|
||||
if err = headBranchPRs.LoadAttributes(ctx); err != nil {
|
||||
log.Error("PullRequestList.LoadAttributes: %v", err)
|
||||
}
|
||||
if invalidationErr := checkForInvalidation(ctx, headBranchPRs, opts.RepoID, opts.Doer, opts.Branch); invalidationErr != nil {
|
||||
log.Error("checkForInvalidation: %v", invalidationErr)
|
||||
if invalidationErr := InvalidateCodeComments(ctx, headBranchPRs, repo, opts.Branch); invalidationErr != nil {
|
||||
log.Error("InvalidateCodeComments: %v", invalidationErr)
|
||||
}
|
||||
if err == nil {
|
||||
for _, pr := range headBranchPRs {
|
||||
@@ -781,9 +761,15 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
|
||||
return "", err
|
||||
}
|
||||
|
||||
mergeMessage := strings.TrimSpace(pr.Issue.Content) // use PR's title and description as squash commit message
|
||||
if setting.Repository.PullRequest.PopulateSquashCommentWithCommitMessages {
|
||||
mergeMessage = formatSquashMergeCommitMessages(limitedCommits) // use PR's commit messages as squash commit message
|
||||
if err := pr.LoadBaseRepo(ctx); err != nil {
|
||||
return "", err
|
||||
}
|
||||
var mergeMessage string
|
||||
switch pr.BaseRepo.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig().DefaultSquashMergeMessage {
|
||||
case repo_model.SquashMergeMessageCommitMessages:
|
||||
mergeMessage = formatSquashMergeCommitMessages(limitedCommits)
|
||||
case repo_model.SquashMergeMessagePRBody:
|
||||
mergeMessage = strings.TrimSpace(pr.Issue.Content)
|
||||
}
|
||||
coAuthors := collectSquashMergeCommitCoAuthors(ctx, gitRepo, pr, headCommitRef, mergeBaseRef, limit, limitedCommits)
|
||||
return buildSquashMergeCommitMessages(mergeMessage, coAuthors), nil
|
||||
@@ -797,7 +783,7 @@ func buildSquashMergeCommitMessages(mergeMessage string, coAuthors []string) str
|
||||
msgContent, msgSep, msgTrailer := git.CommitMessageSplitTrailer(mergeMessage)
|
||||
if (msgSep == "" || msgSep == "\n\n") && msgTrailer == "" {
|
||||
msgContent = strings.TrimRightFunc(msgContent, unicode.IsSpace)
|
||||
msgSep = "\n\n---------\n\n"
|
||||
msgSep = util.Iif(msgContent == "", "", "\n\n---------\n\n")
|
||||
}
|
||||
var sb strings.Builder
|
||||
sb.WriteString(msgContent)
|
||||
|
||||
@@ -99,6 +99,7 @@ func TestBuildSquashMergeCommitMessages(t *testing.T) {
|
||||
coAuthors []string
|
||||
expected string
|
||||
}{
|
||||
{"", []string{"the-user"}, "Co-authored-by: the-user\n"},
|
||||
{"title", nil, "title"},
|
||||
{"title", []string{"the-user"}, "title\n\n---------\n\nCo-authored-by: the-user\n"},
|
||||
{"title\n\n", []string{"the-user"}, "title\n\n---------\n\nCo-authored-by: the-user\n"},
|
||||
|
||||
+77
-99
@@ -17,14 +17,13 @@ import (
|
||||
"gitea.dev/modules/git/gitcmd"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/services/gitdiff"
|
||||
notify_service "gitea.dev/services/notify"
|
||||
)
|
||||
|
||||
func isErrBlameNotFoundOrNotEnoughLines(err error) bool {
|
||||
return gitcmd.IsStderr(err, gitcmd.StderrNoSuchPath, gitcmd.StderrFileNoEnoughLines)
|
||||
func isErrBlameInvalidated(err error) bool {
|
||||
return gitcmd.IsStderr(err, gitcmd.StderrNoSuchPath, gitcmd.StderrFileNoEnoughLines, gitcmd.StderrBadObject)
|
||||
}
|
||||
|
||||
// ErrDismissRequestOnClosedPR represents an error when a user tries to dismiss a review associated to a closed or merged PR.
|
||||
@@ -47,41 +46,27 @@ func (err ErrDismissRequestOnClosedPR) Unwrap() error {
|
||||
// ErrSubmitReviewOnClosedPR represents an error when a user tries to submit an approve or reject review associated to a closed or merged PR.
|
||||
var ErrSubmitReviewOnClosedPR = errors.New("can't submit review for a closed or merged PR")
|
||||
|
||||
// LineBlame returns the latest commit at the given line
|
||||
func lineBlame(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, branch, file string, line uint) (*git.Commit, error) {
|
||||
sha, err := git.LineBlame(ctx, repo, branch, file, line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(sha) < 40 {
|
||||
return nil, fmt.Errorf("invalid result of blame: %s", sha)
|
||||
}
|
||||
|
||||
objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName)
|
||||
return gitRepo.GetCommit(ctx, sha[:objectFormat.FullLength()])
|
||||
}
|
||||
|
||||
// checkInvalidation checks if the line of code comment got changed by another commit.
|
||||
// If the line got changed the comment is going to be invalidated.
|
||||
func checkInvalidation(ctx context.Context, c *issues_model.Comment, repo *repo_model.Repository, gitRepo *git.Repository, branch string) error {
|
||||
func checkInvalidation(ctx context.Context, c *issues_model.Comment, repo *repo_model.Repository, branch string) error {
|
||||
// FIXME differentiate between previous and proposed line
|
||||
commit, err := lineBlame(ctx, repo, gitRepo, branch, c.TreePath, uint(c.UnsignedLine()))
|
||||
if isErrBlameNotFoundOrNotEnoughLines(err) {
|
||||
commitID, err := git.LineBlameCommitID(ctx, repo, branch, c.TreePath, uint(c.UnsignedLine()))
|
||||
if isErrBlameInvalidated(err) {
|
||||
c.Invalidated = true
|
||||
return issues_model.UpdateCommentInvalidate(ctx, c)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.CommitSHA != "" && c.CommitSHA != commit.ID.String() {
|
||||
if c.CommitSHA != "" && c.CommitSHA != commitID {
|
||||
c.Invalidated = true
|
||||
return issues_model.UpdateCommentInvalidate(ctx, c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InvalidateCodeComments will lookup the prs for code comments which got invalidated by change
|
||||
func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestList, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branch string) error {
|
||||
// InvalidateCodeComments will look up the prs for code comments which got invalidated by change
|
||||
func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestList, repo *repo_model.Repository, branch string) error {
|
||||
if len(prs) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -97,35 +82,31 @@ func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestLis
|
||||
return fmt.Errorf("find code comments: %v", err)
|
||||
}
|
||||
for _, comment := range codeComments {
|
||||
if err := checkInvalidation(ctx, comment, repo, gitRepo, branch); err != nil {
|
||||
if err := checkInvalidation(ctx, comment, repo, branch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateCodeComment creates a comment on the code line
|
||||
func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *issues_model.Issue, line int64, content, treePath string, pendingReview bool, replyReviewID int64, latestCommitID string, attachments []string) (*issues_model.Comment, error) {
|
||||
var (
|
||||
existsReview bool
|
||||
err error
|
||||
)
|
||||
|
||||
// CreateCodeComment() is used for:
|
||||
// - Single comments
|
||||
// - Comments that are part of a review
|
||||
// - Comments that reply to an existing review
|
||||
|
||||
if !pendingReview && replyReviewID != 0 {
|
||||
// CreateCodeComment creates a code comment in a review on the code line:
|
||||
// When pendingReview=false:
|
||||
// * no replyReviewID: create a new review and add the comment to it, and submit the review
|
||||
// * replyReviewID is set: add the comment to the existing submitted review
|
||||
// When pendingReview=true: get current pending review or create a new one, and add the comment to it
|
||||
func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *issues_model.Issue, line int64, content, treePath string, pendingReview bool, replyReviewID int64, latestCommitID string, attachments []string) (_ *issues_model.Comment, err error) {
|
||||
replyToSubmittedReview := !pendingReview && replyReviewID != 0
|
||||
if replyToSubmittedReview {
|
||||
// It's not part of a review; maybe a reply to a review comment or a single comment.
|
||||
// Check if there are reviews for that line already; if there are, this is a reply
|
||||
if existsReview, err = issues_model.ReviewExists(ctx, issue, treePath, line); err != nil {
|
||||
if replyToSubmittedReview, err = issues_model.ReviewExists(ctx, issue, treePath, line); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Comments that are replies don't require a review header to show up in the issue view
|
||||
if !pendingReview && existsReview {
|
||||
// Add the code comment to existing submitted review
|
||||
if replyToSubmittedReview {
|
||||
if err = issue.LoadRepo(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -154,39 +135,43 @@ func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.
|
||||
return comment, nil
|
||||
}
|
||||
|
||||
review, err := issues_model.GetCurrentReview(ctx, doer, issue)
|
||||
if err != nil {
|
||||
if !issues_model.IsErrReviewNotExist(err) {
|
||||
// If no existing submitted review, create a pending review and its comment in a single transaction
|
||||
// to avoid failed code comment creation leaving a pending review without any comments
|
||||
comment, err := db.WithTx2(ctx, func(ctx context.Context) (*issues_model.Comment, error) {
|
||||
curReview, err := issues_model.GetCurrentReview(ctx, doer, issue)
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
// create a new pending review for the code comment
|
||||
if curReview, err = issues_model.CreateReview(ctx, issues_model.CreateReviewOptions{
|
||||
Type: issues_model.ReviewTypePending,
|
||||
Reviewer: doer,
|
||||
Issue: issue,
|
||||
Official: false,
|
||||
CommitID: latestCommitID,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if review, err = issues_model.CreateReview(ctx, issues_model.CreateReviewOptions{
|
||||
Type: issues_model.ReviewTypePending,
|
||||
Reviewer: doer,
|
||||
Issue: issue,
|
||||
Official: false,
|
||||
CommitID: latestCommitID,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
comment, err := createCodeComment(ctx,
|
||||
doer,
|
||||
issue.Repo,
|
||||
issue,
|
||||
content,
|
||||
treePath,
|
||||
line,
|
||||
review.ID,
|
||||
attachments,
|
||||
)
|
||||
// add the code comment to the pending review
|
||||
return createCodeComment(ctx,
|
||||
doer,
|
||||
issue.Repo,
|
||||
issue,
|
||||
content,
|
||||
treePath,
|
||||
line,
|
||||
curReview.ID,
|
||||
attachments,
|
||||
)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !pendingReview && !existsReview {
|
||||
// Submit the review we've just created so the comment shows up in the issue view
|
||||
if !pendingReview {
|
||||
// Doer is creating a submitted review, so submit the pending review we've just created,
|
||||
// then the comment shows up in the issue view
|
||||
if _, _, err = SubmitReview(ctx, doer, gitRepo, issue, issues_model.ReviewTypeComment, "", latestCommitID, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -214,7 +199,7 @@ func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_mo
|
||||
defer closer.Close()
|
||||
|
||||
invalidated := false
|
||||
head := pr.GetGitHeadRefName()
|
||||
headRef := pr.GetGitHeadRefName()
|
||||
if line > 0 {
|
||||
if reviewID != 0 {
|
||||
first, err := issues_model.FindComments(ctx, &issues_model.FindCommentsOptions{
|
||||
@@ -227,62 +212,55 @@ func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_mo
|
||||
Page: 1,
|
||||
},
|
||||
})
|
||||
if err == nil && len(first) > 0 {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find first comment for %d line %d path %s. Error: %w", reviewID, line, treePath, err)
|
||||
}
|
||||
|
||||
if len(first) > 0 {
|
||||
// if there are existing comments, use the first comment's information to create the new comment
|
||||
commitID = first[0].CommitSHA
|
||||
invalidated = first[0].Invalidated
|
||||
patch = first[0].Patch
|
||||
} else if err != nil && !issues_model.IsErrCommentNotExist(err) {
|
||||
return nil, fmt.Errorf("Find first comment for %d line %d path %s. Error: %w", reviewID, line, treePath, err)
|
||||
} else {
|
||||
// otherwise, try to get the commitID from the review itself
|
||||
review, err := issues_model.GetReviewByID(ctx, reviewID)
|
||||
if err == nil && len(review.CommitID) > 0 {
|
||||
head = review.CommitID
|
||||
} else if err != nil && !issues_model.IsErrReviewNotExist(err) {
|
||||
return nil, fmt.Errorf("GetReviewByID %d. Error: %w", reviewID, err)
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
return nil, fmt.Errorf("createCodeComment: get review %d: %w", reviewID, err)
|
||||
}
|
||||
if review != nil && review.CommitID != "" {
|
||||
headRef = review.CommitID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(commitID) == 0 {
|
||||
// FIXME validate treePath
|
||||
// Get latest commit referencing the commented line
|
||||
// No need for get commit for base branch changes
|
||||
commit, err := lineBlame(ctx, pr.BaseRepo, gitRepo, head, treePath, uint(line))
|
||||
if err == nil {
|
||||
commitID = commit.ID.String()
|
||||
} else if !isErrBlameNotFoundOrNotEnoughLines(err) {
|
||||
return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %w", pr.GetGitHeadRefName(), gitRepo.LogString(), treePath, line, err)
|
||||
if commitID == "" {
|
||||
// FIXME validate treePath (TODO: unclear comment)
|
||||
// Get latest commit referencing the commented line, error can be ignored then the head commit will be used as fallback
|
||||
commitID, err = git.LineBlameCommitID(ctx, pr.BaseRepo, headRef, treePath, uint(line))
|
||||
// Preserve attribution to the reviewed revision, falling back to the PR head if blame fails.
|
||||
if err != nil && headRef != pr.GetGitHeadRefName() {
|
||||
commitID, err = git.LineBlameCommitID(ctx, pr.BaseRepo, pr.GetGitHeadRefName(), treePath, uint(line))
|
||||
}
|
||||
if err != nil {
|
||||
log.Debug("LineBlame[%s, %s, %s, %d]: %v", pr.GetGitHeadRefName(), gitRepo.LogString(), treePath, line, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only fetch diff if comment is review comment
|
||||
if len(patch) == 0 && reviewID != 0 {
|
||||
if patch == "" && reviewID != 0 {
|
||||
headCommitID, err := gitRepo.GetRefCommitID(ctx, pr.GetGitHeadRefName())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetRefCommitID[%s]: %w", pr.GetGitHeadRefName(), err)
|
||||
}
|
||||
if len(commitID) == 0 {
|
||||
if commitID == "" {
|
||||
commitID = headCommitID
|
||||
}
|
||||
|
||||
patch, err = git.GetFileDiffCutAroundLine(ctx,
|
||||
gitRepo, pr.MergeBase, headCommitID, treePath,
|
||||
int64((&issues_model.Comment{Line: line}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines,
|
||||
)
|
||||
patch, err = gitdiff.GetCodeCommentPatch(ctx, gitRepo, pr.MergeBase, headCommitID, treePath, line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If patch is still empty (unchanged line), generate code context
|
||||
contextCommitID := util.Iif(line < 0, pr.MergeBase, headCommitID) // the line number is relative to the diff sides, not to the blamed commitID
|
||||
if patch == "" && contextCommitID != "" {
|
||||
patch, err = gitdiff.GeneratePatchForUnchangedLine(ctx, gitRepo, contextCommitID, treePath, line, setting.UI.CodeCommentLines)
|
||||
if err != nil {
|
||||
// Log the error but don't fail comment creation
|
||||
log.Debug("Unable to generate patch for unchanged line (file=%s, line=%d, commit=%s): %v", treePath, line, contextCommitID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
Type: issues_model.CommentTypeCode,
|
||||
|
||||
Loaded 100 of 230 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user