mirror of
https://github.com/go-gitea/gitea.git
synced 2026-01-17 09:30:38 +00:00
Compare commits
314 Commits
v1.26.0-de
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
393c854f7b | ||
|
|
65422fde4d | ||
|
|
49edbbbc2e | ||
|
|
69c5921d71 | ||
|
|
67e75f30a8 | ||
|
|
3f46de8265 | ||
|
|
4a9ac53862 | ||
|
|
915a2cd86f | ||
|
|
8e9fb4d14c | ||
|
|
14e8c9b767 | ||
|
|
7b5de594cd | ||
|
|
07ac29da32 | ||
|
|
8a98ac2213 | ||
|
|
1c1a7b8492 | ||
|
|
95ea2df00a | ||
|
|
ed5720af2a | ||
|
|
e95c30eb80 | ||
|
|
2859b0602a | ||
|
|
040fc93046 | ||
|
|
7a23e247e6 | ||
|
|
c8b5a1ddf7 | ||
|
|
b1b5897795 | ||
|
|
1d399bb1d1 | ||
|
|
fbea2c68e8 | ||
|
|
48d5adb39c | ||
|
|
da036f3f35 | ||
|
|
8319d8f381 | ||
|
|
18c393419f | ||
|
|
eec8ee056c | ||
|
|
f6d3c70818 | ||
|
|
e226720cff | ||
|
|
ee9d8893a7 | ||
|
|
f9d3983de2 | ||
|
|
1d01286f4c | ||
|
|
1ee7f8e966 | ||
|
|
426bb491c0 | ||
|
|
78ad28d052 | ||
|
|
af95cbc0de | ||
|
|
b79dbfa990 | ||
|
|
6fb3547417 | ||
|
|
d2baa8103f | ||
|
|
8373f7deb3 | ||
|
|
98981eb749 | ||
|
|
91d871611e | ||
|
|
094104bc91 | ||
|
|
1771569300 | ||
|
|
0ad94dfc70 | ||
|
|
d0cb198c89 | ||
|
|
85dd16b3fc | ||
|
|
c7b3cdf7b1 | ||
|
|
83527d3f8a | ||
|
|
19e1997ee2 | ||
|
|
00cc84e37c | ||
|
|
64fcf847ce | ||
|
|
ff3d68b98a | ||
|
|
776e406363 | ||
|
|
fbbed8c4c4 | ||
|
|
324dcf6f64 | ||
|
|
42d294941c | ||
|
|
eddf875992 | ||
|
|
957151937f | ||
|
|
04607f7d4a | ||
|
|
60de6cefed | ||
|
|
5151e30fb7 | ||
|
|
b6ffe0e4e9 | ||
|
|
bf0b377879 | ||
|
|
0a3a9fb068 | ||
|
|
495fee4555 | ||
|
|
05c3b84f84 | ||
|
|
b4c9057f92 | ||
|
|
d3656ebd95 | ||
|
|
ffea9a27c3 | ||
|
|
9764ae87d2 | ||
|
|
684a541799 | ||
|
|
8f672cea4a | ||
|
|
e06040efd8 | ||
|
|
16aa0fcc98 | ||
|
|
0043ae0139 | ||
|
|
b915e6908c | ||
|
|
36aa39fffe | ||
|
|
0a9c09879d | ||
|
|
a9a4457dc3 | ||
|
|
51e1ab5d7d | ||
|
|
5fa40bacea | ||
|
|
6d0fe5ed39 | ||
|
|
dcad5d8879 | ||
|
|
5f5a87f015 | ||
|
|
aca6726607 | ||
|
|
e0214ab841 | ||
|
|
b671d507e3 | ||
|
|
3e566172f5 | ||
|
|
efd5dd4f0b | ||
|
|
1e22bd712f | ||
|
|
ebf9b4dc6b | ||
|
|
ad49b7bf31 | ||
|
|
852bf5e2a5 | ||
|
|
eaa47c3e09 | ||
|
|
0e916c67cc | ||
|
|
14911d4293 | ||
|
|
d19db18ee7 | ||
|
|
4c67aac23b | ||
|
|
84b74d7c3e | ||
|
|
3bb0770160 | ||
|
|
822ee60bae | ||
|
|
da087270ff | ||
|
|
26602fd207 | ||
|
|
ff70ed6c67 | ||
|
|
7190519fb3 | ||
|
|
1f5237e0d7 | ||
|
|
29057ea55f | ||
|
|
ac8308b5cb | ||
|
|
1e72b15639 | ||
|
|
3102c04c1e | ||
|
|
3e57ba5b36 | ||
|
|
4c06c98dda | ||
|
|
87b855bd15 | ||
|
|
906adff0c1 | ||
|
|
4cbcb91b7b | ||
|
|
bfbc38f40c | ||
|
|
d2a372fc59 | ||
|
|
f25409fab8 | ||
|
|
01351cc6c7 | ||
|
|
a440116a16 | ||
|
|
24b81ac8b9 | ||
|
|
1c69fdccdd | ||
|
|
ed698d1a61 | ||
|
|
d83a071db9 | ||
|
|
69700f9cdd | ||
|
|
98ef79d73a | ||
|
|
b41ccb0627 | ||
|
|
c287a8cdb5 | ||
|
|
ca8c4ebecd | ||
|
|
6675ddc117 | ||
|
|
5fdc84841a | ||
|
|
64960a18f9 | ||
|
|
cb5082f8fe | ||
|
|
ee365f5100 | ||
|
|
b49dd8e32f | ||
|
|
ee6e371e44 | ||
|
|
e30a130b9a | ||
|
|
97cb4409fb | ||
|
|
46d7adefe0 | ||
|
|
9f268edd2f | ||
|
|
ca4b21c305 | ||
|
|
a04a16dc2b | ||
|
|
1e777f92c7 | ||
|
|
5340db4dbe | ||
|
|
7d6861ac54 | ||
|
|
b54af8811e | ||
|
|
f4e38e6367 | ||
|
|
a36951aef6 | ||
|
|
9668913d76 | ||
|
|
ede7f1a069 | ||
|
|
1816c7f9c1 | ||
|
|
66707bc3ea | ||
|
|
000c06d41b | ||
|
|
abe2755f7a | ||
|
|
688430e3ce | ||
|
|
87d5a8507d | ||
|
|
ed977d9702 | ||
|
|
62d750eadb | ||
|
|
a60a8c6966 | ||
|
|
4c51acb26b | ||
|
|
543e3bf7e9 | ||
|
|
1baca49870 | ||
|
|
afc25c5145 | ||
|
|
98eb2b0aba | ||
|
|
adece922f1 | ||
|
|
1da1e644ed | ||
|
|
e1372e5bc1 | ||
|
|
de69e7f16a | ||
|
|
0fb3be7f0e | ||
|
|
d6dc531d4b | ||
|
|
358de23a50 | ||
|
|
018156079b | ||
|
|
1f3558b65c | ||
|
|
b95fd7e13e | ||
|
|
372d24b84b | ||
|
|
2223be2cc4 | ||
|
|
9affb513a8 | ||
|
|
e31f224ad2 | ||
|
|
1c8c56503f | ||
|
|
60314cb688 | ||
|
|
c4c4cf5687 | ||
|
|
050c9485df | ||
|
|
919348665b | ||
|
|
c12bc4aa30 | ||
|
|
367a289b29 | ||
|
|
bfaddbcd0d | ||
|
|
0ce7d66368 | ||
|
|
b2feeddf42 | ||
|
|
eef9406c6b | ||
|
|
aaa8033ee9 | ||
|
|
23a37b4b77 | ||
|
|
61e5cc173e | ||
|
|
84d7496b9d | ||
|
|
525265c1a8 | ||
|
|
d0ca2f6bc3 | ||
|
|
a0f492d9f4 | ||
|
|
206f4c88b1 | ||
|
|
851db77256 | ||
|
|
2be51d0b27 | ||
|
|
850012bf5c | ||
|
|
bb1f52347a | ||
|
|
de26c8acce | ||
|
|
d9c0f86de8 | ||
|
|
37208fef7e | ||
|
|
aa7ec64a54 | ||
|
|
17a6a2bab1 | ||
|
|
685c8c314f | ||
|
|
5cb453c01b | ||
|
|
f2d7931b70 | ||
|
|
b3e5b96111 | ||
|
|
1dac4d13f3 | ||
|
|
b148bef471 | ||
|
|
de70cd3853 | ||
|
|
ef90befef1 | ||
|
|
c3472dd395 | ||
|
|
8b290b87e9 | ||
|
|
3ab8ae5807 | ||
|
|
73e229eb42 | ||
|
|
98ff7d0773 | ||
|
|
8aa1179ce4 | ||
|
|
39c08ce4c1 | ||
|
|
fe25997157 | ||
|
|
95b18eb781 | ||
|
|
d69eede59b | ||
|
|
91839ca01a | ||
|
|
6b5563c54a | ||
|
|
cddff73bbd | ||
|
|
87d670c96b | ||
|
|
2f309b844c | ||
|
|
bc50431e8b | ||
|
|
2a6af15448 | ||
|
|
c9beb0b01f | ||
|
|
5454fdacd4 | ||
|
|
304d836a61 | ||
|
|
cb72c901b3 | ||
|
|
0d740a6a72 | ||
|
|
9a73a1fb83 | ||
|
|
397d666432 | ||
|
|
e03a68c48b | ||
|
|
cab35ff17a | ||
|
|
522c466e24 | ||
|
|
8085c75356 | ||
|
|
195fc715ff | ||
|
|
08b9776970 | ||
|
|
a9f2ea720b | ||
|
|
5f0697243c | ||
|
|
c28aab6714 | ||
|
|
a4e23b81d3 | ||
|
|
3917d27467 | ||
|
|
a2eea2fb2e | ||
|
|
b2ee5be52e | ||
|
|
897e48dde3 | ||
|
|
66ee8f3553 | ||
|
|
c30d74d0f9 | ||
|
|
2d36a0c9ff | ||
|
|
322cb048e7 | ||
|
|
a7eceb57a9 | ||
|
|
ebd88af075 | ||
|
|
5bf7cf788d | ||
|
|
bf8ecf7c93 | ||
|
|
990201dc93 | ||
|
|
c55a017225 | ||
|
|
1bdb0b71b1 | ||
|
|
9ae2e9e76f | ||
|
|
16fc3323b9 | ||
|
|
731d803d19 | ||
|
|
96102c69e7 | ||
|
|
22b92e30ca | ||
|
|
49a0a11f55 | ||
|
|
912515e63a | ||
|
|
f9a4b2753c | ||
|
|
2401812b76 | ||
|
|
3d264ba636 | ||
|
|
662a44d924 | ||
|
|
24a595c3fc | ||
|
|
25c4eb1659 | ||
|
|
b029ad431b | ||
|
|
40f71bcd4c | ||
|
|
327d0a7fdd | ||
|
|
165a3ead52 | ||
|
|
9f664ab330 | ||
|
|
94d99c9c3c | ||
|
|
b8e5e2a93e | ||
|
|
0bc129481d | ||
|
|
03fce8f3d0 | ||
|
|
69f5ee970c | ||
|
|
c9e7fde8b3 | ||
|
|
ad2ff67343 | ||
|
|
cdc0733047 | ||
|
|
0a0baeb3b0 | ||
|
|
851d8f9f7c | ||
|
|
4e7a97dea0 | ||
|
|
e1c2fef593 | ||
|
|
6589326e96 | ||
|
|
17c8aa6587 | ||
|
|
71360a94cb | ||
|
|
c4532101a4 | ||
|
|
efc48c36ff | ||
|
|
340aed3982 | ||
|
|
b907b9fb1a | ||
|
|
c5d74e5869 | ||
|
|
c5332fdc55 | ||
|
|
0f668145e9 | ||
|
|
fbe80e6df2 | ||
|
|
151ef80e28 | ||
|
|
8106d95577 | ||
|
|
1f32170060 | ||
|
|
7bf2972379 | ||
|
|
8ad2a538da | ||
|
|
53dfbbb2ee | ||
|
|
d83676c97a |
@@ -4,7 +4,7 @@ tmp_dir = ".air"
|
||||
[build]
|
||||
pre_cmd = ["killall -9 gitea 2>/dev/null || true"] # kill off potential zombie processes from previous runs
|
||||
cmd = "make --no-print-directory backend"
|
||||
bin = "gitea"
|
||||
entrypoint = ["./gitea"]
|
||||
delay = 2000
|
||||
include_ext = ["go", "tmpl"]
|
||||
include_file = ["main.go"]
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"ghcr.io/devcontainers/features/git-lfs:1.2.5": {},
|
||||
"ghcr.io/jsburckhardt/devcontainer-features/uv:1": {},
|
||||
"ghcr.io/devcontainers/features/python:1": {
|
||||
"version": "3.13"
|
||||
"version": "3.14"
|
||||
},
|
||||
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
|
||||
},
|
||||
|
||||
@@ -74,6 +74,9 @@ cpu.out
|
||||
/VERSION
|
||||
/.air
|
||||
/.go-licenses
|
||||
/Dockerfile
|
||||
/Dockerfile.rootless
|
||||
/.venv
|
||||
|
||||
# Files and folders that were previously generated
|
||||
/public/assets/img/webpack
|
||||
|
||||
@@ -25,6 +25,10 @@ insert_final_newline = false
|
||||
[templates/user/auth/oidc_wellknown.tmpl]
|
||||
indent_style = space
|
||||
|
||||
[templates/shared/actions/runner_badge_*.tmpl]
|
||||
# editconfig lint requires these XML-like files to have charset defined, but the files don't have.
|
||||
charset = unset
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -8,3 +8,4 @@
|
||||
/vendor/** -text -eol linguist-vendored
|
||||
/web_src/js/vendor/** -text -eol linguist-vendored
|
||||
Dockerfile.* linguist-language=Dockerfile
|
||||
Makefile.* linguist-language=Makefile
|
||||
|
||||
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
labels: [modifies/dependencies]
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
cooldown:
|
||||
default-days: 5
|
||||
5
.github/labeler.yml
vendored
5
.github/labeler.yml
vendored
@@ -46,12 +46,11 @@ modifies/internal:
|
||||
- ".gitpod.yml"
|
||||
- ".markdownlint.yaml"
|
||||
- ".spectral.yaml"
|
||||
- "stylelint.config.js"
|
||||
- "stylelint.config.*"
|
||||
- ".yamllint.yaml"
|
||||
- ".github/**"
|
||||
- ".gitea/**"
|
||||
- ".devcontainer/**"
|
||||
- "build.go"
|
||||
- "build/**"
|
||||
- "contrib/**"
|
||||
|
||||
@@ -90,4 +89,4 @@ topic/code-linting:
|
||||
- ".markdownlint.yaml"
|
||||
- ".spectral.yaml"
|
||||
- ".yamllint.yaml"
|
||||
- "stylelint.config.js"
|
||||
- "stylelint.config.*"
|
||||
|
||||
8
.github/workflows/cron-licenses.yml
vendored
8
.github/workflows/cron-licenses.yml
vendored
@@ -9,16 +9,18 @@ jobs:
|
||||
cron-licenses:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'go-gitea/gitea'
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- run: make generate-gitignore
|
||||
timeout-minutes: 40
|
||||
- name: push translations to repo
|
||||
uses: appleboy/git-push-action@v0.0.3
|
||||
uses: appleboy/git-push-action@v1.2.0
|
||||
with:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: GiteaBot
|
||||
|
||||
8
.github/workflows/cron-translations.yml
vendored
8
.github/workflows/cron-translations.yml
vendored
@@ -9,9 +9,11 @@ jobs:
|
||||
crowdin-pull:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'go-gitea/gitea'
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: crowdin/github-action@v1
|
||||
- uses: actions/checkout@v6
|
||||
- uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
@@ -27,7 +29,7 @@ jobs:
|
||||
- name: update locales
|
||||
run: ./build/update-locales.sh
|
||||
- name: push translations to repo
|
||||
uses: appleboy/git-push-action@v0.0.3
|
||||
uses: appleboy/git-push-action@v1.2.0
|
||||
with:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: GiteaBot
|
||||
|
||||
12
.github/workflows/files-changed.yml
vendored
12
.github/workflows/files-changed.yml
vendored
@@ -19,11 +19,15 @@ on:
|
||||
value: ${{ jobs.detect.outputs.swagger }}
|
||||
yaml:
|
||||
value: ${{ jobs.detect.outputs.yaml }}
|
||||
json:
|
||||
value: ${{ jobs.detect.outputs.json }}
|
||||
|
||||
jobs:
|
||||
detect:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 3
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
backend: ${{ steps.changes.outputs.backend }}
|
||||
frontend: ${{ steps.changes.outputs.frontend }}
|
||||
@@ -33,8 +37,9 @@ jobs:
|
||||
docker: ${{ steps.changes.outputs.docker }}
|
||||
swagger: ${{ steps.changes.outputs.swagger }}
|
||||
yaml: ${{ steps.changes.outputs.yaml }}
|
||||
json: ${{ steps.changes.outputs.json }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: changes
|
||||
with:
|
||||
@@ -48,7 +53,7 @@ jobs:
|
||||
- "Makefile"
|
||||
- ".golangci.yml"
|
||||
- ".editorconfig"
|
||||
- "options/locale/locale_en-US.ini"
|
||||
- "options/locale/locale_en-US.json"
|
||||
|
||||
frontend:
|
||||
- "*.js"
|
||||
@@ -98,3 +103,6 @@ jobs:
|
||||
- "**/*.yaml"
|
||||
- ".yamllint.yaml"
|
||||
- "pyproject.toml"
|
||||
|
||||
json:
|
||||
- "**/*.json"
|
||||
|
||||
103
.github/workflows/pull-compliance.yml
vendored
103
.github/workflows/pull-compliance.yml
vendored
@@ -10,14 +10,18 @@ concurrency:
|
||||
jobs:
|
||||
files-changed:
|
||||
uses: ./.github/workflows/files-changed.yml
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
lint-backend:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -30,14 +34,18 @@ jobs:
|
||||
if: needs.files-changed.outputs.templates == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/setup-uv@v6
|
||||
- run: uv python install 3.12
|
||||
- uses: actions/checkout@v6
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
- run: uv python install 3.14
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-py
|
||||
- run: make deps-frontend
|
||||
- run: make lint-templates
|
||||
@@ -46,23 +54,44 @@ jobs:
|
||||
if: needs.files-changed.outputs.yaml == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/setup-uv@v6
|
||||
- run: uv python install 3.12
|
||||
- uses: actions/checkout@v6
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
- run: uv python install 3.14
|
||||
- run: make deps-py
|
||||
- run: make lint-yaml
|
||||
|
||||
lint-json:
|
||||
if: needs.files-changed.outputs.json == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 24
|
||||
- run: make deps-frontend
|
||||
- run: make lint-json
|
||||
|
||||
lint-swagger:
|
||||
if: needs.files-changed.outputs.swagger == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend
|
||||
- run: make lint-swagger
|
||||
|
||||
@@ -70,9 +99,11 @@ jobs:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.templates == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -82,9 +113,11 @@ jobs:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -99,9 +132,11 @@ jobs:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -114,9 +149,11 @@ jobs:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -127,12 +164,16 @@ jobs:
|
||||
if: needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend
|
||||
- run: make lint-frontend
|
||||
- run: make checks-frontend
|
||||
@@ -143,9 +184,11 @@ jobs:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -175,12 +218,16 @@ jobs:
|
||||
if: needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend
|
||||
- run: make lint-md
|
||||
|
||||
@@ -188,9 +235,11 @@ jobs:
|
||||
if: needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
|
||||
38
.github/workflows/pull-db-tests.yml
vendored
38
.github/workflows/pull-db-tests.yml
vendored
@@ -10,11 +10,15 @@ concurrency:
|
||||
jobs:
|
||||
files-changed:
|
||||
uses: ./.github/workflows/files-changed.yml
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
test-pgsql:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
services:
|
||||
pgsql:
|
||||
image: postgres:14
|
||||
@@ -38,8 +42,8 @@ jobs:
|
||||
ports:
|
||||
- "9000:9000"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -65,20 +69,22 @@ jobs:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- run: make deps-backend
|
||||
- run: make backend
|
||||
- run: GOEXPERIMENT='' make backend
|
||||
env:
|
||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||
- name: run migration tests
|
||||
run: make test-sqlite-migration
|
||||
- name: run tests
|
||||
run: make test-sqlite
|
||||
run: GOEXPERIMENT='' make test-sqlite
|
||||
timeout-minutes: 50
|
||||
env:
|
||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||
@@ -90,6 +96,8 @@ jobs:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
services:
|
||||
elasticsearch:
|
||||
image: elasticsearch:7.5.0
|
||||
@@ -124,8 +132,8 @@ jobs:
|
||||
ports:
|
||||
- 10000:10000
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -142,7 +150,7 @@ jobs:
|
||||
RACE_ENABLED: true
|
||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
||||
- name: unit-tests-gogit
|
||||
run: make unit-test-coverage test-check
|
||||
run: GOEXPERIMENT='' make unit-test-coverage test-check
|
||||
env:
|
||||
TAGS: bindata gogit
|
||||
RACE_ENABLED: true
|
||||
@@ -152,6 +160,8 @@ jobs:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
services:
|
||||
mysql:
|
||||
# the bitnami mysql image has more options than the official one, it's easier to customize
|
||||
@@ -177,8 +187,8 @@ jobs:
|
||||
- "587:587"
|
||||
- "993:993"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
@@ -203,6 +213,8 @@ jobs:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
services:
|
||||
mssql:
|
||||
image: mcr.microsoft.com/mssql/server:2019-latest
|
||||
@@ -217,8 +229,8 @@ jobs:
|
||||
ports:
|
||||
- 10000:10000
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
|
||||
22
.github/workflows/pull-docker-dryrun.yml
vendored
22
.github/workflows/pull-docker-dryrun.yml
vendored
@@ -10,26 +10,28 @@ concurrency:
|
||||
jobs:
|
||||
files-changed:
|
||||
uses: ./.github/workflows/files-changed.yml
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
regular:
|
||||
container:
|
||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/build-push-action@v5
|
||||
- name: Build regular container image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
tags: gitea/gitea:linux-amd64
|
||||
|
||||
rootless:
|
||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/build-push-action@v5
|
||||
- name: Build rootless container image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
file: Dockerfile.rootless
|
||||
tags: gitea/gitea:linux-amd64
|
||||
|
||||
35
.github/workflows/pull-e2e-tests.yml
vendored
35
.github/workflows/pull-e2e-tests.yml
vendored
@@ -1,35 +0,0 @@
|
||||
name: e2e-tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
files-changed:
|
||||
uses: ./.github/workflows/files-changed.yml
|
||||
|
||||
test-e2e:
|
||||
# the "test-e2e" won't pass, and it seems that there is no useful test, so skip
|
||||
# if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
if: false
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 24
|
||||
- run: make deps-frontend frontend deps-backend
|
||||
- run: pnpm exec playwright install --with-deps
|
||||
- run: make test-e2e-sqlite
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
USE_REPO_TEST_DIR: 1
|
||||
2
.github/workflows/pull-labeler.yml
vendored
2
.github/workflows/pull-labeler.yml
vendored
@@ -15,6 +15,6 @@ jobs:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/labeler@v6
|
||||
with:
|
||||
sync-labels: true
|
||||
|
||||
98
.github/workflows/release-nightly.yml
vendored
98
.github/workflows/release-nightly.yml
vendored
@@ -11,19 +11,23 @@ concurrency:
|
||||
jobs:
|
||||
nightly-binary:
|
||||
runs-on: namespace-profile-gitea-release-binary
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
@@ -48,7 +52,7 @@ jobs:
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
@@ -56,19 +60,17 @@ jobs:
|
||||
- name: upload binaries to s3
|
||||
run: |
|
||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
||||
nightly-docker-rootful:
|
||||
|
||||
nightly-container:
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write # to publish to ghcr.io
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- name: Get cleaned branch name
|
||||
@@ -76,6 +78,29 @@ jobs:
|
||||
run: |
|
||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
images: |-
|
||||
gitea/gitea
|
||||
ghcr.io/go-gitea/gitea
|
||||
tags: |
|
||||
type=raw,value=${{ steps.clean_name.outputs.branch }}
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta_rootless
|
||||
with:
|
||||
images: |-
|
||||
gitea/gitea
|
||||
ghcr.io/go-gitea/gitea
|
||||
# each tag below will have the suffix of -rootless
|
||||
flavor: |
|
||||
suffix=-rootless
|
||||
tags: |
|
||||
type=raw,value=${{ steps.clean_name.outputs.branch }}
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -87,57 +112,20 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: fetch go modules
|
||||
run: make vendor
|
||||
- name: build rootful docker image
|
||||
uses: docker/build-push-action@v5
|
||||
- name: build regular docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
push: true
|
||||
tags: |-
|
||||
gitea/gitea:${{ steps.clean_name.outputs.branch }}
|
||||
ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}
|
||||
nightly-docker-rootless:
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
permissions:
|
||||
packages: write # to publish to ghcr.io
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
run: |
|
||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Login to GHCR using PAT
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: fetch go modules
|
||||
run: make vendor
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
- name: build rootless docker image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
push: true
|
||||
file: Dockerfile.rootless
|
||||
tags: |-
|
||||
gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
|
||||
ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
|
||||
tags: ${{ steps.meta_rootless.outputs.tags }}
|
||||
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
||||
|
||||
72
.github/workflows/release-tag-rc.yml
vendored
72
.github/workflows/release-tag-rc.yml
vendored
@@ -12,19 +12,23 @@ concurrency:
|
||||
jobs:
|
||||
binary:
|
||||
runs-on: namespace-profile-gitea-release-binary
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
@@ -49,7 +53,7 @@ jobs:
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
@@ -58,7 +62,7 @@ jobs:
|
||||
run: |
|
||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
||||
- name: Install GH CLI
|
||||
uses: dev-hanz-ops/install-gh-cli-action@v0.1.0
|
||||
uses: dev-hanz-ops/install-gh-cli-action@v0.2.1
|
||||
with:
|
||||
gh-cli-version: 2.39.1
|
||||
- name: create github release
|
||||
@@ -66,12 +70,14 @@ jobs:
|
||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
docker-rootful:
|
||||
|
||||
container:
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write # to publish to ghcr.io
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
@@ -88,38 +94,10 @@ jobs:
|
||||
# 1.2.3-rc0
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Login to GHCR using PAT
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: build rootful docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
docker-rootless:
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
permissions:
|
||||
packages: write # to publish to ghcr.io
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
id: meta_rootless
|
||||
with:
|
||||
images: |-
|
||||
gitea/gitea
|
||||
@@ -131,6 +109,8 @@ jobs:
|
||||
# 1.2.3-rc0
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -142,12 +122,20 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: build rootless docker image
|
||||
uses: docker/build-push-action@v5
|
||||
- name: build regular container image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
- name: build rootless container image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
push: true
|
||||
file: Dockerfile.rootless
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: ${{ steps.meta_rootless.outputs.tags }}
|
||||
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
||||
|
||||
69
.github/workflows/release-tag-version.yml
vendored
69
.github/workflows/release-tag-version.yml
vendored
@@ -15,20 +15,23 @@ jobs:
|
||||
binary:
|
||||
runs-on: namespace-profile-gitea-release-binary
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write # to publish to ghcr.io
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
@@ -53,7 +56,7 @@ jobs:
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
@@ -62,7 +65,7 @@ jobs:
|
||||
run: |
|
||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
||||
- name: Install GH CLI
|
||||
uses: dev-hanz-ops/install-gh-cli-action@v0.1.0
|
||||
uses: dev-hanz-ops/install-gh-cli-action@v0.2.1
|
||||
with:
|
||||
gh-cli-version: 2.39.1
|
||||
- name: create github release
|
||||
@@ -70,12 +73,14 @@ jobs:
|
||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-from-tag dist/release/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
docker-rootful:
|
||||
|
||||
container:
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write # to publish to ghcr.io
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
@@ -96,36 +101,10 @@ jobs:
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Login to GHCR using PAT
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: build rootful docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
docker-rootless:
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
id: meta_rootless
|
||||
with:
|
||||
images: |-
|
||||
gitea/gitea
|
||||
@@ -142,6 +121,8 @@ jobs:
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -153,12 +134,20 @@ jobs:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: build rootless docker image
|
||||
uses: docker/build-push-action@v5
|
||||
- name: build regular container image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
- name: build rootless container image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
push: true
|
||||
file: Dockerfile.rootless
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: ${{ steps.meta_rootless.outputs.tags }}
|
||||
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -25,6 +25,9 @@ __debug_bin*
|
||||
# Visual Studio
|
||||
/.vs/
|
||||
|
||||
# mise version managment tool
|
||||
mise.toml
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
@@ -122,3 +125,8 @@ prime/
|
||||
/CLAUDE.md
|
||||
/llms.txt
|
||||
|
||||
# Ignore worktrees when working on multiple branches
|
||||
.worktrees/
|
||||
|
||||
# A Makefile for custom make targets
|
||||
Makefile.local
|
||||
|
||||
@@ -6,14 +6,17 @@ linters:
|
||||
default: none
|
||||
enable:
|
||||
- bidichk
|
||||
- bodyclose
|
||||
- depguard
|
||||
- dupl
|
||||
- errcheck
|
||||
- forbidigo
|
||||
- gocheckcompilerdirectives
|
||||
- gocritic
|
||||
- govet
|
||||
- ineffassign
|
||||
- mirror
|
||||
- modernize
|
||||
- nakedret
|
||||
- nolintlint
|
||||
- perfsprint
|
||||
@@ -45,6 +48,8 @@ linters:
|
||||
desc: do not use the ini package, use gitea's config system instead
|
||||
- pkg: gitea.com/go-chi/cache
|
||||
desc: do not use the go-chi cache package, use gitea's cache system
|
||||
- pkg: github.com/pkg/errors
|
||||
desc: use builtin errors package instead
|
||||
nolintlint:
|
||||
allow-unused: false
|
||||
require-explanation: true
|
||||
@@ -55,6 +60,7 @@ linters:
|
||||
disabled-checks:
|
||||
- ifElseChain
|
||||
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
|
||||
- deprecatedComment # conflicts with go-swagger comments
|
||||
revive:
|
||||
severity: error
|
||||
rules:
|
||||
@@ -107,6 +113,12 @@ linters:
|
||||
- require-error
|
||||
usetesting:
|
||||
os-temp-dir: true
|
||||
perfsprint:
|
||||
concat-loop: false
|
||||
govet:
|
||||
enable:
|
||||
- nilness
|
||||
- unusedwrite
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
@@ -153,6 +165,7 @@ linters:
|
||||
text: '(?i)exitAfterDefer:'
|
||||
paths:
|
||||
- node_modules
|
||||
- .venv
|
||||
- public
|
||||
- web_src
|
||||
- third_party$
|
||||
@@ -172,6 +185,7 @@ formatters:
|
||||
generated: lax
|
||||
paths:
|
||||
- node_modules
|
||||
- .venv
|
||||
- public
|
||||
- web_src
|
||||
- third_party$
|
||||
|
||||
414
CHANGELOG.md
414
CHANGELOG.md
@@ -4,6 +4,420 @@ This changelog goes through the changes that have been made in each release
|
||||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
|
||||
## [1.25.3](https://github.com/go-gitea/gitea/releases/tag/1.25.3) - 2025-12-17
|
||||
|
||||
* SECURITY
|
||||
* Bump toolchain to go1.25.5, misc fixes (#36082)
|
||||
* ENHANCEMENTS
|
||||
* Add strikethrough button to markdown editor (#36087) (#36104)
|
||||
* Add "site admin" back to profile menu (#36010) (#36013)
|
||||
* Improve math rendering (#36124) (#36125)
|
||||
* BUGFIXES
|
||||
* Check user visibility when redirecting to a renamed user (#36148) (#36159)
|
||||
* Fix various bugs (#36139) (#36151)
|
||||
* Fix bug when viewing the commit diff page with non-ANSI files (#36149) (#36150)
|
||||
* Hide RSS icon when viewing a file not under a branch (#36135) (#36141)
|
||||
* Fix SVG size calulation, only use `style` attribute (#36133) (#36134)
|
||||
* Make Golang correctly delete temp files during uploading (#36128) (#36129)
|
||||
* Fix the bug when ssh clone with redirect user or repository (#36039) (#36090)
|
||||
* Use Golang net/smtp instead of gomail's smtp to send email (#36055) (#36083)
|
||||
* Fix edit user email bug in API (#36068) (#36081)
|
||||
* Fix bug when updating user email (#36058) (#36066)
|
||||
* Fix incorrect viewed files counter if file has changed (#36009) (#36047)
|
||||
* Fix container registry error handling (#36021) (#36037)
|
||||
* Fix webAuthn insecure error view (#36165) (#36179)
|
||||
* Fix some file icon ui (#36078) (#36088)
|
||||
* Fix Actions `pull_request.paths` being triggered incorrectly by rebase (#36045) (#36054)
|
||||
* Fix error handling in mailer and wiki services (#36041) (#36053)
|
||||
* Fix bugs when comparing and creating pull request (#36166) (#36144)
|
||||
|
||||
## [1.25.2](https://github.com/go-gitea/gitea/releases/tag/1.25.2) - 2025-11-23
|
||||
|
||||
* SECURITY
|
||||
* Upgrade golang.org/x/crypto to 0.45.0 (#35985) (#35988)
|
||||
* Fix various permission & login related bugs (#36002) (#36004)
|
||||
* ENHANCEMENTS
|
||||
* Display source code downloads last for release attachments (#35897) (#35903)
|
||||
* Change project default column icon to 'star' (#35967) (#35979)
|
||||
* BUGFIXES
|
||||
* Allow empty commit when merging pull request with squash style (#35989) (#36003)
|
||||
* Fix container push tag overwriting (#35936) (#35954)
|
||||
* Fix corrupted external render content (#35946) and upgrade golang.org/x packages (#35950)
|
||||
* Limit reading bytes instead of ReadAll (#35928) (#35934)
|
||||
* Use correct form field for allowed force push users in branch protection API (#35894) (#35908)
|
||||
* Fix team member access check (#35899) (#35905)
|
||||
* Fix conda null depend issue (#35900) (#35902)
|
||||
* Set the dates to now when not specified by the caller (#35861) (#35874)
|
||||
* Fix gogit ListEntriesRecursiveWithSize (#35862)
|
||||
* Misc CSS fixes (#35888) (#35981)
|
||||
* Don't show unnecessary error message to end users for DeleteBranchAfterMerge (#35937) (#35941)
|
||||
* Load jQuery as early as possible to support custom scripts (#35926) (#35929)
|
||||
* Allow to display embed images/pdfs when SERVE_DIRECT was enabled on MinIO storage (#35882) (#35917)
|
||||
* Make OAuth2 issuer configurable (#35915) (#35916)
|
||||
* Fix #35763: Add proper page title for project pages (#35773) (#35909)
|
||||
* Fix avatar upload error handling (#35887) (#35890)
|
||||
* Contribution heatmap improvements (#35876) (#35880)
|
||||
* Remove padding override on `.ui .sha.label` (#35864) (#35873)
|
||||
* Fix pull description code label background (#35865) (#35870)
|
||||
|
||||
## [1.25.1](https://github.com/go-gitea/gitea/releases/tag/v1.25.1) - 2025-11-03
|
||||
|
||||
* BUGFIXES
|
||||
* Make ACME email optional (#35849) #35857
|
||||
* Add a doctor command to fix inconsistent run status (#35840) (#35845)
|
||||
* Remove wrong code (#35846)
|
||||
* Fix viewed files number is not right if not all files loaded (#35821) (#35844)
|
||||
* Fix incorrect pull request counter (#35819) (#35841)
|
||||
* Upgrade go mail to 0.7.2 and fix the bug (#35833) (#35837)
|
||||
* Revert gomail to v0.7.0 to fix sending mail failed (#35816) (#35824)
|
||||
* Fix clone mixed bug (#35810) (#35822)
|
||||
* Fix cli "Before" handling (#35797) (#35808)
|
||||
* Improve and fix markup code preview rendering (#35777) (#35787)
|
||||
* Fix actions rerun bug (#35783) (#35784)
|
||||
* Fix actions schedule update issue (#35767) (#35774)
|
||||
* Fix circular spin animation direction (#35785) (#35823)
|
||||
* Fix file extension on gogs.png (#35793) (#35799)
|
||||
* Add pnpm to Snapcraft (#35778)
|
||||
|
||||
## [1.25.0](https://github.com/go-gitea/gitea/releases/tag/v1.25.0) - 2025-10-30
|
||||
|
||||
* BREAKING
|
||||
* Return 201 Created for CreateVariable API responses (#34517)
|
||||
* Add label 'state' to metric 'gitea_users' (#34326)
|
||||
* SECURITY
|
||||
* Upgrade security public key (#34956)
|
||||
* Also include all security fixes in 1.24.x after 1.25.0-rc0
|
||||
* FEATURES
|
||||
* Stream repo zip/tar.gz/bundle achives by default (#35487)
|
||||
* Use configurable remote name for git commands (#35172)
|
||||
* Send email on Workflow Run Success/Failure (#34982)
|
||||
* Refactor OpenIDConnect to support SSH/FullName sync (#34978)
|
||||
* Refactor repo contents API and add "contents-ext" API (#34822)
|
||||
* Add support for 3D/CAD file formats preview (#34794)
|
||||
* Improve instance wide ssh commit signing (#34341)
|
||||
* Edit file workflow for creating a fork and proposing changes (#34240)
|
||||
* Follow file symlinks in the UI to their target (#28835)
|
||||
* Allow renaming/moving binary/LFS files in the UI (#34350)
|
||||
* PERFORMANCE
|
||||
* Improve the performance when detecting the file editable (#34653)
|
||||
* ENHANCEMENTS
|
||||
* Enable more markdown paste features in textarea editor (#35494)
|
||||
* Don't store repo archives on `gitea dump` (#35467)
|
||||
* Always return the relevant status information, even if no status exists. (#35335)
|
||||
* Add start time on perf trace because it seems some steps haven't been recorded. (#35282)
|
||||
* Remove deprecated auth sources (#35272)
|
||||
* When sorting issues by nearest due date, issues without due date should be sorted ascending (#35267)
|
||||
* Disable field count validation of CSV viewer (#35228)
|
||||
* Add `has_code` to repository REST API (#35214)
|
||||
* Display pull request in merged commit view (#35202)
|
||||
* Support Basic Authentication for archive downloads (#35087)
|
||||
* Add hover background to table rows in user and repo admin page (#35072)
|
||||
* Partially refresh notifications list (#35010)
|
||||
* Also display "recently pushed branch" alert on PR view (#35001)
|
||||
* Refactor time tracker UI (#34983)
|
||||
* Improve CLI commands (#34973)
|
||||
* Improve project & label color picker and image scroll (#34971)
|
||||
* Improve NuGet API Parity (#21291) (#34940)
|
||||
* Support getting last commit message using contents-ext API (#34904)
|
||||
* Adds title on branch commit counts (#34869)
|
||||
* Add "Cancel workflow run" button to Actions list page (#34817)
|
||||
* Improve img lazy loading (#34804)
|
||||
* Forks repository list page follow other repositories page (#34784)
|
||||
* Add ff_only parameter to POST /repos/{owner}/{repo}/merge-upstream (#34770)
|
||||
* Rework delete org and rename org UI (#34762)
|
||||
* Improve nuget/rubygems package registries (#34741)
|
||||
* Add repo file tree item link behavior (#34730)
|
||||
* Add issue delete notifier (#34592)
|
||||
* Improve Actions list (#34530)
|
||||
* Add a default tab on repo header when migrating (#34503)
|
||||
* Add post-installation redirect based on admin account status (#34493)
|
||||
* Trigger 'unlabeled' event when label is Deleted from PR (#34316)
|
||||
* Support annotated tags when using create release API (#31840)
|
||||
* Use lfs label for lfs file rather than a long description (#34363)
|
||||
* Add "View workflow file" to Actions list page (#34538)
|
||||
* Move organization's visibility change to danger zone. (#34814)
|
||||
* Don't block site admin's operation if SECRET_KEY is lost (#35721)
|
||||
* Make restricted users can access public repositories (#35693)
|
||||
* The status icon of the Action step is consistent with GitHub (#35618) #35621
|
||||
* BUGFIXES
|
||||
* Update tab title when navigating file tree (#35757) #35772
|
||||
* Fix "ref-issue" handling in markup (#35739) #35771
|
||||
* Fix webhook to prevent tag events from bypassing branch filters targets (#35567) #35577
|
||||
* Fix markup init after issue comment editing (#35536) #35537
|
||||
* Fix creating pull request failure when the target branch name is the same as some tag (#35552) #35582
|
||||
* Fix auto-expand and auto-scroll for actions logs (#35570) (#35583) #35586
|
||||
* Use inputs context when parsing workflows (#35590) #35595
|
||||
* Fix diffpatch API endpoint (#35610) #35613
|
||||
* Creating push comments before invoke pull request checking (#35647) #35668
|
||||
* Fix missing Close when error occurs and abused connection pool (#35658) #35670
|
||||
* Fix build (#35674)
|
||||
* Use LFS object size instead of blob size when viewing a LFS file (#35679)
|
||||
* Fix workflow run event status while rerunning a failed job (#35689)
|
||||
* Avoid emoji mismatch and allow to only enable chosen emojis (#35692)
|
||||
* Refactor legacy code, fix LFS auth bypass, fix symlink bypass (#35708)
|
||||
* Fix various trivial problems (#35714)
|
||||
* Fix attachment file size limit in server backend (#35519)
|
||||
* Honor delete branch on merge repo setting when using merge API (#35488)
|
||||
* Fix external render, make iframe render work (#35727, #35730)
|
||||
* Upgrade go mail to 0.7.2 (#35748)
|
||||
* Revert #18491, fix oauth2 client link account (#35745)
|
||||
* Fix different behavior in status check pattern matching with double stars (#35474)
|
||||
* Fix overflow in notifications list (#35446)
|
||||
* Fix package link setting can only list limited repositories (#35394)
|
||||
* Extend comment treepath length (#35389)
|
||||
* Fix font-size in inline code comment preview (#35209)
|
||||
* Move git config/remote to gitrepo package and add global lock to resolve possible conflict when updating repository git config file (#35151)
|
||||
* Change some columns from text to longtext and fix column wrong type caused by xorm (#35141)
|
||||
* Redirect to a presigned URL of HEAD for HEAD requests (#35088)
|
||||
* Fix git commit committer parsing and add some tests (#35007)
|
||||
* Fix OCI manifest parser (#34797)
|
||||
* Refactor FindOrgOptions to use enum instead of bool, fix membership visibility (#34629)
|
||||
* Fix notification count positioning for variable-width elements (#34597)
|
||||
* Keeping consistent between UI and API about combined commit status state and fix some bugs (#34562)
|
||||
* Fix possible panic (#34508)
|
||||
* Fix autofocus behavior (#34397)
|
||||
* Fix Actions API (#35204)
|
||||
* Fix ListWorkflowRuns OpenAPI response model. (#35026)
|
||||
* Small fix in Pull Requests page (#34612)
|
||||
* Fix http auth header parsing (#34936)
|
||||
* Fix modal + form abuse (#34921)
|
||||
* Fix PR toggle WIP (#34920)
|
||||
* Fix log fmt (#34810)
|
||||
* Replace stopwatch toggle with explicit start/stop actions (#34818)
|
||||
* Fix some package registry problems (#34759)
|
||||
* Fix RPM package download routing & missing package version count (#34909)
|
||||
* Fix repo search input height (#34330)
|
||||
* Fix "The sidebar of the repository file list does not have a fixed height #34298" (#34321)
|
||||
* Fix minor typos in two files #HSFDPMUW (#34944)
|
||||
* Fix actions skipped commit status indicator (#34507)
|
||||
* Fix job status aggregation logic (#35000)
|
||||
* Fix broken OneDev migration caused by various REST API changes in OneDev 7.8.0 and later (#35216)
|
||||
* Fix typo in oauth2_full_name_claim_name string (#35199)
|
||||
* Fix typo in locale_en-US.ini (#35196)
|
||||
* API
|
||||
* Exposing TimeEstimate field in the API (#35475)
|
||||
* UpdateBranch API supports renaming a branch (#35374)
|
||||
* Add `owner` and `parent` fields clarification to docs (#35023)
|
||||
* Improve OAuth2 provider (correct Issuer, respect ENABLED) (#34966)
|
||||
* Add a `login`/`login-name`/`username` disambiguation to affected endpoint parameters and response/request models (#34901)
|
||||
* Do not mutate incoming options to SearchRepositoryByName (#34553)
|
||||
* Do not mutate incoming options to RenderUserSearch and SearchUsers (#34544)
|
||||
* Export repo's manual merge settings (#34502)
|
||||
* Add date range filtering to commit retrieval endpoints (#34497)
|
||||
* Add endpoint deleting workflow run (#34337)
|
||||
* Add workflow_run api + webhook (#33964)
|
||||
* REFACTOR
|
||||
* Move updateref and removeref to gitrepo and remove unnecessary open repository (#35511)
|
||||
* Remove unused param `doer` (#34545)
|
||||
* Split GetLatestCommitStatus as two functions (#34535)
|
||||
* Use gitrepo.SetDefaultBranch when set default branch of wiki repository (#33911)
|
||||
* Refactor editor (#34780)
|
||||
* Refactor packages (#34777)
|
||||
* Refactor container package (#34877)
|
||||
* Refactor "change file" API (#34855)
|
||||
* Rename pull request GetGitRefName to GetGitHeadRefName to prepare introducing GetGitMergeRefName (#35093)
|
||||
* Move git command to git/gitcmd (#35483)
|
||||
* Use db.WithTx/WithTx2 instead of TxContext when possible (#35428)
|
||||
* Support Node.js 22.6 with type stripping (#35427)
|
||||
* Migrate tools and configs to typescript, require node.js >= 22.18.0 (#35421)
|
||||
* Check user and repo for redirects when using git via SSH transport (#35416)
|
||||
* Remove the duplicated function GetTags (#35375)
|
||||
* Refactor to use reflect.TypeFor (#35370)
|
||||
* Deleting branch could delete broken branch which has database record but git branch is missing (#35360)
|
||||
* Exit with success when already up to date (#35312)
|
||||
* Split admin config settings templates to make it maintain easier (#35294)
|
||||
* A small refactor to use context in the service layer (#35179)
|
||||
* Refactor and update mail templates (#35150)
|
||||
* Use db.WithTx/WithTx2 instead of TxContext when possible (#35130)
|
||||
* Align `issue-title-buttons` with `list-header` (#35018)
|
||||
* Add Notifications section in User Settings (#35008)
|
||||
* Tweak placement of diff file menu (#34999)
|
||||
* Refactor mail template and support preview (#34990)
|
||||
* Rerun job only when run is done (#34970)
|
||||
* Merge index.js (#34963)
|
||||
* Refactor "delete-button" to "link-action" (#34962)
|
||||
* Refactor webhook and fix feishu/lark secret (#34961)
|
||||
* Exclude devtest.ts from tailwindcss (#34935)
|
||||
* Refactor head navbar icons (#34922)
|
||||
* Improve html escape (#34911)
|
||||
* Improve tags list page (#34898)
|
||||
* Improve `labels-list` rendering (#34846)
|
||||
* Remove unused variable HUGO_VERSION (#34840)
|
||||
* Correct migration tab name (#34826)
|
||||
* Refactor template helper (#34819)
|
||||
* Use `shallowRef` instead of `ref` in `.vue` files where possible (#34813)
|
||||
* Use standalone function to update repository cols (#34811)
|
||||
* Refactor wiki (#34805)
|
||||
* Remove unnecessary duplicate code (#34733)
|
||||
* Refactor embedded assets and drop unnecessary dependencies (#34692)
|
||||
* Update x/crypto package and make builtin SSH use default parameters (#34667)
|
||||
* Add `--color-logo`, matching the logo's primary color (#34639)
|
||||
* Add openssh-keygen to rootless image (#34625)
|
||||
* Replace update repository function in some places (#34566)
|
||||
* Change "rejected" to "changes requested" in 3rd party PR review notification (#34481)
|
||||
* Remove legacy template helper functions (#34426)
|
||||
* Use run-name and evaluate workflow variables (#34301)
|
||||
* Move HasWiki to repository service package (#33912)
|
||||
* Move some functions from package git to gitrepo (#33910)
|
||||
* TESTING
|
||||
* Add webhook test for push event (#34442)
|
||||
* Add a webhook push test for dev branch (#34421)
|
||||
* Add migrations tests (#34456) (#34498)
|
||||
* STYLE
|
||||
* Enforce explanation for necessary nolints and fix bugs (#34883)
|
||||
* Fix remaining issues after `gopls modernize` formatting (#34771)
|
||||
* Update gofumpt, add go.mod ignore directive (#35434)
|
||||
* Enforce nolint scope (#34851)
|
||||
* Enable gocritic `equalFold` and fix issues (#34952)
|
||||
* Run `gopls modernize` on codebase (#34751)
|
||||
* Upgrade `gopls` to v0.19.0, add `make fix` (#34772)
|
||||
* BUILD
|
||||
* bump archives&rar dep (#35637) #35638
|
||||
* Use github.com/mholt/archives replace github.com/mholt/archiver (#35390)
|
||||
* Update JS and PY dependencies (#35444)
|
||||
* Upgrade devcontainer go version to 1.24.6 (#35298)
|
||||
* Upgrade golang to 1.25.1 and add descriptions for the swagger structs' fields (#35418)
|
||||
* Update JS and PY deps (#35191)
|
||||
* Update JS and PY dependencies (#34391)
|
||||
* Update go tool dependencies (#34845)
|
||||
* Update `uint8-to-base64`, remove type stub (#34844)
|
||||
* Switch to `@resvg/resvg-wasm` for `generate-images` (#35415)
|
||||
* Switch to pnpm (#35274)
|
||||
* Update chroma to v2.20.0 (#35220)
|
||||
* Migrate to urfave v3 (#34510)
|
||||
* Update JS deps, regenerate SVGs (#34640)
|
||||
* Upgrade dependencies (#35384)
|
||||
* Bump `@github/relative-time-element` to v4.4.8 (#34413)
|
||||
* Update JS dependencies (#34951)
|
||||
* Upgrade orgmode to v1.8.0 (#34721)
|
||||
* Raise minimum Node.js version to 20, test on 24 (#34713)
|
||||
* Update JS deps (#34701)
|
||||
* Upgrade htmx to 2.0.6 (#34887)
|
||||
* Update eslint to v9 (#35485)
|
||||
* Update js dependencies (#35429)
|
||||
* Clean up npm dependencies (#35508)
|
||||
* Clean up npm dependencies (#35484)
|
||||
* Bump setup-node to v5 (#35448)
|
||||
* MISC
|
||||
* Add gitignore rules to exclude LLM instruction files (#35076)
|
||||
* Gitignore: Visual Studio settings folder (#34375)
|
||||
* Improve language in en-US locale strings (#35124)
|
||||
* Fixed all grammatical errors in locale_en-US.ini (#35053)
|
||||
* Docs/fix typo and grammar in CONTRIBUTING.md (#35024)
|
||||
* Improve english grammar and readability in locale_en-US.ini (#35017)
|
||||
|
||||
## [1.24.7](https://github.com/go-gitea/gitea/releases/tag/v1.24.7) - 2025-10-24
|
||||
|
||||
* SECURITY
|
||||
* Refactor legacy code (#35708) (#35713)
|
||||
* Fixing issue #35530: Password Leak in Log Messages (#35584) (#35665)
|
||||
* Fix a bug missed return (#35655) (#35671)
|
||||
* BUGFIXES
|
||||
* Fix inputing review comment will remove reviewer (#35591) (#35664)
|
||||
* TESTING
|
||||
* Mock external service in hcaptcha TestCaptcha (#35604) (#35663)
|
||||
* Fix build (#35669)
|
||||
|
||||
## [1.24.6](https://github.com/go-gitea/gitea/releases/tag/v1.24.6) - 2025-09-10
|
||||
|
||||
* SECURITY
|
||||
* Upgrade xz to v0.5.15 (#35385)
|
||||
* BUGFIXES
|
||||
* Fix a compare page 404 bug when the pull request disabled (#35441) (#35453)
|
||||
* Fix bug when issue disabled, pull request number in the commit message cannot be redirected (#35420) (#35442)
|
||||
* Add author.name field to Swift Package Registry API response (#35410) (#35431)
|
||||
* Remove usernames when empty in discord webhook (#35412) (#35417)
|
||||
* Allow foreachref parser to grow its buffer (#35365) (#35376)
|
||||
* Allow deleting comment with content via API like web did (#35346) (#35354)
|
||||
* Fix atom/rss mixed error (#35345) (#35347)
|
||||
* Fix review request webhook bug (#35339)
|
||||
* Remove duplicate html IDs (#35210) (#35325)
|
||||
* Fix LFS range size header response (#35277) (#35293)
|
||||
* Fix GitHub release assets URL validation (#35287) (#35290)
|
||||
* Fix token lifetime, closes #35230 (#35271) (#35281)
|
||||
* Fix push commits comments when changing the pull request target branch (#35386) (#35443)
|
||||
|
||||
## [1.24.5](https://github.com/go-gitea/gitea/releases/tag/v1.24.5) - 2025-08-12
|
||||
|
||||
* BUGFIXES
|
||||
* Fix a bug where lfs gc never worked. (#35198) (#35255)
|
||||
* Reload issue when sending webhook to make num comments is right. (#35243) (#35248)
|
||||
* Fix bug when review pull request commits (#35192) (#35246)
|
||||
* MISC
|
||||
* Vertically center "Show Resolved" (#35211) (#35218)
|
||||
|
||||
## [1.24.4](https://github.com/go-gitea/gitea/releases/tag/v1.24.4) - 2025-08-03
|
||||
|
||||
* BUGFIXES
|
||||
* Fix various bugs (1.24) (#35186)
|
||||
* Fix migrate input box bug (#35166) (#35171)
|
||||
* Only hide dropzone when no files have been uploaded (#35156) (#35167)
|
||||
* Fix review comment/dimiss comment x reference can be refereced back (#35094) (#35099)
|
||||
* Fix submodule nil check (#35096) (#35098)
|
||||
* MISC
|
||||
* Don't use full-file highlight when there is a git diff textconv (#35114) (#35119)
|
||||
* Increase gap on latest commit (#35104) (#35113)
|
||||
|
||||
## [1.24.3](https://github.com/go-gitea/gitea/releases/tag/v1.24.3) - 2025-07-15
|
||||
|
||||
* BUGFIXES
|
||||
* Fix form property assignment edge case (#35073) (#35078)
|
||||
* Improve submodule relative path handling (#35056) (#35075)
|
||||
* Fix incorrect comment diff hunk parsing, fix github asset ID nil panic (#35046) (#35055)
|
||||
* Fix updating user visibility (#35036) (#35044)
|
||||
* Support base64-encoded agit push options (#35037) (#35041)
|
||||
* Make submodule link work with relative path (#35034) (#35038)
|
||||
* Fix bug when displaying git user avatar in commits list (#35006)
|
||||
* Fix API response for swagger spec (#35029)
|
||||
* Start automerge check again after the conflict check and the schedule (#34988) (#35002)
|
||||
* Fix the response format for actions/workflows (#35009) (#35016)
|
||||
* Fix repo settings and protocol log problems (#35012) (#35013)
|
||||
* Fix project images scroll (#34971) (#34972)
|
||||
* Mark old reviews as stale on agit pr updates (#34933) (#34965)
|
||||
* Fix git graph page (#34948) (#34949)
|
||||
* Don't send trigger for a pending review's comment create/update/delete (#34928) (#34939)
|
||||
* Fix some log and UI problems (#34863) (#34868)
|
||||
* Fix archive API (#34853) (#34857)
|
||||
* Ignore force pushes for changed files in a PR review (#34837) (#34843)
|
||||
* Fix SSH LFS timeout (#34838) (#34842)
|
||||
* Fix team permissions (#34827) (#34836)
|
||||
* Fix job status aggregation logic (#34823) (#34835)
|
||||
* Fix issue filter (#34914) (#34915)
|
||||
* Fix typo in pull request merge warning message text (#34899) (#34903)
|
||||
* Support the open-icon of folder (#34168) (#34896)
|
||||
* Optimize flex layout of release attachment area (#34885) (#34886)
|
||||
* Fix the issue of abnormal interface when there is no issue-item on the project page (#34791) (#34880)
|
||||
* Skip updating timestamp when sync branch (#34875)
|
||||
* Fix required contexts and commit status matching bug (#34815) (#34829)
|
||||
|
||||
## [1.24.2](https://github.com/go-gitea/gitea/releases/tag/v1.24.2) - 2025-06-20
|
||||
|
||||
* BUGFIXES
|
||||
* Fix container range bug (#34795) (#34796)
|
||||
* Upgrade chi to v5.2.2 (#34798) (#34799)
|
||||
* BUILD
|
||||
* Bump poetry feature to new url for dev container (#34787) (#34790)
|
||||
|
||||
## [1.24.1](https://github.com/go-gitea/gitea/releases/tag/v1.24.1) - 2025-06-18
|
||||
|
||||
* ENHANCEMENTS
|
||||
* Improve alignment of commit status icon on commit page (#34750) (#34757)
|
||||
* Support title and body query parameters for new PRs (#34537) (#34752)
|
||||
|
||||
* BUGFIXES
|
||||
* When using rules to delete packages, remove unclean bugs (#34632) (#34761)
|
||||
* Fix ghost user in feeds when pushing in an actions, it should be gitea-actions (#34703) (#34756)
|
||||
* Prevent double markdown link brackets when pasting URL (#34745) (#34748)
|
||||
* Prevent duplicate form submissions when creating forks (#34714) (#34735)
|
||||
* Fix markdown wrap (#34697) (#34702)
|
||||
* Fix pull requests API convert panic when head repository is deleted. (#34685) (#34687)
|
||||
* Fix commit message rendering and some UI problems (#34680) (#34683)
|
||||
* Fix container range bug (#34725) (#34732)
|
||||
* Fix incorrect cli default values (#34765) (#34766)
|
||||
* Fix dropdown filter (#34708) (#34711)
|
||||
* Hide href attribute of a tag if there is no target_url (#34556) (#34684)
|
||||
* Fix tag target (#34781) #34783
|
||||
|
||||
## [1.24.0](https://github.com/go-gitea/gitea/releases/tag/1.24.0) - 2025-05-26
|
||||
|
||||
* BREAKING
|
||||
|
||||
@@ -80,7 +80,7 @@ The more detailed and specific you are, the faster we can fix the issue. \
|
||||
It is really helpful if you can reproduce your problem on a site running on the latest commits, i.e. <https://demo.gitea.com>, as perhaps your problem has already been fixed on a current version. \
|
||||
Please follow the guidelines described in [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) for your report.
|
||||
|
||||
Please be kind, remember that Gitea comes at no cost to you, and you're getting free help.
|
||||
Please be kind—remember that Gitea comes at no cost to you, and you're getting free help.
|
||||
|
||||
### Types of issues
|
||||
|
||||
@@ -166,19 +166,19 @@ Here's how to run the test suite:
|
||||
|
||||
- code lint
|
||||
|
||||
| | |
|
||||
| :-------------------- | :---------------------------------------------------------------- |
|
||||
| | |
|
||||
| :-------------------- | :--------------------------------------------------------------------------- |
|
||||
|``make lint`` | lint everything (not needed if you only change the front- **or** backend) |
|
||||
|``make lint-frontend`` | lint frontend files |
|
||||
|``make lint-backend`` | lint backend files |
|
||||
|``make lint-frontend`` | lint frontend files |
|
||||
|``make lint-backend`` | lint backend files |
|
||||
|
||||
- run tests (we suggest running them on Linux)
|
||||
|
||||
| Command | Action | |
|
||||
| :------------------------------------- | :----------------------------------------------- | ------------ |
|
||||
|``make test[\#SpecificTestName]`` | run unit test(s) | |
|
||||
|``make test-sqlite[\#SpecificTestName]``| run [integration](tests/integration) test(s) for SQLite |[More details](tests/integration/README.md) |
|
||||
|``make test-e2e-sqlite[\#SpecificTestName]``| run [end-to-end](tests/e2e) test(s) for SQLite |[More details](tests/e2e/README.md) |
|
||||
| Command | Action | |
|
||||
| :------------------------------------------ | :------------------------------------------------------- | ------------------------------------------- |
|
||||
|``make test[\#SpecificTestName]`` | run unit test(s) | |
|
||||
|``make test-sqlite[\#SpecificTestName]`` | run [integration](tests/integration) test(s) for SQLite | [More details](tests/integration/README.md) |
|
||||
|``make test-e2e-sqlite[\#SpecificTestName]`` | run [end-to-end](tests/e2e) test(s) for SQLite | [More details](tests/e2e/README.md) |
|
||||
|
||||
## Translation
|
||||
|
||||
|
||||
47
Dockerfile
47
Dockerfile
@@ -1,8 +1,8 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# Build stage
|
||||
FROM docker.io/library/golang:1.25-alpine3.22 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-direct}
|
||||
ARG GOPROXY=direct
|
||||
|
||||
ARG GITEA_VERSION
|
||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||
@@ -14,35 +14,32 @@ RUN apk --no-cache add \
|
||||
build-base \
|
||||
git \
|
||||
nodejs \
|
||||
npm \
|
||||
&& npm install -g pnpm@10 \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
pnpm
|
||||
|
||||
# Setup repo
|
||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
||||
# Use COPY but not "mount" because some directories like "node_modules" contain platform-depended contents and these directories need to be ignored.
|
||||
# ".git" directory will be mounted later separately for getting version data.
|
||||
# TODO: in the future, maybe we can pre-build the frontend assets on one platform and share them for different platforms, the benefit is that it won't be affected by webpack plugin compatibility problems, then the working directory can be fully mounted and the COPY is not needed.
|
||||
COPY --exclude=.git/ . .
|
||||
|
||||
# Checkout version if set
|
||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||
&& make clean-all build
|
||||
# Build gitea, .git mount is required for version data
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target="/root/.cache/go-build" \
|
||||
--mount=type=cache,target=/root/.local/share/pnpm/store \
|
||||
--mount=type=bind,source=".git/",target=".git/" \
|
||||
make
|
||||
|
||||
# Begin env-to-ini build
|
||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
||||
|
||||
# Copy local files
|
||||
COPY docker/root /tmp/local
|
||||
|
||||
# Set permissions
|
||||
# Set permissions for builds that made under windows which strips the executable bit from file
|
||||
RUN chmod 755 /tmp/local/usr/bin/entrypoint \
|
||||
/tmp/local/usr/local/bin/gitea \
|
||||
/tmp/local/usr/local/bin/* \
|
||||
/tmp/local/etc/s6/gitea/* \
|
||||
/tmp/local/etc/s6/openssh/* \
|
||||
/tmp/local/etc/s6/.s6-svscan/* \
|
||||
/go/src/code.gitea.io/gitea/gitea \
|
||||
/go/src/code.gitea.io/gitea/environment-to-ini
|
||||
/go/src/code.gitea.io/gitea/gitea
|
||||
|
||||
FROM docker.io/library/alpine:3.22
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
FROM docker.io/library/alpine:3.22 AS gitea
|
||||
|
||||
EXPOSE 22 3000
|
||||
|
||||
@@ -57,8 +54,7 @@ RUN apk --no-cache add \
|
||||
s6 \
|
||||
sqlite \
|
||||
su-exec \
|
||||
gnupg \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
gnupg
|
||||
|
||||
RUN addgroup \
|
||||
-S -g 1000 \
|
||||
@@ -72,6 +68,9 @@ RUN addgroup \
|
||||
git && \
|
||||
echo "git:*" | chpasswd -e
|
||||
|
||||
COPY --from=build-env /tmp/local /
|
||||
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||
|
||||
ENV USER=git
|
||||
ENV GITEA_CUSTOM=/data/gitea
|
||||
|
||||
@@ -79,7 +78,3 @@ VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||
CMD ["/usr/bin/s6-svscan", "/etc/s6"]
|
||||
|
||||
COPY --from=build-env /tmp/local /
|
||||
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||
|
||||
@@ -1,46 +1,39 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# Build stage
|
||||
FROM docker.io/library/golang:1.25-alpine3.22 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-direct}
|
||||
ARG GOPROXY=direct
|
||||
|
||||
ARG GITEA_VERSION
|
||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||
ENV TAGS="bindata timetzdata $TAGS"
|
||||
ARG CGO_EXTRA_CFLAGS
|
||||
|
||||
#Build deps
|
||||
# Build deps
|
||||
RUN apk --no-cache add \
|
||||
build-base \
|
||||
git \
|
||||
nodejs \
|
||||
npm \
|
||||
&& npm install -g pnpm@10 \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
pnpm
|
||||
|
||||
# Setup repo
|
||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
||||
# See the comments in Dockerfile
|
||||
COPY --exclude=.git/ . .
|
||||
|
||||
# Checkout version if set
|
||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||
&& make clean-all build
|
||||
# Build gitea, .git mount is required for version data
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target="/root/.cache/go-build" \
|
||||
--mount=type=cache,target=/root/.local/share/pnpm/store \
|
||||
--mount=type=bind,source=".git/",target=".git/" \
|
||||
make
|
||||
|
||||
# Begin env-to-ini build
|
||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
||||
|
||||
# Copy local files
|
||||
COPY docker/rootless /tmp/local
|
||||
|
||||
# Set permissions
|
||||
RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
|
||||
/tmp/local/usr/local/bin/docker-setup.sh \
|
||||
/tmp/local/usr/local/bin/gitea \
|
||||
/go/src/code.gitea.io/gitea/gitea \
|
||||
/go/src/code.gitea.io/gitea/environment-to-ini
|
||||
# Set permissions for builds that made under windows which strips the executable bit from file
|
||||
RUN chmod 755 /tmp/local/usr/local/bin/* \
|
||||
/go/src/code.gitea.io/gitea/gitea
|
||||
|
||||
FROM docker.io/library/alpine:3.22
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
FROM docker.io/library/alpine:3.22 AS gitea-rootless
|
||||
|
||||
EXPOSE 2222 3000
|
||||
|
||||
@@ -52,8 +45,7 @@ RUN apk --no-cache add \
|
||||
git \
|
||||
curl \
|
||||
gnupg \
|
||||
openssh-keygen \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
openssh-keygen
|
||||
|
||||
RUN addgroup \
|
||||
-S -g 1000 \
|
||||
@@ -71,7 +63,6 @@ RUN chown git:git /var/lib/gitea /etc/gitea
|
||||
|
||||
COPY --from=build-env /tmp/local /
|
||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||
|
||||
# git:git
|
||||
USER 1000:1000
|
||||
|
||||
87
Makefile
87
Makefile
@@ -18,6 +18,10 @@ DIST := dist
|
||||
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
||||
IMPORT := code.gitea.io/gitea
|
||||
|
||||
# By default use go's 1.25 experimental json v2 library when building
|
||||
# TODO: remove when no longer experimental
|
||||
export GOEXPERIMENT ?= jsonv2
|
||||
|
||||
GO ?= go
|
||||
SHASUM ?= shasum -a 256
|
||||
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
|
||||
@@ -27,17 +31,15 @@ XGO_VERSION := go-1.25.x
|
||||
|
||||
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.1
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.2
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.7.2
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.7.0
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@717e3cb29becaaf00e56953556c6d80f8a01b286
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.33.1
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
|
||||
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
|
||||
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.20.0
|
||||
GOPLS_MODERNIZE_PACKAGE ?= golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@v0.20.0
|
||||
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.9
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
@@ -159,13 +161,13 @@ TEST_TAGS ?= $(TAGS_SPLIT) sqlite sqlite_unlock_notify
|
||||
|
||||
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
|
||||
|
||||
GO_DIRS := build cmd models modules routers services tests
|
||||
GO_DIRS := build cmd models modules routers services tests tools
|
||||
WEB_DIRS := web_src/js web_src/css
|
||||
|
||||
ESLINT_FILES := web_src/js tools *.ts tests/e2e
|
||||
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
|
||||
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*))
|
||||
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
|
||||
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.json .github $(filter-out CHANGELOG.md, $(wildcard *.go *.md *.yml *.yaml *.toml))
|
||||
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.json
|
||||
|
||||
GO_SOURCES := $(wildcard *.go)
|
||||
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go")
|
||||
@@ -195,6 +197,10 @@ TEST_MSSQL_DBNAME ?= gitea
|
||||
TEST_MSSQL_USERNAME ?= sa
|
||||
TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
|
||||
|
||||
# Include local Makefile
|
||||
# Makefile.local is listed in .gitignore
|
||||
sinclude Makefile.local
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
@@ -254,7 +260,7 @@ clean: ## delete backend and integration files
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: ## format the Go and template code
|
||||
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run tools/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
|
||||
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
|
||||
@# whitespace before it
|
||||
@@ -272,19 +278,6 @@ fmt-check: fmt
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: fix
|
||||
fix: ## apply automated fixes to Go code
|
||||
$(GO) run $(GOPLS_MODERNIZE_PACKAGE) -fix ./...
|
||||
|
||||
.PHONY: fix-check
|
||||
fix-check: fix
|
||||
@diff=$$(git diff --color=always $(GO_SOURCES)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fix' and commit the result:"; \
|
||||
printf "%s" "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: $(TAGS_EVIDENCE)
|
||||
$(TAGS_EVIDENCE):
|
||||
@mkdir -p $(MAKE_EVIDENCE_DIR)
|
||||
@@ -324,7 +317,7 @@ checks: checks-frontend checks-backend ## run various consistency checks
|
||||
checks-frontend: lockfile-check svg-check ## check frontend files
|
||||
|
||||
.PHONY: checks-backend
|
||||
checks-backend: tidy-check swagger-check fmt-check fix-check swagger-validate security-check ## check backend files
|
||||
checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check ## check backend files
|
||||
|
||||
.PHONY: lint
|
||||
lint: lint-frontend lint-backend lint-spell ## lint everything
|
||||
@@ -339,19 +332,19 @@ lint-frontend: lint-js lint-css ## lint frontend files
|
||||
lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues
|
||||
|
||||
.PHONY: lint-backend
|
||||
lint-backend: lint-go lint-go-gitea-vet lint-go-gopls lint-editorconfig ## lint backend files
|
||||
lint-backend: lint-go lint-go-gitea-vet lint-editorconfig ## lint backend files
|
||||
|
||||
.PHONY: lint-backend-fix
|
||||
lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
|
||||
|
||||
.PHONY: lint-js
|
||||
lint-js: node_modules ## lint js files
|
||||
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --flag unstable_native_nodejs_ts_config $(ESLINT_FILES)
|
||||
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 $(ESLINT_FILES)
|
||||
$(NODE_VARS) pnpm exec vue-tsc
|
||||
|
||||
.PHONY: lint-js-fix
|
||||
lint-js-fix: node_modules ## lint js files and fix issues
|
||||
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --flag unstable_native_nodejs_ts_config $(ESLINT_FILES) --fix
|
||||
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 $(ESLINT_FILES) --fix
|
||||
$(NODE_VARS) pnpm exec vue-tsc
|
||||
|
||||
.PHONY: lint-css
|
||||
@@ -370,13 +363,17 @@ lint-swagger: node_modules ## lint swagger files
|
||||
lint-md: node_modules ## lint markdown files
|
||||
$(NODE_VARS) pnpm exec markdownlint *.md
|
||||
|
||||
.PHONY: lint-md-fix
|
||||
lint-md-fix: node_modules ## lint markdown files and fix issues
|
||||
$(NODE_VARS) pnpm exec markdownlint --fix *.md
|
||||
|
||||
.PHONY: lint-spell
|
||||
lint-spell: ## lint spelling
|
||||
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES)
|
||||
@go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -error $(SPELLCHECK_FILES)
|
||||
|
||||
.PHONY: lint-spell-fix
|
||||
lint-spell-fix: ## lint spelling and fix issues
|
||||
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
|
||||
@go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -w $(SPELLCHECK_FILES)
|
||||
|
||||
.PHONY: lint-go
|
||||
lint-go: ## lint go files
|
||||
@@ -396,13 +393,7 @@ lint-go-windows:
|
||||
.PHONY: lint-go-gitea-vet
|
||||
lint-go-gitea-vet: ## lint go files with gitea-vet
|
||||
@echo "Running gitea-vet..."
|
||||
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
||||
@$(GO) vet -vettool=gitea-vet ./...
|
||||
|
||||
.PHONY: lint-go-gopls
|
||||
lint-go-gopls: ## lint go files with gopls
|
||||
@echo "Running gopls check..."
|
||||
@GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES)
|
||||
@$(GO) vet -vettool="$(shell GOOS= GOARCH= go tool -n gitea-vet)" ./...
|
||||
|
||||
.PHONY: lint-editorconfig
|
||||
lint-editorconfig:
|
||||
@@ -422,6 +413,14 @@ lint-templates: .venv node_modules ## lint template files
|
||||
lint-yaml: .venv ## lint yaml files
|
||||
@uv run --frozen yamllint -s .
|
||||
|
||||
.PHONY: lint-json
|
||||
lint-json: node_modules ## lint json files
|
||||
$(NODE_VARS) pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0
|
||||
|
||||
.PHONY: lint-json-fix
|
||||
lint-json-fix: node_modules ## lint and fix json files
|
||||
$(NODE_VARS) pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0 --fix
|
||||
|
||||
.PHONY: watch
|
||||
watch: ## watch everything and continuously rebuild
|
||||
@bash tools/watch.sh
|
||||
@@ -468,7 +467,7 @@ test\#%:
|
||||
coverage:
|
||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' coverage.out > coverage-bodged.out
|
||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' integration.coverage.out > integration.coverage-bodged.out
|
||||
$(GO) run build/gocovmerge.go integration.coverage-bodged.out coverage-bodged.out > coverage.all
|
||||
$(GO) run tools/gocovmerge.go integration.coverage-bodged.out coverage-bodged.out > coverage.all
|
||||
|
||||
.PHONY: unit-test-coverage
|
||||
unit-test-coverage:
|
||||
@@ -766,7 +765,7 @@ generate-go: $(TAGS_PREREQ)
|
||||
|
||||
.PHONY: security-check
|
||||
security-check:
|
||||
go run $(GOVULNCHECK_PACKAGE) -show color ./...
|
||||
GOEXPERIMENT= go run $(GOVULNCHECK_PACKAGE) -show color ./...
|
||||
|
||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||
ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)
|
||||
@@ -847,8 +846,6 @@ deps-tools: ## install tool dependencies
|
||||
$(GO) install $(GO_LICENSES_PACKAGE) & \
|
||||
$(GO) install $(GOVULNCHECK_PACKAGE) & \
|
||||
$(GO) install $(ACTIONLINT_PACKAGE) & \
|
||||
$(GO) install $(GOPLS_PACKAGE) & \
|
||||
$(GO) install $(GOPLS_MODERNIZE_PACKAGE) & \
|
||||
wait
|
||||
|
||||
node_modules: pnpm-lock.yaml
|
||||
@@ -914,16 +911,6 @@ lockfile-check:
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: update-translations
|
||||
update-translations:
|
||||
mkdir -p ./translations
|
||||
cd ./translations && curl -L https://crowdin.com/download/project/gitea.zip > gitea.zip && unzip gitea.zip
|
||||
rm ./translations/gitea.zip
|
||||
$(SED_INPLACE) -e 's/="/=/g' -e 's/"$$//g' ./translations/*.ini
|
||||
$(SED_INPLACE) -e 's/\\"/"/g' ./translations/*.ini
|
||||
mv ./translations/*.ini ./options/locale/
|
||||
rmdir ./translations
|
||||
|
||||
.PHONY: generate-gitignore
|
||||
generate-gitignore: ## update gitignore files
|
||||
$(GO) run build/generate-gitignores.go
|
||||
|
||||
13
assets/go-licenses.json
generated
13
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
14
build.go
14
build.go
@@ -1,14 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build vendor
|
||||
|
||||
package main
|
||||
|
||||
// Libraries that are included to vendor utilities used during Makefile build.
|
||||
// These libraries will not be included in a normal compilation.
|
||||
|
||||
import (
|
||||
// for vet
|
||||
_ "code.gitea.io/gitea-vet"
|
||||
)
|
||||
@@ -1,52 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
# this script runs in alpine image which only has `sh` shell
|
||||
|
||||
set +e
|
||||
if sed --version 2>/dev/null | grep -q GNU; then
|
||||
SED_INPLACE="sed -i"
|
||||
else
|
||||
SED_INPLACE="sed -i ''"
|
||||
fi
|
||||
set -e
|
||||
|
||||
if [ ! -f ./options/locale/locale_en-US.ini ]; then
|
||||
if [ ! -f ./options/locale/locale_en-US.json ]; then
|
||||
echo "please run this script in the root directory of the project"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mv ./options/locale/locale_en-US.ini ./options/
|
||||
|
||||
# the "ini" library for locale has many quirks, its behavior is different from Crowdin.
|
||||
# see i18n_test.go for more details
|
||||
|
||||
# this script helps to unquote the Crowdin outputs for the quirky ini library
|
||||
# * find all `key="...\"..."` lines
|
||||
# * remove the leading quote
|
||||
# * remove the trailing quote
|
||||
# * unescape the quotes
|
||||
# * eg: key="...\"..." => key=..."...
|
||||
$SED_INPLACE -r -e '/^[-.A-Za-z0-9_]+[ ]*=[ ]*".*"$/ {
|
||||
s/^([-.A-Za-z0-9_]+)[ ]*=[ ]*"/\1=/
|
||||
s/"$//
|
||||
s/\\"/"/g
|
||||
}' ./options/locale/*.ini
|
||||
|
||||
# * if the escaped line is incomplete like `key="...` or `key=..."`, quote it with backticks
|
||||
# * eg: key="... => key=`"...`
|
||||
# * eg: key=..." => key=`..."`
|
||||
$SED_INPLACE -r -e 's/^([-.A-Za-z0-9_]+)[ ]*=[ ]*(".*[^"])$/\1=`\2`/' ./options/locale/*.ini
|
||||
$SED_INPLACE -r -e 's/^([-.A-Za-z0-9_]+)[ ]*=[ ]*([^"].*")$/\1=`\2`/' ./options/locale/*.ini
|
||||
mv ./options/locale/locale_en-US.json ./options/
|
||||
|
||||
# Remove translation under 25% of en_us
|
||||
baselines=$(wc -l "./options/locale_en-US.ini" | cut -d" " -f1)
|
||||
baselines=$(cat "./options/locale_en-US.json" | wc -l)
|
||||
baselines=$((baselines / 4))
|
||||
for filename in ./options/locale/*.ini; do
|
||||
lines=$(wc -l "$filename" | cut -d" " -f1)
|
||||
if [ $lines -lt $baselines ]; then
|
||||
for filename in ./options/locale/*.json; do
|
||||
lines=$(cat "$filename" | wc -l)
|
||||
if [ "$lines" -lt "$baselines" ]; then
|
||||
echo "Removing $filename: $lines/$baselines"
|
||||
rm "$filename"
|
||||
fi
|
||||
done
|
||||
|
||||
mv ./options/locale_en-US.ini ./options/locale/
|
||||
mv ./options/locale_en-US.json ./options/locale/
|
||||
|
||||
@@ -121,7 +121,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
|
||||
}
|
||||
log.Trace("Processing next %d repos of %d", len(repos), count)
|
||||
for _, repo := range repos {
|
||||
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath())
|
||||
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RelativePath())
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
|
||||
if err != nil {
|
||||
log.Warn("OpenRepository: %v", err)
|
||||
@@ -147,7 +147,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Trace(" repo %s releases synchronized to tags: from %d to %d",
|
||||
log.Trace("repo %s releases synchronized to tags: from %d to %d",
|
||||
repo.FullName(), oldnum, count)
|
||||
gitRepo.Close()
|
||||
}
|
||||
|
||||
@@ -94,6 +94,10 @@ func commonLdapCLIFlags() []cli.Flag {
|
||||
Name: "public-ssh-key-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "ssh-keys-are-verified",
|
||||
Usage: "Set to true to automatically flag SSH keys in LDAP as verified.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "skip-local-2fa",
|
||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
||||
@@ -294,6 +298,9 @@ func parseLdapConfig(c *cli.Command, config *ldap.Source) error {
|
||||
if c.IsSet("public-ssh-key-attribute") {
|
||||
config.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
||||
}
|
||||
if c.IsSet("ssh-keys-are-verified") {
|
||||
config.SSHKeysAreVerified = c.Bool("ssh-keys-are-verified")
|
||||
}
|
||||
if c.IsSet("avatar-attribute") {
|
||||
config.AttributeAvatar = c.String("avatar-attribute")
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ func TestAddLdapBindDn(t *testing.T) {
|
||||
},
|
||||
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
|
||||
assert.FailNow(t, "getAuthSourceByID called", "case %d: should not call getAuthSourceByID", n)
|
||||
return nil, nil
|
||||
return nil, nil //nolint:nilnil // mock function covering improper behavior
|
||||
},
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ func TestAddLdapSimpleAuth(t *testing.T) {
|
||||
},
|
||||
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
|
||||
assert.FailNow(t, "getAuthSourceById called", "case %d: should not call getAuthSourceByID", n)
|
||||
return nil, nil
|
||||
return nil, nil //nolint:nilnil // mock function covering improper behavior
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// codeql[disable-next-line=go/clear-text-logging]
|
||||
fmt.Printf("generated random password is '%s'\n", password)
|
||||
} else if userType == user_model.UserTypeIndividual {
|
||||
return errors.New("must set either password or random-password flag")
|
||||
|
||||
@@ -58,6 +58,7 @@ func runMustChangePassword(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// codeql[disable-next-line=go/clear-text-logging]
|
||||
fmt.Printf("Updated %d users setting MustChangePassword to %t\n", n, mustChangePassword)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -121,6 +121,12 @@ func globalBool(c *cli.Command, name string) bool {
|
||||
// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever.
|
||||
func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cli.Command) (context.Context, error) {
|
||||
return func(ctx context.Context, c *cli.Command) (context.Context, error) {
|
||||
if setting.InstallLock {
|
||||
// During config loading, there might also be logs (for example: deprecation warnings).
|
||||
// It must make sure that console logger is set up before config is loaded.
|
||||
log.Error("Config is loaded before console logger is setup, it will cause bugs. Please fix it.")
|
||||
return nil, errors.New("console logger must be setup before config is loaded")
|
||||
}
|
||||
level := defaultLevel
|
||||
if globalBool(c, "quiet") {
|
||||
level = log.FATAL
|
||||
|
||||
156
cmd/config.go
Normal file
156
cmd/config.go
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func cmdConfig() *cli.Command {
|
||||
subcmdConfigEditIni := &cli.Command{
|
||||
Name: "edit-ini",
|
||||
Usage: "Load an existing INI file, apply environment variables, keep specified keys, and output to a new INI file.",
|
||||
Description: `
|
||||
Help users to edit the Gitea configuration INI file.
|
||||
|
||||
# Keep Specified Keys
|
||||
|
||||
If you need to re-create the configuration file with only a subset of keys,
|
||||
you can provide an INI template file for the kept keys and use the "--config-keep-keys" flag.
|
||||
For example, if a helm chart needs to reset the settings and only keep SECRET_KEY,
|
||||
it can use a template file (only keys take effect, values are ignored):
|
||||
|
||||
[security]
|
||||
SECRET_KEY=
|
||||
|
||||
$ ./gitea config edit-ini --config app-old.ini --config-keep-keys app-keys.ini --out app-new.ini
|
||||
|
||||
# Map Environment Variables to INI Configuration
|
||||
|
||||
Environment variables of the form "GITEA__section_name__KEY_NAME"
|
||||
will be mapped to the ini section "[section_name]" and the key
|
||||
"KEY_NAME" with the value as provided.
|
||||
|
||||
Environment variables of the form "GITEA__section_name__KEY_NAME__FILE"
|
||||
will be mapped to the ini section "[section_name]" and the key
|
||||
"KEY_NAME" with the value loaded from the specified file.
|
||||
|
||||
Environment variable keys can only contain characters "0-9A-Z_",
|
||||
if a section or key name contains dot ".", it needs to be escaped as _0x2E_.
|
||||
For example, to apply this config:
|
||||
|
||||
[git.config]
|
||||
foo.bar=val
|
||||
|
||||
$ export GITEA__git_0x2E_config__foo_0x2E_bar=val
|
||||
|
||||
# Put All Together
|
||||
|
||||
$ ./gitea config edit-ini --config app.ini --config-keep-keys app-keys.ini --apply-env {--in-place|--out app-new.ini}
|
||||
`,
|
||||
Flags: []cli.Flag{
|
||||
// "--config" flag is provided by global flags, and this flag is also used by "environment-to-ini" script wrapper
|
||||
// "--in-place" is also used by "environment-to-ini" script wrapper for its old behavior: always overwrite the existing config file
|
||||
&cli.BoolFlag{
|
||||
Name: "in-place",
|
||||
Usage: "Output to the same config file as input. This flag will be ignored if --out is set.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config-keep-keys",
|
||||
Usage: "An INI template file containing keys for keeping. Only the keys defined in the INI template will be kept from old config. If not set, all keys will be kept.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "apply-env",
|
||||
Usage: "Apply all GITEA__* variables from the environment to the config.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "out",
|
||||
Usage: "Destination config file to write to.",
|
||||
},
|
||||
},
|
||||
Action: runConfigEditIni,
|
||||
}
|
||||
|
||||
return &cli.Command{
|
||||
Name: "config",
|
||||
Usage: "Manage Gitea configuration",
|
||||
Commands: []*cli.Command{
|
||||
subcmdConfigEditIni,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runConfigEditIni(_ context.Context, c *cli.Command) error {
|
||||
// the config system may change the environment variables, so get a copy first, to be used later
|
||||
env := append([]string{}, os.Environ()...)
|
||||
|
||||
// don't use the guessed setting.CustomConf, instead, require the user to provide --config explicitly
|
||||
if !c.IsSet("config") {
|
||||
return errors.New("flag is required but not set: --config")
|
||||
}
|
||||
configFileIn := c.String("config")
|
||||
|
||||
cfgIn, err := setting.NewConfigProviderFromFile(configFileIn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config file %q: %v", configFileIn, err)
|
||||
}
|
||||
|
||||
// determine output config file: use "--out" flag or use "--in-place" flag to overwrite input file
|
||||
inPlace := c.Bool("in-place")
|
||||
configFileOut := c.String("out")
|
||||
if configFileOut == "" {
|
||||
if !inPlace {
|
||||
return errors.New("either --in-place or --out must be specified")
|
||||
}
|
||||
configFileOut = configFileIn // in-place edit
|
||||
}
|
||||
|
||||
needWriteOut := configFileOut != configFileIn
|
||||
|
||||
cfgOut := cfgIn
|
||||
configKeepKeys := c.String("config-keep-keys")
|
||||
if configKeepKeys != "" {
|
||||
needWriteOut = true
|
||||
cfgOut, err = setting.NewConfigProviderFromFile(configKeepKeys)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config-keep-keys template file %q: %v", configKeepKeys, err)
|
||||
}
|
||||
|
||||
for _, secOut := range cfgOut.Sections() {
|
||||
for _, keyOut := range secOut.Keys() {
|
||||
secIn := cfgIn.Section(secOut.Name())
|
||||
keyIn := setting.ConfigSectionKey(secIn, keyOut.Name())
|
||||
if keyIn != nil {
|
||||
keyOut.SetValue(keyIn.String())
|
||||
} else {
|
||||
secOut.DeleteKey(keyOut.Name())
|
||||
}
|
||||
}
|
||||
if len(secOut.Keys()) == 0 {
|
||||
cfgOut.DeleteSection(secOut.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.Bool("apply-env") {
|
||||
if setting.EnvironmentToConfig(cfgOut, env) {
|
||||
needWriteOut = true
|
||||
}
|
||||
}
|
||||
|
||||
if needWriteOut {
|
||||
err = cfgOut.SaveTo(configFileOut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
85
cmd/config_test.go
Normal file
85
cmd/config_test.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConfigEdit(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
configOld := tmpDir + "/app-old.ini"
|
||||
configTemplate := tmpDir + "/app-template.ini"
|
||||
_ = os.WriteFile(configOld, []byte(`
|
||||
[sec]
|
||||
k1=v1
|
||||
k2=v2
|
||||
`), os.ModePerm)
|
||||
|
||||
_ = os.WriteFile(configTemplate, []byte(`
|
||||
[sec]
|
||||
k1=in-template
|
||||
|
||||
[sec2]
|
||||
k3=v3
|
||||
`), os.ModePerm)
|
||||
|
||||
t.Setenv("GITEA__EnV__KeY", "val")
|
||||
|
||||
t.Run("OutputToNewWithEnv", func(t *testing.T) {
|
||||
configNew := tmpDir + "/app-new.ini"
|
||||
err := NewMainApp(AppVersion{}).Run(t.Context(), []string{
|
||||
"./gitea", "--config", configOld,
|
||||
"config", "edit-ini",
|
||||
"--apply-env",
|
||||
"--config-keep-keys", configTemplate,
|
||||
"--out", configNew,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// "k1" old value is kept because its key is in the template
|
||||
// "k2" is removed because it isn't in the template
|
||||
// "k3" isn't in new config because it isn't in the old config
|
||||
// [env] is applied from environment variable
|
||||
data, _ := os.ReadFile(configNew)
|
||||
require.Equal(t, `[sec]
|
||||
k1 = v1
|
||||
|
||||
[env]
|
||||
KeY = val
|
||||
`, string(data))
|
||||
})
|
||||
|
||||
t.Run("OutputToExisting(environment-to-ini)", func(t *testing.T) {
|
||||
// the legacy "environment-to-ini" (now a wrapper script) behavior:
|
||||
// if no "--out", then "--in-place" must be used to overwrite the existing "--config" file
|
||||
err := NewMainApp(AppVersion{}).Run(t.Context(), []string{
|
||||
"./gitea", "config", "edit-ini",
|
||||
"--apply-env",
|
||||
"--config", configOld,
|
||||
})
|
||||
require.ErrorContains(t, err, "either --in-place or --out must be specified")
|
||||
|
||||
// simulate the "environment-to-ini" behavior with "--in-place"
|
||||
err = NewMainApp(AppVersion{}).Run(t.Context(), []string{
|
||||
"./gitea", "config", "edit-ini",
|
||||
"--in-place",
|
||||
"--apply-env",
|
||||
"--config", configOld,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
data, _ := os.ReadFile(configOld)
|
||||
require.Equal(t, `[sec]
|
||||
k1 = v1
|
||||
k2 = v2
|
||||
|
||||
[env]
|
||||
KeY = val
|
||||
`, string(data))
|
||||
})
|
||||
}
|
||||
@@ -91,6 +91,7 @@ func runGenerateSecretKey(_ context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// codeql[disable-next-line=go/clear-text-logging]
|
||||
fmt.Printf("%s", secretKey)
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
|
||||
30
cmd/hook.go
30
cmd/hook.go
@@ -163,6 +163,14 @@ func (n *nilWriter) WriteString(s string) (int, error) {
|
||||
return len(s), nil
|
||||
}
|
||||
|
||||
func parseGitHookCommitRefLine(line string) (oldCommitID, newCommitID string, refFullName git.RefName, ok bool) {
|
||||
fields := strings.Split(line, " ")
|
||||
if len(fields) != 3 {
|
||||
return "", "", "", false
|
||||
}
|
||||
return fields[0], fields[1], git.RefName(fields[2]), true
|
||||
}
|
||||
|
||||
func runHookPreReceive(ctx context.Context, c *cli.Command) error {
|
||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
||||
return nil
|
||||
@@ -186,7 +194,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
userID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
|
||||
deployKeyID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvDeployKeyID), 10, 64)
|
||||
actionPerm, _ := strconv.ParseInt(os.Getenv(repo_module.EnvActionPerm), 10, 64)
|
||||
actionPerm, _ := strconv.Atoi(os.Getenv(repo_module.EnvActionPerm))
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserID: userID,
|
||||
@@ -196,7 +204,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
GitPushOptions: pushOptions(),
|
||||
PullRequestID: prID,
|
||||
DeployKeyID: deployKeyID,
|
||||
ActionPerm: int(actionPerm),
|
||||
ActionPerm: actionPerm,
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
@@ -228,14 +236,11 @@ Gitea or set your environment appropriately.`, "")
|
||||
continue
|
||||
}
|
||||
|
||||
fields := bytes.Fields(scanner.Bytes())
|
||||
if len(fields) != 3 {
|
||||
oldCommitID, newCommitID, refFullName, ok := parseGitHookCommitRefLine(scanner.Text())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
oldCommitID := string(fields[0])
|
||||
newCommitID := string(fields[1])
|
||||
refFullName := git.RefName(fields[2])
|
||||
total++
|
||||
lastline++
|
||||
|
||||
@@ -313,7 +318,7 @@ func runHookPostReceive(ctx context.Context, c *cli.Command) error {
|
||||
setup(ctx, c.Bool("debug"))
|
||||
|
||||
// First of all run update-server-info no matter what
|
||||
if _, _, err := gitcmd.NewCommand("update-server-info").RunStdString(ctx, nil); err != nil {
|
||||
if _, _, err := gitcmd.NewCommand("update-server-info").RunStdString(ctx); err != nil {
|
||||
return fmt.Errorf("failed to call 'git update-server-info': %w", err)
|
||||
}
|
||||
|
||||
@@ -378,16 +383,13 @@ Gitea or set your environment appropriately.`, "")
|
||||
continue
|
||||
}
|
||||
|
||||
fields := bytes.Fields(scanner.Bytes())
|
||||
if len(fields) != 3 {
|
||||
var ok bool
|
||||
oldCommitIDs[count], newCommitIDs[count], refFullNames[count], ok = parseGitHookCommitRefLine(scanner.Text())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, ".")
|
||||
oldCommitIDs[count] = string(fields[0])
|
||||
newCommitIDs[count] = string(fields[1])
|
||||
refFullNames[count] = git.RefName(fields[2])
|
||||
|
||||
commitID, _ := git.NewIDFromString(newCommitIDs[count])
|
||||
if refFullNames[count] == git.BranchPrefix+"master" && !commitID.IsZero() && count == total {
|
||||
masterPushed = true
|
||||
|
||||
@@ -39,3 +39,17 @@ func TestPktLine(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("0007a\nb"), w.Bytes())
|
||||
}
|
||||
|
||||
func TestParseGitHookCommitRefLine(t *testing.T) {
|
||||
oldCommitID, newCommitID, refName, ok := parseGitHookCommitRefLine("a b c")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "a", oldCommitID)
|
||||
assert.Equal(t, "b", newCommitID)
|
||||
assert.Equal(t, "c", string(refName))
|
||||
|
||||
_, _, _, ok = parseGitHookCommitRefLine("a\tb\tc")
|
||||
assert.False(t, ok)
|
||||
|
||||
_, _, _, ok = parseGitHookCommitRefLine("a b")
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
var CmdKeys = &cli.Command{
|
||||
Name: "keys",
|
||||
Usage: "(internal) Should only be called by SSH server",
|
||||
Hidden: true, // internal commands shouldn't not be visible
|
||||
Hidden: true, // internal commands shouldn't be visible
|
||||
Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint",
|
||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||
Action: runKeys,
|
||||
|
||||
11
cmd/main.go
11
cmd/main.go
@@ -50,11 +50,15 @@ DEFAULT CONFIGURATION:
|
||||
|
||||
func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) {
|
||||
originBefore := originCmd.Before
|
||||
originCmd.Before = func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
|
||||
prepareWorkPathAndCustomConf(cmd)
|
||||
originCmd.Before = func(ctxOrig context.Context, cmd *cli.Command) (ctx context.Context, err error) {
|
||||
ctx = ctxOrig
|
||||
if originBefore != nil {
|
||||
return originBefore(ctx, cmd)
|
||||
ctx, err = originBefore(ctx, cmd)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
}
|
||||
prepareWorkPathAndCustomConf(cmd)
|
||||
return ctx, nil
|
||||
}
|
||||
}
|
||||
@@ -128,6 +132,7 @@ func NewMainApp(appVer AppVersion) *cli.Command {
|
||||
|
||||
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
|
||||
subCmdStandalone := []*cli.Command{
|
||||
cmdConfig(),
|
||||
cmdCert(),
|
||||
CmdGenerate,
|
||||
CmdDocs,
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v3"
|
||||
@@ -28,11 +29,11 @@ func makePathOutput(workPath, customPath, customConf string) string {
|
||||
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
|
||||
}
|
||||
|
||||
func newTestApp(testCmdAction cli.ActionFunc) *cli.Command {
|
||||
func newTestApp(testCmd cli.Command) *cli.Command {
|
||||
app := NewMainApp(AppVersion{})
|
||||
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
|
||||
prepareSubcommandWithGlobalFlags(testCmd)
|
||||
app.Commands = append(app.Commands, testCmd)
|
||||
testCmd.Name = util.IfZero(testCmd.Name, "test-cmd")
|
||||
prepareSubcommandWithGlobalFlags(&testCmd)
|
||||
app.Commands = append(app.Commands, &testCmd)
|
||||
app.DefaultCommand = testCmd.Name
|
||||
return app
|
||||
}
|
||||
@@ -156,9 +157,11 @@ func TestCliCmd(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.cmd, func(t *testing.T) {
|
||||
app := newTestApp(func(ctx context.Context, cmd *cli.Command) error {
|
||||
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
||||
return nil
|
||||
app := newTestApp(cli.Command{
|
||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
||||
return nil
|
||||
},
|
||||
})
|
||||
for k, v := range c.env {
|
||||
t.Setenv(k, v)
|
||||
@@ -173,31 +176,54 @@ func TestCliCmd(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCliCmdError(t *testing.T) {
|
||||
app := newTestApp(func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") })
|
||||
app := newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") }})
|
||||
r, err := runTestApp(app, "./gitea", "test-cmd")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 1, r.ExitCode)
|
||||
assert.Empty(t, r.Stdout)
|
||||
assert.Equal(t, "Command error: normal error\n", r.Stderr)
|
||||
|
||||
app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) })
|
||||
app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) }})
|
||||
r, err = runTestApp(app, "./gitea", "test-cmd")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 2, r.ExitCode)
|
||||
assert.Empty(t, r.Stdout)
|
||||
assert.Equal(t, "exit error\n", r.Stderr)
|
||||
|
||||
app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil })
|
||||
app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }})
|
||||
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 1, r.ExitCode)
|
||||
assert.Empty(t, r.Stdout)
|
||||
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
|
||||
|
||||
app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil })
|
||||
app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }})
|
||||
r, err = runTestApp(app, "./gitea", "test-cmd")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called
|
||||
assert.Empty(t, r.Stdout)
|
||||
assert.Empty(t, r.Stderr)
|
||||
}
|
||||
|
||||
func TestCliCmdBefore(t *testing.T) {
|
||||
ctxNew := context.WithValue(context.Background(), any("key"), "value")
|
||||
configValues := map[string]string{}
|
||||
setting.CustomConf = "/tmp/any.ini"
|
||||
var actionCtx context.Context
|
||||
app := newTestApp(cli.Command{
|
||||
Before: func(context.Context, *cli.Command) (context.Context, error) {
|
||||
configValues["before"] = setting.CustomConf
|
||||
return ctxNew, nil
|
||||
},
|
||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||
configValues["action"] = setting.CustomConf
|
||||
actionCtx = ctx
|
||||
return nil
|
||||
},
|
||||
})
|
||||
_, err := runTestApp(app, "./gitea", "--config", "/dev/null", "test-cmd")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, ctxNew, actionCtx)
|
||||
assert.Equal(t, "/tmp/any.ini", configValues["before"], "BeforeFunc must be called before preparing config")
|
||||
assert.Equal(t, "/dev/null", configValues["action"])
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ var CmdMigrateStorage = &cli.Command{
|
||||
Name: "type",
|
||||
Aliases: []string{"t"},
|
||||
Value: "",
|
||||
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts",
|
||||
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts'",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "storage",
|
||||
|
||||
41
cmd/serv.go
41
cmd/serv.go
@@ -13,13 +13,12 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/repo"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
@@ -32,7 +31,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/lfs"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/kballard/go-shellquote"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
@@ -133,27 +131,6 @@ func getAccessMode(verb, lfsVerb string) perm.AccessMode {
|
||||
return perm.AccessModeNone
|
||||
}
|
||||
|
||||
func getLFSAuthToken(ctx context.Context, lfsVerb string, results *private.ServCommandResults) (string, error) {
|
||||
now := time.Now()
|
||||
claims := lfs.Claims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(now.Add(setting.LFS.HTTPAuthExpiry)),
|
||||
NotBefore: jwt.NewNumericDate(now),
|
||||
},
|
||||
RepoID: results.RepoID,
|
||||
Op: lfsVerb,
|
||||
UserID: results.UserID,
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
// Sign and get the complete encoded token as a string using the secret
|
||||
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
|
||||
if err != nil {
|
||||
return "", fail(ctx, "Failed to sign JWT Token", "Failed to sign JWT token: %v", err)
|
||||
}
|
||||
return "Bearer " + tokenString, nil
|
||||
}
|
||||
|
||||
func runServ(ctx context.Context, c *cli.Command) error {
|
||||
// FIXME: This needs to internationalised
|
||||
setup(ctx, c.Bool("debug"))
|
||||
@@ -230,7 +207,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
username := repoPathFields[0]
|
||||
reponame := strings.TrimSuffix(repoPathFields[1], ".git") // “the-repo-name" or "the-repo-name.wiki"
|
||||
|
||||
if !repo.IsValidSSHAccessRepoName(reponame) {
|
||||
if !repo_model.IsValidSSHAccessRepoName(reponame) {
|
||||
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
|
||||
}
|
||||
|
||||
@@ -276,14 +253,16 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
return fail(ctx, extra.UserMsg, "ServCommand failed: %s", extra.Error)
|
||||
}
|
||||
|
||||
// LowerCase and trim the repoPath as that's how they are stored.
|
||||
// This should be done after splitting the repoPath into username and reponame
|
||||
// so that username and reponame are not affected.
|
||||
repoPath = strings.ToLower(results.OwnerName + "/" + results.RepoName + ".git")
|
||||
// because the original repoPath maybe redirected, we need to use the returned actual repository information
|
||||
if results.IsWiki {
|
||||
repoPath = repo_model.RelativeWikiPath(results.OwnerName, results.RepoName)
|
||||
} else {
|
||||
repoPath = repo_model.RelativePath(results.OwnerName, results.RepoName)
|
||||
}
|
||||
|
||||
// LFS SSH protocol
|
||||
if verb == git.CmdVerbLfsTransfer {
|
||||
token, err := getLFSAuthToken(ctx, lfsVerb, results)
|
||||
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -294,7 +273,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
if verb == git.CmdVerbLfsAuthenticate {
|
||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
||||
|
||||
token, err := getLFSAuthToken(ctx, lfsVerb, results)
|
||||
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
17
cmd/web.go
17
cmd/web.go
@@ -8,14 +8,13 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/gtprof"
|
||||
@@ -156,7 +155,6 @@ func serveInstall(cmd *cli.Command) error {
|
||||
case <-graceful.GetManager().IsShutdown():
|
||||
<-graceful.GetManager().Done()
|
||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||
log.GetManager().Close()
|
||||
return err
|
||||
default:
|
||||
}
|
||||
@@ -231,17 +229,22 @@ func serveInstalled(c *cli.Command) error {
|
||||
err := listen(webRoutes, true)
|
||||
<-graceful.GetManager().Done()
|
||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||
log.GetManager().Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func servePprof() {
|
||||
// FIXME: it shouldn't use the global DefaultServeMux, and it should use a proper context
|
||||
http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
mux.Handle("/debug/fgprof", fgprof.Handler())
|
||||
// FIXME: it should use a proper context
|
||||
_, _, finished := process.GetManager().AddTypedContext(context.TODO(), "Web: PProf Server", process.SystemProcessType, true)
|
||||
// The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment, it's not worth introducing a configurable option for it.
|
||||
log.Info("Starting pprof server on localhost:6060")
|
||||
log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", nil))
|
||||
log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", mux))
|
||||
finished()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
Environment To Ini
|
||||
==================
|
||||
|
||||
Multiple docker users have requested that the Gitea docker is changed
|
||||
to permit arbitrary configuration via environment variables.
|
||||
|
||||
Gitea needs to use an ini file for configuration because the running
|
||||
environment that starts the docker may not be the same as that used
|
||||
by the hooks. An ini file also gives a good default and means that
|
||||
users do not have to completely provide a full environment.
|
||||
|
||||
With those caveats above, this command provides a generic way of
|
||||
converting suitably structured environment variables into any ini
|
||||
value.
|
||||
|
||||
To use the command is very simple just run it and the default gitea
|
||||
app.ini will be rewritten to take account of the variables provided,
|
||||
however there are various options to give slightly different
|
||||
behavior and these can be interrogated with the `-h` option.
|
||||
|
||||
The environment variables should be of the form:
|
||||
|
||||
GITEA__SECTION_NAME__KEY_NAME
|
||||
|
||||
Note, SECTION_NAME in the notation above is case-insensitive.
|
||||
|
||||
Environment variables are usually restricted to a reduced character
|
||||
set "0-9A-Z_" - in order to allow the setting of sections with
|
||||
characters outside of that set, they should be escaped as following:
|
||||
"_0X2E_" for "." and "_0X2D_" for "-". The entire section and key names
|
||||
can be escaped as a UTF8 byte string if necessary. E.g. to configure:
|
||||
|
||||
"""
|
||||
...
|
||||
[log.console]
|
||||
COLORIZE=false
|
||||
STDERR=true
|
||||
...
|
||||
"""
|
||||
|
||||
You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false"
|
||||
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
|
||||
on the configuration cheat sheet.
|
||||
|
||||
To build locally, run:
|
||||
|
||||
go build contrib/environment-to-ini/environment-to-ini.go
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.Command{}
|
||||
app.Name = "environment-to-ini"
|
||||
app.Usage = "Use provided environment to update configuration ini"
|
||||
app.Description = `As a helper to allow docker users to update the gitea configuration
|
||||
through the environment, this command allows environment variables to
|
||||
be mapped to values in the ini.
|
||||
|
||||
Environment variables of the form "GITEA__SECTION_NAME__KEY_NAME"
|
||||
will be mapped to the ini section "[section_name]" and the key
|
||||
"KEY_NAME" with the value as provided.
|
||||
|
||||
Environment variables of the form "GITEA__SECTION_NAME__KEY_NAME__FILE"
|
||||
will be mapped to the ini section "[section_name]" and the key
|
||||
"KEY_NAME" with the value loaded from the specified file.
|
||||
|
||||
Environment variables are usually restricted to a reduced character
|
||||
set "0-9A-Z_" - in order to allow the setting of sections with
|
||||
characters outside of that set, they should be escaped as following:
|
||||
"_0X2E_" for ".". The entire section and key names can be escaped as
|
||||
a UTF8 byte string if necessary. E.g. to configure:
|
||||
|
||||
"""
|
||||
...
|
||||
[log.console]
|
||||
COLORIZE=false
|
||||
STDERR=true
|
||||
...
|
||||
"""
|
||||
|
||||
You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false"
|
||||
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
|
||||
on the configuration cheat sheet.`
|
||||
app.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "custom-path",
|
||||
Aliases: []string{"C"},
|
||||
Value: setting.CustomPath,
|
||||
Usage: "Custom path file path",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config",
|
||||
Aliases: []string{"c"},
|
||||
Value: setting.CustomConf,
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "work-path",
|
||||
Aliases: []string{"w"},
|
||||
Value: setting.AppWorkPath,
|
||||
Usage: "Set the gitea working path",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "out",
|
||||
Aliases: []string{"o"},
|
||||
Value: "",
|
||||
Usage: "Destination file to write to",
|
||||
},
|
||||
}
|
||||
app.Action = runEnvironmentToIni
|
||||
err := app.Run(context.Background(), os.Args)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to run app with %s: %v", os.Args, err)
|
||||
}
|
||||
}
|
||||
|
||||
func runEnvironmentToIni(_ context.Context, c *cli.Command) error {
|
||||
// the config system may change the environment variables, so get a copy first, to be used later
|
||||
env := append([]string{}, os.Environ()...)
|
||||
setting.InitWorkPathAndCfgProvider(os.Getenv, setting.ArgWorkPathAndCustomConf{
|
||||
WorkPath: c.String("work-path"),
|
||||
CustomPath: c.String("custom-path"),
|
||||
CustomConf: c.String("config"),
|
||||
})
|
||||
|
||||
cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||
}
|
||||
|
||||
changed := setting.EnvironmentToConfig(cfg, env)
|
||||
|
||||
// try to save the config file
|
||||
destination := c.String("out")
|
||||
if len(destination) == 0 {
|
||||
destination = setting.CustomConf
|
||||
}
|
||||
if destination != setting.CustomConf || changed {
|
||||
log.Info("Settings saved to: %q", destination)
|
||||
err = cfg.SaveTo(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,9 +4,9 @@ base_path: "."
|
||||
base_url: "https://api.crowdin.com"
|
||||
preserve_hierarchy: true
|
||||
files:
|
||||
- source: "/options/locale/locale_en-US.ini"
|
||||
translation: "/options/locale/locale_%locale%.ini"
|
||||
type: "ini"
|
||||
- source: "/options/locale/locale_en-US.json"
|
||||
translation: "/options/locale/locale_%locale%.json"
|
||||
type: "json"
|
||||
skip_untranslated_strings: true
|
||||
export_only_approved: true
|
||||
update_option: "update_as_unapproved"
|
||||
|
||||
@@ -503,9 +503,6 @@ INTERNAL_TOKEN =
|
||||
;; Password Hash algorithm, either "argon2", "pbkdf2", "scrypt" or "bcrypt"
|
||||
;PASSWORD_HASH_ALGO = pbkdf2
|
||||
;;
|
||||
;; Set false to allow JavaScript to read CSRF cookie
|
||||
;CSRF_COOKIE_HTTP_ONLY = true
|
||||
;;
|
||||
;; Validate against https://haveibeenpwned.com/Passwords to see if a password has been exposed
|
||||
;PASSWORD_CHECK_PWN = false
|
||||
;;
|
||||
@@ -567,6 +564,11 @@ ENABLED = true
|
||||
;; Alternative location to specify OAuth2 authentication secret. You cannot specify both this and JWT_SECRET, and must pick one
|
||||
;JWT_SECRET_URI = file:/etc/gitea/oauth2_jwt_secret
|
||||
;;
|
||||
;; The "issuer" claim identifies the principal that issued the JWT.
|
||||
;; Gitea 1.25 makes it default to "ROOT_URL without the last slash" to follow the standard.
|
||||
;; If you have old logins from before 1.25, you may want to set it to the old (non-standard) value "ROOT_URL with the last slash".
|
||||
;JWT_CLAIM_ISSUER =
|
||||
;;
|
||||
;; Lifetime of an OAuth2 access token in seconds
|
||||
;ACCESS_TOKEN_EXPIRATION_TIME = 3600
|
||||
;;
|
||||
@@ -728,6 +730,9 @@ LEVEL = Info
|
||||
;DISABLE_CORE_PROTECT_NTFS=false
|
||||
;; Disable the usage of using partial clones for git.
|
||||
;DISABLE_PARTIAL_CLONE = false
|
||||
;; Set the similarity threshold passed to git commands via `--find-renames=<threshold>`.
|
||||
;; Default is 50%, the same as git. Must be a integer percentage between 0% and 100%.
|
||||
;DIFF_RENAME_SIMILARITY_THRESHOLD = 50%
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Git Operation timeout in seconds
|
||||
@@ -1343,6 +1348,10 @@ LEVEL = Info
|
||||
;; Dont mistake it for Reactions.
|
||||
;CUSTOM_EMOJIS = gitea, codeberg, gitlab, git, github, gogs
|
||||
;;
|
||||
;; Comma separated list of enabled emojis, for example: smile, thumbsup, thumbsdown
|
||||
;; Leave it empty to enable all emojis.
|
||||
;ENABLED_EMOJIS =
|
||||
;;
|
||||
;; Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
|
||||
;DEFAULT_SHOW_FULL_NAME = false
|
||||
;;
|
||||
@@ -2325,7 +2334,7 @@ LEVEL = Info
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Resynchronize pre-receive, update and post-receive hooks of all repositories.
|
||||
;; Resynchronize git hooks of all repositories (pre-receive, update, post-receive, proc-receive, ...)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[cron.resync_all_hooks]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -2536,7 +2545,19 @@ LEVEL = Info
|
||||
;; * sanitized: Sanitize the content and render it inside current page, default to only allow a few HTML tags and attributes. Customized sanitizer rules can be defined in [markup.sanitizer.*] .
|
||||
;; * no-sanitizer: Disable the sanitizer and render the content inside current page. It's **insecure** and may lead to XSS attack if the content contains malicious code.
|
||||
;; * iframe: Render the content in a separate standalone page and embed it into current page by iframe. The iframe is in sandbox mode with same-origin disabled, and the JS code are safely isolated from parent page.
|
||||
;RENDER_CONTENT_MODE=sanitized
|
||||
;RENDER_CONTENT_MODE = sanitized
|
||||
;; The sandbox applied to the iframe and Content-Security-Policy header when RENDER_CONTENT_MODE is `iframe`.
|
||||
;; It defaults to a safe set of "allow-*" restrictions (space separated).
|
||||
;; You can also set it by your requirements or use "disabled" to disable the sandbox completely.
|
||||
;; When set it, make sure there is no security risk:
|
||||
;; * PDF-only content: generally safe to use "disabled", and it needs to be "disabled" because PDF only renders with no sandbox.
|
||||
;; * HTML content with JS: if the "RENDER_COMMAND" can guarantee there is no XSS, then it is safe, otherwise, you need to fine tune the "allow-*" restrictions.
|
||||
;RENDER_CONTENT_SANDBOX =
|
||||
;; Whether post-process the rendered HTML content, including:
|
||||
;; resolve relative links and image sources, recognizing issue/commit references, escaping invisible characters,
|
||||
;; mentioning users, rendering permlink code blocks, replacing emoji shorthands, etc.
|
||||
;; By default, this is true when RENDER_CONTENT_MODE is `sanitized`, otherwise false.
|
||||
;NEED_POST_PROCESS = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
2
docker/root/usr/local/bin/environment-to-ini
Normal file
2
docker/root/usr/local/bin/environment-to-ini
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exec /app/gitea/gitea config edit-ini --in-place --apply-env "$@"
|
||||
2
docker/rootless/usr/local/bin/environment-to-ini
Normal file
2
docker/rootless/usr/local/bin/environment-to-ini
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exec /app/gitea/gitea config edit-ini --in-place --apply-env "$@"
|
||||
@@ -3,7 +3,6 @@ import comments from '@eslint-community/eslint-plugin-eslint-comments';
|
||||
import github from 'eslint-plugin-github';
|
||||
import globals from 'globals';
|
||||
import importPlugin from 'eslint-plugin-import-x';
|
||||
import noUseExtendNative from 'eslint-plugin-no-use-extend-native';
|
||||
import playwright from 'eslint-plugin-playwright';
|
||||
import regexp from 'eslint-plugin-regexp';
|
||||
import sonarjs from 'eslint-plugin-sonarjs';
|
||||
@@ -33,10 +32,6 @@ export default defineConfig([
|
||||
languageOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
parser: typescriptParser,
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
@@ -49,24 +44,19 @@ export default defineConfig([
|
||||
},
|
||||
linterOptions: {
|
||||
reportUnusedDisableDirectives: 2,
|
||||
reportUnusedInlineConfigs: 2,
|
||||
},
|
||||
plugins: {
|
||||
'@eslint-community/eslint-comments': comments,
|
||||
// @ts-expect-error
|
||||
'@stylistic': stylistic,
|
||||
'@typescript-eslint': typescriptPlugin.plugin,
|
||||
'array-func': arrayFunc,
|
||||
// @ts-expect-error -- https://github.com/un-ts/eslint-plugin-import-x/issues/203
|
||||
'import-x': importPlugin,
|
||||
'no-use-extend-native': noUseExtendNative,
|
||||
// @ts-expect-error
|
||||
regexp,
|
||||
// @ts-expect-error
|
||||
sonarjs,
|
||||
// @ts-expect-error
|
||||
unicorn,
|
||||
github,
|
||||
// @ts-expect-error
|
||||
wc,
|
||||
},
|
||||
settings: {
|
||||
@@ -159,7 +149,7 @@ export default defineConfig([
|
||||
'@typescript-eslint/ban-tslint-comment': [0],
|
||||
'@typescript-eslint/class-literal-property-style': [0],
|
||||
'@typescript-eslint/class-methods-use-this': [0],
|
||||
'@typescript-eslint/consistent-generic-constructors': [0],
|
||||
'@typescript-eslint/consistent-generic-constructors': [2, 'constructor'],
|
||||
'@typescript-eslint/consistent-indexed-object-style': [0],
|
||||
'@typescript-eslint/consistent-return': [0],
|
||||
'@typescript-eslint/consistent-type-assertions': [2, {assertionStyle: 'as', objectLiteralTypeAssertions: 'allow'}],
|
||||
@@ -221,7 +211,7 @@ export default defineConfig([
|
||||
'@typescript-eslint/no-unnecessary-condition': [0],
|
||||
'@typescript-eslint/no-unnecessary-qualifier': [0],
|
||||
'@typescript-eslint/no-unnecessary-template-expression': [0],
|
||||
'@typescript-eslint/no-unnecessary-type-arguments': [0],
|
||||
'@typescript-eslint/no-unnecessary-type-arguments': [2],
|
||||
'@typescript-eslint/no-unnecessary-type-assertion': [2],
|
||||
'@typescript-eslint/no-unnecessary-type-constraint': [2],
|
||||
'@typescript-eslint/no-unnecessary-type-conversion': [2],
|
||||
@@ -234,10 +224,12 @@ export default defineConfig([
|
||||
'@typescript-eslint/no-unsafe-member-access': [0],
|
||||
'@typescript-eslint/no-unsafe-return': [0],
|
||||
'@typescript-eslint/no-unsafe-unary-minus': [2],
|
||||
'@typescript-eslint/no-unused-expressions': [0],
|
||||
'@typescript-eslint/no-unused-expressions': [2],
|
||||
'@typescript-eslint/no-unused-private-class-members': [2],
|
||||
'@typescript-eslint/no-unused-vars': [2, {vars: 'all', args: 'all', caughtErrors: 'all', ignoreRestSiblings: false, argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_'}],
|
||||
'@typescript-eslint/no-use-before-define': [2, {functions: false, classes: true, variables: true, allowNamedExports: true, typedefs: false, enums: false, ignoreTypeReferences: true}],
|
||||
'@typescript-eslint/no-useless-constructor': [0],
|
||||
'@typescript-eslint/no-useless-default-assignment': [2],
|
||||
'@typescript-eslint/no-useless-empty-export': [0],
|
||||
'@typescript-eslint/no-wrapper-object-types': [2],
|
||||
'@typescript-eslint/non-nullable-type-assertion-style': [0],
|
||||
@@ -268,6 +260,7 @@ export default defineConfig([
|
||||
'@typescript-eslint/restrict-template-expressions': [0],
|
||||
'@typescript-eslint/return-await': [0],
|
||||
'@typescript-eslint/strict-boolean-expressions': [0],
|
||||
'@typescript-eslint/strict-void-return': [0],
|
||||
'@typescript-eslint/switch-exhaustiveness-check': [0],
|
||||
'@typescript-eslint/triple-slash-reference': [2],
|
||||
'@typescript-eslint/typedef': [0],
|
||||
@@ -366,7 +359,7 @@ export default defineConfig([
|
||||
'import-x/no-self-import': [2],
|
||||
'import-x/no-unassigned-import': [0],
|
||||
'import-x/no-unresolved': [2, {commonjs: true, ignore: ['\\?.+$']}],
|
||||
// 'import-x/no-unused-modules': [2, {unusedExports: true}], // not compatible with eslint 9
|
||||
'import-x/no-unused-modules': [0], // incompatible with eslint 9
|
||||
'import-x/no-useless-path-segments': [2, {commonjs: true}],
|
||||
'import-x/no-webpack-loader-syntax': [2],
|
||||
'import-x/order': [0],
|
||||
@@ -589,12 +582,12 @@ export default defineConfig([
|
||||
'no-unreachable': [2],
|
||||
'no-unsafe-finally': [2],
|
||||
'no-unsafe-negation': [2],
|
||||
'no-unused-expressions': [2],
|
||||
'no-unused-expressions': [0], // handled by @typescript-eslint/no-unused-expressions
|
||||
'no-unused-labels': [2],
|
||||
'no-unused-private-class-members': [2],
|
||||
'no-unused-private-class-members': [0], // handled by @typescript-eslint/no-unused-private-class-members
|
||||
'no-unused-vars': [0], // handled by @typescript-eslint/no-unused-vars
|
||||
'no-use-before-define': [0], // handled by @typescript-eslint/no-use-before-define
|
||||
'no-use-extend-native/no-use-extend-native': [2],
|
||||
'no-useless-assignment': [2],
|
||||
'no-useless-backreference': [2],
|
||||
'no-useless-call': [2],
|
||||
'no-useless-catch': [2],
|
||||
@@ -777,6 +770,7 @@ export default defineConfig([
|
||||
'unicorn/no-empty-file': [2],
|
||||
'unicorn/no-for-loop': [0],
|
||||
'unicorn/no-hex-escape': [0],
|
||||
'unicorn/no-immediate-mutation': [0],
|
||||
'unicorn/no-instanceof-array': [0],
|
||||
'unicorn/no-invalid-fetch-options': [2],
|
||||
'unicorn/no-invalid-remove-event-listener': [2],
|
||||
@@ -802,6 +796,7 @@ export default defineConfig([
|
||||
'unicorn/no-unreadable-array-destructuring': [0],
|
||||
'unicorn/no-unreadable-iife': [2],
|
||||
'unicorn/no-unused-properties': [2],
|
||||
'unicorn/no-useless-collection-argument': [2],
|
||||
'unicorn/no-useless-fallback-in-spread': [2],
|
||||
'unicorn/no-useless-length-check': [2],
|
||||
'unicorn/no-useless-promise-resolve-reject': [2],
|
||||
@@ -813,8 +808,8 @@ export default defineConfig([
|
||||
'unicorn/numeric-separators-style': [0],
|
||||
'unicorn/prefer-add-event-listener': [2],
|
||||
'unicorn/prefer-array-find': [2],
|
||||
'unicorn/prefer-array-flat-map': [2],
|
||||
'unicorn/prefer-array-flat': [2],
|
||||
'unicorn/prefer-array-flat-map': [2],
|
||||
'unicorn/prefer-array-index-of': [2],
|
||||
'unicorn/prefer-array-some': [2],
|
||||
'unicorn/prefer-at': [0],
|
||||
@@ -849,6 +844,7 @@ export default defineConfig([
|
||||
'unicorn/prefer-query-selector': [2],
|
||||
'unicorn/prefer-reflect-apply': [0],
|
||||
'unicorn/prefer-regexp-test': [2],
|
||||
'unicorn/prefer-response-static-json': [2],
|
||||
'unicorn/prefer-set-has': [0],
|
||||
'unicorn/prefer-set-size': [2],
|
||||
'unicorn/prefer-spread': [0],
|
||||
@@ -900,7 +896,6 @@ export default defineConfig([
|
||||
'yoda': [2, 'never'],
|
||||
},
|
||||
},
|
||||
// @ts-expect-error
|
||||
{
|
||||
...playwright.configs['flat/recommended'],
|
||||
files: ['tests/e2e/**'],
|
||||
@@ -916,7 +911,6 @@ export default defineConfig([
|
||||
},
|
||||
},
|
||||
extends: [
|
||||
// @ts-expect-error
|
||||
vue.configs['flat/recommended'],
|
||||
// @ts-expect-error
|
||||
vueScopedCss.configs['flat/recommended'],
|
||||
@@ -926,6 +920,7 @@ export default defineConfig([
|
||||
'vue/html-closing-bracket-spacing': [2, {startTag: 'never', endTag: 'never', selfClosingTag: 'never'}],
|
||||
'vue/max-attributes-per-line': [0],
|
||||
'vue/singleline-html-element-content-newline': [0],
|
||||
'vue/require-typed-ref': [2],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -936,7 +931,6 @@ export default defineConfig([
|
||||
},
|
||||
{
|
||||
files: ['**/*.test.ts', 'web_src/js/test/setup.ts'],
|
||||
// @ts-expect-error - https://github.com/vitest-dev/eslint-plugin-vitest/issues/737
|
||||
plugins: {vitest},
|
||||
languageOptions: {globals: globals.vitest},
|
||||
rules: {
|
||||
@@ -992,38 +986,19 @@ export default defineConfig([
|
||||
'vitest/valid-title': [2],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['web_src/js/types.ts'],
|
||||
rules: {
|
||||
'import-x/no-unused-modules': [0],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.d.ts'],
|
||||
rules: {
|
||||
'import-x/no-unused-modules': [0],
|
||||
'@typescript-eslint/consistent-type-definitions': [0],
|
||||
'@typescript-eslint/consistent-type-imports': [0],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.config.*'],
|
||||
rules: {
|
||||
'import-x/no-unused-modules': [0],
|
||||
},
|
||||
files: ['*', 'tools/**/*'],
|
||||
languageOptions: {globals: globals.node},
|
||||
},
|
||||
{
|
||||
files: ['web_src/**/*', 'docs/**/*'],
|
||||
languageOptions: {globals: globals.browser},
|
||||
},
|
||||
{
|
||||
files: ['web_src/**/*'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
__webpack_public_path__: true,
|
||||
process: false, // https://github.com/webpack/webpack/issues/15833
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
30
eslint.json.config.ts
Normal file
30
eslint.json.config.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {defineConfig, globalIgnores} from 'eslint/config';
|
||||
import json from '@eslint/json';
|
||||
|
||||
export default defineConfig([
|
||||
globalIgnores([
|
||||
'**/.venv',
|
||||
'**/node_modules',
|
||||
'**/public',
|
||||
]),
|
||||
{
|
||||
files: ['**/*.json'],
|
||||
plugins: {json},
|
||||
language: 'json/json',
|
||||
extends: ['json/recommended'],
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'tsconfig.json',
|
||||
'.devcontainer/*.json',
|
||||
'.vscode/*.json',
|
||||
'contrib/ide/vscode/*.json',
|
||||
],
|
||||
plugins: {json},
|
||||
language: 'json/jsonc',
|
||||
languageOptions: {
|
||||
allowTrailingCommas: true,
|
||||
},
|
||||
extends: ['json/recommended'],
|
||||
},
|
||||
]);
|
||||
40
flake.lock
generated
40
flake.lock
generated
@@ -1,30 +1,12 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1755186698,
|
||||
"narHash": "sha256-wNO3+Ks2jZJ4nTHMuks+cxAiVBGNuEBXsT29Bz6HASo=",
|
||||
"lastModified": 1760038930,
|
||||
"narHash": "sha256-Oncbh0UmHjSlxO7ErQDM3KM0A5/Znfofj2BSzlHLeVw=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "fbcf476f790d8a217c3eab4e12033dc4a0f6d23c",
|
||||
"rev": "0b4defa2584313f3b781240b29d61f6f9f7e0df3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -36,24 +18,8 @@
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
139
flake.nix
139
flake.nix
@@ -1,73 +1,94 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
outputs =
|
||||
{ nixpkgs, flake-utils, ... }:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
devShells.default =
|
||||
with pkgs;
|
||||
{ nixpkgs, ... }:
|
||||
let
|
||||
supportedSystems = [
|
||||
"aarch64-darwin"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"x86_64-linux"
|
||||
];
|
||||
|
||||
forEachSupportedSystem =
|
||||
f:
|
||||
nixpkgs.lib.genAttrs supportedSystems (
|
||||
system:
|
||||
let
|
||||
# only bump toolchain versions here
|
||||
go = go_1_25;
|
||||
nodejs = nodejs_24;
|
||||
python3 = python312;
|
||||
pnpm = pnpm_10;
|
||||
|
||||
# Platform-specific dependencies
|
||||
linuxOnlyInputs = lib.optionals pkgs.stdenv.isLinux [
|
||||
glibc.static
|
||||
];
|
||||
|
||||
linuxOnlyEnv = lib.optionalAttrs pkgs.stdenv.isLinux {
|
||||
CFLAGS = "-I${glibc.static.dev}/include";
|
||||
LDFLAGS = "-L ${glibc.static}/lib";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in
|
||||
pkgs.mkShell (
|
||||
{
|
||||
buildInputs = [
|
||||
# generic
|
||||
git
|
||||
git-lfs
|
||||
gnumake
|
||||
gnused
|
||||
gnutar
|
||||
gzip
|
||||
zip
|
||||
f { inherit pkgs; }
|
||||
);
|
||||
in
|
||||
{
|
||||
devShells = forEachSupportedSystem (
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
default =
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
|
||||
# frontend
|
||||
nodejs
|
||||
pnpm
|
||||
cairo
|
||||
pixman
|
||||
pkg-config
|
||||
# only bump toolchain versions here
|
||||
go = pkgs.go_1_25;
|
||||
nodejs = pkgs.nodejs_24;
|
||||
python3 = pkgs.python312;
|
||||
pnpm = pkgs.pnpm_10;
|
||||
|
||||
# linting
|
||||
python3
|
||||
uv
|
||||
# Platform-specific dependencies
|
||||
linuxOnlyInputs = lib.optionals pkgs.stdenv.isLinux [
|
||||
pkgs.glibc.static
|
||||
];
|
||||
|
||||
# backend
|
||||
go
|
||||
gofumpt
|
||||
sqlite
|
||||
]
|
||||
++ linuxOnlyInputs;
|
||||
linuxOnlyEnv = lib.optionalAttrs pkgs.stdenv.isLinux {
|
||||
CFLAGS = "-I${pkgs.glibc.static.dev}/include";
|
||||
LDFLAGS = "-L ${pkgs.glibc.static}/lib";
|
||||
};
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages =
|
||||
with pkgs;
|
||||
[
|
||||
# generic
|
||||
git
|
||||
git-lfs
|
||||
gnumake
|
||||
gnused
|
||||
gnutar
|
||||
gzip
|
||||
zip
|
||||
|
||||
GO = "${go}/bin/go";
|
||||
GOROOT = "${go}/share/go";
|
||||
# frontend
|
||||
nodejs
|
||||
pnpm
|
||||
cairo
|
||||
pixman
|
||||
pkg-config
|
||||
|
||||
TAGS = "sqlite sqlite_unlock_notify";
|
||||
STATIC = "true";
|
||||
}
|
||||
// linuxOnlyEnv
|
||||
);
|
||||
}
|
||||
);
|
||||
# linting
|
||||
python3
|
||||
uv
|
||||
|
||||
# backend
|
||||
go
|
||||
gofumpt
|
||||
sqlite
|
||||
]
|
||||
++ linuxOnlyInputs;
|
||||
|
||||
env = {
|
||||
GO = "${go}/bin/go";
|
||||
GOROOT = "${go}/share/go";
|
||||
|
||||
TAGS = "sqlite sqlite_unlock_notify";
|
||||
STATIC = "true";
|
||||
}
|
||||
// linuxOnlyEnv;
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
60
go.mod
60
go.mod
@@ -1,6 +1,8 @@
|
||||
module code.gitea.io/gitea
|
||||
|
||||
go 1.25.1
|
||||
go 1.25.0
|
||||
|
||||
toolchain go1.25.5
|
||||
|
||||
// rfc5280 said: "The serial number is an integer assigned by the CA to each certificate."
|
||||
// But some CAs use negative serial number, just relax the check. related:
|
||||
@@ -9,14 +11,13 @@ godebug x509negativeserial=1
|
||||
|
||||
require (
|
||||
code.gitea.io/actions-proto-go v0.4.1
|
||||
code.gitea.io/gitea-vet v0.2.3
|
||||
code.gitea.io/sdk/gitea v0.22.0
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||
connectrpc.com/connect v1.18.1
|
||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
|
||||
gitea.com/go-chi/cache v0.2.1
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
|
||||
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96
|
||||
gitea.com/go-chi/session v0.0.0-20251124165456-68e0254e989e
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||
github.com/42wim/httpsig v1.2.3
|
||||
@@ -27,7 +28,7 @@ require (
|
||||
github.com/ProtonMail/go-crypto v1.3.0
|
||||
github.com/PuerkitoBio/goquery v1.10.3
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0
|
||||
github.com/alecthomas/chroma/v2 v2.20.0
|
||||
github.com/alecthomas/chroma/v2 v2.22.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.10
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.32.2
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
@@ -35,7 +36,7 @@ require (
|
||||
github.com/bohde/codel v0.2.0
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.8
|
||||
github.com/caddyserver/certmagic v0.24.0
|
||||
github.com/charmbracelet/git-lfs-transfer v0.2.0
|
||||
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20251013092601-6327009efd21
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
||||
github.com/djherbis/buffer v1.2.0
|
||||
@@ -56,11 +57,12 @@ require (
|
||||
github.com/go-co-op/gocron v1.37.0
|
||||
github.com/go-enry/go-enry/v2 v2.9.2
|
||||
github.com/go-git/go-billy/v5 v5.6.2
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/go-git/go-git/v5 v5.16.3
|
||||
github.com/go-ldap/ldap/v3 v3.4.11
|
||||
github.com/go-redsync/redsync/v4 v4.13.0
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/go-webauthn/webauthn v0.13.4
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
@@ -75,7 +77,6 @@ require (
|
||||
github.com/huandu/xstrings v1.5.0
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
||||
github.com/jhillyerd/enmime v1.3.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/klauspost/cpuid/v2 v2.3.0
|
||||
@@ -84,7 +85,7 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-sqlite3 v1.14.32
|
||||
github.com/meilisearch/meilisearch-go v0.33.2
|
||||
github.com/mholt/archives v0.1.3
|
||||
github.com/mholt/archives v0.0.0-20251009205813-e30ac6010726
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/microsoft/go-mssqldb v1.9.3
|
||||
github.com/minio/minio-go/v7 v7.0.95
|
||||
@@ -94,7 +95,6 @@ require (
|
||||
github.com/olivere/elastic/v7 v7.0.32
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/quasoft/websspi v1.1.2
|
||||
@@ -109,20 +109,20 @@ require (
|
||||
github.com/ulikunitz/xz v0.5.15
|
||||
github.com/urfave/cli-docs/v3 v3.0.0-alpha6
|
||||
github.com/urfave/cli/v3 v3.4.1
|
||||
github.com/wneessen/go-mail v0.6.2
|
||||
github.com/wneessen/go-mail v0.7.2
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.7.13
|
||||
github.com/yuin/goldmark v1.7.16
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
gitlab.com/gitlab-org/api/client-go v0.142.4
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/image v0.30.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/text v0.28.0
|
||||
golang.org/x/sync v0.18.0
|
||||
golang.org/x/sys v0.38.0
|
||||
golang.org/x/text v0.31.0
|
||||
google.golang.org/grpc v1.75.0
|
||||
google.golang.org/protobuf v1.36.8
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
@@ -135,6 +135,7 @@ require (
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
||||
code.gitea.io/gitea-vet v0.2.3 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||
@@ -142,7 +143,7 @@ require (
|
||||
github.com/DataDog/zstd v1.5.7 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/RoaringBitmap/roaring/v2 v2.10.0 // indirect
|
||||
github.com/STARRY-S/zip v0.2.1 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||
@@ -172,7 +173,7 @@ require (
|
||||
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.1 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
|
||||
@@ -200,7 +201,6 @@ require (
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-webauthn/x v0.1.24 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
@@ -220,6 +220,7 @@ require (
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/libdns/libdns v1.1.1 // indirect
|
||||
@@ -233,14 +234,14 @@ require (
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minlz v1.0.0 // indirect
|
||||
github.com/minio/minlz v1.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.1.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250817074551-3280053e4e00 // indirect
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/ll v0.1.0 // indirect
|
||||
@@ -249,6 +250,7 @@ require (
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/pjbgf/sha1cd v0.4.0 // 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.2 // indirect
|
||||
github.com/prometheus/common v0.65.0 // indirect
|
||||
@@ -259,7 +261,8 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.5 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/tinylib/msgp v1.4.0 // indirect
|
||||
github.com/unknwon/com v1.0.1 // indirect
|
||||
@@ -277,10 +280,10 @@ require (
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
@@ -295,10 +298,7 @@ replace github.com/jaytaylor/html2text => github.com/Necoro/html2text v0.0.0-202
|
||||
|
||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||
|
||||
replace github.com/nektos/act => gitea.com/gitea/act v0.261.6
|
||||
|
||||
// TODO: the only difference is in `PutObject`: the fork doesn't use `NewVerifyingReader(r, sha256.New(), oid, expectedSize)`, need to figure out why
|
||||
replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0
|
||||
replace github.com/nektos/act => gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763
|
||||
|
||||
replace git.sr.ht/~mariusor/go-xsd-duration => gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078
|
||||
|
||||
@@ -309,3 +309,5 @@ exclude github.com/gofrs/uuid v4.0.0+incompatible
|
||||
exclude github.com/goccy/go-json v0.4.11
|
||||
|
||||
exclude github.com/satori/go.uuid v1.2.0
|
||||
|
||||
tool code.gitea.io/gitea-vet
|
||||
|
||||
100
go.sum
100
go.sum
@@ -31,10 +31,8 @@ dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
gitea.com/gitea/act v0.261.6 h1:CjZwKOyejonNFDmsXOw3wGm5Vet573hHM6VMLsxtvPY=
|
||||
gitea.com/gitea/act v0.261.6/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
|
||||
gitea.com/gitea/git-lfs-transfer v0.2.0 h1:baHaNoBSRaeq/xKayEXwiDQtlIjps4Ac/Ll4KqLMB40=
|
||||
gitea.com/gitea/git-lfs-transfer v0.2.0/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits=
|
||||
gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763 h1:ohdxegvslDEllZmRNDqpKun6L4Oq81jNdEDtGgHEV2c=
|
||||
gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
|
||||
gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:BAFmdZpRW7zMQZQDClaCWobRj9uL1MR3MzpCVJvc5s4=
|
||||
gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
|
||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso=
|
||||
@@ -43,8 +41,8 @@ gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
|
||||
gitea.com/go-chi/cache v0.2.1/go.mod h1:Qic0HZ8hOHW62ETGbonpwz8WYypj9NieU9659wFUJ8Q=
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo=
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098/go.mod h1:LjzIOHlRemuUyO7WR12fmm18VZIlCAaOt9L3yKw40pk=
|
||||
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96 h1:IFDiMBObsP6CZIRaDLd54SR6zPYAffPXiXck5Xslu0Q=
|
||||
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96/go.mod h1:0iEpFKnwO5dG0aF98O4eq6FMsAiXkNBaDIlUOlq4BtM=
|
||||
gitea.com/go-chi/session v0.0.0-20251124165456-68e0254e989e h1:4bugwPyGMLvblEm3pZ8fZProSPVxE4l0UXF2Kv6IJoY=
|
||||
gitea.com/go-chi/session v0.0.0-20251124165456-68e0254e989e/go.mod h1:KDvcfMUoXfATPHs2mbMoXFTXT45/FAFAS39waz9tPk0=
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:VyMQP6ue6MKHM8UsOXfNfuMKD0oSAWZdXVcpHIN2yaY=
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHqdhS7keYWioqfmxdnfblFDTGoOwcZ+o=
|
||||
@@ -93,18 +91,18 @@ github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06
|
||||
github.com/RoaringBitmap/roaring v0.7.1/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.10.0 h1:HbJ8Cs71lfCJyvmSptxeMX2PtvOC8yonlU0GQcy2Ak0=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.10.0/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
|
||||
github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg=
|
||||
github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4=
|
||||
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0 h1:tgjwQrDH5m6jIYB7kac5IQZmfUzQNseac/e3H4VoCNE=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0/go.mod h1:1HmmMEVsr+0R1QWahSeMJkjSkq6CYAZu1aIbYSpfJ4o=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
|
||||
github.com/alecthomas/chroma/v2 v2.22.0 h1:PqEhf+ezz5F5owoDeOUKFzW+W3ZJDShNCaHg4sZuItI=
|
||||
github.com/alecthomas/chroma/v2 v2.22.0/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
||||
github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
@@ -193,8 +191,8 @@ github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTS
|
||||
github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=
|
||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
||||
github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A=
|
||||
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
|
||||
github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4=
|
||||
github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8=
|
||||
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
||||
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||
github.com/bohde/codel v0.2.0 h1:fzF7ibgKmCfQbOzQCblmQcwzDRmV7WO7VMLm/hDvD3E=
|
||||
@@ -219,6 +217,8 @@ github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20251013092601-6327009efd21 h1:2d64+4Jek9vjYwhY93AjbleiVH+AeWvPwPmDi1mfKFQ=
|
||||
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20251013092601-6327009efd21/go.mod h1:fNlYtCHWTRC8MofQERZkVUNUWaOvZeTBqHn/amSbKZI=
|
||||
github.com/chi-middleware/proxy v1.1.1 h1:4HaXUp8o2+bhHr1OhVy+VjN0+L7/07JDcn6v7YrTjrQ=
|
||||
github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdikvbVJVHv/M+0=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
@@ -339,8 +339,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8=
|
||||
github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
@@ -572,8 +572,8 @@ github.com/meilisearch/meilisearch-go v0.33.2 h1:YgsQSLYhAkRN2ias6I1KNRTjdYCN5w2
|
||||
github.com/meilisearch/meilisearch-go v0.33.2/go.mod h1:6eOPcQ+OAuwXvnONlfSgfgvr7TIAWM/6OdhcVHg8cF0=
|
||||
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
|
||||
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||
github.com/mholt/archives v0.1.3 h1:aEAaOtNra78G+TvV5ohmXrJOAzf++dIlYeDW3N9q458=
|
||||
github.com/mholt/archives v0.1.3/go.mod h1:LUCGp++/IbV/I0Xq4SzcIR6uwgeh2yjnQWamjRQfLTU=
|
||||
github.com/mholt/archives v0.0.0-20251009205813-e30ac6010726 h1:narluFTg20M5KBwKxedpFiSMkdjQRRNUlpY4uAsKMwk=
|
||||
github.com/mholt/archives v0.0.0-20251009205813-e30ac6010726/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/microsoft/go-mssqldb v1.9.3 h1:hy4p+LDC8LIGvI3JATnLVmBOLMJbmn5X400mr5j0lPs=
|
||||
@@ -588,8 +588,8 @@ github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
|
||||
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
|
||||
github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
|
||||
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
|
||||
github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
@@ -610,8 +610,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0=
|
||||
github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48=
|
||||
github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U=
|
||||
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A=
|
||||
github.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
@@ -714,9 +714,11 @@ github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYl
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg=
|
||||
github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk=
|
||||
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.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
@@ -729,6 +731,7 @@ github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -765,8 +768,8 @@ github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZ
|
||||
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/wneessen/go-mail v0.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8=
|
||||
github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
|
||||
github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8=
|
||||
github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
@@ -789,8 +792,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
|
||||
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
|
||||
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
|
||||
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
|
||||
@@ -837,9 +840,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -848,8 +850,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=
|
||||
@@ -876,8 +878,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -906,8 +908,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -930,9 +932,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -974,9 +975,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -987,9 +987,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1003,9 +1002,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
@@ -1041,8 +1039,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/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=
|
||||
|
||||
1
main.go
1
main.go
@@ -44,6 +44,7 @@ func main() {
|
||||
}
|
||||
app := cmd.NewMainApp(cmd.AppVersion{Version: Version, Extra: formatBuiltWith()})
|
||||
_ = cmd.RunMainApp(app, os.Args...) // all errors should have been handled by the RunMainApp
|
||||
// flush the queued logs before exiting, it is a MUST, otherwise there will be log loss
|
||||
log.GetManager().Close()
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
FixtureFiles: []string{
|
||||
"action_runner_token.yml",
|
||||
"action_run.yml",
|
||||
"repository.yml",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
|
||||
"github.com/nektos/act/pkg/jobparser"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
type ActionRun struct {
|
||||
ID int64
|
||||
Title string
|
||||
RepoID int64 `xorm:"index unique(repo_index)"`
|
||||
RepoID int64 `xorm:"unique(repo_index) index(repo_concurrency)"`
|
||||
Repo *repo_model.Repository `xorm:"-"`
|
||||
OwnerID int64 `xorm:"index"`
|
||||
WorkflowID string `xorm:"index"` // the name of workflow file
|
||||
@@ -49,6 +49,9 @@ type ActionRun struct {
|
||||
TriggerEvent string // the trigger event defined in the `on` configuration of the triggered workflow
|
||||
Status Status `xorm:"index"`
|
||||
Version int `xorm:"version default 0"` // Status could be updated concomitantly, so an optimistic lock is needed
|
||||
RawConcurrency string // raw concurrency
|
||||
ConcurrencyGroup string `xorm:"index(repo_concurrency) NOT NULL DEFAULT ''"`
|
||||
ConcurrencyCancel bool `xorm:"NOT NULL DEFAULT FALSE"`
|
||||
// Started and Stopped is used for recording last run time, if rerun happened, they will be reset to 0
|
||||
Started timeutil.TimeStamp
|
||||
Stopped timeutil.TimeStamp
|
||||
@@ -102,6 +105,15 @@ func (run *ActionRun) PrettyRef() string {
|
||||
return refName.ShortName()
|
||||
}
|
||||
|
||||
// RefTooltip return a tooltop of run's ref. For pull request, it's the title of the PR, otherwise it's the ShortName.
|
||||
func (run *ActionRun) RefTooltip() string {
|
||||
payload, err := run.GetPullRequestEventPayload()
|
||||
if err == nil && payload != nil && payload.PullRequest != nil {
|
||||
return payload.PullRequest.Title
|
||||
}
|
||||
return git.RefName(run.Ref).ShortName()
|
||||
}
|
||||
|
||||
// LoadAttributes load Repo TriggerUser if not loaded
|
||||
func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
||||
if run == nil {
|
||||
@@ -181,9 +193,11 @@ func (run *ActionRun) IsSchedule() bool {
|
||||
return run.ScheduleID > 0
|
||||
}
|
||||
|
||||
func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
|
||||
// UpdateRepoRunsNumbers updates the number of runs and closed runs of a repository.
|
||||
func UpdateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
|
||||
_, err := db.GetEngine(ctx).ID(repo.ID).
|
||||
NoAutoTime().
|
||||
Cols("num_action_runs", "num_closed_action_runs").
|
||||
SetExpr("num_action_runs",
|
||||
builder.Select("count(*)").From("action_run").
|
||||
Where(builder.Eq{"repo_id": repo.ID}),
|
||||
@@ -238,116 +252,62 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
|
||||
return cancelledJobs, err
|
||||
}
|
||||
|
||||
// Iterate over each job and attempt to cancel it.
|
||||
for _, job := range jobs {
|
||||
// Skip jobs that are already in a terminal state (completed, cancelled, etc.).
|
||||
status := job.Status
|
||||
if status.IsDone() {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the job has no associated task (probably an error), set its status to 'Cancelled' and stop it.
|
||||
if job.TaskID == 0 {
|
||||
job.Status = StatusCancelled
|
||||
job.Stopped = timeutil.TimeStampNow()
|
||||
|
||||
// Update the job's status and stopped time in the database.
|
||||
n, err := UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped")
|
||||
if err != nil {
|
||||
return cancelledJobs, err
|
||||
}
|
||||
|
||||
// If the update affected 0 rows, it means the job has changed in the meantime, so we need to try again.
|
||||
if n == 0 {
|
||||
return cancelledJobs, errors.New("job has changed, try again")
|
||||
}
|
||||
|
||||
cancelledJobs = append(cancelledJobs, job)
|
||||
// Continue with the next job.
|
||||
continue
|
||||
}
|
||||
|
||||
// If the job has an associated task, try to stop the task, effectively cancelling the job.
|
||||
if err := StopTask(ctx, job.TaskID, StatusCancelled); err != nil {
|
||||
return cancelledJobs, err
|
||||
}
|
||||
cancelledJobs = append(cancelledJobs, job)
|
||||
cjs, err := CancelJobs(ctx, jobs)
|
||||
if err != nil {
|
||||
return cancelledJobs, err
|
||||
}
|
||||
cancelledJobs = append(cancelledJobs, cjs...)
|
||||
}
|
||||
|
||||
// Return nil to indicate successful cancellation of all running and waiting jobs.
|
||||
return cancelledJobs, nil
|
||||
}
|
||||
|
||||
// InsertRun inserts a run
|
||||
// The title will be cut off at 255 characters if it's longer than 255 characters.
|
||||
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
run.Index = index
|
||||
run.Title = util.EllipsisDisplayString(run.Title, 255)
|
||||
|
||||
if err := db.Insert(ctx, run); err != nil {
|
||||
return err
|
||||
func CancelJobs(ctx context.Context, jobs []*ActionRunJob) ([]*ActionRunJob, error) {
|
||||
cancelledJobs := make([]*ActionRunJob, 0, len(jobs))
|
||||
// Iterate over each job and attempt to cancel it.
|
||||
for _, job := range jobs {
|
||||
// Skip jobs that are already in a terminal state (completed, cancelled, etc.).
|
||||
status := job.Status
|
||||
if status.IsDone() {
|
||||
continue
|
||||
}
|
||||
|
||||
if run.Repo == nil {
|
||||
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
|
||||
// If the job has no associated task (probably an error), set its status to 'Cancelled' and stop it.
|
||||
if job.TaskID == 0 {
|
||||
job.Status = StatusCancelled
|
||||
job.Stopped = timeutil.TimeStampNow()
|
||||
|
||||
// Update the job's status and stopped time in the database.
|
||||
n, err := UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped")
|
||||
if err != nil {
|
||||
return err
|
||||
return cancelledJobs, err
|
||||
}
|
||||
run.Repo = repo
|
||||
|
||||
// If the update affected 0 rows, it means the job has changed in the meantime
|
||||
if n == 0 {
|
||||
log.Error("Failed to cancel job %d because it has changed", job.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
cancelledJobs = append(cancelledJobs, job)
|
||||
// Continue with the next job.
|
||||
continue
|
||||
}
|
||||
|
||||
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
|
||||
return err
|
||||
// If the job has an associated task, try to stop the task, effectively cancelling the job.
|
||||
if err := StopTask(ctx, job.TaskID, StatusCancelled); err != nil {
|
||||
return cancelledJobs, err
|
||||
}
|
||||
updatedJob, err := GetRunJobByID(ctx, job.ID)
|
||||
if err != nil {
|
||||
return cancelledJobs, fmt.Errorf("get job: %w", err)
|
||||
}
|
||||
cancelledJobs = append(cancelledJobs, updatedJob)
|
||||
}
|
||||
|
||||
runJobs := make([]*ActionRunJob, 0, len(jobs))
|
||||
var hasWaiting bool
|
||||
for _, v := range jobs {
|
||||
id, job := v.Job()
|
||||
needs := job.Needs()
|
||||
if err := v.SetJob(id, job.EraseNeeds()); err != nil {
|
||||
return err
|
||||
}
|
||||
payload, _ := v.Marshal()
|
||||
status := StatusWaiting
|
||||
if len(needs) > 0 || run.NeedApproval {
|
||||
status = StatusBlocked
|
||||
} else {
|
||||
hasWaiting = true
|
||||
}
|
||||
job.Name = util.EllipsisDisplayString(job.Name, 255)
|
||||
runJobs = append(runJobs, &ActionRunJob{
|
||||
RunID: run.ID,
|
||||
RepoID: run.RepoID,
|
||||
OwnerID: run.OwnerID,
|
||||
CommitSHA: run.CommitSHA,
|
||||
IsForkPullRequest: run.IsForkPullRequest,
|
||||
Name: job.Name,
|
||||
WorkflowPayload: payload,
|
||||
JobID: id,
|
||||
Needs: needs,
|
||||
RunsOn: job.RunsOn(),
|
||||
Status: status,
|
||||
})
|
||||
}
|
||||
if err := db.Insert(ctx, runJobs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if there is a job in the waiting status, increase tasks version.
|
||||
if hasWaiting {
|
||||
if err := IncreaseTaskVersion(ctx, run.OwnerID, run.RepoID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// Return nil to indicate successful cancellation of all running and waiting jobs.
|
||||
return cancelledJobs, nil
|
||||
}
|
||||
|
||||
func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {
|
||||
@@ -432,7 +392,7 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error {
|
||||
if err = run.LoadRepo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
|
||||
if err := UpdateRepoRunsNumbers(ctx, run.Repo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -441,3 +401,59 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error {
|
||||
}
|
||||
|
||||
type ActionRunIndex db.ResourceIndex
|
||||
|
||||
func GetConcurrentRunsAndJobs(ctx context.Context, repoID int64, concurrencyGroup string, status []Status) ([]*ActionRun, []*ActionRunJob, error) {
|
||||
runs, err := db.Find[ActionRun](ctx, &FindRunOptions{
|
||||
RepoID: repoID,
|
||||
ConcurrencyGroup: concurrencyGroup,
|
||||
Status: status,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("find runs: %w", err)
|
||||
}
|
||||
|
||||
jobs, err := db.Find[ActionRunJob](ctx, &FindRunJobOptions{
|
||||
RepoID: repoID,
|
||||
ConcurrencyGroup: concurrencyGroup,
|
||||
Statuses: status,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("find jobs: %w", err)
|
||||
}
|
||||
|
||||
return runs, jobs, nil
|
||||
}
|
||||
|
||||
func CancelPreviousJobsByRunConcurrency(ctx context.Context, actionRun *ActionRun) ([]*ActionRunJob, error) {
|
||||
if actionRun.ConcurrencyGroup == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var jobsToCancel []*ActionRunJob
|
||||
|
||||
statusFindOption := []Status{StatusWaiting, StatusBlocked}
|
||||
if actionRun.ConcurrencyCancel {
|
||||
statusFindOption = append(statusFindOption, StatusRunning)
|
||||
}
|
||||
runs, jobs, err := GetConcurrentRunsAndJobs(ctx, actionRun.RepoID, actionRun.ConcurrencyGroup, statusFindOption)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find concurrent runs and jobs: %w", err)
|
||||
}
|
||||
jobsToCancel = append(jobsToCancel, jobs...)
|
||||
|
||||
// cancel runs in the same concurrency group
|
||||
for _, run := range runs {
|
||||
if run.ID == actionRun.ID {
|
||||
continue
|
||||
}
|
||||
jobs, err := db.Find[ActionRunJob](ctx, FindRunJobOptions{
|
||||
RunID: run.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find run %d jobs: %w", run.ID, err)
|
||||
}
|
||||
jobsToCancel = append(jobsToCancel, jobs...)
|
||||
}
|
||||
|
||||
return CancelJobs(ctx, jobsToCancel)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/nektos/act/pkg/jobparser"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
@@ -22,23 +23,38 @@ type ActionRunJob struct {
|
||||
ID int64
|
||||
RunID int64 `xorm:"index"`
|
||||
Run *ActionRun `xorm:"-"`
|
||||
RepoID int64 `xorm:"index"`
|
||||
RepoID int64 `xorm:"index(repo_concurrency)"`
|
||||
Repo *repo_model.Repository `xorm:"-"`
|
||||
OwnerID int64 `xorm:"index"`
|
||||
CommitSHA string `xorm:"index"`
|
||||
IsForkPullRequest bool
|
||||
Name string `xorm:"VARCHAR(255)"`
|
||||
Attempt int64
|
||||
WorkflowPayload []byte
|
||||
JobID string `xorm:"VARCHAR(255)"` // job id in workflow, not job's id
|
||||
Needs []string `xorm:"JSON TEXT"`
|
||||
RunsOn []string `xorm:"JSON TEXT"`
|
||||
TaskID int64 // the latest task of the job
|
||||
Status Status `xorm:"index"`
|
||||
Started timeutil.TimeStamp
|
||||
Stopped timeutil.TimeStamp
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated index"`
|
||||
|
||||
// WorkflowPayload is act/jobparser.SingleWorkflow for act/jobparser.Parse
|
||||
// it should contain exactly one job with global workflow fields for this model
|
||||
WorkflowPayload []byte
|
||||
|
||||
JobID string `xorm:"VARCHAR(255)"` // job id in workflow, not job's id
|
||||
Needs []string `xorm:"JSON TEXT"`
|
||||
RunsOn []string `xorm:"JSON TEXT"`
|
||||
TaskID int64 // the latest task of the job
|
||||
Status Status `xorm:"index"`
|
||||
|
||||
RawConcurrency string // raw concurrency from job YAML's "concurrency" section
|
||||
|
||||
// IsConcurrencyEvaluated is only valid/needed when this job's RawConcurrency is not empty.
|
||||
// If RawConcurrency can't be evaluated (e.g. depend on other job's outputs or have errors), this field will be false.
|
||||
// If RawConcurrency has been successfully evaluated, this field will be true, ConcurrencyGroup and ConcurrencyCancel are also set.
|
||||
IsConcurrencyEvaluated bool
|
||||
|
||||
ConcurrencyGroup string `xorm:"index(repo_concurrency) NOT NULL DEFAULT ''"` // evaluated concurrency.group
|
||||
ConcurrencyCancel bool `xorm:"NOT NULL DEFAULT FALSE"` // evaluated concurrency.cancel-in-progress
|
||||
|
||||
Started timeutil.TimeStamp
|
||||
Stopped timeutil.TimeStamp
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated index"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -84,6 +100,24 @@ func (job *ActionRunJob) LoadAttributes(ctx context.Context) error {
|
||||
return job.Run.LoadAttributes(ctx)
|
||||
}
|
||||
|
||||
// ParseJob parses the job structure from the ActionRunJob.WorkflowPayload
|
||||
func (job *ActionRunJob) ParseJob() (*jobparser.Job, error) {
|
||||
// job.WorkflowPayload is a SingleWorkflow created from an ActionRun's workflow, which exactly contains this job's YAML definition.
|
||||
// Ideally it shouldn't be called "Workflow", it is just a job with global workflow fields + trigger
|
||||
parsedWorkflows, err := jobparser.Parse(job.WorkflowPayload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("job %d single workflow: unable to parse: %w", job.ID, err)
|
||||
} else if len(parsedWorkflows) != 1 {
|
||||
return nil, fmt.Errorf("job %d single workflow: not single workflow", job.ID)
|
||||
}
|
||||
_, workflowJob := parsedWorkflows[0].Job()
|
||||
if workflowJob == nil {
|
||||
// it shouldn't happen, and since the callers don't check nil, so return an error instead of nil
|
||||
return nil, util.ErrorWrap(util.ErrNotExist, "job %d single workflow: payload doesn't contain a job", job.ID)
|
||||
}
|
||||
return workflowJob, nil
|
||||
}
|
||||
|
||||
func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) {
|
||||
var job ActionRunJob
|
||||
has, err := db.GetEngine(ctx).Where("id=?", id).Get(&job)
|
||||
@@ -125,7 +159,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
if affected != 0 && slices.Contains(cols, "status") && job.Status.IsWaiting() {
|
||||
if slices.Contains(cols, "status") && job.Status.IsWaiting() {
|
||||
// if the status of job changes to waiting again, increase tasks version.
|
||||
if err := IncreaseTaskVersion(ctx, job.OwnerID, job.RepoID); err != nil {
|
||||
return 0, err
|
||||
@@ -197,3 +231,39 @@ func AggregateJobStatus(jobs []*ActionRunJob) Status {
|
||||
return StatusUnknown // it shouldn't happen
|
||||
}
|
||||
}
|
||||
|
||||
func CancelPreviousJobsByJobConcurrency(ctx context.Context, job *ActionRunJob) (jobsToCancel []*ActionRunJob, _ error) {
|
||||
if job.RawConcurrency == "" {
|
||||
return nil, nil
|
||||
}
|
||||
if !job.IsConcurrencyEvaluated {
|
||||
return nil, nil
|
||||
}
|
||||
if job.ConcurrencyGroup == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
statusFindOption := []Status{StatusWaiting, StatusBlocked}
|
||||
if job.ConcurrencyCancel {
|
||||
statusFindOption = append(statusFindOption, StatusRunning)
|
||||
}
|
||||
runs, jobs, err := GetConcurrentRunsAndJobs(ctx, job.RepoID, job.ConcurrencyGroup, statusFindOption)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find concurrent runs and jobs: %w", err)
|
||||
}
|
||||
jobs = slices.DeleteFunc(jobs, func(j *ActionRunJob) bool { return j.ID == job.ID })
|
||||
jobsToCancel = append(jobsToCancel, jobs...)
|
||||
|
||||
// cancel runs in the same concurrency group
|
||||
for _, run := range runs {
|
||||
jobs, err := db.Find[ActionRunJob](ctx, FindRunJobOptions{
|
||||
RunID: run.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find run %d jobs: %w", run.ID, err)
|
||||
}
|
||||
jobsToCancel = append(jobsToCancel, jobs...)
|
||||
}
|
||||
|
||||
return CancelJobs(ctx, jobsToCancel)
|
||||
}
|
||||
|
||||
@@ -69,12 +69,13 @@ func (jobs ActionJobList) LoadAttributes(ctx context.Context, withRepo bool) err
|
||||
|
||||
type FindRunJobOptions struct {
|
||||
db.ListOptions
|
||||
RunID int64
|
||||
RepoID int64
|
||||
OwnerID int64
|
||||
CommitSHA string
|
||||
Statuses []Status
|
||||
UpdatedBefore timeutil.TimeStamp
|
||||
RunID int64
|
||||
RepoID int64
|
||||
OwnerID int64
|
||||
CommitSHA string
|
||||
Statuses []Status
|
||||
UpdatedBefore timeutil.TimeStamp
|
||||
ConcurrencyGroup string
|
||||
}
|
||||
|
||||
func (opts FindRunJobOptions) ToConds() builder.Cond {
|
||||
@@ -94,6 +95,12 @@ func (opts FindRunJobOptions) ToConds() builder.Cond {
|
||||
if opts.UpdatedBefore > 0 {
|
||||
cond = cond.And(builder.Lt{"`action_run_job`.updated": opts.UpdatedBefore})
|
||||
}
|
||||
if opts.ConcurrencyGroup != "" {
|
||||
if opts.RepoID == 0 {
|
||||
panic("Invalid FindRunJobOptions: repo_id is required")
|
||||
}
|
||||
cond = cond.And(builder.Eq{"`action_run_job`.concurrency_group": opts.ConcurrencyGroup})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
|
||||
@@ -64,15 +64,16 @@ func (runs RunList) LoadRepos(ctx context.Context) error {
|
||||
|
||||
type FindRunOptions struct {
|
||||
db.ListOptions
|
||||
RepoID int64
|
||||
OwnerID int64
|
||||
WorkflowID string
|
||||
Ref string // the commit/tag/… that caused this workflow
|
||||
TriggerUserID int64
|
||||
TriggerEvent webhook_module.HookEventType
|
||||
Approved bool // not util.OptionalBool, it works only when it's true
|
||||
Status []Status
|
||||
CommitSHA string
|
||||
RepoID int64
|
||||
OwnerID int64
|
||||
WorkflowID string
|
||||
Ref string // the commit/tag/… that caused this workflow
|
||||
TriggerUserID int64
|
||||
TriggerEvent webhook_module.HookEventType
|
||||
Approved bool // not util.OptionalBool, it works only when it's true
|
||||
Status []Status
|
||||
ConcurrencyGroup string
|
||||
CommitSHA string
|
||||
}
|
||||
|
||||
func (opts FindRunOptions) ToConds() builder.Cond {
|
||||
@@ -101,6 +102,12 @@ func (opts FindRunOptions) ToConds() builder.Cond {
|
||||
if opts.CommitSHA != "" {
|
||||
cond = cond.And(builder.Eq{"`action_run`.commit_sha": opts.CommitSHA})
|
||||
}
|
||||
if len(opts.ConcurrencyGroup) > 0 {
|
||||
if opts.RepoID == 0 {
|
||||
panic("Invalid FindRunOptions: repo_id is required")
|
||||
}
|
||||
cond = cond.And(builder.Eq{"`action_run`.concurrency_group": opts.ConcurrencyGroup})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
|
||||
35
models/actions/run_test.go
Normal file
35
models/actions/run_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUpdateRepoRunsNumbers(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// update the number to a wrong one, the original is 3
|
||||
_, err := db.GetEngine(t.Context()).ID(4).Cols("num_closed_action_runs").Update(&repo_model.Repository{
|
||||
NumClosedActionRuns: 2,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
assert.Equal(t, 4, repo.NumActionRuns)
|
||||
assert.Equal(t, 2, repo.NumClosedActionRuns)
|
||||
|
||||
// now update will correct them, only num_actionr_runs and num_closed_action_runs should be updated
|
||||
err = UpdateRepoRunsNumbers(t.Context(), repo)
|
||||
assert.NoError(t, err)
|
||||
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
assert.Equal(t, 5, repo.NumActionRuns)
|
||||
assert.Equal(t, 3, repo.NumClosedActionRuns)
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/shared/types"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
@@ -173,6 +174,13 @@ func (r *ActionRunner) GenerateToken() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// CanMatchLabels checks whether the runner's labels can match a job's "runs-on"
|
||||
// See https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idruns-on
|
||||
func (r *ActionRunner) CanMatchLabels(jobRunsOn []string) bool {
|
||||
runnerLabelSet := container.SetOf(r.AgentLabels...)
|
||||
return runnerLabelSet.Contains(jobRunsOn...) // match all labels
|
||||
}
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(&ActionRunner{})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
@@ -21,7 +20,6 @@ import (
|
||||
|
||||
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
|
||||
lru "github.com/hashicorp/golang-lru/v2"
|
||||
"github.com/nektos/act/pkg/jobparser"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@@ -246,7 +244,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
|
||||
var job *ActionRunJob
|
||||
log.Trace("runner labels: %v", runner.AgentLabels)
|
||||
for _, v := range jobs {
|
||||
if isSubset(runner.AgentLabels, v.RunsOn) {
|
||||
if runner.CanMatchLabels(v.RunsOn) {
|
||||
job = v
|
||||
break
|
||||
}
|
||||
@@ -278,13 +276,10 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
parsedWorkflows, err := jobparser.Parse(job.WorkflowPayload)
|
||||
workflowJob, err := job.ParseJob()
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("parse workflow of job %d: %w", job.ID, err)
|
||||
} else if len(parsedWorkflows) != 1 {
|
||||
return nil, false, fmt.Errorf("workflow of job %d: not single workflow", job.ID)
|
||||
return nil, false, fmt.Errorf("load job %d: %w", job.ID, err)
|
||||
}
|
||||
_, workflowJob := parsedWorkflows[0].Job()
|
||||
|
||||
if _, err := e.Insert(task); err != nil {
|
||||
return nil, false, err
|
||||
@@ -479,20 +474,6 @@ func FindOldTasksToExpire(ctx context.Context, olderThan timeutil.TimeStamp, lim
|
||||
Find(&tasks)
|
||||
}
|
||||
|
||||
func isSubset(set, subset []string) bool {
|
||||
m := make(container.Set[string], len(set))
|
||||
for _, v := range set {
|
||||
m.Add(v)
|
||||
}
|
||||
|
||||
for _, v := range subset {
|
||||
if !m.Contains(v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func convertTimestamp(timestamp *timestamppb.Timestamp) timeutil.TimeStamp {
|
||||
if timestamp.GetSeconds() == 0 && timestamp.GetNanos() == 0 {
|
||||
return timeutil.TimeStamp(0)
|
||||
|
||||
@@ -386,7 +386,7 @@ func SetNotificationStatus(ctx context.Context, notificationID int64, user *user
|
||||
|
||||
notification.Status = status
|
||||
|
||||
_, err = db.GetEngine(ctx).ID(notificationID).Update(notification)
|
||||
_, err = db.GetEngine(ctx).ID(notificationID).Cols("status").Update(notification)
|
||||
return notification, err
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/migration"
|
||||
"code.gitea.io/gitea/modules/secret"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -123,17 +124,17 @@ func (task *Task) MigrateConfig() (*migration.MigrateOptions, error) {
|
||||
// decrypt credentials
|
||||
if opts.CloneAddrEncrypted != "" {
|
||||
if opts.CloneAddr, err = secret.DecryptSecret(setting.SecretKey, opts.CloneAddrEncrypted); err != nil {
|
||||
return nil, err
|
||||
log.Error("Unable to decrypt CloneAddr, maybe SECRET_KEY is wrong: %v", err)
|
||||
}
|
||||
}
|
||||
if opts.AuthPasswordEncrypted != "" {
|
||||
if opts.AuthPassword, err = secret.DecryptSecret(setting.SecretKey, opts.AuthPasswordEncrypted); err != nil {
|
||||
return nil, err
|
||||
log.Error("Unable to decrypt AuthPassword, maybe SECRET_KEY is wrong: %v", err)
|
||||
}
|
||||
}
|
||||
if opts.AuthTokenEncrypted != "" {
|
||||
if opts.AuthToken, err = secret.DecryptSecret(setting.SecretKey, opts.AuthTokenEncrypted); err != nil {
|
||||
return nil, err
|
||||
log.Error("Unable to decrypt AuthToken, maybe SECRET_KEY is wrong: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st
|
||||
}
|
||||
|
||||
key.Verified = true
|
||||
if _, err := db.GetEngine(ctx).ID(key.ID).SetExpr("verified", true).Update(new(GPGKey)); err != nil {
|
||||
if _, err := db.GetEngine(ctx).ID(key.ID).Cols("verified").Update(key); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
@@ -67,13 +67,6 @@ func (key *PublicKey) OmitEmail() string {
|
||||
return strings.Join(strings.Split(key.Content, " ")[:2], " ")
|
||||
}
|
||||
|
||||
// AuthorizedString returns formatted public key string for authorized_keys file.
|
||||
//
|
||||
// TODO: Consider dropping this function
|
||||
func (key *PublicKey) AuthorizedString() string {
|
||||
return AuthorizedStringForKey(key)
|
||||
}
|
||||
|
||||
func addKey(ctx context.Context, key *PublicKey) (err error) {
|
||||
if len(key.Fingerprint) == 0 {
|
||||
key.Fingerprint, err = CalcFingerprint(key.Content)
|
||||
@@ -91,7 +84,7 @@ func addKey(ctx context.Context, key *PublicKey) (err error) {
|
||||
}
|
||||
|
||||
// AddPublicKey adds new public key to database and authorized_keys file.
|
||||
func AddPublicKey(ctx context.Context, ownerID int64, name, content string, authSourceID int64) (*PublicKey, error) {
|
||||
func AddPublicKey(ctx context.Context, ownerID int64, name, content string, authSourceID int64, verified bool) (*PublicKey, error) {
|
||||
log.Trace(content)
|
||||
|
||||
fingerprint, err := CalcFingerprint(content)
|
||||
@@ -122,6 +115,7 @@ func AddPublicKey(ctx context.Context, ownerID int64, name, content string, auth
|
||||
Mode: perm.AccessModeWrite,
|
||||
Type: KeyTypeUser,
|
||||
LoginSourceID: authSourceID,
|
||||
Verified: verified,
|
||||
}
|
||||
if err = addKey(ctx, key); err != nil {
|
||||
return nil, fmt.Errorf("addKey: %w", err)
|
||||
@@ -305,7 +299,7 @@ func deleteKeysMarkedForDeletion(ctx context.Context, keys []string) (bool, erro
|
||||
}
|
||||
|
||||
// AddPublicKeysBySource add a users public keys. Returns true if there are changes.
|
||||
func AddPublicKeysBySource(ctx context.Context, usr *user_model.User, s *auth.Source, sshPublicKeys []string) bool {
|
||||
func AddPublicKeysBySource(ctx context.Context, usr *user_model.User, s *auth.Source, sshPublicKeys []string, verified bool) bool {
|
||||
var sshKeysNeedUpdate bool
|
||||
for _, sshKey := range sshPublicKeys {
|
||||
var err error
|
||||
@@ -324,7 +318,7 @@ func AddPublicKeysBySource(ctx context.Context, usr *user_model.User, s *auth.So
|
||||
marshalled = marshalled[:len(marshalled)-1]
|
||||
sshKeyName := fmt.Sprintf("%s-%s", s.Name, ssh.FingerprintSHA256(out))
|
||||
|
||||
if _, err := AddPublicKey(ctx, usr.ID, sshKeyName, marshalled, s.ID); err != nil {
|
||||
if _, err := AddPublicKey(ctx, usr.ID, sshKeyName, marshalled, s.ID, verified); err != nil {
|
||||
if IsErrKeyAlreadyExist(err) {
|
||||
log.Trace("AddPublicKeysBySource[%s]: Public SSH Key %s already exists for user", sshKeyName, usr.Name)
|
||||
} else {
|
||||
@@ -343,7 +337,7 @@ func AddPublicKeysBySource(ctx context.Context, usr *user_model.User, s *auth.So
|
||||
}
|
||||
|
||||
// SynchronizePublicKeys updates a user's public keys. Returns true if there are changes.
|
||||
func SynchronizePublicKeys(ctx context.Context, usr *user_model.User, s *auth.Source, sshPublicKeys []string) bool {
|
||||
func SynchronizePublicKeys(ctx context.Context, usr *user_model.User, s *auth.Source, sshPublicKeys []string, verified bool) bool {
|
||||
var sshKeysNeedUpdate bool
|
||||
|
||||
log.Trace("synchronizePublicKeys[%s]: Handling Public SSH Key synchronization for user %s", s.Name, usr.Name)
|
||||
@@ -388,7 +382,7 @@ func SynchronizePublicKeys(ctx context.Context, usr *user_model.User, s *auth.So
|
||||
newKeys = append(newKeys, key)
|
||||
}
|
||||
}
|
||||
if AddPublicKeysBySource(ctx, usr, s, newKeys) {
|
||||
if AddPublicKeysBySource(ctx, usr, s, newKeys, verified) {
|
||||
sshKeysNeedUpdate = true
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -17,29 +18,13 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// _____ __ .__ .__ .___
|
||||
// / _ \ __ ___/ |_| |__ ___________|__|_______ ____ __| _/
|
||||
// / /_\ \| | \ __\ | \ / _ \_ __ \ \___ // __ \ / __ |
|
||||
// / | \ | /| | | Y ( <_> ) | \/ |/ /\ ___// /_/ |
|
||||
// \____|__ /____/ |__| |___| /\____/|__| |__/_____ \\___ >____ |
|
||||
// \/ \/ \/ \/ \/
|
||||
// ____ __.
|
||||
// | |/ _|____ ___.__. ______
|
||||
// | <_/ __ < | |/ ___/
|
||||
// | | \ ___/\___ |\___ \
|
||||
// |____|__ \___ > ____/____ >
|
||||
// \/ \/\/ \/
|
||||
//
|
||||
// This file contains functions for creating authorized_keys files
|
||||
//
|
||||
// There is a dependence on the database within RegeneratePublicKeys however most of these functions probably belong in a module
|
||||
|
||||
const (
|
||||
tplCommentPrefix = `# gitea public key`
|
||||
tplPublicKey = tplCommentPrefix + "\n" + `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict %s` + "\n"
|
||||
)
|
||||
// AuthorizedStringCommentPrefix is a magic tag
|
||||
// some functions like RegeneratePublicKeys needs this tag to skip the keys generated by Gitea, while keep other keys
|
||||
const AuthorizedStringCommentPrefix = `# gitea public key`
|
||||
|
||||
var sshOpLocker sync.Mutex
|
||||
|
||||
@@ -50,17 +35,73 @@ func WithSSHOpLocker(f func() error) error {
|
||||
}
|
||||
|
||||
// AuthorizedStringForKey creates the authorized keys string appropriate for the provided key
|
||||
func AuthorizedStringForKey(key *PublicKey) string {
|
||||
func AuthorizedStringForKey(key *PublicKey) (string, error) {
|
||||
sb := &strings.Builder{}
|
||||
_ = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sb, map[string]any{
|
||||
_, err := writeAuthorizedStringForKey(key, sb)
|
||||
return sb.String(), err
|
||||
}
|
||||
|
||||
// WriteAuthorizedStringForValidKey writes the authorized key for the provided key. If the key is invalid, it does nothing.
|
||||
func WriteAuthorizedStringForValidKey(key *PublicKey, w io.Writer) error {
|
||||
validKey, err := writeAuthorizedStringForKey(key, w)
|
||||
if !validKey {
|
||||
log.Debug("WriteAuthorizedStringForValidKey: key %s is not valid: %v", key, err)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
principalRegexp *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// principalRegexp expresses whether a principal is considered valid.
|
||||
// This reverse engineers how sshd parses the authorized keys file,
|
||||
// see e.g. https://github.com/openssh/openssh-portable/blob/32deb00b38b4ee2b3302f261ea1e68c04e020a08/auth2-pubkeyfile.c#L221-L256
|
||||
// Any newline or # comment will be stripped when parsing, so don't allow
|
||||
// those. Also, if any space or tab is present in the principal, the part
|
||||
// proceeding this would be parsed as an option, so just avoid any whitespace
|
||||
// altogether.
|
||||
ret.principalRegexp = regexp.MustCompile(`^[^\s#]+$`)
|
||||
return ret
|
||||
})
|
||||
|
||||
func writeAuthorizedStringForKey(key *PublicKey, w io.Writer) (keyValid bool, err error) {
|
||||
const tpl = AuthorizedStringCommentPrefix + "\n" + `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict %s` + "\n"
|
||||
|
||||
var sshKey string
|
||||
|
||||
if key.Type == KeyTypePrincipal {
|
||||
// TODO: actually using PublicKey to store "principal" is an abuse
|
||||
if !globalVars().principalRegexp.MatchString(key.Content) {
|
||||
return false, fmt.Errorf("invalid principal key: %s", key.Content)
|
||||
}
|
||||
sshKey = fmt.Sprintf("%s # user-%d", key.Content, key.OwnerID)
|
||||
} else {
|
||||
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key.Content))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
sshKeyMarshalled := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(pubKey)))
|
||||
sshKey = fmt.Sprintf("%s user-%d", sshKeyMarshalled, key.OwnerID)
|
||||
}
|
||||
|
||||
// now the key is valid, the code below could only return template/IO related errors
|
||||
sbCmd := &strings.Builder{}
|
||||
err = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sbCmd, map[string]any{
|
||||
"AppPath": util.ShellEscape(setting.AppPath),
|
||||
"AppWorkPath": util.ShellEscape(setting.AppWorkPath),
|
||||
"CustomConf": util.ShellEscape(setting.CustomConf),
|
||||
"CustomPath": util.ShellEscape(setting.CustomPath),
|
||||
"Key": key,
|
||||
})
|
||||
|
||||
return fmt.Sprintf(tplPublicKey, util.ShellEscape(sb.String()), key.Content)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
sshCommandEscaped := util.ShellEscape(sbCmd.String())
|
||||
_, err = fmt.Fprintf(w, tpl, sshCommandEscaped, sshKey)
|
||||
return true, err
|
||||
}
|
||||
|
||||
// appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file.
|
||||
@@ -112,7 +153,7 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
|
||||
if key.Type == KeyTypePrincipal {
|
||||
continue
|
||||
}
|
||||
if _, err = f.WriteString(key.AuthorizedString()); err != nil {
|
||||
if err = WriteAuthorizedStringForValidKey(key, f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -120,10 +161,9 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
|
||||
}
|
||||
|
||||
// RegeneratePublicKeys regenerates the authorized_keys file
|
||||
func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
|
||||
func RegeneratePublicKeys(ctx context.Context, t io.Writer) error {
|
||||
if err := db.GetEngine(ctx).Where("type != ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean any) (err error) {
|
||||
_, err = t.WriteString((bean.(*PublicKey)).AuthorizedString())
|
||||
return err
|
||||
return WriteAuthorizedStringForValidKey(bean.(*PublicKey), t)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -144,11 +184,11 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, tplCommentPrefix) {
|
||||
if strings.HasPrefix(line, AuthorizedStringCommentPrefix) {
|
||||
scanner.Scan()
|
||||
continue
|
||||
}
|
||||
_, err = t.WriteString(line + "\n")
|
||||
_, err = io.WriteString(t, line+"\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
90
models/asymkey/ssh_key_authorized_keys_test.go
Normal file
90
models/asymkey/ssh_key_authorized_keys_test.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package asymkey
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWriteAuthorizedStringForKey(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.AppPath, "/tmp/gitea")()
|
||||
defer test.MockVariableValue(&setting.CustomConf, "/tmp/app.ini")()
|
||||
writeKey := func(t *testing.T, key *PublicKey) (bool, string, error) {
|
||||
sb := &strings.Builder{}
|
||||
valid, err := writeAuthorizedStringForKey(key, sb)
|
||||
return valid, sb.String(), err
|
||||
}
|
||||
const validKeyContent = `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICV0MGX/W9IvLA4FXpIuUcdDcbj5KX4syHgsTy7soVgf`
|
||||
|
||||
testValid := func(t *testing.T, key *PublicKey, expected string) {
|
||||
valid, content, err := writeKey(t, key)
|
||||
assert.True(t, valid)
|
||||
assert.Equal(t, expected, content)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
testInvalid := func(t *testing.T, key *PublicKey) {
|
||||
valid, content, err := writeKey(t, key)
|
||||
assert.False(t, valid)
|
||||
assert.Empty(t, content)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
t.Run("PublicKey", func(t *testing.T) {
|
||||
testValid(t, &PublicKey{
|
||||
OwnerID: 123,
|
||||
Content: validKeyContent + " any-comment",
|
||||
Type: KeyTypeUser,
|
||||
}, `# gitea public key
|
||||
command="/tmp/gitea --config=/tmp/app.ini serv key-0",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICV0MGX/W9IvLA4FXpIuUcdDcbj5KX4syHgsTy7soVgf user-123
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("PublicKeyWithNewLine", func(t *testing.T) {
|
||||
testValid(t, &PublicKey{
|
||||
OwnerID: 123,
|
||||
Content: validKeyContent + "\nany-more", // the new line should be ignored
|
||||
Type: KeyTypeUser,
|
||||
}, `# gitea public key
|
||||
command="/tmp/gitea --config=/tmp/app.ini serv key-0",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICV0MGX/W9IvLA4FXpIuUcdDcbj5KX4syHgsTy7soVgf user-123
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("PublicKeyInvalid", func(t *testing.T) {
|
||||
testInvalid(t, &PublicKey{
|
||||
OwnerID: 123,
|
||||
Content: validKeyContent + "any-more",
|
||||
Type: KeyTypeUser,
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Principal", func(t *testing.T) {
|
||||
testValid(t, &PublicKey{
|
||||
OwnerID: 123,
|
||||
Content: "any-content",
|
||||
Type: KeyTypePrincipal,
|
||||
}, `# gitea public key
|
||||
command="/tmp/gitea --config=/tmp/app.ini serv key-0",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict any-content # user-123
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("PrincipalInvalid", func(t *testing.T) {
|
||||
testInvalid(t, &PublicKey{
|
||||
OwnerID: 123,
|
||||
Content: "a b",
|
||||
Type: KeyTypePrincipal,
|
||||
})
|
||||
testInvalid(t, &PublicKey{
|
||||
OwnerID: 123,
|
||||
Content: "a\nb",
|
||||
Type: KeyTypePrincipal,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -111,11 +111,11 @@ func (t *TwoFactor) SetSecret(secretString string) error {
|
||||
func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
|
||||
decodedStoredSecret, err := base64.StdEncoding.DecodeString(t.Secret)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, fmt.Errorf("ValidateTOTP invalid base64: %w", err)
|
||||
}
|
||||
secretBytes, err := secret.AesDecrypt(t.getEncryptionKey(), decodedStoredSecret)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, fmt.Errorf("ValidateTOTP unable to decrypt (maybe SECRET_KEY is wrong): %w", err)
|
||||
}
|
||||
secretStr := string(secretBytes)
|
||||
return totp.Validate(passcode, secretStr), nil
|
||||
|
||||
@@ -139,3 +139,43 @@
|
||||
updated: 1683636626
|
||||
need_approval: 0
|
||||
approved_by: 0
|
||||
-
|
||||
id: 804
|
||||
title: "use a private action"
|
||||
repo_id: 60
|
||||
owner_id: 40
|
||||
workflow_id: "run.yaml"
|
||||
index: 189
|
||||
trigger_user_id: 40
|
||||
ref: "refs/heads/master"
|
||||
commit_sha: "6e64b26de7ba966d01d90ecfaf5c7f14ef203e86"
|
||||
event: "push"
|
||||
trigger_event: "push"
|
||||
is_fork_pull_request: 0
|
||||
status: 1
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
created: 1683636108
|
||||
updated: 1683636626
|
||||
need_approval: 0
|
||||
approved_by: 0
|
||||
-
|
||||
id: 805
|
||||
title: "update actions"
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
workflow_id: "artifact.yaml"
|
||||
index: 191
|
||||
trigger_user_id: 1
|
||||
ref: "refs/heads/master"
|
||||
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
|
||||
event: "push"
|
||||
trigger_event: "push"
|
||||
is_fork_pull_request: 0
|
||||
status: 5
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
created: 1683636108
|
||||
updated: 1683636626
|
||||
need_approval: 0
|
||||
approved_by: 0
|
||||
|
||||
@@ -129,3 +129,31 @@
|
||||
status: 5
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
-
|
||||
id: 205
|
||||
run_id: 804
|
||||
repo_id: 6
|
||||
owner_id: 10
|
||||
commit_sha: 6e64b26de7ba966d01d90ecfaf5c7f14ef203e86
|
||||
is_fork_pull_request: 0
|
||||
name: job_2
|
||||
attempt: 1
|
||||
job_id: job_2
|
||||
task_id: 48
|
||||
status: 1
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
-
|
||||
id: 206
|
||||
run_id: 805
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: 0
|
||||
name: job_2
|
||||
attempt: 1
|
||||
job_id: job_2
|
||||
task_id: 56
|
||||
status: 3
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
|
||||
@@ -177,3 +177,42 @@
|
||||
log_length: 0
|
||||
log_size: 0
|
||||
log_expired: 0
|
||||
-
|
||||
id: 55
|
||||
job_id: 205
|
||||
attempt: 1
|
||||
runner_id: 1
|
||||
status: 6 # 6 is the status code for "running"
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
repo_id: 6
|
||||
owner_id: 10
|
||||
commit_sha: 6e64b26de7ba966d01d90ecfaf5c7f14ef203e86
|
||||
is_fork_pull_request: 0
|
||||
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc478422b
|
||||
token_salt: ERxJGHvg3I
|
||||
token_last_eight: 182199eb
|
||||
log_filename: collaborative-owner-test/1a/49.log
|
||||
log_in_storage: 1
|
||||
log_length: 707
|
||||
log_size: 90179
|
||||
log_expired: 0
|
||||
-
|
||||
id: 56
|
||||
attempt: 1
|
||||
runner_id: 1
|
||||
status: 3 # 3 is the status code for "cancelled"
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: 0
|
||||
token_hash: 6d8ef48297195edcc8e22c70b3020eaa06c52976db67d39b4240c64a69a2cc1508825121b7b8394e48e00b1bf3718b2aaaab
|
||||
token_salt: eeeeeeee
|
||||
token_last_eight: eeeeeeee
|
||||
log_filename: artifact-test2/2f/47.log
|
||||
log_in_storage: 1
|
||||
log_length: 707
|
||||
log_size: 90179
|
||||
log_expired: 0
|
||||
|
||||
@@ -213,3 +213,39 @@
|
||||
is_deleted: false
|
||||
deleted_by_id: 0
|
||||
deleted_unix: 0
|
||||
|
||||
-
|
||||
id: 26
|
||||
repo_id: 10
|
||||
name: 'feature/1'
|
||||
commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
|
||||
commit_message: 'Initial commit'
|
||||
commit_time: 1489950479
|
||||
pusher_id: 2
|
||||
is_deleted: false
|
||||
deleted_by_id: 0
|
||||
deleted_unix: 0
|
||||
|
||||
-
|
||||
id: 27
|
||||
repo_id: 1
|
||||
name: 'DefaultBranch'
|
||||
commit_id: '90c1019714259b24fb81711d4416ac0f18667dfa'
|
||||
commit_message: 'add license'
|
||||
commit_time: 1709345946
|
||||
pusher_id: 1
|
||||
is_deleted: false
|
||||
deleted_by_id: 0
|
||||
deleted_unix: 0
|
||||
|
||||
-
|
||||
id: 28
|
||||
repo_id: 1
|
||||
name: 'sub-home-md-img-check'
|
||||
commit_id: '4649299398e4d39a5c09eb4f534df6f1e1eb87cc'
|
||||
commit_message: "Test how READMEs render images when found in a subfolder"
|
||||
commit_time: 1678403550
|
||||
pusher_id: 1
|
||||
is_deleted: false
|
||||
deleted_by_id: 0
|
||||
deleted_unix: 0
|
||||
|
||||
@@ -733,3 +733,17 @@
|
||||
type: 3
|
||||
config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowRebaseMerge\":true,\"AllowSquash\":true}"
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 111
|
||||
repo_id: 3
|
||||
type: 10
|
||||
config: "{}"
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 112
|
||||
repo_id: 4
|
||||
type: 10
|
||||
config: "{}"
|
||||
created_unix: 946684810
|
||||
|
||||
@@ -110,6 +110,8 @@
|
||||
num_closed_milestones: 0
|
||||
num_projects: 0
|
||||
num_closed_projects: 1
|
||||
num_action_runs: 4
|
||||
num_closed_action_runs: 3
|
||||
is_private: false
|
||||
is_empty: false
|
||||
is_archived: false
|
||||
|
||||
@@ -368,7 +368,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
|
||||
}
|
||||
|
||||
// 1. update branch in database
|
||||
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
|
||||
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Cols("name").Update(&Branch{
|
||||
Name: to,
|
||||
}); err != nil {
|
||||
return err
|
||||
|
||||
@@ -30,17 +30,21 @@ import (
|
||||
|
||||
// CommitStatus holds a single Status of a single Commit
|
||||
type CommitStatus struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Index int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
|
||||
Repo *repo_model.Repository `xorm:"-"`
|
||||
State commitstatus.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
|
||||
SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
|
||||
TargetURL string `xorm:"TEXT"`
|
||||
Description string `xorm:"TEXT"`
|
||||
ContextHash string `xorm:"VARCHAR(64) index"`
|
||||
Context string `xorm:"TEXT"`
|
||||
Creator *user_model.User `xorm:"-"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Index int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
|
||||
Repo *repo_model.Repository `xorm:"-"`
|
||||
State commitstatus.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
|
||||
SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
|
||||
|
||||
// TargetURL points to the commit status page reported by a CI system
|
||||
// If Gitea Actions is used, it is a relative link like "{RepoLink}/actions/runs/{RunID}/jobs{JobID}"
|
||||
TargetURL string `xorm:"TEXT"`
|
||||
|
||||
Description string `xorm:"TEXT"`
|
||||
ContextHash string `xorm:"VARCHAR(64) index"`
|
||||
Context string `xorm:"TEXT"`
|
||||
Creator *user_model.User `xorm:"-"`
|
||||
CreatorID int64
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
@@ -211,21 +215,45 @@ func (status *CommitStatus) LocaleString(lang translation.Locale) string {
|
||||
|
||||
// HideActionsURL set `TargetURL` to an empty string if the status comes from Gitea Actions
|
||||
func (status *CommitStatus) HideActionsURL(ctx context.Context) {
|
||||
if _, ok := status.cutTargetURLGiteaActionsPrefix(ctx); ok {
|
||||
status.TargetURL = ""
|
||||
}
|
||||
}
|
||||
|
||||
func (status *CommitStatus) cutTargetURLGiteaActionsPrefix(ctx context.Context) (string, bool) {
|
||||
if status.RepoID == 0 {
|
||||
return
|
||||
return "", false
|
||||
}
|
||||
|
||||
if status.Repo == nil {
|
||||
if err := status.loadRepository(ctx); err != nil {
|
||||
log.Error("loadRepository: %v", err)
|
||||
return
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
prefix := status.Repo.Link() + "/actions"
|
||||
if strings.HasPrefix(status.TargetURL, prefix) {
|
||||
status.TargetURL = ""
|
||||
return strings.CutPrefix(status.TargetURL, prefix)
|
||||
}
|
||||
|
||||
// ParseGiteaActionsTargetURL parses the commit status target URL as Gitea Actions link
|
||||
func (status *CommitStatus) ParseGiteaActionsTargetURL(ctx context.Context) (runID, jobID int64, ok bool) {
|
||||
s, ok := status.cutTargetURLGiteaActionsPrefix(ctx)
|
||||
if !ok {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
parts := strings.Split(s, "/") // expect: /runs/{runID}/jobs/{jobID}
|
||||
if len(parts) < 5 || parts[1] != "runs" || parts[3] != "jobs" {
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
runID, err1 := strconv.ParseInt(parts[2], 10, 64)
|
||||
jobID, err2 := strconv.ParseInt(parts[4], 10, 64)
|
||||
if err1 != nil || err2 != nil {
|
||||
return 0, 0, false
|
||||
}
|
||||
return runID, jobID, true
|
||||
}
|
||||
|
||||
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
@@ -42,30 +41,6 @@ func (err ErrLFSLockNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrLFSUnauthorizedAction represents a "LFSUnauthorizedAction" kind of error.
|
||||
type ErrLFSUnauthorizedAction struct {
|
||||
RepoID int64
|
||||
UserName string
|
||||
Mode perm.AccessMode
|
||||
}
|
||||
|
||||
// IsErrLFSUnauthorizedAction checks if an error is a ErrLFSUnauthorizedAction.
|
||||
func IsErrLFSUnauthorizedAction(err error) bool {
|
||||
_, ok := err.(ErrLFSUnauthorizedAction)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrLFSUnauthorizedAction) Error() string {
|
||||
if err.Mode == perm.AccessModeWrite {
|
||||
return fmt.Sprintf("User %s doesn't have write access for lfs lock [rid: %d]", err.UserName, err.RepoID)
|
||||
}
|
||||
return fmt.Sprintf("User %s doesn't have read access for lfs lock [rid: %d]", err.UserName, err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrLFSUnauthorizedAction) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrLFSLockAlreadyExist represents a "LFSLockAlreadyExist" kind of error.
|
||||
type ErrLFSLockAlreadyExist struct {
|
||||
RepoID int64
|
||||
@@ -93,12 +68,6 @@ type ErrLFSFileLocked struct {
|
||||
UserName string
|
||||
}
|
||||
|
||||
// IsErrLFSFileLocked checks if an error is a ErrLFSFileLocked.
|
||||
func IsErrLFSFileLocked(err error) bool {
|
||||
_, ok := err.(ErrLFSFileLocked)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrLFSFileLocked) Error() string {
|
||||
return fmt.Sprintf("File is lfs locked [repo: %d, locked by: %s, path: %s]", err.RepoID, err.UserName, err.Path)
|
||||
}
|
||||
|
||||
@@ -11,10 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@@ -71,10 +68,6 @@ func (l *LFSLock) LoadOwner(ctx context.Context) error {
|
||||
// CreateLFSLock creates a new lock.
|
||||
func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
|
||||
return db.WithTx2(ctx, func(ctx context.Context) (*LFSLock, error) {
|
||||
if err := CheckLFSAccessForRepo(ctx, lock.OwnerID, repo, perm.AccessModeWrite); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lock.Path = util.PathJoinRel(lock.Path)
|
||||
lock.RepoID = repo.ID
|
||||
|
||||
@@ -108,10 +101,10 @@ func GetLFSLock(ctx context.Context, repo *repo_model.Repository, path string) (
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
// GetLFSLockByID returns release by given id.
|
||||
func GetLFSLockByID(ctx context.Context, id int64) (*LFSLock, error) {
|
||||
// GetLFSLockByIDAndRepo returns lfs lock by given id and repository id.
|
||||
func GetLFSLockByIDAndRepo(ctx context.Context, id, repoID int64) (*LFSLock, error) {
|
||||
lock := new(LFSLock)
|
||||
has, err := db.GetEngine(ctx).ID(id).Get(lock)
|
||||
has, err := db.GetEngine(ctx).ID(id).And("repo_id = ?", repoID).Get(lock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
@@ -160,15 +153,11 @@ func CountLFSLockByRepoID(ctx context.Context, repoID int64) (int64, error) {
|
||||
// DeleteLFSLockByID deletes a lock by given ID.
|
||||
func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) {
|
||||
return db.WithTx2(ctx, func(ctx context.Context) (*LFSLock, error) {
|
||||
lock, err := GetLFSLockByID(ctx, id)
|
||||
lock, err := GetLFSLockByIDAndRepo(ctx, id, repo.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := CheckLFSAccessForRepo(ctx, u.ID, repo, perm.AccessModeWrite); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !force && u.ID != lock.OwnerID {
|
||||
return nil, errors.New("user doesn't own lock and force flag is not set")
|
||||
}
|
||||
@@ -180,22 +169,3 @@ func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repositor
|
||||
return lock, nil
|
||||
})
|
||||
}
|
||||
|
||||
// CheckLFSAccessForRepo check needed access mode base on action
|
||||
func CheckLFSAccessForRepo(ctx context.Context, ownerID int64, repo *repo_model.Repository, mode perm.AccessMode) error {
|
||||
if ownerID == 0 {
|
||||
return ErrLFSUnauthorizedAction{repo.ID, "undefined", mode}
|
||||
}
|
||||
u, err := user_model.GetUserByID(ctx, ownerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, repo, u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !perm.CanAccess(mode, unit.TypeCode) {
|
||||
return ErrLFSUnauthorizedAction{repo.ID, u.DisplayName(), mode}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
82
models/git/lfs_lock_test.go
Normal file
82
models/git/lfs_lock_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func createTestLock(t *testing.T, repo *repo_model.Repository, owner *user_model.User) *LFSLock {
|
||||
t.Helper()
|
||||
|
||||
path := fmt.Sprintf("%s-%d-%d", t.Name(), repo.ID, time.Now().UnixNano())
|
||||
lock, err := CreateLFSLock(t.Context(), repo, &LFSLock{
|
||||
OwnerID: owner.ID,
|
||||
Path: path,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return lock
|
||||
}
|
||||
|
||||
func TestGetLFSLockByIDAndRepo(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
|
||||
lockRepo1 := createTestLock(t, repo1, user2)
|
||||
lockRepo3 := createTestLock(t, repo3, user4)
|
||||
|
||||
fetched, err := GetLFSLockByIDAndRepo(t.Context(), lockRepo1.ID, repo1.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, lockRepo1.ID, fetched.ID)
|
||||
assert.Equal(t, repo1.ID, fetched.RepoID)
|
||||
|
||||
_, err = GetLFSLockByIDAndRepo(t.Context(), lockRepo1.ID, repo3.ID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrLFSLockNotExist(err))
|
||||
|
||||
_, err = GetLFSLockByIDAndRepo(t.Context(), lockRepo3.ID, repo1.ID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrLFSLockNotExist(err))
|
||||
}
|
||||
|
||||
func TestDeleteLFSLockByIDRequiresRepoMatch(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
|
||||
lockRepo1 := createTestLock(t, repo1, user2)
|
||||
lockRepo3 := createTestLock(t, repo3, user4)
|
||||
|
||||
_, err := DeleteLFSLockByID(t.Context(), lockRepo3.ID, repo1, user2, true)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrLFSLockNotExist(err))
|
||||
|
||||
existing, err := GetLFSLockByIDAndRepo(t.Context(), lockRepo3.ID, repo3.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, lockRepo3.ID, existing.ID)
|
||||
|
||||
deleted, err := DeleteLFSLockByID(t.Context(), lockRepo3.ID, repo3, user4, true)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, lockRepo3.ID, deleted.ID)
|
||||
|
||||
deleted, err = DeleteLFSLockByID(t.Context(), lockRepo1.ID, repo1, user2, false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, lockRepo1.ID, deleted.ID)
|
||||
}
|
||||
@@ -5,7 +5,6 @@ package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -25,7 +24,7 @@ import (
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
var ErrBranchIsProtected = errors.New("branch is protected")
|
||||
var ErrBranchIsProtected = util.ErrorWrap(util.ErrPermissionDenied, "branch is protected")
|
||||
|
||||
// ProtectedBranch struct
|
||||
type ProtectedBranch struct {
|
||||
@@ -467,11 +466,13 @@ func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, c
|
||||
return currentWhitelist, nil
|
||||
}
|
||||
|
||||
prUserIDs, err := access_model.GetUserIDsWithUnitAccess(ctx, repo, perm.AccessModeRead, unit.TypePullRequests)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
whitelist = make([]int64, 0, len(newWhitelist))
|
||||
for _, userID := range newWhitelist {
|
||||
if reader, err := access_model.IsRepoReader(ctx, repo, userID); err != nil {
|
||||
return nil, err
|
||||
} else if !reader {
|
||||
if !prUserIDs.Contains(userID) {
|
||||
continue
|
||||
}
|
||||
whitelist = append(whitelist, userID)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
@@ -233,11 +234,17 @@ func (r RoleInRepo) LocaleHelper(lang translation.Locale) string {
|
||||
return lang.TrString("repo.issues.role." + string(r) + "_helper")
|
||||
}
|
||||
|
||||
type SpecialDoerNameType string
|
||||
|
||||
const SpecialDoerNameCodeOwners SpecialDoerNameType = "CODEOWNERS"
|
||||
|
||||
// CommentMetaData stores metadata for a comment, these data will not be changed once inserted into database
|
||||
type CommentMetaData struct {
|
||||
ProjectColumnID int64 `json:"project_column_id,omitempty"`
|
||||
ProjectColumnTitle string `json:"project_column_title,omitempty"`
|
||||
ProjectTitle string `json:"project_title,omitempty"`
|
||||
|
||||
SpecialDoerName SpecialDoerNameType `json:"special_doer_name,omitempty"` // e.g. "CODEOWNERS" for CODEOWNERS-triggered review requests
|
||||
}
|
||||
|
||||
// Comment represents a comment in commit and issue page.
|
||||
@@ -764,6 +771,37 @@ func (c *Comment) CodeCommentLink(ctx context.Context) string {
|
||||
return fmt.Sprintf("%s/files#%s", c.Issue.Link(), c.HashTag())
|
||||
}
|
||||
|
||||
func (c *Comment) MetaSpecialDoerTr(locale translation.Locale) template.HTML {
|
||||
if c.CommentMetaData == nil {
|
||||
return ""
|
||||
}
|
||||
if c.CommentMetaData.SpecialDoerName == SpecialDoerNameCodeOwners {
|
||||
return locale.Tr("repo.issues.review.codeowners_rules")
|
||||
}
|
||||
return htmlutil.HTMLFormat("%s", c.CommentMetaData.SpecialDoerName)
|
||||
}
|
||||
|
||||
func (c *Comment) TimelineRequestedReviewTr(locale translation.Locale, createdStr template.HTML) template.HTML {
|
||||
if c.AssigneeID > 0 {
|
||||
// it guarantees LoadAssigneeUserAndTeam has been called, and c.Assignee is Ghost user but not nil if the user doesn't exist
|
||||
if c.RemovedAssignee {
|
||||
if c.PosterID == c.AssigneeID {
|
||||
return locale.Tr("repo.issues.review.remove_review_request_self", createdStr)
|
||||
}
|
||||
return locale.Tr("repo.issues.review.remove_review_request", c.Assignee.GetDisplayName(), createdStr)
|
||||
}
|
||||
return locale.Tr("repo.issues.review.add_review_request", c.Assignee.GetDisplayName(), createdStr)
|
||||
}
|
||||
teamName := "Ghost Team"
|
||||
if c.AssigneeTeam != nil {
|
||||
teamName = c.AssigneeTeam.Name
|
||||
}
|
||||
if c.RemovedAssignee {
|
||||
return locale.Tr("repo.issues.review.remove_review_request", teamName, createdStr)
|
||||
}
|
||||
return locale.Tr("repo.issues.review.add_review_request", teamName, createdStr)
|
||||
}
|
||||
|
||||
// CreateComment creates comment with context
|
||||
func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) {
|
||||
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||
@@ -780,6 +818,11 @@ func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment,
|
||||
ProjectTitle: opts.ProjectTitle,
|
||||
}
|
||||
}
|
||||
if opts.SpecialDoerName != "" {
|
||||
commentMetaData = &CommentMetaData{
|
||||
SpecialDoerName: opts.SpecialDoerName,
|
||||
}
|
||||
}
|
||||
|
||||
comment := &Comment{
|
||||
Type: opts.Type,
|
||||
@@ -862,10 +905,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
|
||||
if err = UpdateCommentAttachments(ctx, comment, opts.Attachments); err != nil {
|
||||
return err
|
||||
}
|
||||
case CommentTypeReopen, CommentTypeClose:
|
||||
if err = repo_model.UpdateRepoIssueNumbers(ctx, opts.Issue.RepoID, opts.Issue.IsPull, true); err != nil {
|
||||
return err
|
||||
}
|
||||
// comment type reopen and close event have their own logic to update numbers but not here
|
||||
}
|
||||
// update the issue's updated_unix column
|
||||
return UpdateIssueCols(ctx, opts.Issue, "updated_unix")
|
||||
@@ -979,6 +1019,7 @@ type CreateCommentOptions struct {
|
||||
RefIsPull bool
|
||||
IsForcePush bool
|
||||
Invalidated bool
|
||||
SpecialDoerName SpecialDoerNameType // e.g. "CODEOWNERS" for CODEOWNERS-triggered review requests
|
||||
}
|
||||
|
||||
// GetCommentByID returns the comment by given ID.
|
||||
|
||||
@@ -476,7 +476,7 @@ func applySubscribedCondition(sess *xorm.Session, subscriberID int64) {
|
||||
),
|
||||
builder.Eq{"issue.poster_id": subscriberID},
|
||||
builder.In("issue.repo_id", builder.
|
||||
Select("id").
|
||||
Select("repo_id").
|
||||
From("watch").
|
||||
Where(builder.And(builder.Eq{"user_id": subscriberID},
|
||||
builder.In("mode", repo_model.WatchModeNormal, repo_model.WatchModeAuto))),
|
||||
|
||||
@@ -197,6 +197,12 @@ func TestIssues(t *testing.T) {
|
||||
},
|
||||
[]int64{2},
|
||||
},
|
||||
{
|
||||
issues_model.IssuesOptions{
|
||||
SubscriberID: 11,
|
||||
},
|
||||
[]int64{11, 5, 9, 8, 3, 2, 1},
|
||||
},
|
||||
} {
|
||||
issues, err := issues_model.Issues(t.Context(), &test.Opts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -146,8 +146,19 @@ func updateIssueNumbers(ctx context.Context, issue *Issue, doer *user_model.User
|
||||
}
|
||||
|
||||
// update repository's issue closed number
|
||||
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil {
|
||||
return nil, err
|
||||
switch cmtType {
|
||||
case CommentTypeClose, CommentTypeMergePull:
|
||||
// only increase closed count
|
||||
if err := IncrRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case CommentTypeReopen:
|
||||
// only decrease closed count
|
||||
if err := DecrRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid comment type: %d", cmtType)
|
||||
}
|
||||
|
||||
return CreateComment(ctx, &CreateCommentOptions{
|
||||
@@ -318,7 +329,6 @@ type NewIssueOptions struct {
|
||||
Issue *Issue
|
||||
LabelIDs []int64
|
||||
Attachments []string // In UUID format.
|
||||
IsPull bool
|
||||
}
|
||||
|
||||
// NewIssueWithIndex creates issue with given index
|
||||
@@ -369,7 +379,8 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
|
||||
}
|
||||
}
|
||||
|
||||
if err := repo_model.UpdateRepoIssueNumbers(ctx, opts.Issue.RepoID, opts.IsPull, false); err != nil {
|
||||
// Update repository issue total count
|
||||
if err := IncrRepoIssueNumbers(ctx, opts.Repo.ID, opts.Issue.IsPull, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -439,6 +450,42 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *Issue, la
|
||||
})
|
||||
}
|
||||
|
||||
// IncrRepoIssueNumbers increments repository issue numbers.
|
||||
func IncrRepoIssueNumbers(ctx context.Context, repoID int64, isPull, totalOrClosed bool) error {
|
||||
dbSession := db.GetEngine(ctx)
|
||||
var colName string
|
||||
if totalOrClosed {
|
||||
colName = util.Iif(isPull, "num_pulls", "num_issues")
|
||||
} else {
|
||||
colName = util.Iif(isPull, "num_closed_pulls", "num_closed_issues")
|
||||
}
|
||||
_, err := dbSession.Incr(colName).ID(repoID).
|
||||
NoAutoCondition().NoAutoTime().
|
||||
Update(new(repo_model.Repository))
|
||||
return err
|
||||
}
|
||||
|
||||
// DecrRepoIssueNumbers decrements repository issue numbers.
|
||||
func DecrRepoIssueNumbers(ctx context.Context, repoID int64, isPull, includeTotal, includeClosed bool) error {
|
||||
if !includeTotal && !includeClosed {
|
||||
return fmt.Errorf("no numbers to decrease for repo id %d", repoID)
|
||||
}
|
||||
|
||||
dbSession := db.GetEngine(ctx)
|
||||
if includeTotal {
|
||||
colName := util.Iif(isPull, "num_pulls", "num_issues")
|
||||
dbSession = dbSession.Decr(colName)
|
||||
}
|
||||
if includeClosed {
|
||||
closedColName := util.Iif(isPull, "num_closed_pulls", "num_closed_issues")
|
||||
dbSession = dbSession.Decr(closedColName)
|
||||
}
|
||||
_, err := dbSession.ID(repoID).
|
||||
NoAutoCondition().NoAutoTime().
|
||||
Update(new(repo_model.Repository))
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateIssueMentions updates issue-user relations for mentioned users.
|
||||
func UpdateIssueMentions(ctx context.Context, issueID int64, mentions []*user_model.User) error {
|
||||
if len(mentions) == 0 {
|
||||
|
||||
@@ -75,6 +75,8 @@ func init() {
|
||||
func (m *Milestone) BeforeUpdate() {
|
||||
if m.NumIssues > 0 {
|
||||
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
|
||||
} else if m.IsClosed {
|
||||
m.Completeness = 100
|
||||
} else {
|
||||
m.Completeness = 0
|
||||
}
|
||||
@@ -181,6 +183,7 @@ func updateMilestone(ctx context.Context, m *Milestone) error {
|
||||
func UpdateMilestoneCounters(ctx context.Context, id int64) error {
|
||||
e := db.GetEngine(ctx)
|
||||
_, err := e.ID(id).
|
||||
Cols("num_issues", "num_closed_issues").
|
||||
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
||||
builder.Eq{"milestone_id": id},
|
||||
)).
|
||||
@@ -194,8 +197,8 @@ func UpdateMilestoneCounters(ctx context.Context, id int64) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = e.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
|
||||
id,
|
||||
_, err = e.Exec("UPDATE `milestone` SET completeness=(CASE WHEN is_closed = ? AND num_issues = 0 THEN 100 ELSE 100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) END) WHERE id=?",
|
||||
true, id,
|
||||
)
|
||||
return err
|
||||
}
|
||||
@@ -239,6 +242,11 @@ func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) err
|
||||
if count < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := UpdateMilestoneCounters(ctx, m.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return updateRepoMilestoneNum(ctx, m.RepoID)
|
||||
}
|
||||
|
||||
|
||||
@@ -417,10 +417,6 @@ func (pr *PullRequest) GetGitHeadRefName() string {
|
||||
return fmt.Sprintf("%s%d/head", git.PullPrefix, pr.Index)
|
||||
}
|
||||
|
||||
func (pr *PullRequest) GetGitHeadBranchRefName() string {
|
||||
return fmt.Sprintf("%s%s", git.BranchPrefix, pr.HeadBranch)
|
||||
}
|
||||
|
||||
// GetReviewCommentsCount returns the number of review comments made on the diff of a PR review (not including comments on commits or issues in a PR)
|
||||
func (pr *PullRequest) GetReviewCommentsCount(ctx context.Context) int {
|
||||
opts := FindCommentsOptions{
|
||||
@@ -471,13 +467,13 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Iss
|
||||
|
||||
issue.Index = idx
|
||||
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
||||
issue.IsPull = true
|
||||
|
||||
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
|
||||
Repo: repo,
|
||||
Issue: issue,
|
||||
LabelIDs: labelIDs,
|
||||
Attachments: uuids,
|
||||
IsPull: true,
|
||||
}); err != nil {
|
||||
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
|
||||
return err
|
||||
@@ -646,9 +642,8 @@ func (pr *PullRequest) UpdateCols(ctx context.Context, cols ...string) error {
|
||||
}
|
||||
|
||||
// UpdateColsIfNotMerged updates specific fields of a pull request if it has not been merged
|
||||
func (pr *PullRequest) UpdateColsIfNotMerged(ctx context.Context, cols ...string) error {
|
||||
_, err := db.GetEngine(ctx).Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr)
|
||||
return err
|
||||
func (pr *PullRequest) UpdateColsIfNotMerged(ctx context.Context, cols ...string) (int64, error) {
|
||||
return db.GetEngine(ctx).Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr)
|
||||
}
|
||||
|
||||
// IsWorkInProgress determine if the Pull Request is a Work In Progress by its title
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
@@ -324,12 +325,26 @@ func (prs PullRequestList) LoadReviews(ctx context.Context) (ReviewList, error)
|
||||
|
||||
// HasMergedPullRequestInRepo returns whether the user(poster) has merged pull-request in the repo
|
||||
func HasMergedPullRequestInRepo(ctx context.Context, repoID, posterID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).
|
||||
return HasMergedPullRequestInRepoBefore(ctx, repoID, posterID, 0, 0)
|
||||
}
|
||||
|
||||
// HasMergedPullRequestInRepoBefore returns whether the user has a merged PR before a timestamp (0 = no limit)
|
||||
func HasMergedPullRequestInRepoBefore(ctx context.Context, repoID, posterID int64, beforeUnix timeutil.TimeStamp, excludePullID int64) (bool, error) {
|
||||
sess := db.GetEngine(ctx).
|
||||
Join("INNER", "pull_request", "pull_request.issue_id = issue.id").
|
||||
Where("repo_id=?", repoID).
|
||||
And("poster_id=?", posterID).
|
||||
And("is_pull=?", true).
|
||||
And("pull_request.has_merged=?", true).
|
||||
And("pull_request.has_merged=?", true)
|
||||
|
||||
if beforeUnix > 0 {
|
||||
sess.And("pull_request.merged_unix < ?", beforeUnix)
|
||||
}
|
||||
if excludePullID > 0 {
|
||||
sess.And("pull_request.id != ?", excludePullID)
|
||||
}
|
||||
|
||||
return sess.
|
||||
Select("issue.id").
|
||||
Limit(1).
|
||||
Get(new(Issue))
|
||||
|
||||
@@ -130,7 +130,7 @@ func TestLoadRequestedReviewers(t *testing.T) {
|
||||
user1, err := user_model.GetUserByID(t.Context(), 1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
comment, err := issues_model.AddReviewRequest(t.Context(), issue, user1, &user_model.User{})
|
||||
comment, err := issues_model.AddReviewRequest(t.Context(), issue, user1, &user_model.User{}, false)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, comment)
|
||||
|
||||
|
||||
@@ -643,7 +643,7 @@ func InsertReviews(ctx context.Context, reviews []*Review) error {
|
||||
}
|
||||
|
||||
// AddReviewRequest add a review request from one reviewer
|
||||
func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
|
||||
func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User, isCodeOwners bool) (*Comment, error) {
|
||||
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
@@ -702,6 +702,7 @@ func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_mo
|
||||
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
|
||||
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
|
||||
ReviewID: review.ID,
|
||||
SpecialDoerName: util.Iif(isCodeOwners, SpecialDoerNameCodeOwners, ""),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -767,7 +768,7 @@ func restoreLatestOfficialReview(ctx context.Context, issueID, reviewerID int64)
|
||||
}
|
||||
|
||||
// AddTeamReviewRequest add a review request from one team
|
||||
func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
|
||||
func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User, isCodeOwners bool) (*Comment, error) {
|
||||
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
|
||||
if err != nil && !IsErrReviewNotExist(err) {
|
||||
@@ -812,6 +813,7 @@ func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organizat
|
||||
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
|
||||
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
|
||||
ReviewID: review.ID,
|
||||
SpecialDoerName: util.Iif(isCodeOwners, SpecialDoerNameCodeOwners, ""),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CreateComment(): %w", err)
|
||||
|
||||
@@ -173,7 +173,7 @@ func GetReviewsByIssueID(ctx context.Context, issueID int64) (latestReviews, mig
|
||||
reviewersMap := make(map[int64][]*Review) // key is reviewer id
|
||||
originalReviewersMap := make(map[int64][]*Review) // key is original author id
|
||||
reviewTeamsMap := make(map[int64][]*Review) // key is reviewer team id
|
||||
countedReivewTypes := []ReviewType{ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest}
|
||||
countedReivewTypes := []ReviewType{ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, ReviewTypeComment}
|
||||
for _, review := range reviews {
|
||||
if review.ReviewerTeamID == 0 && slices.Contains(countedReivewTypes, review.Type) && !review.Dismissed {
|
||||
if review.OriginalAuthorID != 0 {
|
||||
|
||||
@@ -122,6 +122,7 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3})
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
@@ -129,6 +130,12 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
||||
|
||||
expectedReviews := []*issues_model.Review{}
|
||||
expectedReviews = append(expectedReviews,
|
||||
&issues_model.Review{
|
||||
ID: 5,
|
||||
Reviewer: user1,
|
||||
Type: issues_model.ReviewTypeComment,
|
||||
UpdatedUnix: 946684810,
|
||||
},
|
||||
&issues_model.Review{
|
||||
ID: 7,
|
||||
Reviewer: org3,
|
||||
@@ -167,8 +174,9 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
||||
for _, review := range allReviews {
|
||||
assert.NoError(t, review.LoadReviewer(t.Context()))
|
||||
}
|
||||
if assert.Len(t, allReviews, 5) {
|
||||
if assert.Len(t, allReviews, 6) {
|
||||
for i, review := range allReviews {
|
||||
assert.Equal(t, expectedReviews[i].ID, review.ID)
|
||||
assert.Equal(t, expectedReviews[i].Reviewer, review.Reviewer)
|
||||
assert.Equal(t, expectedReviews[i].Type, review.Type)
|
||||
assert.Equal(t, expectedReviews[i].UpdatedUnix, review.UpdatedUnix)
|
||||
@@ -313,14 +321,28 @@ func TestAddReviewRequest(t *testing.T) {
|
||||
pull.HasMerged = false
|
||||
assert.NoError(t, pull.UpdateCols(t.Context(), "has_merged"))
|
||||
issue.IsClosed = true
|
||||
_, err = issues_model.AddReviewRequest(t.Context(), issue, reviewer, &user_model.User{})
|
||||
_, err = issues_model.AddReviewRequest(t.Context(), issue, reviewer, &user_model.User{}, false)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, issues_model.IsErrReviewRequestOnClosedPR(err))
|
||||
|
||||
pull.HasMerged = true
|
||||
assert.NoError(t, pull.UpdateCols(t.Context(), "has_merged"))
|
||||
issue.IsClosed = false
|
||||
_, err = issues_model.AddReviewRequest(t.Context(), issue, reviewer, &user_model.User{})
|
||||
_, err = issues_model.AddReviewRequest(t.Context(), issue, reviewer, &user_model.User{}, false)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, issues_model.IsErrReviewRequestOnClosedPR(err))
|
||||
|
||||
// Test CODEOWNERS review request stores metadata correctly
|
||||
pull2 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
|
||||
assert.NoError(t, pull2.LoadIssue(t.Context()))
|
||||
issue2 := pull2.Issue
|
||||
assert.NoError(t, issue2.LoadRepo(t.Context()))
|
||||
reviewer2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 7})
|
||||
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
comment, err := issues_model.AddReviewRequest(t.Context(), issue2, reviewer2, doer, true)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, comment)
|
||||
assert.NotNil(t, comment.CommentMetaData)
|
||||
assert.Equal(t, issues_model.SpecialDoerNameCodeOwners, comment.CommentMetaData.SpecialDoerName)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// Stopwatch represents a stopwatch for time tracking.
|
||||
@@ -232,3 +234,14 @@ func CancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (
|
||||
})
|
||||
return ok, err
|
||||
}
|
||||
|
||||
// RemoveStopwatchesByRepoID removes all stopwatches for a user in a specific repository
|
||||
// this function should be called before removing all the issues of the repository
|
||||
func RemoveStopwatchesByRepoID(ctx context.Context, userID, repoID int64) error {
|
||||
_, err := db.GetEngine(ctx).
|
||||
Where("`stopwatch`.user_id = ?", userID).
|
||||
And(builder.In("`stopwatch`.issue_id",
|
||||
builder.Select("id").From("issue").Where(builder.Eq{"repo_id": repoID}))).
|
||||
Delete(new(Stopwatch))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
// FIXME: this file shouldn't be in a normal package, it should only be compiled for tests
|
||||
@@ -88,6 +89,16 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
|
||||
return x, deferFn
|
||||
}
|
||||
|
||||
func LoadTableSchemasMap(t *testing.T, x *xorm.Engine) map[string]*schemas.Table {
|
||||
tables, err := x.DBMetas()
|
||||
require.NoError(t, err)
|
||||
tableMap := make(map[string]*schemas.Table)
|
||||
for _, table := range tables {
|
||||
tableMap[table.Name] = table
|
||||
}
|
||||
return tableMap
|
||||
}
|
||||
|
||||
func MainTest(m *testing.M) {
|
||||
testlogger.Init()
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"code.gitea.io/gitea/models/migrations/v1_23"
|
||||
"code.gitea.io/gitea/models/migrations/v1_24"
|
||||
"code.gitea.io/gitea/models/migrations/v1_25"
|
||||
"code.gitea.io/gitea/models/migrations/v1_26"
|
||||
"code.gitea.io/gitea/models/migrations/v1_6"
|
||||
"code.gitea.io/gitea/models/migrations/v1_7"
|
||||
"code.gitea.io/gitea/models/migrations/v1_8"
|
||||
@@ -379,8 +380,8 @@ func prepareMigrationTasks() []*migration {
|
||||
newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices),
|
||||
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
|
||||
newMigration(311, "Add TimeEstimate to Issue table", v1_23.AddTimeEstimateColumnToIssueTable),
|
||||
|
||||
// Gitea 1.23.0-rc0 ends at migration ID number 311 (database version 312)
|
||||
|
||||
newMigration(312, "Add DeleteBranchAfterMerge to AutoMerge", v1_24.AddDeleteBranchAfterMergeForAutoMerge),
|
||||
newMigration(313, "Move PinOrder from issue table to a new table issue_pin", v1_24.MovePinOrderToTableIssuePin),
|
||||
newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables),
|
||||
@@ -390,10 +391,14 @@ func prepareMigrationTasks() []*migration {
|
||||
newMigration(318, "Add anonymous_access_mode for repo_unit", v1_24.AddRepoUnitAnonymousAccessMode),
|
||||
newMigration(319, "Add ExclusiveOrder to Label table", v1_24.AddExclusiveOrderColumnToLabelTable),
|
||||
newMigration(320, "Migrate two_factor_policy to login_source table", v1_24.MigrateSkipTwoFactor),
|
||||
// Gitea 1.24.0 ends at migration ID number 320 (database version 321)
|
||||
|
||||
// Gitea 1.24.0 ends at database version 321
|
||||
newMigration(321, "Use LONGTEXT for some columns and fix review_state.updated_files column", v1_25.UseLongTextInSomeColumnsAndFixBugs),
|
||||
newMigration(322, "Extend comment tree_path length limit", v1_25.ExtendCommentTreePathLength),
|
||||
// Gitea 1.25.0 ends at migration ID number 322 (database version 323)
|
||||
|
||||
newMigration(323, "Add support for actions concurrency", v1_26.AddActionsConcurrency),
|
||||
newMigration(324, "Fix closed milestone completeness for milestones with no issues", v1_26.FixClosedMilestoneCompleteness),
|
||||
}
|
||||
return preparedMigrations
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user