mirror of
https://github.com/go-gitea/gitea.git
synced 2025-12-13 18:32:54 +00:00
Compare commits
177 Commits
v1.26.0-de
...
b49dd8e32f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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]
|
[build]
|
||||||
pre_cmd = ["killall -9 gitea 2>/dev/null || true"] # kill off potential zombie processes from previous runs
|
pre_cmd = ["killall -9 gitea 2>/dev/null || true"] # kill off potential zombie processes from previous runs
|
||||||
cmd = "make --no-print-directory backend"
|
cmd = "make --no-print-directory backend"
|
||||||
bin = "gitea"
|
entrypoint = ["./gitea"]
|
||||||
delay = 2000
|
delay = 2000
|
||||||
include_ext = ["go", "tmpl"]
|
include_ext = ["go", "tmpl"]
|
||||||
include_file = ["main.go"]
|
include_file = ["main.go"]
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ cpu.out
|
|||||||
/VERSION
|
/VERSION
|
||||||
/.air
|
/.air
|
||||||
/.go-licenses
|
/.go-licenses
|
||||||
|
/Dockerfile
|
||||||
|
/Dockerfile.rootless
|
||||||
|
/.venv
|
||||||
|
|
||||||
# Files and folders that were previously generated
|
# Files and folders that were previously generated
|
||||||
/public/assets/img/webpack
|
/public/assets/img/webpack
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ insert_final_newline = false
|
|||||||
[templates/user/auth/oidc_wellknown.tmpl]
|
[templates/user/auth/oidc_wellknown.tmpl]
|
||||||
indent_style = space
|
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]
|
[Makefile]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
||||||
|
|||||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -8,3 +8,4 @@
|
|||||||
/vendor/** -text -eol linguist-vendored
|
/vendor/** -text -eol linguist-vendored
|
||||||
/web_src/js/vendor/** -text -eol linguist-vendored
|
/web_src/js/vendor/** -text -eol linguist-vendored
|
||||||
Dockerfile.* linguist-language=Dockerfile
|
Dockerfile.* linguist-language=Dockerfile
|
||||||
|
Makefile.* linguist-language=Makefile
|
||||||
|
|||||||
1
.github/labeler.yml
vendored
1
.github/labeler.yml
vendored
@@ -51,7 +51,6 @@ modifies/internal:
|
|||||||
- ".github/**"
|
- ".github/**"
|
||||||
- ".gitea/**"
|
- ".gitea/**"
|
||||||
- ".devcontainer/**"
|
- ".devcontainer/**"
|
||||||
- "build.go"
|
|
||||||
- "build/**"
|
- "build/**"
|
||||||
- "contrib/**"
|
- "contrib/**"
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/cron-licenses.yml
vendored
4
.github/workflows/cron-licenses.yml
vendored
@@ -10,8 +10,8 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'go-gitea/gitea'
|
if: github.repository == 'go-gitea/gitea'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|||||||
2
.github/workflows/cron-translations.yml
vendored
2
.github/workflows/cron-translations.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'go-gitea/gitea'
|
if: github.repository == 'go-gitea/gitea'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: crowdin/github-action@v1
|
- uses: crowdin/github-action@v1
|
||||||
with:
|
with:
|
||||||
upload_sources: true
|
upload_sources: true
|
||||||
|
|||||||
2
.github/workflows/files-changed.yml
vendored
2
.github/workflows/files-changed.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
|||||||
swagger: ${{ steps.changes.outputs.swagger }}
|
swagger: ${{ steps.changes.outputs.swagger }}
|
||||||
yaml: ${{ steps.changes.outputs.yaml }}
|
yaml: ${{ steps.changes.outputs.yaml }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: dorny/paths-filter@v3
|
- uses: dorny/paths-filter@v3
|
||||||
id: changes
|
id: changes
|
||||||
with:
|
with:
|
||||||
|
|||||||
38
.github/workflows/pull-compliance.yml
vendored
38
.github/workflows/pull-compliance.yml
vendored
@@ -16,8 +16,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: astral-sh/setup-uv@v6
|
- uses: astral-sh/setup-uv@v6
|
||||||
- run: uv python install 3.12
|
- run: uv python install 3.12
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: astral-sh/setup-uv@v6
|
- uses: astral-sh/setup-uv@v6
|
||||||
- run: uv python install 3.12
|
- run: uv python install 3.12
|
||||||
- run: make deps-py
|
- run: make deps-py
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- uses: actions/setup-node@v5
|
- uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
@@ -71,8 +71,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -83,8 +83,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -100,8 +100,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -115,8 +115,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -128,7 +128,7 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- uses: actions/setup-node@v5
|
- uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
@@ -144,8 +144,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -176,7 +176,7 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
- uses: actions/setup-node@v5
|
- uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
@@ -189,8 +189,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|||||||
26
.github/workflows/pull-db-tests.yml
vendored
26
.github/workflows/pull-db-tests.yml
vendored
@@ -38,8 +38,8 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- "9000:9000"
|
- "9000:9000"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -66,19 +66,19 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- run: make deps-backend
|
- run: make deps-backend
|
||||||
- run: make backend
|
- run: GOEXPERIMENT='' make backend
|
||||||
env:
|
env:
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
- name: run migration tests
|
- name: run migration tests
|
||||||
run: make test-sqlite-migration
|
run: make test-sqlite-migration
|
||||||
- name: run tests
|
- name: run tests
|
||||||
run: make test-sqlite
|
run: GOEXPERIMENT='' make test-sqlite
|
||||||
timeout-minutes: 50
|
timeout-minutes: 50
|
||||||
env:
|
env:
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
@@ -124,8 +124,8 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- 10000:10000
|
- 10000:10000
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -142,7 +142,7 @@ jobs:
|
|||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
||||||
- name: unit-tests-gogit
|
- name: unit-tests-gogit
|
||||||
run: make unit-test-coverage test-check
|
run: GOEXPERIMENT='' make unit-test-coverage test-check
|
||||||
env:
|
env:
|
||||||
TAGS: bindata gogit
|
TAGS: bindata gogit
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
@@ -177,8 +177,8 @@ jobs:
|
|||||||
- "587:587"
|
- "587:587"
|
||||||
- "993:993"
|
- "993:993"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -217,8 +217,8 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- 10000:10000
|
- 10000:10000
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|||||||
18
.github/workflows/pull-docker-dryrun.yml
vendored
18
.github/workflows/pull-docker-dryrun.yml
vendored
@@ -11,25 +11,23 @@ jobs:
|
|||||||
files-changed:
|
files-changed:
|
||||||
uses: ./.github/workflows/files-changed.yml
|
uses: ./.github/workflows/files-changed.yml
|
||||||
|
|
||||||
regular:
|
container:
|
||||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v3
|
||||||
- uses: docker/build-push-action@v5
|
- name: Build regular container image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
|
context: .
|
||||||
push: false
|
push: false
|
||||||
tags: gitea/gitea:linux-amd64
|
tags: gitea/gitea:linux-amd64
|
||||||
|
- name: Build rootless container image
|
||||||
rootless:
|
uses: docker/build-push-action@v5
|
||||||
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
|
|
||||||
with:
|
with:
|
||||||
|
context: .
|
||||||
push: false
|
push: false
|
||||||
file: Dockerfile.rootless
|
file: Dockerfile.rootless
|
||||||
tags: gitea/gitea:linux-amd64
|
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
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v5
|
- uses: actions/labeler@v6
|
||||||
with:
|
with:
|
||||||
sync-labels: true
|
sync-labels: true
|
||||||
|
|||||||
84
.github/workflows/release-nightly.yml
vendored
84
.github/workflows/release-nightly.yml
vendored
@@ -12,11 +12,11 @@ jobs:
|
|||||||
nightly-binary:
|
nightly-binary:
|
||||||
runs-on: namespace-profile-gitea-release-binary
|
runs-on: namespace-profile-gitea-release-binary
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
# 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
|
# 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
|
- run: git fetch --unshallow --quiet --tags --force
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -56,19 +56,15 @@ jobs:
|
|||||||
- name: upload binaries to s3
|
- name: upload binaries to s3
|
||||||
run: |
|
run: |
|
||||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
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
|
runs-on: namespace-profile-gitea-release-docker
|
||||||
permissions:
|
permissions:
|
||||||
packages: write # to publish to ghcr.io
|
packages: write # to publish to ghcr.io
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
# 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
|
# 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
|
- 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-qemu-action@v3
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v3
|
||||||
- name: Get cleaned branch name
|
- name: Get cleaned branch name
|
||||||
@@ -76,6 +72,29 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
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"
|
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
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -87,57 +106,20 @@ jobs:
|
|||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: fetch go modules
|
- name: build regular docker image
|
||||||
run: make vendor
|
|
||||||
- name: build rootful docker image
|
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
push: true
|
push: true
|
||||||
tags: |-
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
gitea/gitea:${{ steps.clean_name.outputs.branch }}
|
annotations: ${{ steps.meta.outputs.annotations }}
|
||||||
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
|
|
||||||
- name: build rootless docker image
|
- name: build rootless docker image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
push: true
|
push: true
|
||||||
file: Dockerfile.rootless
|
file: Dockerfile.rootless
|
||||||
tags: |-
|
tags: ${{ steps.meta_rootless.outputs.tags }}
|
||||||
gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
|
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
||||||
ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
|
|
||||||
|
|||||||
58
.github/workflows/release-tag-rc.yml
vendored
58
.github/workflows/release-tag-rc.yml
vendored
@@ -13,11 +13,11 @@ jobs:
|
|||||||
binary:
|
binary:
|
||||||
runs-on: namespace-profile-gitea-release-binary
|
runs-on: namespace-profile-gitea-release-binary
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
# 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
|
# 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
|
- run: git fetch --unshallow --quiet --tags --force
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -66,12 +66,12 @@ jobs:
|
|||||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
|
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
docker-rootful:
|
container:
|
||||||
runs-on: namespace-profile-gitea-release-docker
|
runs-on: namespace-profile-gitea-release-docker
|
||||||
permissions:
|
permissions:
|
||||||
packages: write # to publish to ghcr.io
|
packages: write # to publish to ghcr.io
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
# 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
|
# 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
|
- run: git fetch --unshallow --quiet --tags --force
|
||||||
@@ -88,38 +88,10 @@ jobs:
|
|||||||
# 1.2.3-rc0
|
# 1.2.3-rc0
|
||||||
tags: |
|
tags: |
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
- name: Login to Docker Hub
|
annotations: |
|
||||||
uses: docker/login-action@v3
|
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||||
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
|
|
||||||
- uses: docker/metadata-action@v5
|
- uses: docker/metadata-action@v5
|
||||||
id: meta
|
id: meta_rootless
|
||||||
with:
|
with:
|
||||||
images: |-
|
images: |-
|
||||||
gitea/gitea
|
gitea/gitea
|
||||||
@@ -131,6 +103,8 @@ jobs:
|
|||||||
# 1.2.3-rc0
|
# 1.2.3-rc0
|
||||||
tags: |
|
tags: |
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
|
annotations: |
|
||||||
|
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -142,12 +116,20 @@ jobs:
|
|||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: build rootless docker image
|
- name: build regular container image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
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@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
push: true
|
push: true
|
||||||
file: Dockerfile.rootless
|
file: Dockerfile.rootless
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta_rootless.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
||||||
|
|||||||
56
.github/workflows/release-tag-version.yml
vendored
56
.github/workflows/release-tag-version.yml
vendored
@@ -17,11 +17,11 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
packages: write # to publish to ghcr.io
|
packages: write # to publish to ghcr.io
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
# 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
|
# 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
|
- run: git fetch --unshallow --quiet --tags --force
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -70,12 +70,12 @@ jobs:
|
|||||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-from-tag dist/release/*
|
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-from-tag dist/release/*
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
docker-rootful:
|
container:
|
||||||
runs-on: namespace-profile-gitea-release-docker
|
runs-on: namespace-profile-gitea-release-docker
|
||||||
permissions:
|
permissions:
|
||||||
packages: write # to publish to ghcr.io
|
packages: write # to publish to ghcr.io
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
# 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
|
# 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
|
- run: git fetch --unshallow --quiet --tags --force
|
||||||
@@ -96,36 +96,10 @@ jobs:
|
|||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
type=semver,pattern={{major}}
|
type=semver,pattern={{major}}
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
- name: Login to Docker Hub
|
annotations: |
|
||||||
uses: docker/login-action@v3
|
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||||
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
|
|
||||||
- uses: docker/metadata-action@v5
|
- uses: docker/metadata-action@v5
|
||||||
id: meta
|
id: meta_rootless
|
||||||
with:
|
with:
|
||||||
images: |-
|
images: |-
|
||||||
gitea/gitea
|
gitea/gitea
|
||||||
@@ -142,6 +116,8 @@ jobs:
|
|||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
type=semver,pattern={{major}}
|
type=semver,pattern={{major}}
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
annotations: |
|
||||||
|
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -153,12 +129,20 @@ jobs:
|
|||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: build rootless docker image
|
- name: build regular container image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
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@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
push: true
|
push: true
|
||||||
file: Dockerfile.rootless
|
file: Dockerfile.rootless
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta_rootless.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
||||||
|
|||||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -25,6 +25,9 @@ __debug_bin*
|
|||||||
# Visual Studio
|
# Visual Studio
|
||||||
/.vs/
|
/.vs/
|
||||||
|
|
||||||
|
# mise version managment tool
|
||||||
|
mise.toml
|
||||||
|
|
||||||
*.cgo1.go
|
*.cgo1.go
|
||||||
*.cgo2.c
|
*.cgo2.c
|
||||||
_cgo_defun.c
|
_cgo_defun.c
|
||||||
@@ -122,3 +125,8 @@ prime/
|
|||||||
/CLAUDE.md
|
/CLAUDE.md
|
||||||
/llms.txt
|
/llms.txt
|
||||||
|
|
||||||
|
# Ignore worktrees when working on multiple branches
|
||||||
|
.worktrees/
|
||||||
|
|
||||||
|
# A Makefile for custom make targets
|
||||||
|
Makefile.local
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ linters:
|
|||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- mirror
|
- mirror
|
||||||
|
- modernize
|
||||||
- nakedret
|
- nakedret
|
||||||
- nolintlint
|
- nolintlint
|
||||||
- perfsprint
|
- perfsprint
|
||||||
@@ -55,6 +56,7 @@ linters:
|
|||||||
disabled-checks:
|
disabled-checks:
|
||||||
- ifElseChain
|
- ifElseChain
|
||||||
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
|
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
|
||||||
|
- deprecatedComment # conflicts with go-swagger comments
|
||||||
revive:
|
revive:
|
||||||
severity: error
|
severity: error
|
||||||
rules:
|
rules:
|
||||||
@@ -107,6 +109,15 @@ linters:
|
|||||||
- require-error
|
- require-error
|
||||||
usetesting:
|
usetesting:
|
||||||
os-temp-dir: true
|
os-temp-dir: true
|
||||||
|
modernize:
|
||||||
|
disable:
|
||||||
|
- stringsbuilder
|
||||||
|
perfsprint:
|
||||||
|
concat-loop: false
|
||||||
|
govet:
|
||||||
|
enable:
|
||||||
|
- nilness
|
||||||
|
- unusedwrite
|
||||||
exclusions:
|
exclusions:
|
||||||
generated: lax
|
generated: lax
|
||||||
presets:
|
presets:
|
||||||
@@ -153,6 +164,7 @@ linters:
|
|||||||
text: '(?i)exitAfterDefer:'
|
text: '(?i)exitAfterDefer:'
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- node_modules
|
||||||
|
- .venv
|
||||||
- public
|
- public
|
||||||
- web_src
|
- web_src
|
||||||
- third_party$
|
- third_party$
|
||||||
@@ -172,6 +184,7 @@ formatters:
|
|||||||
generated: lax
|
generated: lax
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- node_modules
|
||||||
|
- .venv
|
||||||
- public
|
- public
|
||||||
- web_src
|
- web_src
|
||||||
- third_party$
|
- third_party$
|
||||||
|
|||||||
@@ -166,19 +166,19 @@ Here's how to run the test suite:
|
|||||||
|
|
||||||
- code lint
|
- code lint
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
| :-------------------- | :---------------------------------------------------------------- |
|
| :-------------------- | :--------------------------------------------------------------------------- |
|
||||||
|``make lint`` | lint everything (not needed if you only change the front- **or** backend) |
|
|``make lint`` | lint everything (not needed if you only change the front- **or** backend) |
|
||||||
|``make lint-frontend`` | lint frontend files |
|
|``make lint-frontend`` | lint frontend files |
|
||||||
|``make lint-backend`` | lint backend files |
|
|``make lint-backend`` | lint backend files |
|
||||||
|
|
||||||
- run tests (we suggest running them on Linux)
|
- run tests (we suggest running them on Linux)
|
||||||
|
|
||||||
| Command | Action | |
|
| Command | Action | |
|
||||||
| :------------------------------------- | :----------------------------------------------- | ------------ |
|
| :------------------------------------------ | :------------------------------------------------------- | ------------------------------------------- |
|
||||||
|``make test[\#SpecificTestName]`` | run unit test(s) | |
|
|``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-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) |
|
|``make test-e2e-sqlite[\#SpecificTestName]`` | run [end-to-end](tests/e2e) test(s) for SQLite | [More details](tests/e2e/README.md) |
|
||||||
|
|
||||||
## Translation
|
## Translation
|
||||||
|
|
||||||
|
|||||||
47
Dockerfile
47
Dockerfile
@@ -1,8 +1,8 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
# Build stage
|
# Build stage
|
||||||
FROM docker.io/library/golang:1.25-alpine3.22 AS build-env
|
FROM docker.io/library/golang:1.25-alpine3.22 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY=direct
|
||||||
ENV GOPROXY=${GOPROXY:-direct}
|
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
ARG GITEA_VERSION
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
@@ -14,35 +14,32 @@ RUN apk --no-cache add \
|
|||||||
build-base \
|
build-base \
|
||||||
git \
|
git \
|
||||||
nodejs \
|
nodejs \
|
||||||
npm \
|
pnpm
|
||||||
&& npm install -g pnpm@10 \
|
|
||||||
&& rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
# Setup repo
|
|
||||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
|
||||||
WORKDIR ${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
|
# Build gitea, .git mount is required for version data
|
||||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||||
&& make clean-all build
|
--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
|
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 \
|
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/gitea/* \
|
||||||
/tmp/local/etc/s6/openssh/* \
|
/tmp/local/etc/s6/openssh/* \
|
||||||
/tmp/local/etc/s6/.s6-svscan/* \
|
/tmp/local/etc/s6/.s6-svscan/* \
|
||||||
/go/src/code.gitea.io/gitea/gitea \
|
/go/src/code.gitea.io/gitea/gitea
|
||||||
/go/src/code.gitea.io/gitea/environment-to-ini
|
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.22
|
FROM docker.io/library/alpine:3.22 AS gitea
|
||||||
LABEL maintainer="maintainers@gitea.io"
|
|
||||||
|
|
||||||
EXPOSE 22 3000
|
EXPOSE 22 3000
|
||||||
|
|
||||||
@@ -57,8 +54,7 @@ RUN apk --no-cache add \
|
|||||||
s6 \
|
s6 \
|
||||||
sqlite \
|
sqlite \
|
||||||
su-exec \
|
su-exec \
|
||||||
gnupg \
|
gnupg
|
||||||
&& rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
RUN addgroup \
|
RUN addgroup \
|
||||||
-S -g 1000 \
|
-S -g 1000 \
|
||||||
@@ -72,6 +68,9 @@ RUN addgroup \
|
|||||||
git && \
|
git && \
|
||||||
echo "git:*" | chpasswd -e
|
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 USER=git
|
||||||
ENV GITEA_CUSTOM=/data/gitea
|
ENV GITEA_CUSTOM=/data/gitea
|
||||||
|
|
||||||
@@ -79,7 +78,3 @@ VOLUME ["/data"]
|
|||||||
|
|
||||||
ENTRYPOINT ["/usr/bin/entrypoint"]
|
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||||
CMD ["/usr/bin/s6-svscan", "/etc/s6"]
|
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
|
# Build stage
|
||||||
FROM docker.io/library/golang:1.25-alpine3.22 AS build-env
|
FROM docker.io/library/golang:1.25-alpine3.22 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY=direct
|
||||||
ENV GOPROXY=${GOPROXY:-direct}
|
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
ARG GITEA_VERSION
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
ENV TAGS="bindata timetzdata $TAGS"
|
ENV TAGS="bindata timetzdata $TAGS"
|
||||||
ARG CGO_EXTRA_CFLAGS
|
ARG CGO_EXTRA_CFLAGS
|
||||||
|
|
||||||
#Build deps
|
# Build deps
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add \
|
||||||
build-base \
|
build-base \
|
||||||
git \
|
git \
|
||||||
nodejs \
|
nodejs \
|
||||||
npm \
|
pnpm
|
||||||
&& npm install -g pnpm@10 \
|
|
||||||
&& rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
# Setup repo
|
|
||||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
|
||||||
WORKDIR ${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
|
# Build gitea, .git mount is required for version data
|
||||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||||
&& make clean-all build
|
--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
|
COPY docker/rootless /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/local/bin/docker-entrypoint.sh \
|
RUN chmod 755 /tmp/local/usr/local/bin/* \
|
||||||
/tmp/local/usr/local/bin/docker-setup.sh \
|
/go/src/code.gitea.io/gitea/gitea
|
||||||
/tmp/local/usr/local/bin/gitea \
|
|
||||||
/go/src/code.gitea.io/gitea/gitea \
|
|
||||||
/go/src/code.gitea.io/gitea/environment-to-ini
|
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.22
|
FROM docker.io/library/alpine:3.22 AS gitea-rootless
|
||||||
LABEL maintainer="maintainers@gitea.io"
|
|
||||||
|
|
||||||
EXPOSE 2222 3000
|
EXPOSE 2222 3000
|
||||||
|
|
||||||
@@ -52,8 +45,7 @@ RUN apk --no-cache add \
|
|||||||
git \
|
git \
|
||||||
curl \
|
curl \
|
||||||
gnupg \
|
gnupg \
|
||||||
openssh-keygen \
|
openssh-keygen
|
||||||
&& rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
RUN addgroup \
|
RUN addgroup \
|
||||||
-S -g 1000 \
|
-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 /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/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
|
# git:git
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
|
|||||||
63
Makefile
63
Makefile
@@ -18,6 +18,10 @@ DIST := dist
|
|||||||
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
||||||
IMPORT := code.gitea.io/gitea
|
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
|
GO ?= go
|
||||||
SHASUM ?= shasum -a 256
|
SHASUM ?= shasum -a 256
|
||||||
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
|
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
|
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
||||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3
|
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3
|
||||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.1
|
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.2
|
||||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0
|
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.7.0
|
||||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15
|
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15
|
||||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.7.0
|
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
|
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
|
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
|
||||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
|
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
|
||||||
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
|
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.9
|
||||||
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
|
|
||||||
|
|
||||||
DOCKER_IMAGE ?= gitea/gitea
|
DOCKER_IMAGE ?= gitea/gitea
|
||||||
DOCKER_TAG ?= latest
|
DOCKER_TAG ?= latest
|
||||||
@@ -159,12 +161,12 @@ 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)
|
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
|
WEB_DIRS := web_src/js web_src/css
|
||||||
|
|
||||||
ESLINT_FILES := web_src/js tools *.ts tests/e2e
|
ESLINT_FILES := web_src/js tools *.ts tests/e2e
|
||||||
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
|
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/*))
|
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.md *.yml *.yaml *.toml))
|
||||||
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
|
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
|
||||||
|
|
||||||
GO_SOURCES := $(wildcard *.go)
|
GO_SOURCES := $(wildcard *.go)
|
||||||
@@ -195,6 +197,10 @@ TEST_MSSQL_DBNAME ?= gitea
|
|||||||
TEST_MSSQL_USERNAME ?= sa
|
TEST_MSSQL_USERNAME ?= sa
|
||||||
TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
|
TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
|
||||||
|
|
||||||
|
# Include local Makefile
|
||||||
|
# Makefile.local is listed in .gitignore
|
||||||
|
sinclude Makefile.local
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
@@ -254,7 +260,7 @@ clean: ## delete backend and integration files
|
|||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt: ## format the Go and template code
|
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'))
|
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
|
||||||
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
|
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
|
||||||
@# whitespace before it
|
@# whitespace before it
|
||||||
@@ -272,19 +278,6 @@ fmt-check: fmt
|
|||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
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)
|
.PHONY: $(TAGS_EVIDENCE)
|
||||||
$(TAGS_EVIDENCE):
|
$(TAGS_EVIDENCE):
|
||||||
@mkdir -p $(MAKE_EVIDENCE_DIR)
|
@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
|
checks-frontend: lockfile-check svg-check ## check frontend files
|
||||||
|
|
||||||
.PHONY: checks-backend
|
.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
|
.PHONY: lint
|
||||||
lint: lint-frontend lint-backend lint-spell ## lint everything
|
lint: lint-frontend lint-backend lint-spell ## lint everything
|
||||||
@@ -339,7 +332,7 @@ lint-frontend: lint-js lint-css ## lint frontend files
|
|||||||
lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues
|
lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues
|
||||||
|
|
||||||
.PHONY: lint-backend
|
.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
|
.PHONY: lint-backend-fix
|
||||||
lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
|
lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
|
||||||
@@ -370,13 +363,17 @@ lint-swagger: node_modules ## lint swagger files
|
|||||||
lint-md: node_modules ## lint markdown files
|
lint-md: node_modules ## lint markdown files
|
||||||
$(NODE_VARS) pnpm exec markdownlint *.md
|
$(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
|
.PHONY: lint-spell
|
||||||
lint-spell: ## lint spelling
|
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
|
.PHONY: lint-spell-fix
|
||||||
lint-spell-fix: ## lint spelling and fix issues
|
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
|
.PHONY: lint-go
|
||||||
lint-go: ## lint go files
|
lint-go: ## lint go files
|
||||||
@@ -396,13 +393,7 @@ lint-go-windows:
|
|||||||
.PHONY: lint-go-gitea-vet
|
.PHONY: lint-go-gitea-vet
|
||||||
lint-go-gitea-vet: ## lint go files with gitea-vet
|
lint-go-gitea-vet: ## lint go files with gitea-vet
|
||||||
@echo "Running gitea-vet..."
|
@echo "Running gitea-vet..."
|
||||||
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
@$(GO) vet -vettool="$(shell GOOS= GOARCH= go tool -n 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)
|
|
||||||
|
|
||||||
.PHONY: lint-editorconfig
|
.PHONY: lint-editorconfig
|
||||||
lint-editorconfig:
|
lint-editorconfig:
|
||||||
@@ -468,7 +459,7 @@ test\#%:
|
|||||||
coverage:
|
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]\+\)$$' 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
|
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
|
.PHONY: unit-test-coverage
|
||||||
unit-test-coverage:
|
unit-test-coverage:
|
||||||
@@ -766,7 +757,7 @@ generate-go: $(TAGS_PREREQ)
|
|||||||
|
|
||||||
.PHONY: security-check
|
.PHONY: security-check
|
||||||
security-check:
|
security-check:
|
||||||
go run $(GOVULNCHECK_PACKAGE) -show color ./...
|
GOEXPERIMENT= go run $(GOVULNCHECK_PACKAGE) -show color ./...
|
||||||
|
|
||||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||||
ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)
|
ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)
|
||||||
@@ -847,8 +838,6 @@ deps-tools: ## install tool dependencies
|
|||||||
$(GO) install $(GO_LICENSES_PACKAGE) & \
|
$(GO) install $(GO_LICENSES_PACKAGE) & \
|
||||||
$(GO) install $(GOVULNCHECK_PACKAGE) & \
|
$(GO) install $(GOVULNCHECK_PACKAGE) & \
|
||||||
$(GO) install $(ACTIONLINT_PACKAGE) & \
|
$(GO) install $(ACTIONLINT_PACKAGE) & \
|
||||||
$(GO) install $(GOPLS_PACKAGE) & \
|
|
||||||
$(GO) install $(GOPLS_MODERNIZE_PACKAGE) & \
|
|
||||||
wait
|
wait
|
||||||
|
|
||||||
node_modules: pnpm-lock.yaml
|
node_modules: pnpm-lock.yaml
|
||||||
|
|||||||
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"
|
|
||||||
)
|
|
||||||
@@ -121,7 +121,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
|
|||||||
}
|
}
|
||||||
log.Trace("Processing next %d repos of %d", len(repos), count)
|
log.Trace("Processing next %d repos of %d", len(repos), count)
|
||||||
for _, repo := range repos {
|
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)
|
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("OpenRepository: %v", err)
|
log.Warn("OpenRepository: %v", err)
|
||||||
@@ -147,7 +147,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
|
|||||||
continue
|
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)
|
repo.FullName(), oldnum, count)
|
||||||
gitRepo.Close()
|
gitRepo.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// codeql[disable-next-line=go/clear-text-logging]
|
||||||
fmt.Printf("generated random password is '%s'\n", password)
|
fmt.Printf("generated random password is '%s'\n", password)
|
||||||
} else if userType == user_model.UserTypeIndividual {
|
} else if userType == user_model.UserTypeIndividual {
|
||||||
return errors.New("must set either password or random-password flag")
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// codeql[disable-next-line=go/clear-text-logging]
|
||||||
fmt.Printf("Updated %d users setting MustChangePassword to %t\n", n, mustChangePassword)
|
fmt.Printf("Updated %d users setting MustChangePassword to %t\n", n, mustChangePassword)
|
||||||
return nil
|
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.
|
// 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) {
|
func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cli.Command) (context.Context, error) {
|
||||||
return func(ctx context.Context, c *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
|
level := defaultLevel
|
||||||
if globalBool(c, "quiet") {
|
if globalBool(c, "quiet") {
|
||||||
level = log.FATAL
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// codeql[disable-next-line=go/clear-text-logging]
|
||||||
fmt.Printf("%s", secretKey)
|
fmt.Printf("%s", secretKey)
|
||||||
|
|
||||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
userID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
userID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||||
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
|
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
|
||||||
deployKeyID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvDeployKeyID), 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{
|
hookOptions := private.HookOptions{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
@@ -196,7 +196,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
GitPushOptions: pushOptions(),
|
GitPushOptions: pushOptions(),
|
||||||
PullRequestID: prID,
|
PullRequestID: prID,
|
||||||
DeployKeyID: deployKeyID,
|
DeployKeyID: deployKeyID,
|
||||||
ActionPerm: int(actionPerm),
|
ActionPerm: actionPerm,
|
||||||
}
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
@@ -313,7 +313,7 @@ func runHookPostReceive(ctx context.Context, c *cli.Command) error {
|
|||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
|
|
||||||
// First of all run update-server-info no matter what
|
// 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)
|
return fmt.Errorf("failed to call 'git update-server-info': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
var CmdKeys = &cli.Command{
|
var CmdKeys = &cli.Command{
|
||||||
Name: "keys",
|
Name: "keys",
|
||||||
Usage: "(internal) Should only be called by SSH server",
|
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",
|
Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint",
|
||||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||||
Action: runKeys,
|
Action: runKeys,
|
||||||
|
|||||||
11
cmd/main.go
11
cmd/main.go
@@ -50,11 +50,15 @@ DEFAULT CONFIGURATION:
|
|||||||
|
|
||||||
func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) {
|
func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) {
|
||||||
originBefore := originCmd.Before
|
originBefore := originCmd.Before
|
||||||
originCmd.Before = func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
|
originCmd.Before = func(ctxOrig context.Context, cmd *cli.Command) (ctx context.Context, err error) {
|
||||||
prepareWorkPathAndCustomConf(cmd)
|
ctx = ctxOrig
|
||||||
if originBefore != nil {
|
if originBefore != nil {
|
||||||
return originBefore(ctx, cmd)
|
ctx, err = originBefore(ctx, cmd)
|
||||||
|
if err != nil {
|
||||||
|
return ctx, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
prepareWorkPathAndCustomConf(cmd)
|
||||||
return ctx, nil
|
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.
|
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
|
||||||
subCmdStandalone := []*cli.Command{
|
subCmdStandalone := []*cli.Command{
|
||||||
|
cmdConfig(),
|
||||||
cmdCert(),
|
cmdCert(),
|
||||||
CmdGenerate,
|
CmdGenerate,
|
||||||
CmdDocs,
|
CmdDocs,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/test"
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/urfave/cli/v3"
|
"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)
|
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{})
|
app := NewMainApp(AppVersion{})
|
||||||
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
|
testCmd.Name = util.IfZero(testCmd.Name, "test-cmd")
|
||||||
prepareSubcommandWithGlobalFlags(testCmd)
|
prepareSubcommandWithGlobalFlags(&testCmd)
|
||||||
app.Commands = append(app.Commands, testCmd)
|
app.Commands = append(app.Commands, &testCmd)
|
||||||
app.DefaultCommand = testCmd.Name
|
app.DefaultCommand = testCmd.Name
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
@@ -156,9 +157,11 @@ func TestCliCmd(t *testing.T) {
|
|||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.cmd, func(t *testing.T) {
|
t.Run(c.cmd, func(t *testing.T) {
|
||||||
app := newTestApp(func(ctx context.Context, cmd *cli.Command) error {
|
app := newTestApp(cli.Command{
|
||||||
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||||
return nil
|
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
})
|
})
|
||||||
for k, v := range c.env {
|
for k, v := range c.env {
|
||||||
t.Setenv(k, v)
|
t.Setenv(k, v)
|
||||||
@@ -173,31 +176,54 @@ func TestCliCmd(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCliCmdError(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")
|
r, err := runTestApp(app, "./gitea", "test-cmd")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Equal(t, 1, r.ExitCode)
|
assert.Equal(t, 1, r.ExitCode)
|
||||||
assert.Empty(t, r.Stdout)
|
assert.Empty(t, r.Stdout)
|
||||||
assert.Equal(t, "Command error: normal error\n", r.Stderr)
|
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")
|
r, err = runTestApp(app, "./gitea", "test-cmd")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Equal(t, 2, r.ExitCode)
|
assert.Equal(t, 2, r.ExitCode)
|
||||||
assert.Empty(t, r.Stdout)
|
assert.Empty(t, r.Stdout)
|
||||||
assert.Equal(t, "exit error\n", r.Stderr)
|
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")
|
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Equal(t, 1, r.ExitCode)
|
assert.Equal(t, 1, r.ExitCode)
|
||||||
assert.Empty(t, r.Stdout)
|
assert.Empty(t, r.Stdout)
|
||||||
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
|
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")
|
r, err = runTestApp(app, "./gitea", "test-cmd")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called
|
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called
|
||||||
assert.Empty(t, r.Stdout)
|
assert.Empty(t, r.Stdout)
|
||||||
assert.Empty(t, r.Stderr)
|
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"])
|
||||||
|
}
|
||||||
|
|||||||
41
cmd/serv.go
41
cmd/serv.go
@@ -13,13 +13,12 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"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"
|
||||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
@@ -32,7 +31,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/services/lfs"
|
"code.gitea.io/gitea/services/lfs"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
|
||||||
"github.com/kballard/go-shellquote"
|
"github.com/kballard/go-shellquote"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
@@ -133,27 +131,6 @@ func getAccessMode(verb, lfsVerb string) perm.AccessMode {
|
|||||||
return perm.AccessModeNone
|
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 {
|
func runServ(ctx context.Context, c *cli.Command) error {
|
||||||
// FIXME: This needs to internationalised
|
// FIXME: This needs to internationalised
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
@@ -230,7 +207,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
|||||||
username := repoPathFields[0]
|
username := repoPathFields[0]
|
||||||
reponame := strings.TrimSuffix(repoPathFields[1], ".git") // “the-repo-name" or "the-repo-name.wiki"
|
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)
|
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)
|
return fail(ctx, extra.UserMsg, "ServCommand failed: %s", extra.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LowerCase and trim the repoPath as that's how they are stored.
|
// because the original repoPath maybe redirected, we need to use the returned actual repository information
|
||||||
// This should be done after splitting the repoPath into username and reponame
|
if results.IsWiki {
|
||||||
// so that username and reponame are not affected.
|
repoPath = repo_model.RelativeWikiPath(results.OwnerName, results.RepoName)
|
||||||
repoPath = strings.ToLower(results.OwnerName + "/" + results.RepoName + ".git")
|
} else {
|
||||||
|
repoPath = repo_model.RelativePath(results.OwnerName, results.RepoName)
|
||||||
|
}
|
||||||
|
|
||||||
// LFS SSH protocol
|
// LFS SSH protocol
|
||||||
if verb == git.CmdVerbLfsTransfer {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -294,7 +273,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
|||||||
if verb == git.CmdVerbLfsAuthenticate {
|
if verb == git.CmdVerbLfsAuthenticate {
|
||||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,6 @@ func serveInstall(cmd *cli.Command) error {
|
|||||||
case <-graceful.GetManager().IsShutdown():
|
case <-graceful.GetManager().IsShutdown():
|
||||||
<-graceful.GetManager().Done()
|
<-graceful.GetManager().Done()
|
||||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||||
log.GetManager().Close()
|
|
||||||
return err
|
return err
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@@ -231,7 +230,6 @@ func serveInstalled(c *cli.Command) error {
|
|||||||
err := listen(webRoutes, true)
|
err := listen(webRoutes, true)
|
||||||
<-graceful.GetManager().Done()
|
<-graceful.GetManager().Done()
|
||||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||||
log.GetManager().Close()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -567,6 +567,11 @@ ENABLED = true
|
|||||||
;; Alternative location to specify OAuth2 authentication secret. You cannot specify both this and JWT_SECRET, and must pick one
|
;; 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
|
;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
|
;; Lifetime of an OAuth2 access token in seconds
|
||||||
;ACCESS_TOKEN_EXPIRATION_TIME = 3600
|
;ACCESS_TOKEN_EXPIRATION_TIME = 3600
|
||||||
;;
|
;;
|
||||||
@@ -1343,6 +1348,10 @@ LEVEL = Info
|
|||||||
;; Dont mistake it for Reactions.
|
;; Dont mistake it for Reactions.
|
||||||
;CUSTOM_EMOJIS = gitea, codeberg, gitlab, git, github, gogs
|
;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.
|
;; 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
|
;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]
|
;[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.*] .
|
;; * 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.
|
;; * 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.
|
;; * 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 github from 'eslint-plugin-github';
|
||||||
import globals from 'globals';
|
import globals from 'globals';
|
||||||
import importPlugin from 'eslint-plugin-import-x';
|
import importPlugin from 'eslint-plugin-import-x';
|
||||||
import noUseExtendNative from 'eslint-plugin-no-use-extend-native';
|
|
||||||
import playwright from 'eslint-plugin-playwright';
|
import playwright from 'eslint-plugin-playwright';
|
||||||
import regexp from 'eslint-plugin-regexp';
|
import regexp from 'eslint-plugin-regexp';
|
||||||
import sonarjs from 'eslint-plugin-sonarjs';
|
import sonarjs from 'eslint-plugin-sonarjs';
|
||||||
@@ -49,24 +48,19 @@ export default defineConfig([
|
|||||||
},
|
},
|
||||||
linterOptions: {
|
linterOptions: {
|
||||||
reportUnusedDisableDirectives: 2,
|
reportUnusedDisableDirectives: 2,
|
||||||
|
reportUnusedInlineConfigs: 2,
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
'@eslint-community/eslint-comments': comments,
|
'@eslint-community/eslint-comments': comments,
|
||||||
// @ts-expect-error
|
|
||||||
'@stylistic': stylistic,
|
'@stylistic': stylistic,
|
||||||
'@typescript-eslint': typescriptPlugin.plugin,
|
'@typescript-eslint': typescriptPlugin.plugin,
|
||||||
'array-func': arrayFunc,
|
'array-func': arrayFunc,
|
||||||
// @ts-expect-error -- https://github.com/un-ts/eslint-plugin-import-x/issues/203
|
// @ts-expect-error -- https://github.com/un-ts/eslint-plugin-import-x/issues/203
|
||||||
'import-x': importPlugin,
|
'import-x': importPlugin,
|
||||||
'no-use-extend-native': noUseExtendNative,
|
|
||||||
// @ts-expect-error
|
|
||||||
regexp,
|
regexp,
|
||||||
// @ts-expect-error
|
|
||||||
sonarjs,
|
sonarjs,
|
||||||
// @ts-expect-error
|
|
||||||
unicorn,
|
unicorn,
|
||||||
github,
|
github,
|
||||||
// @ts-expect-error
|
|
||||||
wc,
|
wc,
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
@@ -159,7 +153,7 @@ export default defineConfig([
|
|||||||
'@typescript-eslint/ban-tslint-comment': [0],
|
'@typescript-eslint/ban-tslint-comment': [0],
|
||||||
'@typescript-eslint/class-literal-property-style': [0],
|
'@typescript-eslint/class-literal-property-style': [0],
|
||||||
'@typescript-eslint/class-methods-use-this': [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-indexed-object-style': [0],
|
||||||
'@typescript-eslint/consistent-return': [0],
|
'@typescript-eslint/consistent-return': [0],
|
||||||
'@typescript-eslint/consistent-type-assertions': [2, {assertionStyle: 'as', objectLiteralTypeAssertions: 'allow'}],
|
'@typescript-eslint/consistent-type-assertions': [2, {assertionStyle: 'as', objectLiteralTypeAssertions: 'allow'}],
|
||||||
@@ -235,6 +229,7 @@ export default defineConfig([
|
|||||||
'@typescript-eslint/no-unsafe-return': [0],
|
'@typescript-eslint/no-unsafe-return': [0],
|
||||||
'@typescript-eslint/no-unsafe-unary-minus': [2],
|
'@typescript-eslint/no-unsafe-unary-minus': [2],
|
||||||
'@typescript-eslint/no-unused-expressions': [0],
|
'@typescript-eslint/no-unused-expressions': [0],
|
||||||
|
'@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-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-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-constructor': [0],
|
||||||
@@ -591,10 +586,10 @@ export default defineConfig([
|
|||||||
'no-unsafe-negation': [2],
|
'no-unsafe-negation': [2],
|
||||||
'no-unused-expressions': [2],
|
'no-unused-expressions': [2],
|
||||||
'no-unused-labels': [2],
|
'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-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-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-backreference': [2],
|
||||||
'no-useless-call': [2],
|
'no-useless-call': [2],
|
||||||
'no-useless-catch': [2],
|
'no-useless-catch': [2],
|
||||||
@@ -777,6 +772,7 @@ export default defineConfig([
|
|||||||
'unicorn/no-empty-file': [2],
|
'unicorn/no-empty-file': [2],
|
||||||
'unicorn/no-for-loop': [0],
|
'unicorn/no-for-loop': [0],
|
||||||
'unicorn/no-hex-escape': [0],
|
'unicorn/no-hex-escape': [0],
|
||||||
|
'unicorn/no-immediate-mutation': [0],
|
||||||
'unicorn/no-instanceof-array': [0],
|
'unicorn/no-instanceof-array': [0],
|
||||||
'unicorn/no-invalid-fetch-options': [2],
|
'unicorn/no-invalid-fetch-options': [2],
|
||||||
'unicorn/no-invalid-remove-event-listener': [2],
|
'unicorn/no-invalid-remove-event-listener': [2],
|
||||||
@@ -802,6 +798,7 @@ export default defineConfig([
|
|||||||
'unicorn/no-unreadable-array-destructuring': [0],
|
'unicorn/no-unreadable-array-destructuring': [0],
|
||||||
'unicorn/no-unreadable-iife': [2],
|
'unicorn/no-unreadable-iife': [2],
|
||||||
'unicorn/no-unused-properties': [2],
|
'unicorn/no-unused-properties': [2],
|
||||||
|
'unicorn/no-useless-collection-argument': [2],
|
||||||
'unicorn/no-useless-fallback-in-spread': [2],
|
'unicorn/no-useless-fallback-in-spread': [2],
|
||||||
'unicorn/no-useless-length-check': [2],
|
'unicorn/no-useless-length-check': [2],
|
||||||
'unicorn/no-useless-promise-resolve-reject': [2],
|
'unicorn/no-useless-promise-resolve-reject': [2],
|
||||||
@@ -813,8 +810,8 @@ export default defineConfig([
|
|||||||
'unicorn/numeric-separators-style': [0],
|
'unicorn/numeric-separators-style': [0],
|
||||||
'unicorn/prefer-add-event-listener': [2],
|
'unicorn/prefer-add-event-listener': [2],
|
||||||
'unicorn/prefer-array-find': [2],
|
'unicorn/prefer-array-find': [2],
|
||||||
'unicorn/prefer-array-flat-map': [2],
|
|
||||||
'unicorn/prefer-array-flat': [2],
|
'unicorn/prefer-array-flat': [2],
|
||||||
|
'unicorn/prefer-array-flat-map': [2],
|
||||||
'unicorn/prefer-array-index-of': [2],
|
'unicorn/prefer-array-index-of': [2],
|
||||||
'unicorn/prefer-array-some': [2],
|
'unicorn/prefer-array-some': [2],
|
||||||
'unicorn/prefer-at': [0],
|
'unicorn/prefer-at': [0],
|
||||||
@@ -849,6 +846,7 @@ export default defineConfig([
|
|||||||
'unicorn/prefer-query-selector': [2],
|
'unicorn/prefer-query-selector': [2],
|
||||||
'unicorn/prefer-reflect-apply': [0],
|
'unicorn/prefer-reflect-apply': [0],
|
||||||
'unicorn/prefer-regexp-test': [2],
|
'unicorn/prefer-regexp-test': [2],
|
||||||
|
'unicorn/prefer-response-static-json': [2],
|
||||||
'unicorn/prefer-set-has': [0],
|
'unicorn/prefer-set-has': [0],
|
||||||
'unicorn/prefer-set-size': [2],
|
'unicorn/prefer-set-size': [2],
|
||||||
'unicorn/prefer-spread': [0],
|
'unicorn/prefer-spread': [0],
|
||||||
@@ -900,7 +898,6 @@ export default defineConfig([
|
|||||||
'yoda': [2, 'never'],
|
'yoda': [2, 'never'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// @ts-expect-error
|
|
||||||
{
|
{
|
||||||
...playwright.configs['flat/recommended'],
|
...playwright.configs['flat/recommended'],
|
||||||
files: ['tests/e2e/**'],
|
files: ['tests/e2e/**'],
|
||||||
@@ -916,7 +913,6 @@ export default defineConfig([
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
extends: [
|
extends: [
|
||||||
// @ts-expect-error
|
|
||||||
vue.configs['flat/recommended'],
|
vue.configs['flat/recommended'],
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
vueScopedCss.configs['flat/recommended'],
|
vueScopedCss.configs['flat/recommended'],
|
||||||
@@ -926,6 +922,7 @@ export default defineConfig([
|
|||||||
'vue/html-closing-bracket-spacing': [2, {startTag: 'never', endTag: 'never', selfClosingTag: 'never'}],
|
'vue/html-closing-bracket-spacing': [2, {startTag: 'never', endTag: 'never', selfClosingTag: 'never'}],
|
||||||
'vue/max-attributes-per-line': [0],
|
'vue/max-attributes-per-line': [0],
|
||||||
'vue/singleline-html-element-content-newline': [0],
|
'vue/singleline-html-element-content-newline': [0],
|
||||||
|
'vue/require-typed-ref': [2],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -936,7 +933,6 @@ export default defineConfig([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ['**/*.test.ts', 'web_src/js/test/setup.ts'],
|
files: ['**/*.test.ts', 'web_src/js/test/setup.ts'],
|
||||||
// @ts-expect-error - https://github.com/vitest-dev/eslint-plugin-vitest/issues/737
|
|
||||||
plugins: {vitest},
|
plugins: {vitest},
|
||||||
languageOptions: {globals: globals.vitest},
|
languageOptions: {globals: globals.vitest},
|
||||||
rules: {
|
rules: {
|
||||||
|
|||||||
40
flake.lock
generated
40
flake.lock
generated
@@ -1,30 +1,12 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"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": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1755186698,
|
"lastModified": 1760038930,
|
||||||
"narHash": "sha256-wNO3+Ks2jZJ4nTHMuks+cxAiVBGNuEBXsT29Bz6HASo=",
|
"narHash": "sha256-Oncbh0UmHjSlxO7ErQDM3KM0A5/Znfofj2BSzlHLeVw=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "fbcf476f790d8a217c3eab4e12033dc4a0f6d23c",
|
"rev": "0b4defa2584313f3b781240b29d61f6f9f7e0df3",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -36,24 +18,8 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": "nixpkgs"
|
"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",
|
"root": "root",
|
||||||
|
|||||||
139
flake.nix
139
flake.nix
@@ -1,73 +1,94 @@
|
|||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
|
||||||
};
|
};
|
||||||
outputs =
|
outputs =
|
||||||
{ nixpkgs, flake-utils, ... }:
|
{ nixpkgs, ... }:
|
||||||
flake-utils.lib.eachDefaultSystem (
|
let
|
||||||
system:
|
supportedSystems = [
|
||||||
let
|
"aarch64-darwin"
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
"aarch64-linux"
|
||||||
in
|
"x86_64-darwin"
|
||||||
{
|
"x86_64-linux"
|
||||||
devShells.default =
|
];
|
||||||
with pkgs;
|
|
||||||
|
forEachSupportedSystem =
|
||||||
|
f:
|
||||||
|
nixpkgs.lib.genAttrs supportedSystems (
|
||||||
|
system:
|
||||||
let
|
let
|
||||||
# only bump toolchain versions here
|
pkgs = import nixpkgs {
|
||||||
go = go_1_25;
|
inherit system;
|
||||||
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";
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
pkgs.mkShell (
|
f { inherit pkgs; }
|
||||||
{
|
);
|
||||||
buildInputs = [
|
in
|
||||||
# generic
|
{
|
||||||
git
|
devShells = forEachSupportedSystem (
|
||||||
git-lfs
|
{ pkgs, ... }:
|
||||||
gnumake
|
{
|
||||||
gnused
|
default =
|
||||||
gnutar
|
let
|
||||||
gzip
|
inherit (pkgs) lib;
|
||||||
zip
|
|
||||||
|
|
||||||
# frontend
|
# only bump toolchain versions here
|
||||||
nodejs
|
go = pkgs.go_1_25;
|
||||||
pnpm
|
nodejs = pkgs.nodejs_24;
|
||||||
cairo
|
python3 = pkgs.python312;
|
||||||
pixman
|
pnpm = pkgs.pnpm_10;
|
||||||
pkg-config
|
|
||||||
|
|
||||||
# linting
|
# Platform-specific dependencies
|
||||||
python3
|
linuxOnlyInputs = lib.optionals pkgs.stdenv.isLinux [
|
||||||
uv
|
pkgs.glibc.static
|
||||||
|
];
|
||||||
|
|
||||||
# backend
|
linuxOnlyEnv = lib.optionalAttrs pkgs.stdenv.isLinux {
|
||||||
go
|
CFLAGS = "-I${pkgs.glibc.static.dev}/include";
|
||||||
gofumpt
|
LDFLAGS = "-L ${pkgs.glibc.static}/lib";
|
||||||
sqlite
|
};
|
||||||
]
|
in
|
||||||
++ linuxOnlyInputs;
|
pkgs.mkShell {
|
||||||
|
packages =
|
||||||
|
with pkgs;
|
||||||
|
[
|
||||||
|
# generic
|
||||||
|
git
|
||||||
|
git-lfs
|
||||||
|
gnumake
|
||||||
|
gnused
|
||||||
|
gnutar
|
||||||
|
gzip
|
||||||
|
zip
|
||||||
|
|
||||||
GO = "${go}/bin/go";
|
# frontend
|
||||||
GOROOT = "${go}/share/go";
|
nodejs
|
||||||
|
pnpm
|
||||||
|
cairo
|
||||||
|
pixman
|
||||||
|
pkg-config
|
||||||
|
|
||||||
TAGS = "sqlite sqlite_unlock_notify";
|
# linting
|
||||||
STATIC = "true";
|
python3
|
||||||
}
|
uv
|
||||||
// linuxOnlyEnv
|
|
||||||
);
|
# backend
|
||||||
}
|
go
|
||||||
);
|
gofumpt
|
||||||
|
sqlite
|
||||||
|
]
|
||||||
|
++ linuxOnlyInputs;
|
||||||
|
|
||||||
|
env = {
|
||||||
|
GO = "${go}/bin/go";
|
||||||
|
GOROOT = "${go}/share/go";
|
||||||
|
|
||||||
|
TAGS = "sqlite sqlite_unlock_notify";
|
||||||
|
STATIC = "true";
|
||||||
|
}
|
||||||
|
// linuxOnlyEnv;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
54
go.mod
54
go.mod
@@ -1,6 +1,8 @@
|
|||||||
module code.gitea.io/gitea
|
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."
|
// 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:
|
// But some CAs use negative serial number, just relax the check. related:
|
||||||
@@ -9,14 +11,13 @@ godebug x509negativeserial=1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
code.gitea.io/actions-proto-go v0.4.1
|
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
|
code.gitea.io/sdk/gitea v0.22.0
|
||||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||||
connectrpc.com/connect v1.18.1
|
connectrpc.com/connect v1.18.1
|
||||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
|
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
|
||||||
gitea.com/go-chi/cache v0.2.1
|
gitea.com/go-chi/cache v0.2.1
|
||||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
|
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/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||||
github.com/42wim/httpsig v1.2.3
|
github.com/42wim/httpsig v1.2.3
|
||||||
@@ -35,7 +36,7 @@ require (
|
|||||||
github.com/bohde/codel v0.2.0
|
github.com/bohde/codel v0.2.0
|
||||||
github.com/buildkite/terminal-to-html/v3 v3.16.8
|
github.com/buildkite/terminal-to-html/v3 v3.16.8
|
||||||
github.com/caddyserver/certmagic v0.24.0
|
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/chi-middleware/proxy v1.1.1
|
||||||
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
||||||
github.com/djherbis/buffer v1.2.0
|
github.com/djherbis/buffer v1.2.0
|
||||||
@@ -56,11 +57,12 @@ require (
|
|||||||
github.com/go-co-op/gocron v1.37.0
|
github.com/go-co-op/gocron v1.37.0
|
||||||
github.com/go-enry/go-enry/v2 v2.9.2
|
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-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-ldap/ldap/v3 v3.4.11
|
||||||
github.com/go-redsync/redsync/v4 v4.13.0
|
github.com/go-redsync/redsync/v4 v4.13.0
|
||||||
github.com/go-sql-driver/mysql v1.9.3
|
github.com/go-sql-driver/mysql v1.9.3
|
||||||
github.com/go-webauthn/webauthn v0.13.4
|
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/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||||
@@ -75,7 +77,6 @@ require (
|
|||||||
github.com/huandu/xstrings v1.5.0
|
github.com/huandu/xstrings v1.5.0
|
||||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
||||||
github.com/jhillyerd/enmime v1.3.0
|
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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/klauspost/compress v1.18.0
|
github.com/klauspost/compress v1.18.0
|
||||||
github.com/klauspost/cpuid/v2 v2.3.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-isatty v0.0.20
|
||||||
github.com/mattn/go-sqlite3 v1.14.32
|
github.com/mattn/go-sqlite3 v1.14.32
|
||||||
github.com/meilisearch/meilisearch-go v0.33.2
|
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/microcosm-cc/bluemonday v1.0.27
|
||||||
github.com/microsoft/go-mssqldb v1.9.3
|
github.com/microsoft/go-mssqldb v1.9.3
|
||||||
github.com/minio/minio-go/v7 v7.0.95
|
github.com/minio/minio-go/v7 v7.0.95
|
||||||
@@ -109,20 +110,20 @@ require (
|
|||||||
github.com/ulikunitz/xz v0.5.15
|
github.com/ulikunitz/xz v0.5.15
|
||||||
github.com/urfave/cli-docs/v3 v3.0.0-alpha6
|
github.com/urfave/cli-docs/v3 v3.0.0-alpha6
|
||||||
github.com/urfave/cli/v3 v3.4.1
|
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/xeipuuv/gojsonschema v1.2.0
|
||||||
github.com/yohcop/openid-go v1.0.1
|
github.com/yohcop/openid-go v1.0.1
|
||||||
github.com/yuin/goldmark v1.7.13
|
github.com/yuin/goldmark v1.7.13
|
||||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||||
github.com/yuin/goldmark-meta v1.1.0
|
github.com/yuin/goldmark-meta v1.1.0
|
||||||
gitlab.com/gitlab-org/api/client-go v0.142.4
|
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/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/oauth2 v0.30.0
|
||||||
golang.org/x/sync v0.16.0
|
golang.org/x/sync v0.18.0
|
||||||
golang.org/x/sys v0.35.0
|
golang.org/x/sys v0.38.0
|
||||||
golang.org/x/text v0.28.0
|
golang.org/x/text v0.31.0
|
||||||
google.golang.org/grpc v1.75.0
|
google.golang.org/grpc v1.75.0
|
||||||
google.golang.org/protobuf v1.36.8
|
google.golang.org/protobuf v1.36.8
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.0
|
||||||
@@ -135,6 +136,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
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
|
dario.cat/mergo v1.0.2 // indirect
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||||
@@ -142,7 +144,7 @@ require (
|
|||||||
github.com/DataDog/zstd v1.5.7 // indirect
|
github.com/DataDog/zstd v1.5.7 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/RoaringBitmap/roaring/v2 v2.10.0 // 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/brotli v1.2.0 // indirect
|
||||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||||
@@ -172,7 +174,7 @@ require (
|
|||||||
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
|
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
|
||||||
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
|
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
|
||||||
github.com/bodgit/plumbing v1.3.0 // 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/bodgit/windows v1.0.1 // indirect
|
||||||
github.com/boombuler/barcode v1.1.0 // indirect
|
github.com/boombuler/barcode v1.1.0 // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
|
||||||
@@ -200,7 +202,6 @@ require (
|
|||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/go-ini/ini v1.67.0 // indirect
|
github.com/go-ini/ini v1.67.0 // indirect
|
||||||
github.com/go-webauthn/x v0.1.24 // 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-jwt/jwt/v4 v4.5.2 // indirect
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||||
@@ -220,6 +221,7 @@ require (
|
|||||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // 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/kevinburke/ssh_config v1.4.0 // indirect
|
||||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||||
github.com/libdns/libdns v1.1.1 // indirect
|
github.com/libdns/libdns v1.1.1 // indirect
|
||||||
@@ -233,14 +235,14 @@ require (
|
|||||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // 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/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||||
github.com/mschoch/smat v0.2.0 // indirect
|
github.com/mschoch/smat v0.2.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 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/cat v0.0.0-20250817074551-3280053e4e00 // indirect
|
||||||
github.com/olekukonko/errors v1.1.0 // indirect
|
github.com/olekukonko/errors v1.1.0 // indirect
|
||||||
github.com/olekukonko/ll v0.1.0 // indirect
|
github.com/olekukonko/ll v0.1.0 // indirect
|
||||||
@@ -259,7 +261,8 @@ require (
|
|||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/skeema/knownhosts v1.3.1 // 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/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||||
github.com/tinylib/msgp v1.4.0 // indirect
|
github.com/tinylib/msgp v1.4.0 // indirect
|
||||||
github.com/unknwon/com v1.0.1 // 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 v1.27.0 // indirect
|
||||||
go.uber.org/zap/exp v0.3.0 // indirect
|
go.uber.org/zap/exp v0.3.0 // indirect
|
||||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
|
||||||
golang.org/x/mod v0.27.0 // indirect
|
golang.org/x/mod v0.29.0 // indirect
|
||||||
golang.org/x/time v0.12.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
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // 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/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||||
|
|
||||||
replace github.com/nektos/act => gitea.com/gitea/act v0.261.6
|
replace github.com/nektos/act => gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763
|
||||||
|
|
||||||
// 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 git.sr.ht/~mariusor/go-xsd-duration => gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078
|
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/goccy/go-json v0.4.11
|
||||||
|
|
||||||
exclude github.com/satori/go.uuid v1.2.0
|
exclude github.com/satori/go.uuid v1.2.0
|
||||||
|
|
||||||
|
tool code.gitea.io/gitea-vet
|
||||||
|
|||||||
88
go.sum
88
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=
|
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 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
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.7-0.20251003180512-ac6e4b751763 h1:ohdxegvslDEllZmRNDqpKun6L4Oq81jNdEDtGgHEV2c=
|
||||||
gitea.com/gitea/act v0.261.6/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
|
gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763/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/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:BAFmdZpRW7zMQZQDClaCWobRj9uL1MR3MzpCVJvc5s4=
|
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/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=
|
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/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 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo=
|
||||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098/go.mod h1:LjzIOHlRemuUyO7WR12fmm18VZIlCAaOt9L3yKw40pk=
|
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-20251124165456-68e0254e989e h1:4bugwPyGMLvblEm3pZ8fZProSPVxE4l0UXF2Kv6IJoY=
|
||||||
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96/go.mod h1:0iEpFKnwO5dG0aF98O4eq6FMsAiXkNBaDIlUOlq4BtM=
|
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 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
|
||||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:VyMQP6ue6MKHM8UsOXfNfuMKD0oSAWZdXVcpHIN2yaY=
|
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=
|
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHqdhS7keYWioqfmxdnfblFDTGoOwcZ+o=
|
||||||
@@ -93,8 +91,8 @@ 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 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 h1:HbJ8Cs71lfCJyvmSptxeMX2PtvOC8yonlU0GQcy2Ak0=
|
||||||
github.com/RoaringBitmap/roaring/v2 v2.10.0/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
|
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.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||||
github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4=
|
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 h1:tgjwQrDH5m6jIYB7kac5IQZmfUzQNseac/e3H4VoCNE=
|
||||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0/go.mod h1:1HmmMEVsr+0R1QWahSeMJkjSkq6CYAZu1aIbYSpfJ4o=
|
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.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 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||||
@@ -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/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 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
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.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4=
|
||||||
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
|
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 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
||||||
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||||
github.com/bohde/codel v0.2.0 h1:fzF7ibgKmCfQbOzQCblmQcwzDRmV7WO7VMLm/hDvD3E=
|
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/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 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
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 h1:4HaXUp8o2+bhHr1OhVy+VjN0+L7/07JDcn6v7YrTjrQ=
|
||||||
github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdikvbVJVHv/M+0=
|
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=
|
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-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 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-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.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8=
|
||||||
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/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 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-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
github.com/go-ini/ini v1.67.0 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/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 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
|
||||||
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
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.0.0-20251009205813-e30ac6010726 h1:narluFTg20M5KBwKxedpFiSMkdjQRRNUlpY4uAsKMwk=
|
||||||
github.com/mholt/archives v0.1.3/go.mod h1:LUCGp++/IbV/I0Xq4SzcIR6uwgeh2yjnQWamjRQfLTU=
|
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 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
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=
|
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/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 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
|
||||||
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
|
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.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
|
||||||
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
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/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.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
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/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 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0=
|
||||||
github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48=
|
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.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A=
|
||||||
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
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.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
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-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 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
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.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||||
github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk=
|
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.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/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/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
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.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.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.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/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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
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 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
||||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
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/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.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8=
|
||||||
github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
|
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 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
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=
|
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||||
@@ -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.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
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.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
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-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-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-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-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
|
||||||
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/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-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.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=
|
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.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.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.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
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-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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/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.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
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.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-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.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.7.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.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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.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.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.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.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.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.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
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.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.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.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.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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
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.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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()})
|
app := cmd.NewMainApp(cmd.AppVersion{Version: Version, Extra: formatBuiltWith()})
|
||||||
_ = cmd.RunMainApp(app, os.Args...) // all errors should have been handled by the RunMainApp
|
_ = 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()
|
log.GetManager().Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ func TestMain(m *testing.M) {
|
|||||||
unittest.MainTest(m, &unittest.TestOptions{
|
unittest.MainTest(m, &unittest.TestOptions{
|
||||||
FixtureFiles: []string{
|
FixtureFiles: []string{
|
||||||
"action_runner_token.yml",
|
"action_runner_token.yml",
|
||||||
|
"action_run.yml",
|
||||||
|
"repository.yml",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ import (
|
|||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
|
||||||
"github.com/nektos/act/pkg/jobparser"
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ import (
|
|||||||
type ActionRun struct {
|
type ActionRun struct {
|
||||||
ID int64
|
ID int64
|
||||||
Title string
|
Title string
|
||||||
RepoID int64 `xorm:"index unique(repo_index)"`
|
RepoID int64 `xorm:"unique(repo_index) index(repo_concurrency)"`
|
||||||
Repo *repo_model.Repository `xorm:"-"`
|
Repo *repo_model.Repository `xorm:"-"`
|
||||||
OwnerID int64 `xorm:"index"`
|
OwnerID int64 `xorm:"index"`
|
||||||
WorkflowID string `xorm:"index"` // the name of workflow file
|
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
|
TriggerEvent string // the trigger event defined in the `on` configuration of the triggered workflow
|
||||||
Status Status `xorm:"index"`
|
Status Status `xorm:"index"`
|
||||||
Version int `xorm:"version default 0"` // Status could be updated concomitantly, so an optimistic lock is needed
|
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 and Stopped is used for recording last run time, if rerun happened, they will be reset to 0
|
||||||
Started timeutil.TimeStamp
|
Started timeutil.TimeStamp
|
||||||
Stopped timeutil.TimeStamp
|
Stopped timeutil.TimeStamp
|
||||||
@@ -102,6 +105,15 @@ func (run *ActionRun) PrettyRef() string {
|
|||||||
return refName.ShortName()
|
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
|
// LoadAttributes load Repo TriggerUser if not loaded
|
||||||
func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
||||||
if run == nil {
|
if run == nil {
|
||||||
@@ -181,9 +193,11 @@ func (run *ActionRun) IsSchedule() bool {
|
|||||||
return run.ScheduleID > 0
|
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).
|
_, err := db.GetEngine(ctx).ID(repo.ID).
|
||||||
NoAutoTime().
|
NoAutoTime().
|
||||||
|
Cols("num_action_runs", "num_closed_action_runs").
|
||||||
SetExpr("num_action_runs",
|
SetExpr("num_action_runs",
|
||||||
builder.Select("count(*)").From("action_run").
|
builder.Select("count(*)").From("action_run").
|
||||||
Where(builder.Eq{"repo_id": repo.ID}),
|
Where(builder.Eq{"repo_id": repo.ID}),
|
||||||
@@ -238,116 +252,62 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
|
|||||||
return cancelledJobs, err
|
return cancelledJobs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over each job and attempt to cancel it.
|
cjs, err := CancelJobs(ctx, jobs)
|
||||||
for _, job := range jobs {
|
if err != nil {
|
||||||
// Skip jobs that are already in a terminal state (completed, cancelled, etc.).
|
return cancelledJobs, err
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
cancelledJobs = append(cancelledJobs, cjs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return nil to indicate successful cancellation of all running and waiting jobs.
|
// Return nil to indicate successful cancellation of all running and waiting jobs.
|
||||||
return cancelledJobs, nil
|
return cancelledJobs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertRun inserts a run
|
func CancelJobs(ctx context.Context, jobs []*ActionRunJob) ([]*ActionRunJob, error) {
|
||||||
// The title will be cut off at 255 characters if it's longer than 255 characters.
|
cancelledJobs := make([]*ActionRunJob, 0, len(jobs))
|
||||||
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
|
// Iterate over each job and attempt to cancel it.
|
||||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
for _, job := range jobs {
|
||||||
index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
|
// Skip jobs that are already in a terminal state (completed, cancelled, etc.).
|
||||||
if err != nil {
|
status := job.Status
|
||||||
return err
|
if status.IsDone() {
|
||||||
}
|
continue
|
||||||
run.Index = index
|
|
||||||
run.Title = util.EllipsisDisplayString(run.Title, 255)
|
|
||||||
|
|
||||||
if err := db.Insert(ctx, run); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if run.Repo == nil {
|
// If the job has no associated task (probably an error), set its status to 'Cancelled' and stop it.
|
||||||
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
|
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 {
|
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 {
|
// If the job has an associated task, try to stop the task, effectively cancelling the job.
|
||||||
return err
|
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))
|
// Return nil to indicate successful cancellation of all running and waiting jobs.
|
||||||
var hasWaiting bool
|
return cancelledJobs, nil
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {
|
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 {
|
if err = run.LoadRepo(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
|
if err := UpdateRepoRunsNumbers(ctx, run.Repo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,3 +401,59 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ActionRunIndex db.ResourceIndex
|
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/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
"github.com/nektos/act/pkg/jobparser"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,23 +23,38 @@ type ActionRunJob struct {
|
|||||||
ID int64
|
ID int64
|
||||||
RunID int64 `xorm:"index"`
|
RunID int64 `xorm:"index"`
|
||||||
Run *ActionRun `xorm:"-"`
|
Run *ActionRun `xorm:"-"`
|
||||||
RepoID int64 `xorm:"index"`
|
RepoID int64 `xorm:"index(repo_concurrency)"`
|
||||||
Repo *repo_model.Repository `xorm:"-"`
|
Repo *repo_model.Repository `xorm:"-"`
|
||||||
OwnerID int64 `xorm:"index"`
|
OwnerID int64 `xorm:"index"`
|
||||||
CommitSHA string `xorm:"index"`
|
CommitSHA string `xorm:"index"`
|
||||||
IsForkPullRequest bool
|
IsForkPullRequest bool
|
||||||
Name string `xorm:"VARCHAR(255)"`
|
Name string `xorm:"VARCHAR(255)"`
|
||||||
Attempt int64
|
Attempt int64
|
||||||
WorkflowPayload []byte
|
|
||||||
JobID string `xorm:"VARCHAR(255)"` // job id in workflow, not job's id
|
// WorkflowPayload is act/jobparser.SingleWorkflow for act/jobparser.Parse
|
||||||
Needs []string `xorm:"JSON TEXT"`
|
// it should contain exactly one job with global workflow fields for this model
|
||||||
RunsOn []string `xorm:"JSON TEXT"`
|
WorkflowPayload []byte
|
||||||
TaskID int64 // the latest task of the job
|
|
||||||
Status Status `xorm:"index"`
|
JobID string `xorm:"VARCHAR(255)"` // job id in workflow, not job's id
|
||||||
Started timeutil.TimeStamp
|
Needs []string `xorm:"JSON TEXT"`
|
||||||
Stopped timeutil.TimeStamp
|
RunsOn []string `xorm:"JSON TEXT"`
|
||||||
Created timeutil.TimeStamp `xorm:"created"`
|
TaskID int64 // the latest task of the job
|
||||||
Updated timeutil.TimeStamp `xorm:"updated index"`
|
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() {
|
func init() {
|
||||||
@@ -84,6 +100,24 @@ func (job *ActionRunJob) LoadAttributes(ctx context.Context) error {
|
|||||||
return job.Run.LoadAttributes(ctx)
|
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) {
|
func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) {
|
||||||
var job ActionRunJob
|
var job ActionRunJob
|
||||||
has, err := db.GetEngine(ctx).Where("id=?", id).Get(&job)
|
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
|
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 the status of job changes to waiting again, increase tasks version.
|
||||||
if err := IncreaseTaskVersion(ctx, job.OwnerID, job.RepoID); err != nil {
|
if err := IncreaseTaskVersion(ctx, job.OwnerID, job.RepoID); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -197,3 +231,39 @@ func AggregateJobStatus(jobs []*ActionRunJob) Status {
|
|||||||
return StatusUnknown // it shouldn't happen
|
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 {
|
type FindRunJobOptions struct {
|
||||||
db.ListOptions
|
db.ListOptions
|
||||||
RunID int64
|
RunID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
OwnerID int64
|
OwnerID int64
|
||||||
CommitSHA string
|
CommitSHA string
|
||||||
Statuses []Status
|
Statuses []Status
|
||||||
UpdatedBefore timeutil.TimeStamp
|
UpdatedBefore timeutil.TimeStamp
|
||||||
|
ConcurrencyGroup string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts FindRunJobOptions) ToConds() builder.Cond {
|
func (opts FindRunJobOptions) ToConds() builder.Cond {
|
||||||
@@ -94,6 +95,12 @@ func (opts FindRunJobOptions) ToConds() builder.Cond {
|
|||||||
if opts.UpdatedBefore > 0 {
|
if opts.UpdatedBefore > 0 {
|
||||||
cond = cond.And(builder.Lt{"`action_run_job`.updated": opts.UpdatedBefore})
|
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
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,15 +64,16 @@ func (runs RunList) LoadRepos(ctx context.Context) error {
|
|||||||
|
|
||||||
type FindRunOptions struct {
|
type FindRunOptions struct {
|
||||||
db.ListOptions
|
db.ListOptions
|
||||||
RepoID int64
|
RepoID int64
|
||||||
OwnerID int64
|
OwnerID int64
|
||||||
WorkflowID string
|
WorkflowID string
|
||||||
Ref string // the commit/tag/… that caused this workflow
|
Ref string // the commit/tag/… that caused this workflow
|
||||||
TriggerUserID int64
|
TriggerUserID int64
|
||||||
TriggerEvent webhook_module.HookEventType
|
TriggerEvent webhook_module.HookEventType
|
||||||
Approved bool // not util.OptionalBool, it works only when it's true
|
Approved bool // not util.OptionalBool, it works only when it's true
|
||||||
Status []Status
|
Status []Status
|
||||||
CommitSHA string
|
ConcurrencyGroup string
|
||||||
|
CommitSHA string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts FindRunOptions) ToConds() builder.Cond {
|
func (opts FindRunOptions) ToConds() builder.Cond {
|
||||||
@@ -101,6 +102,12 @@ func (opts FindRunOptions) ToConds() builder.Cond {
|
|||||||
if opts.CommitSHA != "" {
|
if opts.CommitSHA != "" {
|
||||||
cond = cond.And(builder.Eq{"`action_run`.commit_sha": 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
|
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"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/shared/types"
|
"code.gitea.io/gitea/models/shared/types"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
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/optional"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
@@ -173,6 +174,13 @@ func (r *ActionRunner) GenerateToken() (err error) {
|
|||||||
return err
|
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() {
|
func init() {
|
||||||
db.RegisterModel(&ActionRunner{})
|
db.RegisterModel(&ActionRunner{})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/modules/container"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
@@ -21,7 +20,6 @@ import (
|
|||||||
|
|
||||||
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
|
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
|
||||||
lru "github.com/hashicorp/golang-lru/v2"
|
lru "github.com/hashicorp/golang-lru/v2"
|
||||||
"github.com/nektos/act/pkg/jobparser"
|
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
@@ -246,7 +244,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
|
|||||||
var job *ActionRunJob
|
var job *ActionRunJob
|
||||||
log.Trace("runner labels: %v", runner.AgentLabels)
|
log.Trace("runner labels: %v", runner.AgentLabels)
|
||||||
for _, v := range jobs {
|
for _, v := range jobs {
|
||||||
if isSubset(runner.AgentLabels, v.RunsOn) {
|
if runner.CanMatchLabels(v.RunsOn) {
|
||||||
job = v
|
job = v
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -278,13 +276,10 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
|
|||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedWorkflows, err := jobparser.Parse(job.WorkflowPayload)
|
workflowJob, err := job.ParseJob()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, fmt.Errorf("parse workflow of job %d: %w", job.ID, err)
|
return nil, false, fmt.Errorf("load 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)
|
|
||||||
}
|
}
|
||||||
_, workflowJob := parsedWorkflows[0].Job()
|
|
||||||
|
|
||||||
if _, err := e.Insert(task); err != nil {
|
if _, err := e.Insert(task); err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
@@ -479,20 +474,6 @@ func FindOldTasksToExpire(ctx context.Context, olderThan timeutil.TimeStamp, lim
|
|||||||
Find(&tasks)
|
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 {
|
func convertTimestamp(timestamp *timestamppb.Timestamp) timeutil.TimeStamp {
|
||||||
if timestamp.GetSeconds() == 0 && timestamp.GetNanos() == 0 {
|
if timestamp.GetSeconds() == 0 && timestamp.GetNanos() == 0 {
|
||||||
return timeutil.TimeStamp(0)
|
return timeutil.TimeStamp(0)
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ func SetNotificationStatus(ctx context.Context, notificationID int64, user *user
|
|||||||
|
|
||||||
notification.Status = status
|
notification.Status = status
|
||||||
|
|
||||||
_, err = db.GetEngine(ctx).ID(notificationID).Update(notification)
|
_, err = db.GetEngine(ctx).ID(notificationID).Cols("status").Update(notification)
|
||||||
return notification, err
|
return notification, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/migration"
|
"code.gitea.io/gitea/modules/migration"
|
||||||
"code.gitea.io/gitea/modules/secret"
|
"code.gitea.io/gitea/modules/secret"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@@ -123,17 +124,17 @@ func (task *Task) MigrateConfig() (*migration.MigrateOptions, error) {
|
|||||||
// decrypt credentials
|
// decrypt credentials
|
||||||
if opts.CloneAddrEncrypted != "" {
|
if opts.CloneAddrEncrypted != "" {
|
||||||
if opts.CloneAddr, err = secret.DecryptSecret(setting.SecretKey, opts.CloneAddrEncrypted); err != nil {
|
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.AuthPasswordEncrypted != "" {
|
||||||
if opts.AuthPassword, err = secret.DecryptSecret(setting.SecretKey, opts.AuthPasswordEncrypted); err != nil {
|
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.AuthTokenEncrypted != "" {
|
||||||
if opts.AuthToken, err = secret.DecryptSecret(setting.SecretKey, opts.AuthTokenEncrypted); err != nil {
|
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
|
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
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,13 +67,6 @@ func (key *PublicKey) OmitEmail() string {
|
|||||||
return strings.Join(strings.Split(key.Content, " ")[:2], " ")
|
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) {
|
func addKey(ctx context.Context, key *PublicKey) (err error) {
|
||||||
if len(key.Fingerprint) == 0 {
|
if len(key.Fingerprint) == 0 {
|
||||||
key.Fingerprint, err = CalcFingerprint(key.Content)
|
key.Fingerprint, err = CalcFingerprint(key.Content)
|
||||||
|
|||||||
@@ -17,29 +17,13 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
// _____ __ .__ .__ .___
|
// 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`
|
||||||
// / | \ | /| | | 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"
|
|
||||||
)
|
|
||||||
|
|
||||||
var sshOpLocker sync.Mutex
|
var sshOpLocker sync.Mutex
|
||||||
|
|
||||||
@@ -50,17 +34,45 @@ func WithSSHOpLocker(f func() error) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizedStringForKey creates the authorized keys string appropriate for the provided key
|
// 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{}
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 %s` + "\n"
|
||||||
|
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key.Content))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// 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),
|
"AppPath": util.ShellEscape(setting.AppPath),
|
||||||
"AppWorkPath": util.ShellEscape(setting.AppWorkPath),
|
"AppWorkPath": util.ShellEscape(setting.AppWorkPath),
|
||||||
"CustomConf": util.ShellEscape(setting.CustomConf),
|
"CustomConf": util.ShellEscape(setting.CustomConf),
|
||||||
"CustomPath": util.ShellEscape(setting.CustomPath),
|
"CustomPath": util.ShellEscape(setting.CustomPath),
|
||||||
"Key": key,
|
"Key": key,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
return fmt.Sprintf(tplPublicKey, util.ShellEscape(sb.String()), key.Content)
|
return true, err
|
||||||
|
}
|
||||||
|
sshCommandEscaped := util.ShellEscape(sbCmd.String())
|
||||||
|
sshKeyMarshalled := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(pubKey)))
|
||||||
|
sshKeyComment := fmt.Sprintf("user-%d", key.OwnerID)
|
||||||
|
_, err = fmt.Fprintf(w, tpl, sshCommandEscaped, sshKeyMarshalled, sshKeyComment)
|
||||||
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file.
|
// appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file.
|
||||||
@@ -112,7 +124,7 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
|
|||||||
if key.Type == KeyTypePrincipal {
|
if key.Type == KeyTypePrincipal {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, err = f.WriteString(key.AuthorizedString()); err != nil {
|
if err = WriteAuthorizedStringForValidKey(key, f); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,10 +132,9 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RegeneratePublicKeys regenerates the authorized_keys file
|
// 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) {
|
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 WriteAuthorizedStringForValidKey(bean.(*PublicKey), t)
|
||||||
return err
|
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -144,11 +155,11 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
|
|||||||
scanner := bufio.NewScanner(f)
|
scanner := bufio.NewScanner(f)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
if strings.HasPrefix(line, tplCommentPrefix) {
|
if strings.HasPrefix(line, AuthorizedStringCommentPrefix) {
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, err = t.WriteString(line + "\n")
|
_, err = io.WriteString(t, line+"\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,11 +111,11 @@ func (t *TwoFactor) SetSecret(secretString string) error {
|
|||||||
func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
|
func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
|
||||||
decodedStoredSecret, err := base64.StdEncoding.DecodeString(t.Secret)
|
decodedStoredSecret, err := base64.StdEncoding.DecodeString(t.Secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, fmt.Errorf("ValidateTOTP invalid base64: %w", err)
|
||||||
}
|
}
|
||||||
secretBytes, err := secret.AesDecrypt(t.getEncryptionKey(), decodedStoredSecret)
|
secretBytes, err := secret.AesDecrypt(t.getEncryptionKey(), decodedStoredSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, fmt.Errorf("ValidateTOTP unable to decrypt (maybe SECRET_KEY is wrong): %w", err)
|
||||||
}
|
}
|
||||||
secretStr := string(secretBytes)
|
secretStr := string(secretBytes)
|
||||||
return totp.Validate(passcode, secretStr), nil
|
return totp.Validate(passcode, secretStr), nil
|
||||||
|
|||||||
@@ -139,3 +139,43 @@
|
|||||||
updated: 1683636626
|
updated: 1683636626
|
||||||
need_approval: 0
|
need_approval: 0
|
||||||
approved_by: 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
|
status: 5
|
||||||
started: 1683636528
|
started: 1683636528
|
||||||
stopped: 1683636626
|
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_length: 0
|
||||||
log_size: 0
|
log_size: 0
|
||||||
log_expired: 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
|
is_deleted: false
|
||||||
deleted_by_id: 0
|
deleted_by_id: 0
|
||||||
deleted_unix: 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
|
type: 3
|
||||||
config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowRebaseMerge\":true,\"AllowSquash\":true}"
|
config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowRebaseMerge\":true,\"AllowSquash\":true}"
|
||||||
created_unix: 946684810
|
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_closed_milestones: 0
|
||||||
num_projects: 0
|
num_projects: 0
|
||||||
num_closed_projects: 1
|
num_closed_projects: 1
|
||||||
|
num_action_runs: 4
|
||||||
|
num_closed_action_runs: 3
|
||||||
is_private: false
|
is_private: false
|
||||||
is_empty: false
|
is_empty: false
|
||||||
is_archived: 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
|
// 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,
|
Name: to,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -30,17 +30,21 @@ import (
|
|||||||
|
|
||||||
// CommitStatus holds a single Status of a single Commit
|
// CommitStatus holds a single Status of a single Commit
|
||||||
type CommitStatus struct {
|
type CommitStatus struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
Index int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
|
Index int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
|
||||||
RepoID int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
|
RepoID int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
|
||||||
Repo *repo_model.Repository `xorm:"-"`
|
Repo *repo_model.Repository `xorm:"-"`
|
||||||
State commitstatus.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
|
State commitstatus.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
|
||||||
SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
|
SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
|
||||||
TargetURL string `xorm:"TEXT"`
|
|
||||||
Description string `xorm:"TEXT"`
|
// TargetURL points to the commit status page reported by a CI system
|
||||||
ContextHash string `xorm:"VARCHAR(64) index"`
|
// If Gitea Actions is used, it is a relative link like "{RepoLink}/actions/runs/{RunID}/jobs{JobID}"
|
||||||
Context string `xorm:"TEXT"`
|
TargetURL string `xorm:"TEXT"`
|
||||||
Creator *user_model.User `xorm:"-"`
|
|
||||||
|
Description string `xorm:"TEXT"`
|
||||||
|
ContextHash string `xorm:"VARCHAR(64) index"`
|
||||||
|
Context string `xorm:"TEXT"`
|
||||||
|
Creator *user_model.User `xorm:"-"`
|
||||||
CreatorID int64
|
CreatorID int64
|
||||||
|
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
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
|
// HideActionsURL set `TargetURL` to an empty string if the status comes from Gitea Actions
|
||||||
func (status *CommitStatus) HideActionsURL(ctx context.Context) {
|
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 {
|
if status.RepoID == 0 {
|
||||||
return
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.Repo == nil {
|
if status.Repo == nil {
|
||||||
if err := status.loadRepository(ctx); err != nil {
|
if err := status.loadRepository(ctx); err != nil {
|
||||||
log.Error("loadRepository: %v", err)
|
log.Error("loadRepository: %v", err)
|
||||||
return
|
return "", false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix := status.Repo.Link() + "/actions"
|
prefix := status.Repo.Link() + "/actions"
|
||||||
if strings.HasPrefix(status.TargetURL, prefix) {
|
return strings.CutPrefix(status.TargetURL, prefix)
|
||||||
status.TargetURL = ""
|
}
|
||||||
|
|
||||||
|
// 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
|
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/perm"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
@@ -42,30 +41,6 @@ func (err ErrLFSLockNotExist) Unwrap() error {
|
|||||||
return util.ErrNotExist
|
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.
|
// ErrLFSLockAlreadyExist represents a "LFSLockAlreadyExist" kind of error.
|
||||||
type ErrLFSLockAlreadyExist struct {
|
type ErrLFSLockAlreadyExist struct {
|
||||||
RepoID int64
|
RepoID int64
|
||||||
@@ -93,12 +68,6 @@ type ErrLFSFileLocked struct {
|
|||||||
UserName string
|
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 {
|
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)
|
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"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"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"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
@@ -71,10 +68,6 @@ func (l *LFSLock) LoadOwner(ctx context.Context) error {
|
|||||||
// CreateLFSLock creates a new lock.
|
// CreateLFSLock creates a new lock.
|
||||||
func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
|
func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
|
||||||
return db.WithTx2(ctx, func(ctx context.Context) (*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.Path = util.PathJoinRel(lock.Path)
|
||||||
lock.RepoID = repo.ID
|
lock.RepoID = repo.ID
|
||||||
|
|
||||||
@@ -165,10 +158,6 @@ func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repositor
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := CheckLFSAccessForRepo(ctx, u.ID, repo, perm.AccessModeWrite); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !force && u.ID != lock.OwnerID {
|
if !force && u.ID != lock.OwnerID {
|
||||||
return nil, errors.New("user doesn't own lock and force flag is not set")
|
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
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ package git
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -25,7 +24,7 @@ import (
|
|||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrBranchIsProtected = errors.New("branch is protected")
|
var ErrBranchIsProtected = util.ErrorWrap(util.ErrPermissionDenied, "branch is protected")
|
||||||
|
|
||||||
// ProtectedBranch struct
|
// ProtectedBranch struct
|
||||||
type ProtectedBranch struct {
|
type ProtectedBranch struct {
|
||||||
@@ -467,11 +466,13 @@ func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, c
|
|||||||
return currentWhitelist, nil
|
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))
|
whitelist = make([]int64, 0, len(newWhitelist))
|
||||||
for _, userID := range newWhitelist {
|
for _, userID := range newWhitelist {
|
||||||
if reader, err := access_model.IsRepoReader(ctx, repo, userID); err != nil {
|
if !prUserIDs.Contains(userID) {
|
||||||
return nil, err
|
|
||||||
} else if !reader {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
whitelist = append(whitelist, userID)
|
whitelist = append(whitelist, userID)
|
||||||
|
|||||||
@@ -862,10 +862,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
|
|||||||
if err = UpdateCommentAttachments(ctx, comment, opts.Attachments); err != nil {
|
if err = UpdateCommentAttachments(ctx, comment, opts.Attachments); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case CommentTypeReopen, CommentTypeClose:
|
// comment type reopen and close event have their own logic to update numbers but not here
|
||||||
if err = repo_model.UpdateRepoIssueNumbers(ctx, opts.Issue.RepoID, opts.Issue.IsPull, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// update the issue's updated_unix column
|
// update the issue's updated_unix column
|
||||||
return UpdateIssueCols(ctx, opts.Issue, "updated_unix")
|
return UpdateIssueCols(ctx, opts.Issue, "updated_unix")
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ func applySubscribedCondition(sess *xorm.Session, subscriberID int64) {
|
|||||||
),
|
),
|
||||||
builder.Eq{"issue.poster_id": subscriberID},
|
builder.Eq{"issue.poster_id": subscriberID},
|
||||||
builder.In("issue.repo_id", builder.
|
builder.In("issue.repo_id", builder.
|
||||||
Select("id").
|
Select("repo_id").
|
||||||
From("watch").
|
From("watch").
|
||||||
Where(builder.And(builder.Eq{"user_id": subscriberID},
|
Where(builder.And(builder.Eq{"user_id": subscriberID},
|
||||||
builder.In("mode", repo_model.WatchModeNormal, repo_model.WatchModeAuto))),
|
builder.In("mode", repo_model.WatchModeNormal, repo_model.WatchModeAuto))),
|
||||||
|
|||||||
@@ -197,6 +197,12 @@ func TestIssues(t *testing.T) {
|
|||||||
},
|
},
|
||||||
[]int64{2},
|
[]int64{2},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
issues_model.IssuesOptions{
|
||||||
|
SubscriberID: 11,
|
||||||
|
},
|
||||||
|
[]int64{11, 5, 9, 8, 3, 2, 1},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
issues, err := issues_model.Issues(t.Context(), &test.Opts)
|
issues, err := issues_model.Issues(t.Context(), &test.Opts)
|
||||||
assert.NoError(t, err)
|
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
|
// update repository's issue closed number
|
||||||
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil {
|
switch cmtType {
|
||||||
return nil, err
|
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{
|
return CreateComment(ctx, &CreateCommentOptions{
|
||||||
@@ -318,7 +329,6 @@ type NewIssueOptions struct {
|
|||||||
Issue *Issue
|
Issue *Issue
|
||||||
LabelIDs []int64
|
LabelIDs []int64
|
||||||
Attachments []string // In UUID format.
|
Attachments []string // In UUID format.
|
||||||
IsPull bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIssueWithIndex creates issue with given index
|
// 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
|
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.
|
// UpdateIssueMentions updates issue-user relations for mentioned users.
|
||||||
func UpdateIssueMentions(ctx context.Context, issueID int64, mentions []*user_model.User) error {
|
func UpdateIssueMentions(ctx context.Context, issueID int64, mentions []*user_model.User) error {
|
||||||
if len(mentions) == 0 {
|
if len(mentions) == 0 {
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ func updateMilestone(ctx context.Context, m *Milestone) error {
|
|||||||
func UpdateMilestoneCounters(ctx context.Context, id int64) error {
|
func UpdateMilestoneCounters(ctx context.Context, id int64) error {
|
||||||
e := db.GetEngine(ctx)
|
e := db.GetEngine(ctx)
|
||||||
_, err := e.ID(id).
|
_, err := e.ID(id).
|
||||||
|
Cols("num_issues", "num_closed_issues").
|
||||||
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
||||||
builder.Eq{"milestone_id": id},
|
builder.Eq{"milestone_id": id},
|
||||||
)).
|
)).
|
||||||
|
|||||||
@@ -417,10 +417,6 @@ func (pr *PullRequest) GetGitHeadRefName() string {
|
|||||||
return fmt.Sprintf("%s%d/head", git.PullPrefix, pr.Index)
|
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)
|
// 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 {
|
func (pr *PullRequest) GetReviewCommentsCount(ctx context.Context) int {
|
||||||
opts := FindCommentsOptions{
|
opts := FindCommentsOptions{
|
||||||
@@ -471,13 +467,13 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Iss
|
|||||||
|
|
||||||
issue.Index = idx
|
issue.Index = idx
|
||||||
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
||||||
|
issue.IsPull = true
|
||||||
|
|
||||||
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
|
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
LabelIDs: labelIDs,
|
LabelIDs: labelIDs,
|
||||||
Attachments: uuids,
|
Attachments: uuids,
|
||||||
IsPull: true,
|
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
|
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
|
||||||
return 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
|
// UpdateColsIfNotMerged updates specific fields of a pull request if it has not been merged
|
||||||
func (pr *PullRequest) UpdateColsIfNotMerged(ctx context.Context, cols ...string) error {
|
func (pr *PullRequest) UpdateColsIfNotMerged(ctx context.Context, cols ...string) (int64, error) {
|
||||||
_, err := db.GetEngine(ctx).Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr)
|
return db.GetEngine(ctx).Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsWorkInProgress determine if the Pull Request is a Work In Progress by its title
|
// IsWorkInProgress determine if the Pull Request is a Work In Progress by its title
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ func GetReviewsByIssueID(ctx context.Context, issueID int64) (latestReviews, mig
|
|||||||
reviewersMap := make(map[int64][]*Review) // key is reviewer id
|
reviewersMap := make(map[int64][]*Review) // key is reviewer id
|
||||||
originalReviewersMap := make(map[int64][]*Review) // key is original author id
|
originalReviewersMap := make(map[int64][]*Review) // key is original author id
|
||||||
reviewTeamsMap := make(map[int64][]*Review) // key is reviewer team 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 {
|
for _, review := range reviews {
|
||||||
if review.ReviewerTeamID == 0 && slices.Contains(countedReivewTypes, review.Type) && !review.Dismissed {
|
if review.ReviewerTeamID == 0 && slices.Contains(countedReivewTypes, review.Type) && !review.Dismissed {
|
||||||
if review.OriginalAuthorID != 0 {
|
if review.OriginalAuthorID != 0 {
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
|||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3})
|
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})
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||||
@@ -129,6 +130,12 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
|||||||
|
|
||||||
expectedReviews := []*issues_model.Review{}
|
expectedReviews := []*issues_model.Review{}
|
||||||
expectedReviews = append(expectedReviews,
|
expectedReviews = append(expectedReviews,
|
||||||
|
&issues_model.Review{
|
||||||
|
ID: 5,
|
||||||
|
Reviewer: user1,
|
||||||
|
Type: issues_model.ReviewTypeComment,
|
||||||
|
UpdatedUnix: 946684810,
|
||||||
|
},
|
||||||
&issues_model.Review{
|
&issues_model.Review{
|
||||||
ID: 7,
|
ID: 7,
|
||||||
Reviewer: org3,
|
Reviewer: org3,
|
||||||
@@ -167,8 +174,9 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
|||||||
for _, review := range allReviews {
|
for _, review := range allReviews {
|
||||||
assert.NoError(t, review.LoadReviewer(t.Context()))
|
assert.NoError(t, review.LoadReviewer(t.Context()))
|
||||||
}
|
}
|
||||||
if assert.Len(t, allReviews, 5) {
|
if assert.Len(t, allReviews, 6) {
|
||||||
for i, review := range allReviews {
|
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].Reviewer, review.Reviewer)
|
||||||
assert.Equal(t, expectedReviews[i].Type, review.Type)
|
assert.Equal(t, expectedReviews[i].Type, review.Type)
|
||||||
assert.Equal(t, expectedReviews[i].UpdatedUnix, review.UpdatedUnix)
|
assert.Equal(t, expectedReviews[i].UpdatedUnix, review.UpdatedUnix)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FIXME: this file shouldn't be in a normal package, it should only be compiled for tests
|
// 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
|
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) {
|
func MainTest(m *testing.M) {
|
||||||
testlogger.Init()
|
testlogger.Init()
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
"code.gitea.io/gitea/models/migrations/v1_23"
|
"code.gitea.io/gitea/models/migrations/v1_23"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_24"
|
"code.gitea.io/gitea/models/migrations/v1_24"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_25"
|
"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_6"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_7"
|
"code.gitea.io/gitea/models/migrations/v1_7"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_8"
|
"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(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices),
|
||||||
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
|
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
|
||||||
newMigration(311, "Add TimeEstimate to Issue table", v1_23.AddTimeEstimateColumnToIssueTable),
|
newMigration(311, "Add TimeEstimate to Issue table", v1_23.AddTimeEstimateColumnToIssueTable),
|
||||||
|
|
||||||
// Gitea 1.23.0-rc0 ends at migration ID number 311 (database version 312)
|
// Gitea 1.23.0-rc0 ends at migration ID number 311 (database version 312)
|
||||||
|
|
||||||
newMigration(312, "Add DeleteBranchAfterMerge to AutoMerge", v1_24.AddDeleteBranchAfterMergeForAutoMerge),
|
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(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),
|
newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables),
|
||||||
@@ -390,10 +391,13 @@ func prepareMigrationTasks() []*migration {
|
|||||||
newMigration(318, "Add anonymous_access_mode for repo_unit", v1_24.AddRepoUnitAnonymousAccessMode),
|
newMigration(318, "Add anonymous_access_mode for repo_unit", v1_24.AddRepoUnitAnonymousAccessMode),
|
||||||
newMigration(319, "Add ExclusiveOrder to Label table", v1_24.AddExclusiveOrderColumnToLabelTable),
|
newMigration(319, "Add ExclusiveOrder to Label table", v1_24.AddExclusiveOrderColumnToLabelTable),
|
||||||
newMigration(320, "Migrate two_factor_policy to login_source table", v1_24.MigrateSkipTwoFactor),
|
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(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),
|
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),
|
||||||
}
|
}
|
||||||
return preparedMigrations
|
return preparedMigrations
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,17 +84,17 @@ func FixMergeBase(ctx context.Context, x *xorm.Engine) error {
|
|||||||
|
|
||||||
if !pr.HasMerged {
|
if !pr.HasMerged {
|
||||||
var err error
|
var err error
|
||||||
pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
|
pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).WithDir(repoPath).RunStdString(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var err2 error
|
var err2 error
|
||||||
pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
|
pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).WithDir(repoPath).RunStdString(ctx)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2)
|
log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
|
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithDir(repoPath).RunStdString(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||||
continue
|
continue
|
||||||
@@ -108,7 +108,7 @@ func FixMergeBase(ctx context.Context, x *xorm.Engine) error {
|
|||||||
refs = append(refs, gitRefName)
|
refs = append(refs, gitRefName)
|
||||||
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
|
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
|
||||||
|
|
||||||
pr.MergeBase, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
|
pr.MergeBase, _, err = cmd.WithDir(repoPath).RunStdString(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func RefixMergeBase(ctx context.Context, x *xorm.Engine) error {
|
|||||||
|
|
||||||
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
||||||
|
|
||||||
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
|
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithDir(repoPath).RunStdString(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||||
continue
|
continue
|
||||||
@@ -95,7 +95,7 @@ func RefixMergeBase(ctx context.Context, x *xorm.Engine) error {
|
|||||||
refs = append(refs, gitRefName)
|
refs = append(refs, gitRefName)
|
||||||
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
|
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
|
||||||
|
|
||||||
pr.MergeBase, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
|
pr.MergeBase, _, err = cmd.WithDir(repoPath).RunStdString(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ package v1_12
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@@ -85,12 +84,9 @@ func AddCommitDivergenceToPulls(x *xorm.Engine) error {
|
|||||||
log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID)
|
log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
userPath := filepath.Join(setting.RepoRootPath, strings.ToLower(baseRepo.OwnerName))
|
repoStore := repo_model.StorageRepo(repo_model.RelativePath(baseRepo.OwnerName, baseRepo.Name))
|
||||||
repoPath := filepath.Join(userPath, strings.ToLower(baseRepo.Name)+".git")
|
|
||||||
|
|
||||||
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
||||||
|
divergence, err := gitrepo.GetDivergingCommits(graceful.GetManager().HammerContext(), repoStore, pr.BaseBranch, gitRefName)
|
||||||
divergence, err := git.GetDivergingCommits(graceful.GetManager().HammerContext(), repoPath, pr.BaseBranch, gitRefName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Could not recalculate Divergence for pull: %d", pr.ID)
|
log.Warn("Could not recalculate Divergence for pull: %d", pr.ID)
|
||||||
pr.CommitsAhead = 0
|
pr.CommitsAhead = 0
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ func UpdateOpenMilestoneCounts(x *xorm.Engine) error {
|
|||||||
|
|
||||||
for _, id := range openMilestoneIDs {
|
for _, id := range openMilestoneIDs {
|
||||||
_, err := x.ID(id).
|
_, err := x.ID(id).
|
||||||
|
Cols("num_issues", "num_closed_issues").
|
||||||
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
||||||
builder.Eq{"milestone_id": id},
|
builder.Eq{"milestone_id": id},
|
||||||
)).
|
)).
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_UseLongTextInSomeColumnsAndFixBugs(t *testing.T) {
|
func Test_UseLongTextInSomeColumnsAndFixBugs(t *testing.T) {
|
||||||
@@ -38,33 +39,26 @@ func Test_UseLongTextInSomeColumnsAndFixBugs(t *testing.T) {
|
|||||||
type Notice struct {
|
type Notice struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
Type int
|
Type int
|
||||||
Description string `xorm:"LONGTEXT"`
|
Description string `xorm:"TEXT"`
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare and load the testing database
|
// Prepare and load the testing database
|
||||||
x, deferable := base.PrepareTestEnv(t, 0, new(ReviewState), new(PackageProperty), new(Notice))
|
x, deferrable := base.PrepareTestEnv(t, 0, new(ReviewState), new(PackageProperty), new(Notice))
|
||||||
defer deferable()
|
defer deferrable()
|
||||||
|
|
||||||
assert.NoError(t, UseLongTextInSomeColumnsAndFixBugs(x))
|
require.NoError(t, UseLongTextInSomeColumnsAndFixBugs(x))
|
||||||
|
|
||||||
tables, err := x.DBMetas()
|
tables := base.LoadTableSchemasMap(t, x)
|
||||||
assert.NoError(t, err)
|
table := tables["review_state"]
|
||||||
|
column := table.GetColumn("updated_files")
|
||||||
|
assert.Equal(t, "LONGTEXT", column.SQLType.Name)
|
||||||
|
|
||||||
for _, table := range tables {
|
table = tables["package_property"]
|
||||||
switch table.Name {
|
column = table.GetColumn("value")
|
||||||
case "review_state":
|
assert.Equal(t, "LONGTEXT", column.SQLType.Name)
|
||||||
column := table.GetColumn("updated_files")
|
|
||||||
assert.NotNil(t, column)
|
table = tables["notice"]
|
||||||
assert.Equal(t, "LONGTEXT", column.SQLType.Name)
|
column = table.GetColumn("description")
|
||||||
case "package_property":
|
assert.Equal(t, "LONGTEXT", column.SQLType.Name)
|
||||||
column := table.GetColumn("value")
|
|
||||||
assert.NotNil(t, column)
|
|
||||||
assert.Equal(t, "LONGTEXT", column.SQLType.Name)
|
|
||||||
case "notice":
|
|
||||||
column := table.GetColumn("description")
|
|
||||||
assert.NotNil(t, column)
|
|
||||||
assert.Equal(t, "LONGTEXT", column.SQLType.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
34
models/migrations/v1_25/v322_test.go
Normal file
34
models/migrations/v1_25/v322_test.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_25
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/migrations/base"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ExtendCommentTreePathLength(t *testing.T) {
|
||||||
|
if setting.Database.Type.IsSQLite3() {
|
||||||
|
t.Skip("For SQLITE, varchar or char will always be represented as TEXT")
|
||||||
|
}
|
||||||
|
|
||||||
|
type Comment struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
TreePath string `xorm:"VARCHAR(255)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
x, deferrable := base.PrepareTestEnv(t, 0, new(Comment))
|
||||||
|
defer deferrable()
|
||||||
|
|
||||||
|
require.NoError(t, ExtendCommentTreePathLength(x))
|
||||||
|
table := base.LoadTableSchemasMap(t, x)["comment"]
|
||||||
|
column := table.GetColumn("tree_path")
|
||||||
|
assert.Contains(t, []string{"NVARCHAR", "VARCHAR"}, column.SQLType.Name)
|
||||||
|
assert.EqualValues(t, 4000, column.Length)
|
||||||
|
}
|
||||||
14
models/migrations/v1_26/main_test.go
Normal file
14
models/migrations/v1_26/main_test.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_26
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/migrations/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
base.MainTest(m)
|
||||||
|
}
|
||||||
43
models/migrations/v1_26/v323.go
Normal file
43
models/migrations/v1_26/v323.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_26
|
||||||
|
|
||||||
|
import (
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddActionsConcurrency(x *xorm.Engine) error {
|
||||||
|
type ActionRun struct {
|
||||||
|
RepoID int64 `xorm:"index(repo_concurrency)"`
|
||||||
|
RawConcurrency string
|
||||||
|
ConcurrencyGroup string `xorm:"index(repo_concurrency) NOT NULL DEFAULT ''"`
|
||||||
|
ConcurrencyCancel bool `xorm:"NOT NULL DEFAULT FALSE"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := x.SyncWithOptions(xorm.SyncOptions{
|
||||||
|
IgnoreDropIndices: true,
|
||||||
|
}, new(ActionRun)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := x.Sync(new(ActionRun)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionRunJob struct {
|
||||||
|
RepoID int64 `xorm:"index(repo_concurrency)"`
|
||||||
|
RawConcurrency string
|
||||||
|
IsConcurrencyEvaluated bool
|
||||||
|
ConcurrencyGroup string `xorm:"index(repo_concurrency) NOT NULL DEFAULT ''"`
|
||||||
|
ConcurrencyCancel bool `xorm:"NOT NULL DEFAULT FALSE"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := x.SyncWithOptions(xorm.SyncOptions{
|
||||||
|
IgnoreDropIndices: true,
|
||||||
|
}, new(ActionRunJob)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -429,6 +429,10 @@ func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User)
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !setting.Service.RequireSignInViewStrict && orgOrUser.Visibility == structs.VisibleTypePublic {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(ctx, user.ID) {
|
if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(ctx, user.ID) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import (
|
|||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -382,6 +384,12 @@ func TestHasOrgVisibleTypePublic(t *testing.T) {
|
|||||||
assert.True(t, test1) // owner of org
|
assert.True(t, test1) // owner of org
|
||||||
assert.True(t, test2) // user not a part of org
|
assert.True(t, test2) // user not a part of org
|
||||||
assert.True(t, test3) // logged out user
|
assert.True(t, test3) // logged out user
|
||||||
|
|
||||||
|
restrictedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29, IsRestricted: true})
|
||||||
|
require.True(t, restrictedUser.IsRestricted)
|
||||||
|
assert.True(t, organization.HasOrgOrUserVisible(t.Context(), org.AsUser(), restrictedUser))
|
||||||
|
defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()
|
||||||
|
assert.False(t, organization.HasOrgOrUserVisible(t.Context(), org.AsUser(), restrictedUser))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHasOrgVisibleTypeLimited(t *testing.T) {
|
func TestHasOrgVisibleTypeLimited(t *testing.T) {
|
||||||
|
|||||||
@@ -53,24 +53,45 @@ func RemoveTeamRepo(ctx context.Context, teamID, repoID int64) error {
|
|||||||
// GetTeamsWithAccessToAnyRepoUnit returns all teams in an organization that have given access level to the repository special unit.
|
// GetTeamsWithAccessToAnyRepoUnit returns all teams in an organization that have given access level to the repository special unit.
|
||||||
// This function is only used for finding some teams that can be used as branch protection allowlist or reviewers, it isn't really used for access control.
|
// This function is only used for finding some teams that can be used as branch protection allowlist or reviewers, it isn't really used for access control.
|
||||||
// FIXME: TEAM-UNIT-PERMISSION this logic is not complete, search the fixme keyword to see more details
|
// FIXME: TEAM-UNIT-PERMISSION this logic is not complete, search the fixme keyword to see more details
|
||||||
func GetTeamsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) ([]*Team, error) {
|
func GetTeamsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) (teams []*Team, err error) {
|
||||||
teams := make([]*Team, 0, 5)
|
teamIDs, err := getTeamIDsWithAccessToAnyRepoUnit(ctx, orgID, repoID, mode, unitType, unitTypesMore...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(teamIDs) == 0 {
|
||||||
|
return teams, nil
|
||||||
|
}
|
||||||
|
err = db.GetEngine(ctx).Where(builder.In("id", teamIDs)).OrderBy("team.name").Find(&teams)
|
||||||
|
return teams, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTeamIDsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) (teamIDs []int64, err error) {
|
||||||
sub := builder.Select("team_id").From("team_unit").
|
sub := builder.Select("team_id").From("team_unit").
|
||||||
Where(builder.Expr("team_unit.team_id = team.id")).
|
Where(builder.Expr("team_unit.team_id = team.id")).
|
||||||
And(builder.In("team_unit.type", append([]unit.Type{unitType}, unitTypesMore...))).
|
And(builder.In("team_unit.type", append([]unit.Type{unitType}, unitTypesMore...))).
|
||||||
And(builder.Expr("team_unit.access_mode >= ?", mode))
|
And(builder.Expr("team_unit.access_mode >= ?", mode))
|
||||||
|
|
||||||
err := db.GetEngine(ctx).
|
err = db.GetEngine(ctx).
|
||||||
|
Select("team.id").
|
||||||
|
Table("team").
|
||||||
Join("INNER", "team_repo", "team_repo.team_id = team.id").
|
Join("INNER", "team_repo", "team_repo.team_id = team.id").
|
||||||
And("team_repo.org_id = ?", orgID).
|
And("team_repo.org_id = ? AND team_repo.repo_id = ?", orgID, repoID).
|
||||||
And("team_repo.repo_id = ?", repoID).
|
|
||||||
And(builder.Or(
|
And(builder.Or(
|
||||||
builder.Expr("team.authorize >= ?", mode),
|
builder.Expr("team.authorize >= ?", mode),
|
||||||
builder.In("team.id", sub),
|
builder.In("team.id", sub),
|
||||||
)).
|
)).
|
||||||
OrderBy("name").
|
Find(&teamIDs)
|
||||||
Find(&teams)
|
return teamIDs, err
|
||||||
|
}
|
||||||
return teams, err
|
|
||||||
|
func GetTeamUserIDsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) (userIDs []int64, err error) {
|
||||||
|
teamIDs, err := getTeamIDsWithAccessToAnyRepoUnit(ctx, orgID, repoID, mode, unitType, unitTypesMore...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(teamIDs) == 0 {
|
||||||
|
return userIDs, nil
|
||||||
|
}
|
||||||
|
err = db.GetEngine(ctx).Table("team_user").Select("uid").Where(builder.In("team_id", teamIDs)).Find(&userIDs)
|
||||||
|
return userIDs, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import (
|
|||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
@@ -41,7 +43,12 @@ func accessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Re
|
|||||||
restricted = user.IsRestricted
|
restricted = user.IsRestricted
|
||||||
}
|
}
|
||||||
|
|
||||||
if !restricted && !repo.IsPrivate {
|
if err := repo.LoadOwner(ctx); err != nil {
|
||||||
|
return mode, err
|
||||||
|
}
|
||||||
|
|
||||||
|
repoIsFullyPublic := !setting.Service.RequireSignInViewStrict && repo.Owner.Visibility == structs.VisibleTypePublic && !repo.IsPrivate
|
||||||
|
if (restricted && repoIsFullyPublic) || (!restricted && !repo.IsPrivate) {
|
||||||
mode = perm.AccessModeRead
|
mode = perm.AccessModeRead
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@@ -51,7 +52,14 @@ func TestAccessLevel(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, perm_model.AccessModeNone, level)
|
assert.Equal(t, perm_model.AccessModeNone, level)
|
||||||
|
|
||||||
// restricted user has no access to a public repo
|
// restricted user has default access to a public repo if no sign-in is required
|
||||||
|
setting.Service.RequireSignInViewStrict = false
|
||||||
|
level, err = access_model.AccessLevel(t.Context(), user29, repo1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, perm_model.AccessModeRead, level)
|
||||||
|
|
||||||
|
// restricted user has no access to a public repo if sign-in is required
|
||||||
|
setting.Service.RequireSignInViewStrict = true
|
||||||
level, err = access_model.AccessLevel(t.Context(), user29, repo1)
|
level, err = access_model.AccessLevel(t.Context(), user29, repo1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, perm_model.AccessModeNone, level)
|
assert.Equal(t, perm_model.AccessModeNone, level)
|
||||||
|
|||||||
@@ -5,15 +5,18 @@ package access
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
perm_model "code.gitea.io/gitea/models/perm"
|
perm_model "code.gitea.io/gitea/models/perm"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
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/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
@@ -253,6 +256,44 @@ func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetActionsUserRepoPermission returns the actions user permissions to the repository
|
||||||
|
func GetActionsUserRepoPermission(ctx context.Context, repo *repo_model.Repository, actionsUser *user_model.User, taskID int64) (perm Permission, err error) {
|
||||||
|
if actionsUser.ID != user_model.ActionsUserID {
|
||||||
|
return perm, errors.New("api GetActionsUserRepoPermission can only be called by the actions user")
|
||||||
|
}
|
||||||
|
task, err := actions_model.GetTaskByID(ctx, taskID)
|
||||||
|
if err != nil {
|
||||||
|
return perm, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var accessMode perm_model.AccessMode
|
||||||
|
if task.RepoID != repo.ID {
|
||||||
|
taskRepo, exist, err := db.GetByID[repo_model.Repository](ctx, task.RepoID)
|
||||||
|
if err != nil || !exist {
|
||||||
|
return perm, err
|
||||||
|
}
|
||||||
|
actionsCfg := repo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
|
||||||
|
if !actionsCfg.IsCollaborativeOwner(taskRepo.OwnerID) || !taskRepo.IsPrivate {
|
||||||
|
// The task repo can access the current repo only if the task repo is private and
|
||||||
|
// the owner of the task repo is a collaborative owner of the current repo.
|
||||||
|
// FIXME allow public repo read access if tokenless pull is enabled
|
||||||
|
// FIXME should owner's visibility also be considered here?
|
||||||
|
return perm, nil
|
||||||
|
}
|
||||||
|
accessMode = perm_model.AccessModeRead
|
||||||
|
} else if task.IsForkPullRequest {
|
||||||
|
accessMode = perm_model.AccessModeRead
|
||||||
|
} else {
|
||||||
|
accessMode = perm_model.AccessModeWrite
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repo.LoadUnits(ctx); err != nil {
|
||||||
|
return perm, err
|
||||||
|
}
|
||||||
|
perm.SetUnitsWithDefaultAccessMode(repo.Units, accessMode)
|
||||||
|
return perm, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserRepoPermission returns the user permissions to the repository
|
// GetUserRepoPermission returns the user permissions to the repository
|
||||||
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
|
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -458,54 +499,44 @@ func HasAnyUnitAccess(ctx context.Context, userID int64, repo *repo_model.Reposi
|
|||||||
return perm.HasAnyUnitAccess(), nil
|
return perm.HasAnyUnitAccess(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getUsersWithAccessMode returns users that have at least given access mode to the repository.
|
func GetUsersWithUnitAccess(ctx context.Context, repo *repo_model.Repository, mode perm_model.AccessMode, unitType unit.Type) (users []*user_model.User, err error) {
|
||||||
func getUsersWithAccessMode(ctx context.Context, repo *repo_model.Repository, mode perm_model.AccessMode) (_ []*user_model.User, err error) {
|
userIDs, err := GetUserIDsWithUnitAccess(ctx, repo, mode, unitType)
|
||||||
if err = repo.LoadOwner(ctx); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if len(userIDs) == 0 {
|
||||||
e := db.GetEngine(ctx)
|
return users, nil
|
||||||
accesses := make([]*Access, 0, 10)
|
}
|
||||||
if err = e.Where("repo_id = ? AND mode >= ?", repo.ID, mode).Find(&accesses); err != nil {
|
if err = db.GetEngine(ctx).In("id", userIDs.Values()).OrderBy("`name`").Find(&users); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leave a seat for owner itself to append later, but if owner is an organization
|
|
||||||
// and just waste 1 unit is cheaper than re-allocate memory once.
|
|
||||||
users := make([]*user_model.User, 0, len(accesses)+1)
|
|
||||||
if len(accesses) > 0 {
|
|
||||||
userIDs := make([]int64, len(accesses))
|
|
||||||
for i := 0; i < len(accesses); i++ {
|
|
||||||
userIDs[i] = accesses[i].UserID
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = e.In("id", userIDs).Find(&users); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !repo.Owner.IsOrganization() {
|
|
||||||
users = append(users, repo.Owner)
|
|
||||||
}
|
|
||||||
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRepoReaders returns all users that have explicit read access or higher to the repository.
|
func GetUserIDsWithUnitAccess(ctx context.Context, repo *repo_model.Repository, mode perm_model.AccessMode, unitType unit.Type) (container.Set[int64], error) {
|
||||||
func GetRepoReaders(ctx context.Context, repo *repo_model.Repository) (_ []*user_model.User, err error) {
|
userIDs := container.Set[int64]{}
|
||||||
return getUsersWithAccessMode(ctx, repo, perm_model.AccessModeRead)
|
e := db.GetEngine(ctx)
|
||||||
}
|
accesses := make([]*Access, 0, 10)
|
||||||
|
if err := e.Where("repo_id = ? AND mode >= ?", repo.ID, mode).Find(&accesses); err != nil {
|
||||||
// GetRepoWriters returns all users that have write access to the repository.
|
return nil, err
|
||||||
func GetRepoWriters(ctx context.Context, repo *repo_model.Repository) (_ []*user_model.User, err error) {
|
|
||||||
return getUsersWithAccessMode(ctx, repo, perm_model.AccessModeWrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRepoReader returns true if user has explicit read access or higher to the repository.
|
|
||||||
func IsRepoReader(ctx context.Context, repo *repo_model.Repository, userID int64) (bool, error) {
|
|
||||||
if repo.OwnerID == userID {
|
|
||||||
return true, nil
|
|
||||||
}
|
}
|
||||||
return db.GetEngine(ctx).Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, perm_model.AccessModeRead).Get(&Access{})
|
for _, a := range accesses {
|
||||||
|
userIDs.Add(a.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repo.LoadOwner(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !repo.Owner.IsOrganization() {
|
||||||
|
userIDs.Add(repo.Owner.ID)
|
||||||
|
} else {
|
||||||
|
teamUserIDs, err := organization.GetTeamUserIDsWithAccessToAnyRepoUnit(ctx, repo.OwnerID, repo.ID, mode, unitType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userIDs.AddMultiple(teamUserIDs...)
|
||||||
|
}
|
||||||
|
return userIDs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckRepoUnitUser check whether user could visit the unit of this repository
|
// CheckRepoUnitUser check whether user could visit the unit of this repository
|
||||||
|
|||||||
@@ -169,9 +169,9 @@ func TestGetUserRepoPermission(t *testing.T) {
|
|||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||||
team := &organization.Team{OrgID: org.ID, LowerName: "test_team"}
|
team := &organization.Team{OrgID: org.ID, LowerName: "test_team"}
|
||||||
require.NoError(t, db.Insert(ctx, team))
|
require.NoError(t, db.Insert(ctx, team))
|
||||||
|
require.NoError(t, db.Insert(ctx, &organization.TeamUser{OrgID: org.ID, TeamID: team.ID, UID: user.ID}))
|
||||||
|
|
||||||
t.Run("DoerInTeamWithNoRepo", func(t *testing.T) {
|
t.Run("DoerInTeamWithNoRepo", func(t *testing.T) {
|
||||||
require.NoError(t, db.Insert(ctx, &organization.TeamUser{OrgID: org.ID, TeamID: team.ID, UID: user.ID}))
|
|
||||||
perm, err := GetUserRepoPermission(ctx, repo32, user)
|
perm, err := GetUserRepoPermission(ctx, repo32, user)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
|
assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
|
||||||
@@ -219,6 +219,15 @@ func TestGetUserRepoPermission(t *testing.T) {
|
|||||||
assert.Equal(t, perm_model.AccessModeNone, perm.AccessMode)
|
assert.Equal(t, perm_model.AccessModeNone, perm.AccessMode)
|
||||||
assert.Equal(t, perm_model.AccessModeNone, perm.unitsMode[unit.TypeCode])
|
assert.Equal(t, perm_model.AccessModeNone, perm.unitsMode[unit.TypeCode])
|
||||||
assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues])
|
assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues])
|
||||||
|
|
||||||
|
users, err := GetUsersWithUnitAccess(ctx, repo3, perm_model.AccessModeRead, unit.TypeIssues)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, users, 1)
|
||||||
|
assert.Equal(t, user.ID, users[0].ID)
|
||||||
|
|
||||||
|
users, err = GetUsersWithUnitAccess(ctx, repo3, perm_model.AccessModeWrite, unit.TypeIssues)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, users)
|
||||||
})
|
})
|
||||||
|
|
||||||
require.NoError(t, db.Insert(ctx, repo_model.Collaboration{RepoID: repo3.ID, UserID: user.ID, Mode: perm_model.AccessModeWrite}))
|
require.NoError(t, db.Insert(ctx, repo_model.Collaboration{RepoID: repo3.ID, UserID: user.ID, Mode: perm_model.AccessModeWrite}))
|
||||||
@@ -229,5 +238,10 @@ func TestGetUserRepoPermission(t *testing.T) {
|
|||||||
assert.Equal(t, perm_model.AccessModeWrite, perm.AccessMode)
|
assert.Equal(t, perm_model.AccessModeWrite, perm.AccessMode)
|
||||||
assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeCode])
|
assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeCode])
|
||||||
assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeIssues])
|
assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeIssues])
|
||||||
|
|
||||||
|
users, err := GetUsersWithUnitAccess(ctx, repo3, perm_model.AccessModeWrite, unit.TypeIssues)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, users, 1)
|
||||||
|
assert.Equal(t, user.ID, users[0].ID)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,19 @@ func init() {
|
|||||||
db.RegisterModel(new(ReviewState))
|
db.RegisterModel(new(ReviewState))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rs *ReviewState) GetViewedFileCount() int {
|
||||||
|
if len(rs.UpdatedFiles) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var numViewedFiles int
|
||||||
|
for _, state := range rs.UpdatedFiles {
|
||||||
|
if state == Viewed {
|
||||||
|
numViewedFiles++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numViewedFiles
|
||||||
|
}
|
||||||
|
|
||||||
// GetReviewState returns the ReviewState with all given values prefilled, whether or not it exists in the database.
|
// GetReviewState returns the ReviewState with all given values prefilled, whether or not it exists in the database.
|
||||||
// If the review didn't exist before in the database, it won't afterwards either.
|
// If the review didn't exist before in the database, it won't afterwards either.
|
||||||
// The returned boolean shows whether the review exists in the database
|
// The returned boolean shows whether the review exists in the database
|
||||||
@@ -60,18 +73,18 @@ func GetReviewState(ctx context.Context, userID, pullID int64, commitSHA string)
|
|||||||
|
|
||||||
// UpdateReviewState updates the given review inside the database, regardless of whether it existed before or not
|
// UpdateReviewState updates the given review inside the database, regardless of whether it existed before or not
|
||||||
// The given map of files with their viewed state will be merged with the previous review, if present
|
// The given map of files with their viewed state will be merged with the previous review, if present
|
||||||
func UpdateReviewState(ctx context.Context, userID, pullID int64, commitSHA string, updatedFiles map[string]ViewedState) error {
|
func UpdateReviewState(ctx context.Context, userID, pullID int64, commitSHA string, updatedFiles map[string]ViewedState) (*ReviewState, error) {
|
||||||
log.Trace("Updating review for user %d, repo %d, commit %s with the updated files %v.", userID, pullID, commitSHA, updatedFiles)
|
log.Trace("Updating review for user %d, repo %d, commit %s with the updated files %v.", userID, pullID, commitSHA, updatedFiles)
|
||||||
|
|
||||||
review, exists, err := GetReviewState(ctx, userID, pullID, commitSHA)
|
review, exists, err := GetReviewState(ctx, userID, pullID, commitSHA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if exists {
|
if exists {
|
||||||
review.UpdatedFiles = mergeFiles(review.UpdatedFiles, updatedFiles)
|
review.UpdatedFiles = mergeFiles(review.UpdatedFiles, updatedFiles)
|
||||||
} else if previousReview, err := getNewestReviewStateApartFrom(ctx, userID, pullID, commitSHA); err != nil {
|
} else if previousReview, err := getNewestReviewStateApartFrom(ctx, userID, pullID, commitSHA); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
|
|
||||||
// Overwrite the viewed files of the previous review if present
|
// Overwrite the viewed files of the previous review if present
|
||||||
} else if previousReview != nil {
|
} else if previousReview != nil {
|
||||||
@@ -85,11 +98,11 @@ func UpdateReviewState(ctx context.Context, userID, pullID int64, commitSHA stri
|
|||||||
if !exists {
|
if !exists {
|
||||||
log.Trace("Inserting new review for user %d, repo %d, commit %s with the updated files %v.", userID, pullID, commitSHA, review.UpdatedFiles)
|
log.Trace("Inserting new review for user %d, repo %d, commit %s with the updated files %v.", userID, pullID, commitSHA, review.UpdatedFiles)
|
||||||
_, err := engine.Insert(review)
|
_, err := engine.Insert(review)
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Trace("Updating already existing review with ID %d (user %d, repo %d, commit %s) with the updated files %v.", review.ID, userID, pullID, commitSHA, review.UpdatedFiles)
|
log.Trace("Updating already existing review with ID %d (user %d, repo %d, commit %s) with the updated files %v.", review.ID, userID, pullID, commitSHA, review.UpdatedFiles)
|
||||||
_, err = engine.ID(review.ID).Update(&ReviewState{UpdatedFiles: review.UpdatedFiles})
|
_, err = engine.ID(review.ID).Cols("updated_files").Update(review)
|
||||||
return err
|
return review, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeFiles merges the given maps of files with their viewing state into one map.
|
// mergeFiles merges the given maps of files with their viewing state into one map.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user