mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-10 15:32:55 +09:00
Compare commits
161 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99053ce4fa | ||
|
|
e818de179e | ||
|
|
0a87bf9016 | ||
|
|
86d99e2f38 | ||
|
|
7bfb7567b2 | ||
|
|
7619808137 | ||
|
|
b854930a96 | ||
|
|
935f5e0ad5 | ||
|
|
08c6ea6728 | ||
|
|
67977f0b1c | ||
|
|
78fbcf35ad | ||
|
|
8f5b1d27d4 | ||
|
|
89c99a4dcb | ||
|
|
3c7e7a19dd | ||
|
|
8313b5d998 | ||
|
|
6ca73bf662 | ||
|
|
5e10def7f7 | ||
|
|
1b8efb6fc7 | ||
|
|
8f89e1e174 | ||
|
|
cbc595b9d9 | ||
|
|
cc5ccf44dc | ||
|
|
f91e35b8b7 | ||
|
|
f52ed422dc | ||
|
|
0266ee5de7 | ||
|
|
ac03e65cf4 | ||
|
|
f3e6672c09 | ||
|
|
136ec9ef81 | ||
|
|
79018ae726 | ||
|
|
e11176192a | ||
|
|
4e0269e890 | ||
|
|
04114c637a | ||
|
|
e5540bfa81 | ||
|
|
d22d6ca0d8 | ||
|
|
d49feab428 | ||
|
|
9162f4403a | ||
|
|
d05cf08fad | ||
|
|
f4b4b0bf98 | ||
|
|
99596044d7 | ||
|
|
693d26914f | ||
|
|
315f197790 | ||
|
|
76b8f0c3a7 | ||
|
|
f99bbd7f3f | ||
|
|
f7ef657b5a | ||
|
|
486d274be6 | ||
|
|
ab3d2a944c | ||
|
|
12bfa9e83d | ||
|
|
dd661e92df | ||
|
|
0b31272c7e | ||
|
|
ec0c418719 | ||
|
|
6dc19fc29a | ||
|
|
9f1baa7d18 | ||
|
|
e13deb7a16 | ||
|
|
e5c1b8b632 | ||
|
|
e931b62f33 | ||
|
|
81ee93e5bc | ||
|
|
053f9186bc | ||
|
|
68fcdb6122 | ||
|
|
14ca309c39 | ||
|
|
4aba42519d | ||
|
|
9adf175df0 | ||
|
|
c3fa2a8729 | ||
|
|
89dfed32e0 | ||
|
|
d5062d0c27 | ||
|
|
90e9e79232 | ||
|
|
c6467edcb1 | ||
|
|
5d5b695527 | ||
|
|
0af7a7b79f | ||
|
|
9339661078 | ||
|
|
1e69f085d6 | ||
|
|
0bfccd8ecf | ||
|
|
534b9b35dd | ||
|
|
dbadc59b56 | ||
|
|
a57e2c4bc3 | ||
|
|
acd4e10990 | ||
|
|
0a1df294c8 | ||
|
|
52a964d1fc | ||
|
|
d3dbe0d9ce | ||
|
|
cdbbdbef06 | ||
|
|
79f555d465 | ||
|
|
ae2b795693 | ||
|
|
d1fdbf46bd | ||
|
|
f27a75564a | ||
|
|
958d0db4f4 | ||
|
|
4c2441ba5d | ||
|
|
6f5f0be9e3 | ||
|
|
23d2d224c2 | ||
|
|
a43d829de8 | ||
|
|
8ab1363fef | ||
|
|
178fd90852 | ||
|
|
b39f7a37d1 | ||
|
|
b9ed8fceff | ||
|
|
e6ce72b14a | ||
|
|
2eecd58bbe | ||
|
|
64b9b21790 | ||
|
|
3290aff964 | ||
|
|
7ed1e8987e | ||
|
|
f10e909fce | ||
|
|
a3b25436f2 | ||
|
|
b947bc4363 | ||
|
|
18dc41d6f8 | ||
|
|
bf5d00074d | ||
|
|
fb4e9f92f9 | ||
|
|
468d1919b5 | ||
|
|
1b788946a7 | ||
|
|
e8646ad1d8 | ||
|
|
29dc9c784e | ||
|
|
b1cc4bf77f | ||
|
|
d35161ceb8 | ||
|
|
8defca6d39 | ||
|
|
fac434da0a | ||
|
|
e18eae7129 | ||
|
|
c60bc26fd3 | ||
|
|
bacc69db83 | ||
|
|
c5da032193 | ||
|
|
3ace45c118 | ||
|
|
5d6c5ce71a | ||
|
|
7baa6fa47c | ||
|
|
f9a0b077a7 | ||
|
|
d3317ebabe | ||
|
|
e9481e1da3 | ||
|
|
8965c068e9 | ||
|
|
eaaa158df3 | ||
|
|
f5498421c4 | ||
|
|
a6a14c9a92 | ||
|
|
d0ec1788b8 | ||
|
|
c1202f1b57 | ||
|
|
1162cbccc0 | ||
|
|
038990e0ff | ||
|
|
03ff09870d | ||
|
|
8bf4f2cc8f | ||
|
|
21731c1370 | ||
|
|
a0e272d95a | ||
|
|
47537a8361 | ||
|
|
d018c1b4b1 | ||
|
|
d2cbe2fba0 | ||
|
|
d6233c25b5 | ||
|
|
2bf2d00c8a | ||
|
|
9bd56a8ba0 | ||
|
|
a1dc3c9bd1 | ||
|
|
47ee84d1f3 | ||
|
|
89f1df033a | ||
|
|
94b67f1967 | ||
|
|
0a9a84df11 | ||
|
|
cdac263bb8 | ||
|
|
a5c7df7a4c | ||
|
|
6d738fecc4 | ||
|
|
38cc7453e2 | ||
|
|
b44175c071 | ||
|
|
947358dffe | ||
|
|
be1090cb2d | ||
|
|
c8f3402841 | ||
|
|
a3a95a0b67 | ||
|
|
ed527b664d | ||
|
|
e4717d426e | ||
|
|
16f15d2f7b | ||
|
|
b3f5196241 | ||
|
|
6c5f0af45d | ||
|
|
c95cb7c7e2 | ||
|
|
6747e3e0eb | ||
|
|
a12b5b3640 | ||
|
|
834dad8cef |
@@ -1,19 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "Gitea DevContainer",
|
"name": "Gitea DevContainer",
|
||||||
"image": "mcr.microsoft.com/devcontainers/go:1.25-trixie",
|
"image": "mcr.microsoft.com/devcontainers/go:1.24-bookworm",
|
||||||
"containerEnv": {
|
|
||||||
// override "local" from packaged version
|
|
||||||
"GOTOOLCHAIN": "auto"
|
|
||||||
},
|
|
||||||
"features": {
|
"features": {
|
||||||
// installs nodejs into container
|
// installs nodejs into container
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "latest"
|
"version": "20"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/git-lfs:1.2.5": {},
|
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {},
|
||||||
"ghcr.io/jsburckhardt/devcontainer-features/uv:1": {},
|
"ghcr.io/devcontainers-extra/features/poetry:2": {},
|
||||||
"ghcr.io/devcontainers/features/python:1": {
|
"ghcr.io/devcontainers/features/python:1": {
|
||||||
"version": "3.13"
|
"version": "3.12"
|
||||||
},
|
},
|
||||||
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
|
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -36,6 +36,15 @@ _testmain.go
|
|||||||
coverage.all
|
coverage.all
|
||||||
cpu.out
|
cpu.out
|
||||||
|
|
||||||
|
/modules/migration/bindata.go
|
||||||
|
/modules/migration/bindata.go.hash
|
||||||
|
/modules/options/bindata.go
|
||||||
|
/modules/options/bindata.go.hash
|
||||||
|
/modules/public/bindata.go
|
||||||
|
/modules/public/bindata.go.hash
|
||||||
|
/modules/templates/bindata.go
|
||||||
|
/modules/templates/bindata.go.hash
|
||||||
|
|
||||||
*.db
|
*.db
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
@@ -65,7 +74,6 @@ cpu.out
|
|||||||
/yarn.lock
|
/yarn.lock
|
||||||
/yarn-error.log
|
/yarn-error.log
|
||||||
/npm-debug.log*
|
/npm-debug.log*
|
||||||
/pnpm-debug.log*
|
|
||||||
/public/assets/js
|
/public/assets/js
|
||||||
/public/assets/css
|
/public/assets/css
|
||||||
/public/assets/fonts
|
/public/assets/fonts
|
||||||
@@ -74,9 +82,6 @@ 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
|
||||||
|
|||||||
1004
.eslintrc.cjs
Normal file
1004
.eslintrc.cjs
Normal file
File diff suppressed because it is too large
Load Diff
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -8,4 +8,3 @@
|
|||||||
/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
|
|
||||||
|
|||||||
15
.github/labeler.yml
vendored
15
.github/labeler.yml
vendored
@@ -51,6 +51,7 @@ modifies/internal:
|
|||||||
- ".github/**"
|
- ".github/**"
|
||||||
- ".gitea/**"
|
- ".gitea/**"
|
||||||
- ".devcontainer/**"
|
- ".devcontainer/**"
|
||||||
|
- "build.go"
|
||||||
- "build/**"
|
- "build/**"
|
||||||
- "contrib/**"
|
- "contrib/**"
|
||||||
|
|
||||||
@@ -58,9 +59,9 @@ modifies/dependencies:
|
|||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
- "package.json"
|
- "package.json"
|
||||||
- "pnpm-lock.yaml"
|
- "package-lock.json"
|
||||||
- "pyproject.toml"
|
- "pyproject.toml"
|
||||||
- "uv.lock"
|
- "poetry.lock"
|
||||||
- "go.mod"
|
- "go.mod"
|
||||||
- "go.sum"
|
- "go.sum"
|
||||||
|
|
||||||
@@ -80,13 +81,3 @@ docs-update-needed:
|
|||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
- "custom/conf/app.example.ini"
|
- "custom/conf/app.example.ini"
|
||||||
|
|
||||||
topic/code-linting:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- ".eslintrc.cjs"
|
|
||||||
- ".golangci.yml"
|
|
||||||
- ".markdownlint.yaml"
|
|
||||||
- ".spectral.yaml"
|
|
||||||
- ".yamllint.yaml"
|
|
||||||
- "stylelint.config.js"
|
|
||||||
|
|||||||
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@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
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@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: crowdin/github-action@v1
|
- uses: crowdin/github-action@v1
|
||||||
with:
|
with:
|
||||||
upload_sources: true
|
upload_sources: true
|
||||||
|
|||||||
11
.github/workflows/files-changed.yml
vendored
11
.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@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: dorny/paths-filter@v3
|
- uses: dorny/paths-filter@v3
|
||||||
id: changes
|
id: changes
|
||||||
with:
|
with:
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
- "tools/*.ts"
|
- "tools/*.ts"
|
||||||
- "assets/emoji.json"
|
- "assets/emoji.json"
|
||||||
- "package.json"
|
- "package.json"
|
||||||
- "pnpm-lock.yaml"
|
- "package-lock.json"
|
||||||
- "Makefile"
|
- "Makefile"
|
||||||
- ".eslintrc.cjs"
|
- ".eslintrc.cjs"
|
||||||
- ".npmrc"
|
- ".npmrc"
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
- "**/*.md"
|
- "**/*.md"
|
||||||
- ".markdownlint.yaml"
|
- ".markdownlint.yaml"
|
||||||
- "package.json"
|
- "package.json"
|
||||||
- "pnpm-lock.yaml"
|
- "package-lock.json"
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
- ".github/workflows/*"
|
- ".github/workflows/*"
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
- "tools/lint-templates-*.js"
|
- "tools/lint-templates-*.js"
|
||||||
- "templates/**/*.tmpl"
|
- "templates/**/*.tmpl"
|
||||||
- "pyproject.toml"
|
- "pyproject.toml"
|
||||||
- "uv.lock"
|
- "poetry.lock"
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
- "Dockerfile"
|
- "Dockerfile"
|
||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
- "templates/swagger/v1_input.json"
|
- "templates/swagger/v1_input.json"
|
||||||
- "Makefile"
|
- "Makefile"
|
||||||
- "package.json"
|
- "package.json"
|
||||||
- "pnpm-lock.yaml"
|
- "package-lock.json"
|
||||||
- ".spectral.yaml"
|
- ".spectral.yaml"
|
||||||
|
|
||||||
yaml:
|
yaml:
|
||||||
@@ -98,3 +98,4 @@ jobs:
|
|||||||
- "**/*.yaml"
|
- "**/*.yaml"
|
||||||
- ".yamllint.yaml"
|
- ".yamllint.yaml"
|
||||||
- "pyproject.toml"
|
- "pyproject.toml"
|
||||||
|
- "poetry.lock"
|
||||||
|
|||||||
78
.github/workflows/pull-compliance.yml
vendored
78
.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@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -31,13 +31,16 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: astral-sh/setup-uv@v6
|
- uses: actions/setup-python@v5
|
||||||
- run: uv python install 3.12
|
|
||||||
- uses: pnpm/action-setup@v4
|
|
||||||
- uses: actions/setup-node@v5
|
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
python-version: "3.12"
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
|
- run: pip install poetry
|
||||||
- run: make deps-py
|
- run: make deps-py
|
||||||
- run: make deps-frontend
|
- run: make deps-frontend
|
||||||
- run: make lint-templates
|
- run: make lint-templates
|
||||||
@@ -47,9 +50,11 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: astral-sh/setup-uv@v6
|
- uses: actions/setup-python@v5
|
||||||
- run: uv python install 3.12
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
- run: pip install poetry
|
||||||
- run: make deps-py
|
- run: make deps-py
|
||||||
- run: make lint-yaml
|
- run: make lint-yaml
|
||||||
|
|
||||||
@@ -58,11 +63,12 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: actions/setup-node@v4
|
||||||
- uses: actions/setup-node@v5
|
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend
|
- run: make deps-frontend
|
||||||
- run: make lint-swagger
|
- run: make lint-swagger
|
||||||
|
|
||||||
@@ -71,8 +77,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -83,8 +89,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -100,8 +106,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -115,8 +121,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -128,11 +134,12 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: actions/setup-node@v4
|
||||||
- uses: actions/setup-node@v5
|
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend
|
- run: make deps-frontend
|
||||||
- run: make lint-frontend
|
- run: make lint-frontend
|
||||||
- run: make checks-frontend
|
- run: make checks-frontend
|
||||||
@@ -144,8 +151,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -176,11 +183,12 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: actions/setup-node@v4
|
||||||
- uses: actions/setup-node@v5
|
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend
|
- run: make deps-frontend
|
||||||
- run: make lint-md
|
- run: make lint-md
|
||||||
|
|
||||||
@@ -189,8 +197,8 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
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@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
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@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
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: GOEXPERIMENT='' make backend
|
- run: 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: GOEXPERIMENT='' make test-sqlite
|
run: 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@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
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: GOEXPERIMENT='' make unit-test-coverage test-check
|
run: 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@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
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@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
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,23 +11,25 @@ jobs:
|
|||||||
files-changed:
|
files-changed:
|
||||||
uses: ./.github/workflows/files-changed.yml
|
uses: ./.github/workflows/files-changed.yml
|
||||||
|
|
||||||
container:
|
regular:
|
||||||
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
|
||||||
- name: Build regular container image
|
- uses: docker/build-push-action@v5
|
||||||
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
|
|
||||||
uses: docker/build-push-action@v5
|
rootless:
|
||||||
|
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
- uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
|
||||||
push: false
|
push: false
|
||||||
file: Dockerfile.rootless
|
file: Dockerfile.rootless
|
||||||
tags: gitea/gitea:linux-amd64
|
tags: gitea/gitea:linux-amd64
|
||||||
|
|||||||
13
.github/workflows/pull-e2e-tests.yml
vendored
13
.github/workflows/pull-e2e-tests.yml
vendored
@@ -18,17 +18,18 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: actions/setup-node@v4
|
||||||
- uses: actions/setup-node@v5
|
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend frontend deps-backend
|
- run: make deps-frontend frontend deps-backend
|
||||||
- run: pnpm exec playwright install --with-deps
|
- run: npx playwright install --with-deps
|
||||||
- run: make test-e2e-sqlite
|
- run: make test-e2e-sqlite
|
||||||
timeout-minutes: 40
|
timeout-minutes: 40
|
||||||
env:
|
env:
|
||||||
|
|||||||
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@v6
|
- uses: actions/labeler@v5
|
||||||
with:
|
with:
|
||||||
sync-labels: true
|
sync-labels: true
|
||||||
|
|||||||
101
.github/workflows/release-nightly.yml
vendored
101
.github/workflows/release-nightly.yml
vendored
@@ -12,18 +12,19 @@ jobs:
|
|||||||
nightly-binary:
|
nightly-binary:
|
||||||
runs-on: namespace-profile-gitea-release-binary
|
runs-on: namespace-profile-gitea-release-binary
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- 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 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@v6
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: actions/setup-node@v4
|
||||||
- uses: actions/setup-node@v5
|
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend deps-backend
|
- run: make deps-frontend deps-backend
|
||||||
# xgo build
|
# xgo build
|
||||||
- run: make release
|
- run: make release
|
||||||
@@ -56,45 +57,31 @@ 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-container:
|
nightly-docker-rootful:
|
||||||
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@v5
|
- 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 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
|
||||||
id: clean_name
|
id: clean_name
|
||||||
run: |
|
run: |
|
||||||
|
# if main then say nightly otherwise cleanup name
|
||||||
|
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
|
||||||
|
echo "branch=nightly" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
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:
|
||||||
@@ -106,20 +93,62 @@ 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 regular docker image
|
- name: fetch go modules
|
||||||
|
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: ${{ steps.meta.outputs.tags }}
|
tags: |-
|
||||||
annotations: ${{ steps.meta.outputs.annotations }}
|
gitea/gitea:${{ steps.clean_name.outputs.branch }}
|
||||||
|
ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}
|
||||||
|
nightly-docker-rootless:
|
||||||
|
runs-on: namespace-profile-gitea-release-docker
|
||||||
|
permissions:
|
||||||
|
packages: write # to publish to ghcr.io
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||||
|
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||||
|
- run: git fetch --unshallow --quiet --tags --force
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
check-latest: true
|
||||||
|
- uses: docker/setup-qemu-action@v3
|
||||||
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Get cleaned branch name
|
||||||
|
id: clean_name
|
||||||
|
run: |
|
||||||
|
# if main then say nightly otherwise cleanup name
|
||||||
|
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
|
||||||
|
echo "branch=nightly" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
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,linux/riscv64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
file: Dockerfile.rootless
|
file: Dockerfile.rootless
|
||||||
tags: ${{ steps.meta_rootless.outputs.tags }}
|
tags: |-
|
||||||
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
|
||||||
|
ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
|
||||||
|
|||||||
77
.github/workflows/release-tag-rc.yml
vendored
77
.github/workflows/release-tag-rc.yml
vendored
@@ -13,18 +13,19 @@ jobs:
|
|||||||
binary:
|
binary:
|
||||||
runs-on: namespace-profile-gitea-release-binary
|
runs-on: namespace-profile-gitea-release-binary
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- 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 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@v6
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: actions/setup-node@v4
|
||||||
- uses: actions/setup-node@v5
|
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend deps-backend
|
- run: make deps-frontend deps-backend
|
||||||
# xgo build
|
# xgo build
|
||||||
- run: make release
|
- run: make release
|
||||||
@@ -66,12 +67,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 }}
|
||||||
container:
|
docker-rootful:
|
||||||
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@v5
|
- 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 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,23 +89,6 @@ 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"
|
|
||||||
- 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: |
|
|
||||||
latest=false
|
|
||||||
suffix=-rootless
|
|
||||||
# 1.2.3-rc0
|
|
||||||
tags: |
|
|
||||||
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:
|
||||||
@@ -116,20 +100,55 @@ 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 regular container image
|
- 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: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
annotations: ${{ steps.meta.outputs.annotations }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
- name: build rootless container image
|
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
|
||||||
|
id: meta
|
||||||
|
with:
|
||||||
|
images: |-
|
||||||
|
gitea/gitea
|
||||||
|
ghcr.io/go-gitea/gitea
|
||||||
|
# each tag below will have the suffix of -rootless
|
||||||
|
flavor: |
|
||||||
|
latest=false
|
||||||
|
suffix=-rootless
|
||||||
|
# 1.2.3-rc0
|
||||||
|
tags: |
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GHCR using PAT
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: build rootless 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
|
||||||
file: Dockerfile.rootless
|
file: Dockerfile.rootless
|
||||||
tags: ${{ steps.meta_rootless.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|||||||
63
.github/workflows/release-tag-version.yml
vendored
63
.github/workflows/release-tag-version.yml
vendored
@@ -17,18 +17,19 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
packages: write # to publish to ghcr.io
|
packages: write # to publish to ghcr.io
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- 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 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@v6
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: actions/setup-node@v4
|
||||||
- uses: actions/setup-node@v5
|
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend deps-backend
|
- run: make deps-frontend deps-backend
|
||||||
# xgo build
|
# xgo build
|
||||||
- run: make release
|
- run: make release
|
||||||
@@ -70,12 +71,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 }}
|
||||||
container:
|
docker-rootful:
|
||||||
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@v5
|
- 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 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,10 +97,36 @@ 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: |
|
- name: Login to Docker Hub
|
||||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GHCR using PAT
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: build rootful docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
docker-rootless:
|
||||||
|
runs-on: namespace-profile-gitea-release-docker
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||||
|
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||||
|
- run: git fetch --unshallow --quiet --tags --force
|
||||||
|
- uses: docker/setup-qemu-action@v3
|
||||||
|
- uses: docker/setup-buildx-action@v3
|
||||||
- uses: docker/metadata-action@v5
|
- uses: docker/metadata-action@v5
|
||||||
id: meta_rootless
|
id: meta
|
||||||
with:
|
with:
|
||||||
images: |-
|
images: |-
|
||||||
gitea/gitea
|
gitea/gitea
|
||||||
@@ -116,8 +143,6 @@ 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:
|
||||||
@@ -129,20 +154,12 @@ 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 regular container image
|
- name: build rootless docker 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_rootless.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|||||||
24
.gitignore
vendored
24
.gitignore
vendored
@@ -22,12 +22,6 @@ _test
|
|||||||
.vscode
|
.vscode
|
||||||
__debug_bin*
|
__debug_bin*
|
||||||
|
|
||||||
# Visual Studio
|
|
||||||
/.vs/
|
|
||||||
|
|
||||||
# mise version managment tool
|
|
||||||
mise.toml
|
|
||||||
|
|
||||||
*.cgo1.go
|
*.cgo1.go
|
||||||
*.cgo2.c
|
*.cgo2.c
|
||||||
_cgo_defun.c
|
_cgo_defun.c
|
||||||
@@ -81,7 +75,6 @@ cpu.out
|
|||||||
/yarn.lock
|
/yarn.lock
|
||||||
/yarn-error.log
|
/yarn-error.log
|
||||||
/npm-debug.log*
|
/npm-debug.log*
|
||||||
/.pnpm-store
|
|
||||||
/public/assets/js
|
/public/assets/js
|
||||||
/public/assets/css
|
/public/assets/css
|
||||||
/public/assets/fonts
|
/public/assets/fonts
|
||||||
@@ -113,20 +106,3 @@ prime/
|
|||||||
|
|
||||||
# Manpage
|
# Manpage
|
||||||
/man
|
/man
|
||||||
|
|
||||||
# Ignore AI/LLM instruction files
|
|
||||||
/.claude/
|
|
||||||
/.cursorrules
|
|
||||||
/.cursor/
|
|
||||||
/.goosehints
|
|
||||||
/.windsurfrules
|
|
||||||
/.github/copilot-instructions.md
|
|
||||||
/AGENT.md
|
|
||||||
/CLAUDE.md
|
|
||||||
/llms.txt
|
|
||||||
|
|
||||||
# Ignore worktrees when working on multiple branches
|
|
||||||
.worktrees/
|
|
||||||
|
|
||||||
# A Makefile for custom make targets
|
|
||||||
Makefile.local
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ linters:
|
|||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- mirror
|
- mirror
|
||||||
- modernize
|
|
||||||
- nakedret
|
- nakedret
|
||||||
- nolintlint
|
- nolintlint
|
||||||
- perfsprint
|
- perfsprint
|
||||||
@@ -46,17 +45,10 @@ linters:
|
|||||||
desc: do not use the ini package, use gitea's config system instead
|
desc: do not use the ini package, use gitea's config system instead
|
||||||
- pkg: gitea.com/go-chi/cache
|
- pkg: gitea.com/go-chi/cache
|
||||||
desc: do not use the go-chi cache package, use gitea's cache system
|
desc: do not use the go-chi cache package, use gitea's cache system
|
||||||
nolintlint:
|
|
||||||
allow-unused: false
|
|
||||||
require-explanation: true
|
|
||||||
require-specific: true
|
|
||||||
gocritic:
|
gocritic:
|
||||||
enabled-checks:
|
|
||||||
- equalFold
|
|
||||||
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:
|
||||||
@@ -91,10 +83,6 @@ linters:
|
|||||||
- name: unreachable-code
|
- name: unreachable-code
|
||||||
- name: var-declaration
|
- name: var-declaration
|
||||||
- name: var-naming
|
- name: var-naming
|
||||||
arguments:
|
|
||||||
- [] # AllowList - do not remove as args for the rule are positional and won't work without lists first
|
|
||||||
- [] # DenyList
|
|
||||||
- - skip-package-name-checks: true # supress errors from underscore in migration packages
|
|
||||||
staticcheck:
|
staticcheck:
|
||||||
checks:
|
checks:
|
||||||
- all
|
- all
|
||||||
@@ -109,11 +97,6 @@ linters:
|
|||||||
- require-error
|
- require-error
|
||||||
usetesting:
|
usetesting:
|
||||||
os-temp-dir: true
|
os-temp-dir: true
|
||||||
modernize:
|
|
||||||
disable:
|
|
||||||
- stringsbuilder
|
|
||||||
perfsprint:
|
|
||||||
concat-loop: false
|
|
||||||
exclusions:
|
exclusions:
|
||||||
generated: lax
|
generated: lax
|
||||||
presets:
|
presets:
|
||||||
@@ -160,7 +143,6 @@ linters:
|
|||||||
text: '(?i)exitAfterDefer:'
|
text: '(?i)exitAfterDefer:'
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- node_modules
|
||||||
- .venv
|
|
||||||
- public
|
- public
|
||||||
- web_src
|
- web_src
|
||||||
- third_party$
|
- third_party$
|
||||||
@@ -180,7 +162,6 @@ formatters:
|
|||||||
generated: lax
|
generated: lax
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- node_modules
|
||||||
- .venv
|
|
||||||
- public
|
- public
|
||||||
- web_src
|
- web_src
|
||||||
- third_party$
|
- third_party$
|
||||||
|
|||||||
3
.ignore
3
.ignore
@@ -1,6 +1,9 @@
|
|||||||
*.min.css
|
*.min.css
|
||||||
*.min.js
|
*.min.js
|
||||||
/assets/*.json
|
/assets/*.json
|
||||||
|
/modules/options/bindata.go
|
||||||
|
/modules/public/bindata.go
|
||||||
|
/modules/templates/bindata.go
|
||||||
/options/gitignore
|
/options/gitignore
|
||||||
/options/license
|
/options/license
|
||||||
/public/assets
|
/public/assets
|
||||||
|
|||||||
5
.npmrc
5
.npmrc
@@ -1,7 +1,6 @@
|
|||||||
audit=false
|
audit=false
|
||||||
fund=false
|
fund=false
|
||||||
update-notifier=false
|
update-notifier=false
|
||||||
|
package-lock=true
|
||||||
save-exact=true
|
save-exact=true
|
||||||
auto-install-peers=true
|
lockfile-version=3
|
||||||
dedupe-peer-dependents=false
|
|
||||||
enable-pre-post-scripts=true
|
|
||||||
|
|||||||
128
CHANGELOG.md
128
CHANGELOG.md
@@ -4,7 +4,119 @@ This changelog goes through the changes that have been made in each release
|
|||||||
without substantial changes to our git log; to see the highlights of what has
|
without substantial changes to our git log; to see the highlights of what has
|
||||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||||
|
|
||||||
## [1.24.0](https://github.com/go-gitea/gitea/releases/tag/1.24.0) - 2025-05-26
|
## [1.24.7](https://github.com/go-gitea/gitea/releases/tag/1.24.7) - 2025-10-24
|
||||||
|
|
||||||
|
* SECURITY
|
||||||
|
* Refactor legacy code (#35708) (#35713)
|
||||||
|
* Fixing issue #35530: Password Leak in Log Messages (#35584) (#35665)
|
||||||
|
* Fix a bug missed return (#35655) (#35671)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix inputing review comment will remove reviewer (#35591) (#35664)
|
||||||
|
* TESTING
|
||||||
|
* Mock external service in hcaptcha TestCaptcha (#35604) (#35663)
|
||||||
|
* Fix build (#35669)
|
||||||
|
|
||||||
|
## [1.24.6](https://github.com/go-gitea/gitea/releases/tag/1.24.6) - 2025-09-10
|
||||||
|
|
||||||
|
* SECURITY
|
||||||
|
* Upgrade xz to v0.5.15 (#35385)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix a compare page 404 bug when the pull request disabled (#35441) (#35453)
|
||||||
|
* Fix bug when issue disabled, pull request number in the commit message cannot be redirected (#35420) (#35442)
|
||||||
|
* Add author.name field to Swift Package Registry API response (#35410) (#35431)
|
||||||
|
* Remove usernames when empty in discord webhook (#35412) (#35417)
|
||||||
|
* Allow foreachref parser to grow its buffer (#35365) (#35376)
|
||||||
|
* Allow deleting comment with content via API like web did (#35346) (#35354)
|
||||||
|
* Fix atom/rss mixed error (#35345) (#35347)
|
||||||
|
* Fix review request webhook bug (#35339)
|
||||||
|
* Remove duplicate html IDs (#35210) (#35325)
|
||||||
|
* Fix LFS range size header response (#35277) (#35293)
|
||||||
|
* Fix GitHub release assets URL validation (#35287) (#35290)
|
||||||
|
* Fix token lifetime, closes #35230 (#35271) (#35281)
|
||||||
|
* Fix push commits comments when changing the pull request target branch (#35386) (#35443)
|
||||||
|
|
||||||
|
## [1.24.5](https://github.com/go-gitea/gitea/releases/tag/v1.24.5) - 2025-08-12
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix a bug where lfs gc never worked. (#35198) (#35255)
|
||||||
|
* Reload issue when sending webhook to make num comments is right. (#35243) (#35248)
|
||||||
|
* Fix bug when review pull request commits (#35192) (#35246)
|
||||||
|
* MISC
|
||||||
|
* Vertically center "Show Resolved" (#35211) (#35218)
|
||||||
|
|
||||||
|
## [1.24.4](https://github.com/go-gitea/gitea/releases/tag/v1.24.4) - 2025-08-03
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix various bugs (1.24) (#35186)
|
||||||
|
* Fix migrate input box bug (#35166) (#35171)
|
||||||
|
* Only hide dropzone when no files have been uploaded (#35156) (#35167)
|
||||||
|
* Fix review comment/dimiss comment x reference can be refereced back (#35094) (#35099)
|
||||||
|
* Fix submodule nil check (#35096) (#35098)
|
||||||
|
* MISC
|
||||||
|
* Don't use full-file highlight when there is a git diff textconv (#35114) (#35119)
|
||||||
|
* Increase gap on latest commit (#35104) (#35113)
|
||||||
|
|
||||||
|
## [1.24.3](https://github.com/go-gitea/gitea/releases/tag/v1.24.3) - 2025-07-15
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix form property assignment edge case (#35073) (#35078)
|
||||||
|
* Improve submodule relative path handling (#35056) (#35075)
|
||||||
|
* Fix incorrect comment diff hunk parsing, fix github asset ID nil panic (#35046) (#35055)
|
||||||
|
* Fix updating user visibility (#35036) (#35044)
|
||||||
|
* Support base64-encoded agit push options (#35037) (#35041)
|
||||||
|
* Make submodule link work with relative path (#35034) (#35038)
|
||||||
|
* Fix bug when displaying git user avatar in commits list (#35006)
|
||||||
|
* Fix API response for swagger spec (#35029)
|
||||||
|
* Start automerge check again after the conflict check and the schedule (#34988) (#35002)
|
||||||
|
* Fix the response format for actions/workflows (#35009) (#35016)
|
||||||
|
* Fix repo settings and protocol log problems (#35012) (#35013)
|
||||||
|
* Fix project images scroll (#34971) (#34972)
|
||||||
|
* Mark old reviews as stale on agit pr updates (#34933) (#34965)
|
||||||
|
* Fix git graph page (#34948) (#34949)
|
||||||
|
* Don't send trigger for a pending review's comment create/update/delete (#34928) (#34939)
|
||||||
|
* Fix some log and UI problems (#34863) (#34868)
|
||||||
|
* Fix archive API (#34853) (#34857)
|
||||||
|
* Ignore force pushes for changed files in a PR review (#34837) (#34843)
|
||||||
|
* Fix SSH LFS timeout (#34838) (#34842)
|
||||||
|
* Fix team permissions (#34827) (#34836)
|
||||||
|
* Fix job status aggregation logic (#34823) (#34835)
|
||||||
|
* Fix issue filter (#34914) (#34915)
|
||||||
|
* Fix typo in pull request merge warning message text (#34899) (#34903)
|
||||||
|
* Support the open-icon of folder (#34168) (#34896)
|
||||||
|
* Optimize flex layout of release attachment area (#34885) (#34886)
|
||||||
|
* Fix the issue of abnormal interface when there is no issue-item on the project page (#34791) (#34880)
|
||||||
|
* Skip updating timestamp when sync branch (#34875)
|
||||||
|
* Fix required contexts and commit status matching bug (#34815) (#34829)
|
||||||
|
|
||||||
|
## [1.24.2](https://github.com/go-gitea/gitea/releases/tag/v1.24.2) - 2025-06-20
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix container range bug (#34795) (#34796)
|
||||||
|
* Upgrade chi to v5.2.2 (#34798) (#34799)
|
||||||
|
* BUILD
|
||||||
|
* Bump poetry feature to new url for dev container (#34787) (#34790)
|
||||||
|
|
||||||
|
## [1.24.1](https://github.com/go-gitea/gitea/releases/tag/v1.24.1) - 2025-06-18
|
||||||
|
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Improve alignment of commit status icon on commit page (#34750) (#34757)
|
||||||
|
* Support title and body query parameters for new PRs (#34537) (#34752)
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* When using rules to delete packages, remove unclean bugs (#34632) (#34761)
|
||||||
|
* Fix ghost user in feeds when pushing in an actions, it should be gitea-actions (#34703) (#34756)
|
||||||
|
* Prevent double markdown link brackets when pasting URL (#34745) (#34748)
|
||||||
|
* Prevent duplicate form submissions when creating forks (#34714) (#34735)
|
||||||
|
* Fix markdown wrap (#34697) (#34702)
|
||||||
|
* Fix pull requests API convert panic when head repository is deleted. (#34685) (#34687)
|
||||||
|
* Fix commit message rendering and some UI problems (#34680) (#34683)
|
||||||
|
* Fix container range bug (#34725) (#34732)
|
||||||
|
* Fix incorrect cli default values (#34765) (#34766)
|
||||||
|
* Fix dropdown filter (#34708) (#34711)
|
||||||
|
* Hide href attribute of a tag if there is no target_url (#34556) (#34684)
|
||||||
|
* Fix tag target (#34781) #34783
|
||||||
|
|
||||||
|
## [1.24.0](https://github.com/go-gitea/gitea/releases/tag/v1.24.0) - 2025-05-26
|
||||||
|
|
||||||
* BREAKING
|
* BREAKING
|
||||||
* Make Gitea always use its internal config, ignore `/etc/gitconfig` (#33076)
|
* Make Gitea always use its internal config, ignore `/etc/gitconfig` (#33076)
|
||||||
@@ -374,7 +486,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
|
|||||||
* Bump x/net (#32896) (#32900)
|
* Bump x/net (#32896) (#32900)
|
||||||
* Only activity tab needs heatmap data loading (#34652)
|
* Only activity tab needs heatmap data loading (#34652)
|
||||||
|
|
||||||
## [1.23.8](https://github.com/go-gitea/gitea/releases/tag/1.23.8) - 2025-05-11
|
## [1.23.8](https://github.com/go-gitea/gitea/releases/tag/v1.23.8) - 2025-05-11
|
||||||
|
|
||||||
* SECURITY
|
* SECURITY
|
||||||
* Fix a bug when uploading file via lfs ssh command (#34408) (#34411)
|
* Fix a bug when uploading file via lfs ssh command (#34408) (#34411)
|
||||||
@@ -401,7 +513,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
|
|||||||
* Bump go version in go.mod (#34160)
|
* Bump go version in go.mod (#34160)
|
||||||
* remove hardcoded 'code' string in clone_panel.tmpl (#34153) (#34158)
|
* remove hardcoded 'code' string in clone_panel.tmpl (#34153) (#34158)
|
||||||
|
|
||||||
## [1.23.7](https://github.com/go-gitea/gitea/releases/tag/1.23.7) - 2025-04-07
|
## [1.23.7](https://github.com/go-gitea/gitea/releases/tag/v1.23.7) - 2025-04-07
|
||||||
|
|
||||||
* Enhancements
|
* Enhancements
|
||||||
* Add a config option to block "expensive" pages for anonymous users (#34024) (#34071)
|
* Add a config option to block "expensive" pages for anonymous users (#34024) (#34071)
|
||||||
@@ -499,7 +611,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
|
|||||||
* BUGFIXES
|
* BUGFIXES
|
||||||
* Fix a bug caused by status webhook template #33512
|
* Fix a bug caused by status webhook template #33512
|
||||||
|
|
||||||
## [1.23.2](https://github.com/go-gitea/gitea/releases/tag/1.23.2) - 2025-02-04
|
## [1.23.2](https://github.com/go-gitea/gitea/releases/tag/v1.23.2) - 2025-02-04
|
||||||
|
|
||||||
* BREAKING
|
* BREAKING
|
||||||
* Add tests for webhook and fix some webhook bugs (#33396) (#33442)
|
* Add tests for webhook and fix some webhook bugs (#33396) (#33442)
|
||||||
@@ -3029,7 +3141,7 @@ Key highlights of this release encompass significant changes categorized under `
|
|||||||
* Improve decryption failure message (#24573) (#24575)
|
* Improve decryption failure message (#24573) (#24575)
|
||||||
* Makefile: Use portable !, not GNUish -not, with find(1). (#24565) (#24572)
|
* Makefile: Use portable !, not GNUish -not, with find(1). (#24565) (#24572)
|
||||||
|
|
||||||
## [1.19.3](https://github.com/go-gitea/gitea/releases/tag/1.19.3) - 2023-05-03
|
## [1.19.3](https://github.com/go-gitea/gitea/releases/tag/v1.19.3) - 2023-05-03
|
||||||
|
|
||||||
* SECURITY
|
* SECURITY
|
||||||
* Use golang 1.20.4 to fix CVE-2023-24539, CVE-2023-24540, and CVE-2023-29400
|
* Use golang 1.20.4 to fix CVE-2023-24539, CVE-2023-24540, and CVE-2023-29400
|
||||||
@@ -3042,7 +3154,7 @@ Key highlights of this release encompass significant changes categorized under `
|
|||||||
* Fix incorrect CurrentUser check for docker rootless (#24435)
|
* Fix incorrect CurrentUser check for docker rootless (#24435)
|
||||||
* Getting the tag list does not require being signed in (#24413) (#24416)
|
* Getting the tag list does not require being signed in (#24413) (#24416)
|
||||||
|
|
||||||
## [1.19.2](https://github.com/go-gitea/gitea/releases/tag/1.19.2) - 2023-04-26
|
## [1.19.2](https://github.com/go-gitea/gitea/releases/tag/v1.19.2) - 2023-04-26
|
||||||
|
|
||||||
* SECURITY
|
* SECURITY
|
||||||
* Require repo scope for PATs for private repos and basic authentication (#24362) (#24364)
|
* Require repo scope for PATs for private repos and basic authentication (#24362) (#24364)
|
||||||
@@ -3541,7 +3653,7 @@ Key highlights of this release encompass significant changes categorized under `
|
|||||||
* Display attachments of review comment when comment content is blank (#23035) (#23046)
|
* Display attachments of review comment when comment content is blank (#23035) (#23046)
|
||||||
* Return empty url for submodule tree entries (#23043) (#23048)
|
* Return empty url for submodule tree entries (#23043) (#23048)
|
||||||
|
|
||||||
## [1.18.4](https://github.com/go-gitea/gitea/releases/tag/1.18.4) - 2023-02-20
|
## [1.18.4](https://github.com/go-gitea/gitea/releases/tag/v1.18.4) - 2023-02-20
|
||||||
|
|
||||||
* SECURITY
|
* SECURITY
|
||||||
* Provide the ability to set password hash algorithm parameters (#22942) (#22943)
|
* Provide the ability to set password hash algorithm parameters (#22942) (#22943)
|
||||||
@@ -3968,7 +4080,7 @@ Key highlights of this release encompass significant changes categorized under `
|
|||||||
* Fix the mode of custom dir to 0700 in docker-rootless (#20861) (#20867)
|
* Fix the mode of custom dir to 0700 in docker-rootless (#20861) (#20867)
|
||||||
* Fix UI mis-align for PR commit history (#20845) (#20859)
|
* Fix UI mis-align for PR commit history (#20845) (#20859)
|
||||||
|
|
||||||
## [1.17.1](https://github.com/go-gitea/gitea/releases/tag/1.17.1) - 2022-08-17
|
## [1.17.1](https://github.com/go-gitea/gitea/releases/tag/v1.17.1) - 2022-08-17
|
||||||
|
|
||||||
* SECURITY
|
* SECURITY
|
||||||
* Correctly escape within tribute.js (#20831) (#20832)
|
* Correctly escape within tribute.js (#20831) (#20832)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ These are the values to which people in the Gitea community should aspire.
|
|||||||
- **Be constructive.**
|
- **Be constructive.**
|
||||||
- Avoid derailing: stay on topic; if you want to talk about something else, start a new conversation.
|
- Avoid derailing: stay on topic; if you want to talk about something else, start a new conversation.
|
||||||
- Avoid unconstructive criticism: don't merely decry the current state of affairs; offer—or at least solicit—suggestions as to how things may be improved.
|
- Avoid unconstructive criticism: don't merely decry the current state of affairs; offer—or at least solicit—suggestions as to how things may be improved.
|
||||||
- Avoid snarking (pithy, unproductive, sniping comments).
|
- Avoid snarking (pithy, unproductive, sniping comments)
|
||||||
- Avoid discussing potentially offensive or sensitive issues; this all too often leads to unnecessary conflict.
|
- Avoid discussing potentially offensive or sensitive issues; this all too often leads to unnecessary conflict.
|
||||||
- Avoid microaggressions (brief and commonplace verbal, behavioral and environmental indignities that communicate hostile, derogatory or negative slights and insults to a person or group).
|
- Avoid microaggressions (brief and commonplace verbal, behavioral and environmental indignities that communicate hostile, derogatory or negative slights and insults to a person or group).
|
||||||
- **Be responsible.**
|
- **Be responsible.**
|
||||||
@@ -42,7 +42,7 @@ People are complicated. You should expect to be misunderstood and to misundersta
|
|||||||
|
|
||||||
### Our Pledge
|
### Our Pledge
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
### Our Standards
|
### Our Standards
|
||||||
|
|
||||||
|
|||||||
@@ -591,7 +591,7 @@ be reviewed by two maintainers and must pass the automatic tests.
|
|||||||
## Releasing Gitea
|
## Releasing Gitea
|
||||||
|
|
||||||
- Let $vmaj, $vmin and $vpat be Major, Minor and Patch version numbers, $vpat should be rc1, rc2, 0, 1, ...... $vmaj.$vmin will be kept the same as milestones on github or gitea in future.
|
- Let $vmaj, $vmin and $vpat be Major, Minor and Patch version numbers, $vpat should be rc1, rc2, 0, 1, ...... $vmaj.$vmin will be kept the same as milestones on github or gitea in future.
|
||||||
- Before releasing, confirm all the version's milestone issues or PRs has been resolved. Then discuss the release on Discord channel #maintainers and get agreed with almost all the owners and mergers. Or you can declare the version and if nobody is against it in about several hours.
|
- Before releasing, confirm all the version's milestone issues or PRs has been resolved. Then discuss the release on Discord channel #maintainers and get agreed with almost all the owners and mergers. Or you can declare the version and if nobody against in about serval hours.
|
||||||
- If this is a big version first you have to create PR for changelog on branch `main` with PRs with label `changelog` and after it has been merged do following steps:
|
- If this is a big version first you have to create PR for changelog on branch `main` with PRs with label `changelog` and after it has been merged do following steps:
|
||||||
- Create `-dev` tag as `git tag -s -F release.notes v$vmaj.$vmin.0-dev` and push the tag as `git push origin v$vmaj.$vmin.0-dev`.
|
- Create `-dev` tag as `git tag -s -F release.notes v$vmaj.$vmin.0-dev` and push the tag as `git push origin v$vmaj.$vmin.0-dev`.
|
||||||
- When CI has finished building tag then you have to create a new branch named `release/v$vmaj.$vmin`
|
- When CI has finished building tag then you have to create a new branch named `release/v$vmaj.$vmin`
|
||||||
|
|||||||
50
Dockerfile
50
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.24-alpine3.22 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY=direct
|
ARG GOPROXY
|
||||||
|
ENV GOPROXY=${GOPROXY:-direct}
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
ARG GITEA_VERSION
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
@@ -14,32 +14,35 @@ RUN apk --no-cache add \
|
|||||||
build-base \
|
build-base \
|
||||||
git \
|
git \
|
||||||
nodejs \
|
nodejs \
|
||||||
pnpm
|
npm \
|
||||||
|
&& 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/ . .
|
|
||||||
|
|
||||||
# Build gitea, .git mount is required for version data
|
# Checkout version if set
|
||||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||||
--mount=type=cache,target="/root/.cache/go-build" \
|
&& make clean-all 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 for builds that made under windows which strips the executable bit from file
|
# Set permissions
|
||||||
RUN chmod 755 /tmp/local/usr/bin/entrypoint \
|
RUN chmod 755 /tmp/local/usr/bin/entrypoint \
|
||||||
/tmp/local/usr/local/bin/* \
|
/tmp/local/usr/local/bin/gitea \
|
||||||
/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
|
||||||
|
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.22 AS gitea
|
FROM docker.io/library/alpine:3.22
|
||||||
|
LABEL maintainer="maintainers@gitea.io"
|
||||||
|
|
||||||
EXPOSE 22 3000
|
EXPOSE 22 3000
|
||||||
|
|
||||||
@@ -54,7 +57,8 @@ 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 \
|
||||||
@@ -68,9 +72,6 @@ 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
|
||||||
|
|
||||||
@@ -78,3 +79,8 @@ 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
|
||||||
|
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
|
||||||
|
|||||||
@@ -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.24-alpine3.22 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY=direct
|
ARG GOPROXY
|
||||||
|
ENV GOPROXY=${GOPROXY:-direct}
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
ARG GITEA_VERSION
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
@@ -14,26 +14,33 @@ RUN apk --no-cache add \
|
|||||||
build-base \
|
build-base \
|
||||||
git \
|
git \
|
||||||
nodejs \
|
nodejs \
|
||||||
pnpm
|
npm \
|
||||||
|
&& 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/ . .
|
|
||||||
|
|
||||||
# Build gitea, .git mount is required for version data
|
# Checkout version if set
|
||||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||||
--mount=type=cache,target="/root/.cache/go-build" \
|
&& make clean-all 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 for builds that made under windows which strips the executable bit from file
|
# Set permissions
|
||||||
RUN chmod 755 /tmp/local/usr/local/bin/* \
|
RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
|
||||||
/go/src/code.gitea.io/gitea/gitea
|
/tmp/local/usr/local/bin/docker-setup.sh \
|
||||||
|
/tmp/local/usr/local/bin/gitea \
|
||||||
|
/go/src/code.gitea.io/gitea/gitea \
|
||||||
|
/go/src/code.gitea.io/gitea/environment-to-ini
|
||||||
|
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.22 AS gitea-rootless
|
FROM docker.io/library/alpine:3.22
|
||||||
|
LABEL maintainer="maintainers@gitea.io"
|
||||||
|
|
||||||
EXPOSE 2222 3000
|
EXPOSE 2222 3000
|
||||||
|
|
||||||
@@ -45,7 +52,7 @@ RUN apk --no-cache add \
|
|||||||
git \
|
git \
|
||||||
curl \
|
curl \
|
||||||
gnupg \
|
gnupg \
|
||||||
openssh-keygen
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
RUN addgroup \
|
RUN addgroup \
|
||||||
-S -g 1000 \
|
-S -g 1000 \
|
||||||
@@ -63,6 +70,8 @@ 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
|
||||||
|
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
|
||||||
|
|
||||||
# git:git
|
# git:git
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ a1012112796 <1012112796@qq.com> (@a1012112796)
|
|||||||
Karl Heinz Marbaise <kama@soebes.de> (@khmarbaise)
|
Karl Heinz Marbaise <kama@soebes.de> (@khmarbaise)
|
||||||
Norwin Roosen <git@nroo.de> (@noerw)
|
Norwin Roosen <git@nroo.de> (@noerw)
|
||||||
Kyle Dumont <kdumontnu@gmail.com> (@kdumontnu)
|
Kyle Dumont <kdumontnu@gmail.com> (@kdumontnu)
|
||||||
|
Patrick Schratz <patrick.schratz@gmail.com> (@pat-s)
|
||||||
Janis Estelmann <admin@oldschoolhack.me> (@KN4CK3R)
|
Janis Estelmann <admin@oldschoolhack.me> (@KN4CK3R)
|
||||||
|
Steven Kriegler <sk.bunsenbrenner@gmail.com> (@justusbunsi)
|
||||||
Jimmy Praet <jimmy.praet@telenet.be> (@jpraet)
|
Jimmy Praet <jimmy.praet@telenet.be> (@jpraet)
|
||||||
Leon Hofmeister <dev.lh@web.de> (@delvh)
|
Leon Hofmeister <dev.lh@web.de> (@delvh)
|
||||||
Wim <wim@42.be> (@42wim)
|
Wim <wim@42.be> (@42wim)
|
||||||
@@ -62,5 +64,3 @@ Rowan Bohde <rowan.bohde@gmail.com> (@bohde)
|
|||||||
hiifong <i@hiif.ong> (@hiifong)
|
hiifong <i@hiif.ong> (@hiifong)
|
||||||
metiftikci <metiftikci@hotmail.com> (@metiftikci)
|
metiftikci <metiftikci@hotmail.com> (@metiftikci)
|
||||||
Christopher Homberger <christopher.homberger@web.de> (@ChristopherHX)
|
Christopher Homberger <christopher.homberger@web.de> (@ChristopherHX)
|
||||||
Tobias Balle-Petersen <tobiasbp@gmail.com> (@tobiasbp)
|
|
||||||
TheFox <thefox0x7@gmail.com> (@TheFox0x7)
|
|
||||||
|
|||||||
156
Makefile
156
Makefile
@@ -18,29 +18,25 @@ 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)
|
||||||
COMMA := ,
|
COMMA := ,
|
||||||
|
|
||||||
XGO_VERSION := go-1.25.x
|
XGO_VERSION := go-1.24.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.2.1
|
||||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.2
|
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
|
||||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.6.0
|
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.0.2
|
||||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15
|
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.12
|
||||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.7.0
|
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0
|
||||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.33.1
|
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
|
||||||
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
|
||||||
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.20.0
|
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.1
|
||||||
|
|
||||||
DOCKER_IMAGE ?= gitea/gitea
|
DOCKER_IMAGE ?= gitea/gitea
|
||||||
DOCKER_TAG ?= latest
|
DOCKER_TAG ?= latest
|
||||||
@@ -95,19 +91,11 @@ ifeq ($(RACE_ENABLED),true)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
STORED_VERSION_FILE := VERSION
|
STORED_VERSION_FILE := VERSION
|
||||||
|
HUGO_VERSION ?= 0.111.3
|
||||||
|
|
||||||
GITHUB_REF_TYPE ?= branch
|
GITHUB_REF_TYPE ?= branch
|
||||||
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
|
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
|
||||||
|
|
||||||
# Enable typescript support in Node.js before 22.18
|
|
||||||
# TODO: Remove this once we can raise the minimum Node.js version to 22.18 (alpine >= 3.23)
|
|
||||||
NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v 2>/dev/null | cut -c2- | tr '.' ' '))
|
|
||||||
ifeq ($(shell test "$(NODE_VERSION)" -lt "022018000"; echo $$?),0)
|
|
||||||
NODE_VARS := NODE_OPTIONS="--experimental-strip-types"
|
|
||||||
else
|
|
||||||
NODE_VARS :=
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(GITHUB_REF_TYPE),branch)
|
ifneq ($(GITHUB_REF_TYPE),branch)
|
||||||
VERSION ?= $(subst v,,$(GITHUB_REF_NAME))
|
VERSION ?= $(subst v,,$(GITHUB_REF_NAME))
|
||||||
GITEA_VERSION ?= $(VERSION)
|
GITEA_VERSION ?= $(VERSION)
|
||||||
@@ -139,11 +127,12 @@ GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/m
|
|||||||
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
|
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
|
||||||
|
|
||||||
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
|
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
|
||||||
WEBPACK_CONFIGS := webpack.config.ts tailwind.config.ts
|
WEBPACK_CONFIGS := webpack.config.js tailwind.config.js
|
||||||
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
|
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
|
||||||
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
|
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
|
||||||
|
|
||||||
BINDATA_DEST_WILDCARD := modules/migration/bindata.* modules/public/bindata.* modules/options/bindata.* modules/templates/bindata.*
|
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
|
||||||
|
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
|
||||||
|
|
||||||
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
|
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
|
||||||
|
|
||||||
@@ -162,17 +151,23 @@ 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 tools
|
GO_DIRS := build cmd models modules routers services tests
|
||||||
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 *.js *.ts *.cjs 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))
|
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*))
|
||||||
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)
|
||||||
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go")
|
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/options/bindata.go ! -path modules/public/bindata.go ! -path modules/templates/bindata.go)
|
||||||
GO_SOURCES += $(GENERATED_GO_DEST)
|
GO_SOURCES += $(GENERATED_GO_DEST)
|
||||||
|
GO_SOURCES_NO_BINDATA := $(GO_SOURCES)
|
||||||
|
|
||||||
|
ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
|
||||||
|
GO_SOURCES += $(BINDATA_DEST)
|
||||||
|
GENERATED_GO_DEST += $(BINDATA_DEST)
|
||||||
|
endif
|
||||||
|
|
||||||
# Force installation of playwright dependencies by setting this flag
|
# Force installation of playwright dependencies by setting this flag
|
||||||
ifdef DEPS_PLAYWRIGHT
|
ifdef DEPS_PLAYWRIGHT
|
||||||
@@ -198,10 +193,6 @@ 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
|
||||||
|
|
||||||
@@ -233,13 +224,10 @@ git-check:
|
|||||||
node-check:
|
node-check:
|
||||||
$(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p'))
|
$(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p'))
|
||||||
$(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' ')))
|
$(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' ')))
|
||||||
$(eval PNPM_MISSING := $(shell hash pnpm > /dev/null 2>&1 || echo 1))
|
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');))
|
||||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" ]; then \
|
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
|
||||||
echo "Gitea requires Node.js $(MIN_NODE_VERSION_STR) or greater to build. You can get it at https://nodejs.org/en/download/"; \
|
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
||||||
exit 1; \
|
echo "Gitea requires Node.js $(MIN_NODE_VERSION_STR) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||||
fi
|
|
||||||
@if [ "$(PNPM_MISSING)" = "1" ]; then \
|
|
||||||
echo "Gitea requires pnpm to build. You can install it at https://pnpm.io/installation"; \
|
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -249,7 +237,7 @@ clean-all: clean ## delete backend, frontend and integration files
|
|||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean: ## delete backend and integration files
|
clean: ## delete backend and integration files
|
||||||
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST_WILDCARD) \
|
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
|
||||||
integrations*.test \
|
integrations*.test \
|
||||||
e2e*.test \
|
e2e*.test \
|
||||||
tests/integration/gitea-integration-* \
|
tests/integration/gitea-integration-* \
|
||||||
@@ -260,8 +248,8 @@ clean: ## delete backend and integration files
|
|||||||
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
|
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
|
||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt: ## format the Go and template code
|
fmt: ## format the Go code
|
||||||
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run tools/code-batch-process.go gitea-fmt -w '{file-list}'
|
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/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
|
||||||
@@ -291,7 +279,7 @@ endif
|
|||||||
.PHONY: generate-swagger
|
.PHONY: generate-swagger
|
||||||
generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments
|
generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments
|
||||||
|
|
||||||
$(SWAGGER_SPEC): $(GO_SOURCES) $(SWAGGER_SPEC_INPUT)
|
$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA) $(SWAGGER_SPEC_INPUT)
|
||||||
$(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)'
|
$(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)'
|
||||||
|
|
||||||
.PHONY: swagger-check
|
.PHONY: swagger-check
|
||||||
@@ -340,37 +328,37 @@ lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backen
|
|||||||
|
|
||||||
.PHONY: lint-js
|
.PHONY: lint-js
|
||||||
lint-js: node_modules ## lint js files
|
lint-js: node_modules ## lint js files
|
||||||
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --flag unstable_native_nodejs_ts_config $(ESLINT_FILES)
|
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES)
|
||||||
$(NODE_VARS) pnpm exec vue-tsc
|
npx vue-tsc
|
||||||
|
|
||||||
.PHONY: lint-js-fix
|
.PHONY: lint-js-fix
|
||||||
lint-js-fix: node_modules ## lint js files and fix issues
|
lint-js-fix: node_modules ## lint js files and fix issues
|
||||||
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --flag unstable_native_nodejs_ts_config $(ESLINT_FILES) --fix
|
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) --fix
|
||||||
$(NODE_VARS) pnpm exec vue-tsc
|
npx vue-tsc
|
||||||
|
|
||||||
.PHONY: lint-css
|
.PHONY: lint-css
|
||||||
lint-css: node_modules ## lint css files
|
lint-css: node_modules ## lint css files
|
||||||
$(NODE_VARS) pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES)
|
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES)
|
||||||
|
|
||||||
.PHONY: lint-css-fix
|
.PHONY: lint-css-fix
|
||||||
lint-css-fix: node_modules ## lint css files and fix issues
|
lint-css-fix: node_modules ## lint css files and fix issues
|
||||||
$(NODE_VARS) pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
|
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
|
||||||
|
|
||||||
.PHONY: lint-swagger
|
.PHONY: lint-swagger
|
||||||
lint-swagger: node_modules ## lint swagger files
|
lint-swagger: node_modules ## lint swagger files
|
||||||
$(NODE_VARS) pnpm exec spectral lint -q -F hint $(SWAGGER_SPEC)
|
npx spectral lint -q -F hint $(SWAGGER_SPEC)
|
||||||
|
|
||||||
.PHONY: lint-md
|
.PHONY: lint-md
|
||||||
lint-md: node_modules ## lint markdown files
|
lint-md: node_modules ## lint markdown files
|
||||||
$(NODE_VARS) pnpm exec markdownlint *.md
|
npx markdownlint *.md
|
||||||
|
|
||||||
.PHONY: lint-spell
|
.PHONY: lint-spell
|
||||||
lint-spell: ## lint spelling
|
lint-spell: ## lint spelling
|
||||||
@go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -error $(SPELLCHECK_FILES)
|
@go run $(MISSPELL_PACKAGE) -dict tools/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 assets/misspellings.csv -w $(SPELLCHECK_FILES)
|
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
|
||||||
|
|
||||||
.PHONY: lint-go
|
.PHONY: lint-go
|
||||||
lint-go: ## lint go files
|
lint-go: ## lint go files
|
||||||
@@ -390,12 +378,13 @@ 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..."
|
||||||
@$(GO) vet -vettool="$(shell GOOS= GOARCH= go tool -n gitea-vet)" ./...
|
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
||||||
|
@$(GO) vet -vettool=gitea-vet ./...
|
||||||
|
|
||||||
.PHONY: lint-go-gopls
|
.PHONY: lint-go-gopls
|
||||||
lint-go-gopls: ## lint go files with gopls
|
lint-go-gopls: ## lint go files with gopls
|
||||||
@echo "Running gopls check..."
|
@echo "Running gopls check..."
|
||||||
@GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES)
|
@GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA)
|
||||||
|
|
||||||
.PHONY: lint-editorconfig
|
.PHONY: lint-editorconfig
|
||||||
lint-editorconfig:
|
lint-editorconfig:
|
||||||
@@ -408,12 +397,12 @@ lint-actions: ## lint action workflow files
|
|||||||
|
|
||||||
.PHONY: lint-templates
|
.PHONY: lint-templates
|
||||||
lint-templates: .venv node_modules ## lint template files
|
lint-templates: .venv node_modules ## lint template files
|
||||||
@node tools/lint-templates-svg.ts
|
@node tools/lint-templates-svg.js
|
||||||
@uv run --frozen djlint $(shell find templates -type f -iname '*.tmpl')
|
@poetry run djlint $(shell find templates -type f -iname '*.tmpl')
|
||||||
|
|
||||||
.PHONY: lint-yaml
|
.PHONY: lint-yaml
|
||||||
lint-yaml: .venv ## lint yaml files
|
lint-yaml: .venv ## lint yaml files
|
||||||
@uv run --frozen yamllint -s .
|
@poetry run yamllint -s .
|
||||||
|
|
||||||
.PHONY: watch
|
.PHONY: watch
|
||||||
watch: ## watch everything and continuously rebuild
|
watch: ## watch everything and continuously rebuild
|
||||||
@@ -422,7 +411,7 @@ watch: ## watch everything and continuously rebuild
|
|||||||
.PHONY: watch-frontend
|
.PHONY: watch-frontend
|
||||||
watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild
|
watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild
|
||||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||||
NODE_ENV=development $(NODE_VARS) pnpm exec webpack --watch --progress --disable-interpret
|
NODE_ENV=development npx webpack --watch --progress
|
||||||
|
|
||||||
.PHONY: watch-backend
|
.PHONY: watch-backend
|
||||||
watch-backend: go-check ## watch backend files and continuously rebuild
|
watch-backend: go-check ## watch backend files and continuously rebuild
|
||||||
@@ -438,7 +427,7 @@ test-backend: ## test backend files
|
|||||||
|
|
||||||
.PHONY: test-frontend
|
.PHONY: test-frontend
|
||||||
test-frontend: node_modules ## test frontend files
|
test-frontend: node_modules ## test frontend files
|
||||||
$(NODE_VARS) pnpm exec vitest
|
npx vitest
|
||||||
|
|
||||||
.PHONY: test-check
|
.PHONY: test-check
|
||||||
test-check:
|
test-check:
|
||||||
@@ -461,7 +450,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 tools/gocovmerge.go integration.coverage-bodged.out coverage-bodged.out > coverage.all
|
$(GO) run build/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:
|
||||||
@@ -581,7 +570,7 @@ test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test
|
|||||||
|
|
||||||
.PHONY: playwright
|
.PHONY: playwright
|
||||||
playwright: deps-frontend
|
playwright: deps-frontend
|
||||||
$(NODE_VARS) pnpm exec playwright install $(PLAYWRIGHT_FLAGS)
|
npx playwright install $(PLAYWRIGHT_FLAGS)
|
||||||
|
|
||||||
.PHONY: test-e2e%
|
.PHONY: test-e2e%
|
||||||
test-e2e%: TEST_TYPE ?= e2e
|
test-e2e%: TEST_TYPE ?= e2e
|
||||||
@@ -759,7 +748,7 @@ generate-go: $(TAGS_PREREQ)
|
|||||||
|
|
||||||
.PHONY: security-check
|
.PHONY: security-check
|
||||||
security-check:
|
security-check:
|
||||||
GOEXPERIMENT= go run $(GOVULNCHECK_PACKAGE) -show color ./...
|
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))),)
|
||||||
@@ -843,12 +832,12 @@ deps-tools: ## install tool dependencies
|
|||||||
$(GO) install $(GOPLS_PACKAGE) & \
|
$(GO) install $(GOPLS_PACKAGE) & \
|
||||||
wait
|
wait
|
||||||
|
|
||||||
node_modules: pnpm-lock.yaml
|
node_modules: package-lock.json
|
||||||
$(NODE_VARS) pnpm install --frozen-lockfile
|
npm install --no-save
|
||||||
@touch node_modules
|
@touch node_modules
|
||||||
|
|
||||||
.venv: uv.lock
|
.venv: poetry.lock
|
||||||
uv sync
|
poetry install
|
||||||
@touch .venv
|
@touch .venv
|
||||||
|
|
||||||
.PHONY: update
|
.PHONY: update
|
||||||
@@ -856,34 +845,34 @@ update: update-js update-py ## update js and py dependencies
|
|||||||
|
|
||||||
.PHONY: update-js
|
.PHONY: update-js
|
||||||
update-js: node-check | node_modules ## update js dependencies
|
update-js: node-check | node_modules ## update js dependencies
|
||||||
$(NODE_VARS) pnpm exec updates -u -f package.json
|
npx updates -u -f package.json
|
||||||
rm -rf node_modules pnpm-lock.yaml
|
rm -rf node_modules package-lock.json
|
||||||
$(NODE_VARS) pnpm install
|
npm install --package-lock
|
||||||
$(NODE_VARS) pnpm exec nolyfill install
|
npx nolyfill install
|
||||||
$(NODE_VARS) pnpm install
|
npm install --package-lock
|
||||||
@touch node_modules
|
@touch node_modules
|
||||||
|
|
||||||
.PHONY: update-py
|
.PHONY: update-py
|
||||||
update-py: node-check | node_modules ## update py dependencies
|
update-py: node-check | node_modules ## update py dependencies
|
||||||
$(NODE_VARS) pnpm exec updates -u -f pyproject.toml
|
npx updates -u -f pyproject.toml
|
||||||
rm -rf .venv uv.lock
|
rm -rf .venv poetry.lock
|
||||||
uv sync
|
poetry install
|
||||||
@touch .venv
|
@touch .venv
|
||||||
|
|
||||||
.PHONY: webpack
|
.PHONY: webpack
|
||||||
webpack: $(WEBPACK_DEST) ## build webpack files
|
webpack: $(WEBPACK_DEST) ## build webpack files
|
||||||
|
|
||||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) pnpm-lock.yaml
|
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
||||||
@$(MAKE) -s node-check node_modules
|
@$(MAKE) -s node-check node_modules
|
||||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||||
@echo "Running webpack..."
|
@echo "Running webpack..."
|
||||||
@BROWSERSLIST_IGNORE_OLD_DATA=true $(NODE_VARS) pnpm exec webpack --disable-interpret
|
@BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack
|
||||||
@touch $(WEBPACK_DEST)
|
@touch $(WEBPACK_DEST)
|
||||||
|
|
||||||
.PHONY: svg
|
.PHONY: svg
|
||||||
svg: node-check | node_modules ## build svg files
|
svg: node-check | node_modules ## build svg files
|
||||||
rm -rf $(SVG_DEST_DIR)
|
rm -rf $(SVG_DEST_DIR)
|
||||||
node tools/generate-svg.ts
|
node tools/generate-svg.js
|
||||||
|
|
||||||
.PHONY: svg-check
|
.PHONY: svg-check
|
||||||
svg-check: svg
|
svg-check: svg
|
||||||
@@ -897,11 +886,11 @@ svg-check: svg
|
|||||||
|
|
||||||
.PHONY: lockfile-check
|
.PHONY: lockfile-check
|
||||||
lockfile-check:
|
lockfile-check:
|
||||||
$(NODE_VARS) pnpm install --frozen-lockfile
|
npm install --package-lock-only
|
||||||
@diff=$$(git diff --color=always pnpm-lock.yaml); \
|
@diff=$$(git diff --color=always package-lock.json); \
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "pnpm-lock.yaml is inconsistent with package.json"; \
|
echo "package-lock.json is inconsistent with package.json"; \
|
||||||
echo "Please run 'pnpm install --frozen-lockfile' and commit the result:"; \
|
echo "Please run 'npm install --package-lock-only' and commit the result:"; \
|
||||||
printf "%s" "$${diff}"; \
|
printf "%s" "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
@@ -921,8 +910,9 @@ generate-gitignore: ## update gitignore files
|
|||||||
$(GO) run build/generate-gitignores.go
|
$(GO) run build/generate-gitignores.go
|
||||||
|
|
||||||
.PHONY: generate-images
|
.PHONY: generate-images
|
||||||
generate-images: | node_modules ## generate images
|
generate-images: | node_modules
|
||||||
cd tools && node generate-images.ts $(TAGS)
|
npm install --no-save fabric@6 imagemin-zopfli@7
|
||||||
|
node tools/generate-images.js $(TAGS)
|
||||||
|
|
||||||
.PHONY: generate-manpage
|
.PHONY: generate-manpage
|
||||||
generate-manpage: ## generate manpage
|
generate-manpage: ## generate manpage
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ or if SQLite support is required:
|
|||||||
The `build` target is split into two sub-targets:
|
The `build` target is split into two sub-targets:
|
||||||
|
|
||||||
- `make backend` which requires [Go Stable](https://go.dev/dl/), the required version is defined in [go.mod](/go.mod).
|
- `make backend` which requires [Go Stable](https://go.dev/dl/), the required version is defined in [go.mod](/go.mod).
|
||||||
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and [pnpm](https://pnpm.io/installation).
|
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater.
|
||||||
|
|
||||||
Internet connectivity is required to download the go and npm modules. When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js.
|
Internet connectivity is required to download the go and npm modules. When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js.
|
||||||
|
|
||||||
@@ -80,9 +80,9 @@ Expected workflow is: Fork -> Patch -> Push -> Pull Request
|
|||||||
|
|
||||||
[](https://translate.gitea.com)
|
[](https://translate.gitea.com)
|
||||||
|
|
||||||
Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language, ask one of the managers in the Crowdin project to add a new language there.
|
Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
|
||||||
|
|
||||||
You can also just create an issue for adding a language or ask on Discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty, but we hope to fill it as questions pop up.
|
You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope to fill it as questions pop up.
|
||||||
|
|
||||||
Get more information from [documentation](https://docs.gitea.com/contributing/localization).
|
Get more information from [documentation](https://docs.gitea.com/contributing/localization).
|
||||||
|
|
||||||
|
|||||||
54
SECURITY.md
54
SECURITY.md
@@ -14,12 +14,12 @@ Please **DO NOT** file a public issue, instead send your report privately to `se
|
|||||||
|
|
||||||
Due to the sensitive nature of security information, you can use the below GPG public key to encrypt your mail body.
|
Due to the sensitive nature of security information, you can use the below GPG public key to encrypt your mail body.
|
||||||
|
|
||||||
The PGP key is valid until July 4, 2026.
|
The PGP key is valid until July 9, 2025.
|
||||||
|
|
||||||
```
|
```
|
||||||
Key ID: 6FCD2D5B
|
Key ID: 6FCD2D5B
|
||||||
Key Type: RSA
|
Key Type: RSA
|
||||||
Expires: 7/4/2026
|
Expires: 7/9/2025
|
||||||
Key Size: 4096/4096
|
Key Size: 4096/4096
|
||||||
Fingerprint: 3DE0 3D1E 144A 7F06 9359 99DC AAFD 2381 6FCD 2D5B
|
Fingerprint: 3DE0 3D1E 144A 7F06 9359 99DC AAFD 2381 6FCD 2D5B
|
||||||
```
|
```
|
||||||
@@ -42,18 +42,18 @@ lzpAjnN9/KLtQroutrm+Ft0mdjDiJUeFVl1cOHDhoyfCsQh62HumoyZoZvqzQd6e
|
|||||||
AbN11nq6aViMe2Q3je1AbiBnRnQSHxt1Tc8X4IshO3MQK1Sk7oPI6LA5oQARAQAB
|
AbN11nq6aViMe2Q3je1AbiBnRnQSHxt1Tc8X4IshO3MQK1Sk7oPI6LA5oQARAQAB
|
||||||
tCJHaXRlYSBTZWN1cml0eSA8c2VjdXJpdHlAZ2l0ZWEuaW8+iQJXBBMBCABBAhsD
|
tCJHaXRlYSBTZWN1cml0eSA8c2VjdXJpdHlAZ2l0ZWEuaW8+iQJXBBMBCABBAhsD
|
||||||
BQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAFiEEPeA9HhRKfwaTWZncqv0jgW/N
|
BQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAFiEEPeA9HhRKfwaTWZncqv0jgW/N
|
||||||
LVsFAmhoHmkFCQeT6esACgkQqv0jgW/NLVuFLRAAmjBQSKRAgs2bFIEj7HLAbDp4
|
LVsFAmaMse0FCQW4fW8ACgkQqv0jgW/NLVtXLg/+PF4G9Jhlui15BTNlEBJAV2P/
|
||||||
f+XkdH+GsT3jRPOZ9QZgmtM+TfoE4yNgIVfOl+s4RdjM/W4QzqZuPQ55hbEHd056
|
1QlAV2krk0fP7tykn0FR9RfGIfVV/kwC1f+ouosYPQDDevl9LWdUIM+g94DtNo2o
|
||||||
cJmm7B+6GsHFcdrPmh65sOCEIyh4+t45dUfeWpFsDPqm9j1UHXAJQIpB8vDEVAPH
|
7ACpcL3morvt5lVGpIZHL8TbX0qmFRXL/pB/cB+K6IwYvh2mrbp2zH+r4SCRyFYq
|
||||||
t+3wLCk8GMPJs1o5tIyMmaO23ngvkwn8eG7KgY+rp2PzObrb5g7ppci0ILzILkrp
|
BjgXYFTI1MylJ1ShAjU6Z+m3oJ+2xs5LzHS0X6zkTjzA2Zl4zQzciQ9T+wJcE7Zi
|
||||||
HVjZsEfUWRgSVF7LuU5ppqDKrlcqwUpQq6n3kGMZcLrCp6ACKP04TBmTfUxNwdL7
|
HXdM1+YMF8KGNP8J9Rpug5oNDJ98lgZirRY7c3A/1xmYBiPnULwuuymdqEZO7l70
|
||||||
I0N7apI2Pbct9T1Gv/lYAUFWyU2c3gh/EBLbO6BukaLOFRQHrtNfdJV/YnMPlcXr
|
SeAlE1RWYX8kbOBnBb/KY4XwE3Vic1oEzc9DiPWVH1ElX86WNNsFzuyULiwoBoWg
|
||||||
LUJjK9K4eAH9DsrZqrisz/LthsC2BaNIN3KRMTk5YTYgmIh8GXzSgihORmtDFELC
|
pqZGhL9x1p5+46RGQSDczsHM7YGVtfYOiDo2PAVrmwsT0BnXnK8Oe3YIkvmUPEJu
|
||||||
RroID3pTuS0zjXh+wpY9GuPTh7UW23p42Daxca4fAT4k5EclvDRUrL21xMopPMiL
|
OkLt0Z6A5n8pz8zhQzuApwBsK4ncJ8zTCpvz/pfKKqZC/Vnoh3gKGhDGvOZ+b5IJ
|
||||||
HuNdELz4FVchRTy05PjzKVyjVInDNojE2KUxnjxZDzYJ6aT/g+coD5yfntYm8BEj
|
0kUTe2JsbnwFixDUMDtacQ1op8XOyLoLVmgqLn0+Pws4XPBlMof2bioFir3yHKnP
|
||||||
+ZzL0ndZES54hzKLpv7zwBQwFzam68clZYmDPILOPTflQDfpGEWmJK4undFU5obz
|
gNchsF1agrlSIo5GA8u4ga+IlCSfvFIKrl7+cxacKcJYt/vbOU5KcvVJI5EtHKCG
|
||||||
ZsQRz0R3ulspChATbZxO0d5LX2obLpKO9X3b5VoO1KF+R8Vjw1Y0KxrNZ6rIcfqH
|
xfHjHY2ah1Qww7SxW6IXiRZZzPpsL2mBM2CD7N3qh9bV2s27wxYCdUodsIZbiyHe
|
||||||
Z50QVQKSe9dm08K0ON+5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen
|
oWPzfBnkmiAN8KlZxHm5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen
|
||||||
T+p07yCSSoSlmnJHCQmwh4vfg1blyz0zZ4vkIhtpHsEgc+ZAG+WQXSsJ2iRz+eSN
|
T+p07yCSSoSlmnJHCQmwh4vfg1blyz0zZ4vkIhtpHsEgc+ZAG+WQXSsJ2iRz+eSN
|
||||||
GwoOQl4XC3n+QWkc1ws+btr48+6UqXIQU+F8TPQyx/PIgi2nZXJB7f5+mjCqsk46
|
GwoOQl4XC3n+QWkc1ws+btr48+6UqXIQU+F8TPQyx/PIgi2nZXJB7f5+mjCqsk46
|
||||||
XvH4nTr4kJjuqMSR/++wvre2qNQRa/q/dTsK0OaN/mJsdX6Oi+aGNaQJUhIG7F+E
|
XvH4nTr4kJjuqMSR/++wvre2qNQRa/q/dTsK0OaN/mJsdX6Oi+aGNaQJUhIG7F+E
|
||||||
@@ -65,19 +65,19 @@ s+GsP9I3cmWWQcKYxWHtE8xTXnNCVPFZQj2nwhJzae8ypfOtulBRA3dUKWGKuDH/
|
|||||||
axFENhUsT397aOU3qkP/od4a64JyNIEo4CTTSPVeWd7njsGqli2U3A4xL2CcyYvt
|
axFENhUsT397aOU3qkP/od4a64JyNIEo4CTTSPVeWd7njsGqli2U3A4xL2CcyYvt
|
||||||
D/MWcMBGEoLSNTswwKdom4FaJpn5KThnK/T0bQcmJblJhoCtppXisbexZnCpuS0x
|
D/MWcMBGEoLSNTswwKdom4FaJpn5KThnK/T0bQcmJblJhoCtppXisbexZnCpuS0x
|
||||||
Zdlm2T14KJ3LABEBAAGJAjwEGAEIACYCGwwWIQQ94D0eFEp/BpNZmdyq/SOBb80t
|
Zdlm2T14KJ3LABEBAAGJAjwEGAEIACYCGwwWIQQ94D0eFEp/BpNZmdyq/SOBb80t
|
||||||
WwUCaGgeJAUJB5PppgAKCRCq/SOBb80tW/NWEACB6Jrf0gWlk7e+hNCdnbM0ZVWU
|
WwUCZoyyjQUJBbh+DwAKCRCq/SOBb80tW18XD/9MXztmf01MT+1kZdBouZ/7Rp/7
|
||||||
f2sHNFfXxxsdhpcDgKbNHtkZb8nZgv8AX+5fTtUwMVa3vKcdw30xFiIM5N7cCIPV
|
9kuqo//B1G+RXau4oFtPqb67kNe2WaIc3u5B73PUHsMf3i6z4ib2KbMhZZerLn0O
|
||||||
vg/5z5BtfEaitnabEUG2iiVDIy8IHXIcK10rX+7BosA3QDl2PsiBHwyi5G13lRk8
|
dRglcuPeNWmsASY3dH/XVG0cT0zvvWegagd12TJEl3Vs+7XNrOw4cwDj9L1+GH9m
|
||||||
zGTSNDuOalug33h5/lr2dPigamkq74Aoy29q8Rjad6GfWHipL2bFimgtY+Zdi0BH
|
kSt4uaANWn/6a3RvMRhiVEYuNwhAzcKaactPmYqrLJgoVLbRSDkgyHaMQ2jKgLxk
|
||||||
NLk4EJXxj1SgVx5dtkQzWJReBA5M+FQ4QYQZBO+f4TDoOLmjui152uhkoLBQbGAa
|
ifS/fvluGV0ub2Po6DJiqfRpd1tDvPhe9y1+r1WFDZsOcvTcZUfSt/7dXMGfqGu0
|
||||||
WWJFTVxm0bG5MXloEL3gA8DfU7XDwuW/sHJC5pBko8RpQViooOhckMepZV3Y83DK
|
2daVFlfeSXSALrDE5uc0UxodHCpP3sqRYDZevGLBRaaTkIjYXG/+N898+7K5WJF4
|
||||||
bwLYa3JmPgj2rEv4993dvrJbQhpGd082HOxOsllCs8pgNq1SnXpWYfcGTgGKC3ts
|
xXOLWxM2cwGkG7eC9pugcDnBp9XlF7O+GBiZ05JUe5flXDQFZ+h3exjopu6KHF1B
|
||||||
U8YZUUJUQ7mi2L8Tv3ix20c9EiGmA30JAmA8eZTC3cWup91ZkkVBFRml2czTXajd
|
RnzNy8LC0UKb+AuvRIOLV92a9Q9wGWU/jaVDu6nZ0umAeuSzxiHoDsonm0Fl9QAz
|
||||||
RWZ6GbHV5503ueDQcB8yBVgF3CSixs67+dGSbD3p86OqGrjAcJzM5TFbNKcnGLdE
|
2/xCokebuoeLrEK7R2af3X86mqq3sVO4ax+HPYChzOaVQBiHUW/TAldWcldYYphR
|
||||||
kGbZpNwAISy750lXzXKmyrh5RTCeTOQerbwCMBvHZO+HAevA/LXDTw2OAiSIQlP5
|
/e2WsbmQfvCRtz/bZfo+aUVnrHNjzVMtF2SszdVmA/04Y8pS28MqtuRqhm5DPOOd
|
||||||
sYA4sFYLQ30OAkgJcmdp/pSgVj/erNtSN07ClrOpDb/uFpQymO6K2h0Pst3feNVK
|
g1YeUywK5jRZ1twyo1kzJEFPLaoeaXaycsR1PMVBW0Urik5mrR/pOWq7PPoZoKb2
|
||||||
9M2VbqL9C51z/wyHLg==
|
lXYLE8bwkuQTmsyL1g==
|
||||||
=SfZA
|
=9i7d
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
103
assets/go-licenses.json
generated
103
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
23
build.go
Normal file
23
build.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// 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 build.
|
||||||
|
// These libraries will not be included in a normal compilation.
|
||||||
|
|
||||||
|
import (
|
||||||
|
// for embed
|
||||||
|
_ "github.com/shurcooL/vfsgen"
|
||||||
|
|
||||||
|
// for cover merge
|
||||||
|
_ "golang.org/x/tools/cover"
|
||||||
|
|
||||||
|
// for vet
|
||||||
|
_ "code.gitea.io/gitea-vet"
|
||||||
|
|
||||||
|
// for swagger
|
||||||
|
_ "github.com/go-swagger/go-swagger/cmd/swagger"
|
||||||
|
)
|
||||||
@@ -12,11 +12,10 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/tools/codeformat"
|
"code.gitea.io/gitea/build/codeformat"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Windows has a limitation for command line arguments, the size can not exceed 32KB.
|
// Windows has a limitation for command line arguments, the size can not exceed 32KB.
|
||||||
@@ -182,7 +181,7 @@ func parseArgs() (mainOptions map[string]string, subCmd string, subArgs []string
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mainOptions, subCmd, subArgs
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func showUsage() {
|
func showUsage() {
|
||||||
@@ -218,6 +217,15 @@ func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCol
|
|||||||
return newFileCollector(fileFilter, batchSize)
|
return newFileCollector(fileFilter, batchSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func containsString(a []string, s string) bool {
|
||||||
|
for _, v := range a {
|
||||||
|
if v == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func giteaFormatGoImports(files []string, doWriteFile bool) error {
|
func giteaFormatGoImports(files []string, doWriteFile bool) error {
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if err := codeformat.FormatGoImports(file, doWriteFile); err != nil {
|
if err := codeformat.FormatGoImports(file, doWriteFile); err != nil {
|
||||||
@@ -256,10 +264,10 @@ func main() {
|
|||||||
logVerbose("batch cmd: %s %v", subCmd, substArgs)
|
logVerbose("batch cmd: %s %v", subCmd, substArgs)
|
||||||
switch subCmd {
|
switch subCmd {
|
||||||
case "gitea-fmt":
|
case "gitea-fmt":
|
||||||
if slices.Contains(subArgs, "-d") {
|
if containsString(subArgs, "-d") {
|
||||||
log.Print("the -d option is not supported by gitea-fmt")
|
log.Print("the -d option is not supported by gitea-fmt")
|
||||||
}
|
}
|
||||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, slices.Contains(subArgs, "-w")))
|
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w")))
|
||||||
cmdErrors = append(cmdErrors, passThroughCmd("gofmt", append([]string{"-w", "-r", "interface{} -> any"}, substArgs...)))
|
cmdErrors = append(cmdErrors, passThroughCmd("gofmt", append([]string{"-w", "-r", "interface{} -> any"}, substArgs...)))
|
||||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra"}, substArgs...)))
|
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra"}, substArgs...)))
|
||||||
default:
|
default:
|
||||||
@@ -6,22 +6,87 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha1"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/assetfs"
|
"github.com/shurcooL/vfsgen"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func needsUpdate(dir, filename string) (bool, []byte) {
|
||||||
if len(os.Args) != 3 {
|
needRegen := false
|
||||||
fmt.Println("usage: ./generate-bindata {local-directory} {bindata-filename}")
|
_, err := os.Stat(filename)
|
||||||
os.Exit(1)
|
if err != nil {
|
||||||
|
needRegen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, filename := os.Args[1], os.Args[2]
|
oldHash, err := os.ReadFile(filename + ".hash")
|
||||||
fmt.Printf("generating bindata for %s to %s\n", dir, filename)
|
if err != nil {
|
||||||
if err := assetfs.GenerateEmbedBindata(dir, filename); err != nil {
|
oldHash = []byte{}
|
||||||
fmt.Printf("failed: %s\n", err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasher := sha1.New()
|
||||||
|
|
||||||
|
err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := d.Info()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, _ = hasher.Write([]byte(d.Name()))
|
||||||
|
_, _ = hasher.Write([]byte(info.ModTime().String()))
|
||||||
|
_, _ = hasher.Write([]byte(strconv.FormatInt(info.Size(), 16)))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return true, oldHash
|
||||||
|
}
|
||||||
|
|
||||||
|
newHash := hasher.Sum([]byte{})
|
||||||
|
|
||||||
|
if bytes.Compare(oldHash, newHash) != 0 {
|
||||||
|
return true, newHash
|
||||||
|
}
|
||||||
|
|
||||||
|
return needRegen, newHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 4 {
|
||||||
|
log.Fatal("Insufficient number of arguments. Need: directory packageName filename")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3]
|
||||||
|
var useGlobalModTime bool
|
||||||
|
if len(os.Args) == 5 {
|
||||||
|
useGlobalModTime, _ = strconv.ParseBool(os.Args[4])
|
||||||
|
}
|
||||||
|
|
||||||
|
update, newHash := needsUpdate(dir, filename)
|
||||||
|
|
||||||
|
if !update {
|
||||||
|
fmt.Printf("bindata for %s already up-to-date\n", packageName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("generating bindata for %s\n", packageName)
|
||||||
|
var fsTemplates http.FileSystem = http.Dir(dir)
|
||||||
|
err := vfsgen.Generate(fsTemplates, vfsgen.Options{
|
||||||
|
PackageName: packageName,
|
||||||
|
BuildTags: "bindata",
|
||||||
|
VariableName: "Assets",
|
||||||
|
Filename: filename,
|
||||||
|
UseGlobalModTime: useGlobalModTime,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("%v\n", err)
|
||||||
|
}
|
||||||
|
_ = os.WriteFile(filename+".hash", newHash, 0o666)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -18,7 +17,7 @@ var (
|
|||||||
CmdActions = &cli.Command{
|
CmdActions = &cli.Command{
|
||||||
Name: "actions",
|
Name: "actions",
|
||||||
Usage: "Manage Gitea Actions",
|
Usage: "Manage Gitea Actions",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
subcmdActionsGenRunnerToken,
|
subcmdActionsGenRunnerToken,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -39,7 +38,10 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error {
|
func runGenerateActionsRunnerToken(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setting.MustInstalled()
|
setting.MustInstalled()
|
||||||
|
|
||||||
scope := c.String("scope")
|
scope := c.String("scope")
|
||||||
|
|||||||
45
cmd/admin.go
45
cmd/admin.go
@@ -15,7 +15,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -23,7 +23,7 @@ var (
|
|||||||
CmdAdmin = &cli.Command{
|
CmdAdmin = &cli.Command{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Usage: "Perform common administrative operations",
|
Usage: "Perform common administrative operations",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
subcmdUser,
|
subcmdUser,
|
||||||
subcmdRepoSyncReleases,
|
subcmdRepoSyncReleases,
|
||||||
subcmdRegenerate,
|
subcmdRegenerate,
|
||||||
@@ -41,7 +41,7 @@ var (
|
|||||||
subcmdRegenerate = &cli.Command{
|
subcmdRegenerate = &cli.Command{
|
||||||
Name: "regenerate",
|
Name: "regenerate",
|
||||||
Usage: "Regenerate specific files",
|
Usage: "Regenerate specific files",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
microcmdRegenHooks,
|
microcmdRegenHooks,
|
||||||
microcmdRegenKeys,
|
microcmdRegenKeys,
|
||||||
},
|
},
|
||||||
@@ -50,15 +50,15 @@ var (
|
|||||||
subcmdAuth = &cli.Command{
|
subcmdAuth = &cli.Command{
|
||||||
Name: "auth",
|
Name: "auth",
|
||||||
Usage: "Modify external auth providers",
|
Usage: "Modify external auth providers",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
microcmdAuthAddOauth(),
|
microcmdAuthAddOauth,
|
||||||
microcmdAuthUpdateOauth(),
|
microcmdAuthUpdateOauth,
|
||||||
microcmdAuthAddLdapBindDn(),
|
microcmdAuthAddLdapBindDn,
|
||||||
microcmdAuthUpdateLdapBindDn(),
|
microcmdAuthUpdateLdapBindDn,
|
||||||
microcmdAuthAddLdapSimpleAuth(),
|
microcmdAuthAddLdapSimpleAuth,
|
||||||
microcmdAuthUpdateLdapSimpleAuth(),
|
microcmdAuthUpdateLdapSimpleAuth,
|
||||||
microcmdAuthAddSMTP(),
|
microcmdAuthAddSMTP,
|
||||||
microcmdAuthUpdateSMTP(),
|
microcmdAuthUpdateSMTP,
|
||||||
microcmdAuthList,
|
microcmdAuthList,
|
||||||
microcmdAuthDelete,
|
microcmdAuthDelete,
|
||||||
},
|
},
|
||||||
@@ -71,8 +71,8 @@ var (
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "title",
|
Name: "title",
|
||||||
Usage: "a title of a message",
|
Usage: `a title of a message`,
|
||||||
Required: true,
|
Value: "",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "content",
|
Name: "content",
|
||||||
@@ -86,27 +86,28 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
func idFlag() *cli.Int64Flag {
|
idFlag = &cli.Int64Flag{
|
||||||
return &cli.Int64Flag{
|
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Usage: "ID of authentication source",
|
Usage: "ID of authentication source",
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
|
func runRepoSyncReleases(_ *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
|
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := git.InitSimple(); err != nil {
|
if err := git.InitSimple(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("Synchronizing repository releases (this may take a while)")
|
log.Trace("Synchronizing repository releases (this may take a while)")
|
||||||
for page := 1; ; page++ {
|
for page := 1; ; page++ {
|
||||||
repos, count, err := repo_model.SearchRepositoryByName(ctx, repo_model.SearchRepoOptions{
|
repos, count, err := repo_model.SearchRepositoryByName(ctx, &repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
PageSize: repo_model.RepositoryListDefaultPageSize,
|
PageSize: repo_model.RepositoryListDefaultPageSize,
|
||||||
Page: page,
|
Page: page,
|
||||||
@@ -121,7 +122,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.RelativePath())
|
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath())
|
||||||
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)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -14,14 +13,14 @@ import (
|
|||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
auth_service "code.gitea.io/gitea/services/auth"
|
auth_service "code.gitea.io/gitea/services/auth"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
microcmdAuthDelete = &cli.Command{
|
microcmdAuthDelete = &cli.Command{
|
||||||
Name: "delete",
|
Name: "delete",
|
||||||
Usage: "Delete specific auth source",
|
Usage: "Delete specific auth source",
|
||||||
Flags: []cli.Flag{idFlag()},
|
Flags: []cli.Flag{idFlag},
|
||||||
Action: runDeleteAuth,
|
Action: runDeleteAuth,
|
||||||
}
|
}
|
||||||
microcmdAuthList = &cli.Command{
|
microcmdAuthList = &cli.Command{
|
||||||
@@ -57,7 +56,10 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func runListAuth(ctx context.Context, c *cli.Command) error {
|
func runListAuth(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -88,11 +90,14 @@ func runListAuth(ctx context.Context, c *cli.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDeleteAuth(ctx context.Context, c *cli.Command) error {
|
func runDeleteAuth(c *cli.Context) error {
|
||||||
if !c.IsSet("id") {
|
if !c.IsSet("id") {
|
||||||
return errors.New("--id flag is missing")
|
return errors.New("--id flag is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/services/auth/source/ldap"
|
"code.gitea.io/gitea/services/auth/source/ldap"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -24,8 +24,8 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func commonLdapCLIFlags() []cli.Flag {
|
var (
|
||||||
return []cli.Flag{
|
commonLdapCLIFlags = []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Usage: "Authentication name.",
|
Usage: "Authentication name.",
|
||||||
@@ -103,10 +103,8 @@ func commonLdapCLIFlags() []cli.Flag {
|
|||||||
Usage: "The attribute of the user’s LDAP record containing the user’s avatar.",
|
Usage: "The attribute of the user’s LDAP record containing the user’s avatar.",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func ldapBindDnCLIFlags() []cli.Flag {
|
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
|
||||||
return append(commonLdapCLIFlags(),
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "bind-dn",
|
Name: "bind-dn",
|
||||||
Usage: "The DN to bind to the LDAP server with when searching for the user.",
|
Usage: "The DN to bind to the LDAP server with when searching for the user.",
|
||||||
@@ -159,59 +157,49 @@ func ldapBindDnCLIFlags() []cli.Flag {
|
|||||||
Name: "group-team-map-removal",
|
Name: "group-team-map-removal",
|
||||||
Usage: "Remove users from synchronized teams if user does not belong to corresponding LDAP group",
|
Usage: "Remove users from synchronized teams if user does not belong to corresponding LDAP group",
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
func ldapSimpleAuthCLIFlags() []cli.Flag {
|
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
|
||||||
return append(commonLdapCLIFlags(),
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "user-dn",
|
Name: "user-dn",
|
||||||
Usage: "The user's DN.",
|
Usage: "The user's DN.",
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
func microcmdAuthAddLdapBindDn() *cli.Command {
|
microcmdAuthAddLdapBindDn = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "add-ldap",
|
Name: "add-ldap",
|
||||||
Usage: "Add new LDAP (via Bind DN) authentication source",
|
Usage: "Add new LDAP (via Bind DN) authentication source",
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
Action: func(c *cli.Context) error {
|
||||||
return newAuthService().addLdapBindDn(ctx, cmd)
|
return newAuthService().addLdapBindDn(c)
|
||||||
},
|
},
|
||||||
Flags: ldapBindDnCLIFlags(),
|
Flags: ldapBindDnCLIFlags,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func microcmdAuthUpdateLdapBindDn() *cli.Command {
|
microcmdAuthUpdateLdapBindDn = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "update-ldap",
|
Name: "update-ldap",
|
||||||
Usage: "Update existing LDAP (via Bind DN) authentication source",
|
Usage: "Update existing LDAP (via Bind DN) authentication source",
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
Action: func(c *cli.Context) error {
|
||||||
return newAuthService().updateLdapBindDn(ctx, cmd)
|
return newAuthService().updateLdapBindDn(c)
|
||||||
},
|
},
|
||||||
Flags: append([]cli.Flag{idFlag()}, ldapBindDnCLIFlags()...),
|
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func microcmdAuthAddLdapSimpleAuth() *cli.Command {
|
microcmdAuthAddLdapSimpleAuth = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "add-ldap-simple",
|
Name: "add-ldap-simple",
|
||||||
Usage: "Add new LDAP (simple auth) authentication source",
|
Usage: "Add new LDAP (simple auth) authentication source",
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
Action: func(c *cli.Context) error {
|
||||||
return newAuthService().addLdapSimpleAuth(ctx, cmd)
|
return newAuthService().addLdapSimpleAuth(c)
|
||||||
},
|
},
|
||||||
Flags: ldapSimpleAuthCLIFlags(),
|
Flags: ldapSimpleAuthCLIFlags,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func microcmdAuthUpdateLdapSimpleAuth() *cli.Command {
|
microcmdAuthUpdateLdapSimpleAuth = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "update-ldap-simple",
|
Name: "update-ldap-simple",
|
||||||
Usage: "Update existing LDAP (simple auth) authentication source",
|
Usage: "Update existing LDAP (simple auth) authentication source",
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
Action: func(c *cli.Context) error {
|
||||||
return newAuthService().updateLdapSimpleAuth(ctx, cmd)
|
return newAuthService().updateLdapSimpleAuth(c)
|
||||||
},
|
},
|
||||||
Flags: append([]cli.Flag{idFlag()}, ldapSimpleAuthCLIFlags()...),
|
Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// newAuthService creates a service with default functions.
|
// newAuthService creates a service with default functions.
|
||||||
func newAuthService() *authService {
|
func newAuthService() *authService {
|
||||||
@@ -224,7 +212,7 @@ func newAuthService() *authService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseAuthSourceLdap assigns values on authSource according to command line flags.
|
// parseAuthSourceLdap assigns values on authSource according to command line flags.
|
||||||
func parseAuthSourceLdap(c *cli.Command, authSource *auth.Source) {
|
func parseAuthSourceLdap(c *cli.Context, authSource *auth.Source) {
|
||||||
if c.IsSet("name") {
|
if c.IsSet("name") {
|
||||||
authSource.Name = c.String("name")
|
authSource.Name = c.String("name")
|
||||||
}
|
}
|
||||||
@@ -244,7 +232,7 @@ func parseAuthSourceLdap(c *cli.Command, authSource *auth.Source) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseLdapConfig assigns values on config according to command line flags.
|
// parseLdapConfig assigns values on config according to command line flags.
|
||||||
func parseLdapConfig(c *cli.Command, config *ldap.Source) error {
|
func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
|
||||||
if c.IsSet("name") {
|
if c.IsSet("name") {
|
||||||
config.Name = c.String("name")
|
config.Name = c.String("name")
|
||||||
}
|
}
|
||||||
@@ -257,7 +245,7 @@ func parseLdapConfig(c *cli.Command, config *ldap.Source) error {
|
|||||||
if c.IsSet("security-protocol") {
|
if c.IsSet("security-protocol") {
|
||||||
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
|
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unknown security protocol name: %s", c.String("security-protocol"))
|
return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
|
||||||
}
|
}
|
||||||
config.SecurityProtocol = p
|
config.SecurityProtocol = p
|
||||||
}
|
}
|
||||||
@@ -349,27 +337,32 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
|||||||
|
|
||||||
// getAuthSource gets the login source by its id defined in the command line flags.
|
// getAuthSource gets the login source by its id defined in the command line flags.
|
||||||
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
|
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
|
||||||
func (a *authService) getAuthSource(ctx context.Context, c *cli.Command, authType auth.Type) (*auth.Source, error) {
|
func (a *authService) getAuthSource(ctx context.Context, c *cli.Context, authType auth.Type) (*auth.Source, error) {
|
||||||
if err := argsSet(c, "id"); err != nil {
|
if err := argsSet(c, "id"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
authSource, err := a.getAuthSourceByID(ctx, c.Int64("id"))
|
authSource, err := a.getAuthSourceByID(ctx, c.Int64("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if authSource.Type != authType {
|
if authSource.Type != authType {
|
||||||
return nil, fmt.Errorf("invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String())
|
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return authSource, nil
|
return authSource, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
|
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
|
||||||
func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
|
func (a *authService) addLdapBindDn(c *cli.Context) error {
|
||||||
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
|
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := a.initDB(ctx); err != nil {
|
if err := a.initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -391,7 +384,10 @@ func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
|
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
|
||||||
func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) error {
|
func (a *authService) updateLdapBindDn(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := a.initDB(ctx); err != nil {
|
if err := a.initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -410,11 +406,14 @@ func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
|
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
|
||||||
func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
|
func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
|
||||||
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
|
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := a.initDB(ctx); err != nil {
|
if err := a.initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -436,7 +435,10 @@ func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source.
|
// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source.
|
||||||
func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
|
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := a.initDB(ctx); err != nil {
|
if err := a.initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,17 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/modules/test"
|
|
||||||
"code.gitea.io/gitea/services/auth/source/ldap"
|
"code.gitea.io/gitea/services/auth/source/ldap"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAddLdapBindDn(t *testing.T) {
|
func TestAddLdapBindDn(t *testing.T) {
|
||||||
// Mock cli functions to do not exit on error
|
// Mock cli functions to do not exit on error
|
||||||
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
|
osExiter := cli.OsExiter
|
||||||
|
defer func() { cli.OsExiter = osExiter }()
|
||||||
|
cli.OsExiter = func(code int) {}
|
||||||
|
|
||||||
// Test cases
|
// Test cases
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@@ -134,7 +135,7 @@ func TestAddLdapBindDn(t *testing.T) {
|
|||||||
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
|
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
|
||||||
"--email-attribute", "mail",
|
"--email-attribute", "mail",
|
||||||
},
|
},
|
||||||
errMsg: "unknown security protocol name: zzzzz",
|
errMsg: "Unknown security protocol name: zzzzz",
|
||||||
},
|
},
|
||||||
// case 3
|
// case 3
|
||||||
{
|
{
|
||||||
@@ -238,13 +239,12 @@ func TestAddLdapBindDn(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a copy of command to test
|
// Create a copy of command to test
|
||||||
app := cli.Command{
|
app := cli.NewApp()
|
||||||
Flags: microcmdAuthAddLdapBindDn().Flags,
|
app.Flags = microcmdAuthAddLdapBindDn.Flags
|
||||||
Action: service.addLdapBindDn,
|
app.Action = service.addLdapBindDn
|
||||||
}
|
|
||||||
|
|
||||||
// Run it
|
// Run it
|
||||||
err := app.Run(t.Context(), c.args)
|
err := app.Run(c.args)
|
||||||
if c.errMsg != "" {
|
if c.errMsg != "" {
|
||||||
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
|
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
|
||||||
} else {
|
} else {
|
||||||
@@ -256,7 +256,9 @@ func TestAddLdapBindDn(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddLdapSimpleAuth(t *testing.T) {
|
func TestAddLdapSimpleAuth(t *testing.T) {
|
||||||
// Mock cli functions to do not exit on error
|
// Mock cli functions to do not exit on error
|
||||||
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
|
osExiter := cli.OsExiter
|
||||||
|
defer func() { cli.OsExiter = osExiter }()
|
||||||
|
cli.OsExiter = func(code int) {}
|
||||||
|
|
||||||
// Test cases
|
// Test cases
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@@ -346,12 +348,12 @@ func TestAddLdapSimpleAuth(t *testing.T) {
|
|||||||
"--name", "ldap (simple auth) source",
|
"--name", "ldap (simple auth) source",
|
||||||
"--security-protocol", "zzzzz",
|
"--security-protocol", "zzzzz",
|
||||||
"--host", "ldap-server",
|
"--host", "ldap-server",
|
||||||
"--port", "1234",
|
"--port", "123",
|
||||||
"--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
|
"--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
|
||||||
"--email-attribute", "mail",
|
"--email-attribute", "mail",
|
||||||
"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
|
"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
|
||||||
},
|
},
|
||||||
errMsg: "unknown security protocol name: zzzzz",
|
errMsg: "Unknown security protocol name: zzzzz",
|
||||||
},
|
},
|
||||||
// case 3
|
// case 3
|
||||||
{
|
{
|
||||||
@@ -468,13 +470,12 @@ func TestAddLdapSimpleAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a copy of command to test
|
// Create a copy of command to test
|
||||||
app := &cli.Command{
|
app := cli.NewApp()
|
||||||
Flags: microcmdAuthAddLdapSimpleAuth().Flags,
|
app.Flags = microcmdAuthAddLdapSimpleAuth.Flags
|
||||||
Action: service.addLdapSimpleAuth,
|
app.Action = service.addLdapSimpleAuth
|
||||||
}
|
|
||||||
|
|
||||||
// Run it
|
// Run it
|
||||||
err := app.Run(t.Context(), c.args)
|
err := app.Run(c.args)
|
||||||
if c.errMsg != "" {
|
if c.errMsg != "" {
|
||||||
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
|
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
|
||||||
} else {
|
} else {
|
||||||
@@ -486,7 +487,9 @@ func TestAddLdapSimpleAuth(t *testing.T) {
|
|||||||
|
|
||||||
func TestUpdateLdapBindDn(t *testing.T) {
|
func TestUpdateLdapBindDn(t *testing.T) {
|
||||||
// Mock cli functions to do not exit on error
|
// Mock cli functions to do not exit on error
|
||||||
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
|
osExiter := cli.OsExiter
|
||||||
|
defer func() { cli.OsExiter = osExiter }()
|
||||||
|
cli.OsExiter = func(code int) {}
|
||||||
|
|
||||||
// Test cases
|
// Test cases
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@@ -861,7 +864,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
|||||||
"--id", "1",
|
"--id", "1",
|
||||||
"--security-protocol", "xxxxx",
|
"--security-protocol", "xxxxx",
|
||||||
},
|
},
|
||||||
errMsg: "unknown security protocol name: xxxxx",
|
errMsg: "Unknown security protocol name: xxxxx",
|
||||||
},
|
},
|
||||||
// case 22
|
// case 22
|
||||||
{
|
{
|
||||||
@@ -880,7 +883,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
|||||||
Type: auth.OAuth2,
|
Type: auth.OAuth2,
|
||||||
Cfg: &ldap.Source{},
|
Cfg: &ldap.Source{},
|
||||||
},
|
},
|
||||||
errMsg: "invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2",
|
errMsg: "Invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2",
|
||||||
},
|
},
|
||||||
// case 24
|
// case 24
|
||||||
{
|
{
|
||||||
@@ -944,12 +947,12 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a copy of command to test
|
// Create a copy of command to test
|
||||||
app := cli.Command{
|
app := cli.NewApp()
|
||||||
Flags: microcmdAuthUpdateLdapBindDn().Flags,
|
app.Flags = microcmdAuthUpdateLdapBindDn.Flags
|
||||||
Action: service.updateLdapBindDn,
|
app.Action = service.updateLdapBindDn
|
||||||
}
|
|
||||||
// Run it
|
// Run it
|
||||||
err := app.Run(t.Context(), c.args)
|
err := app.Run(c.args)
|
||||||
if c.errMsg != "" {
|
if c.errMsg != "" {
|
||||||
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
|
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
|
||||||
} else {
|
} else {
|
||||||
@@ -961,7 +964,9 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
|||||||
|
|
||||||
func TestUpdateLdapSimpleAuth(t *testing.T) {
|
func TestUpdateLdapSimpleAuth(t *testing.T) {
|
||||||
// Mock cli functions to do not exit on error
|
// Mock cli functions to do not exit on error
|
||||||
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
|
osExiter := cli.OsExiter
|
||||||
|
defer func() { cli.OsExiter = osExiter }()
|
||||||
|
cli.OsExiter = func(code int) {}
|
||||||
|
|
||||||
// Test cases
|
// Test cases
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@@ -1252,7 +1257,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
|
|||||||
"--id", "1",
|
"--id", "1",
|
||||||
"--security-protocol", "xxxxx",
|
"--security-protocol", "xxxxx",
|
||||||
},
|
},
|
||||||
errMsg: "unknown security protocol name: xxxxx",
|
errMsg: "Unknown security protocol name: xxxxx",
|
||||||
},
|
},
|
||||||
// case 18
|
// case 18
|
||||||
{
|
{
|
||||||
@@ -1271,7 +1276,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
|
|||||||
Type: auth.PAM,
|
Type: auth.PAM,
|
||||||
Cfg: &ldap.Source{},
|
Cfg: &ldap.Source{},
|
||||||
},
|
},
|
||||||
errMsg: "invalid authentication type. expected: LDAP (simple auth), actual: PAM",
|
errMsg: "Invalid authentication type. expected: LDAP (simple auth), actual: PAM",
|
||||||
},
|
},
|
||||||
// case 20
|
// case 20
|
||||||
{
|
{
|
||||||
@@ -1332,12 +1337,12 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a copy of command to test
|
// Create a copy of command to test
|
||||||
app := cli.Command{
|
app := cli.NewApp()
|
||||||
Flags: microcmdAuthUpdateLdapSimpleAuth().Flags,
|
app.Flags = microcmdAuthUpdateLdapSimpleAuth.Flags
|
||||||
Action: service.updateLdapSimpleAuth,
|
app.Action = service.updateLdapSimpleAuth
|
||||||
}
|
|
||||||
// Run it
|
// Run it
|
||||||
err := app.Run(t.Context(), c.args)
|
err := app.Run(c.args)
|
||||||
if c.errMsg != "" {
|
if c.errMsg != "" {
|
||||||
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
|
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -13,11 +12,11 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func oauthCLIFlags() []cli.Flag {
|
var (
|
||||||
return []cli.Flag{
|
oauthCLIFlags = []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Value: "",
|
Value: "",
|
||||||
@@ -87,14 +86,6 @@ func oauthCLIFlags() []cli.Flag {
|
|||||||
Value: nil,
|
Value: nil,
|
||||||
Usage: "Scopes to request when to authenticate against this OAuth2 source",
|
Usage: "Scopes to request when to authenticate against this OAuth2 source",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "ssh-public-key-claim-name",
|
|
||||||
Usage: "Claim name that provides SSH public keys",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "full-name-claim-name",
|
|
||||||
Usage: "Claim name that provides user's full name",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "required-claim-name",
|
Name: "required-claim-name",
|
||||||
Value: "",
|
Value: "",
|
||||||
@@ -130,34 +121,23 @@ func oauthCLIFlags() []cli.Flag {
|
|||||||
Usage: "Activate automatic team membership removal depending on groups",
|
Usage: "Activate automatic team membership removal depending on groups",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func microcmdAuthAddOauth() *cli.Command {
|
microcmdAuthAddOauth = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "add-oauth",
|
Name: "add-oauth",
|
||||||
Usage: "Add new Oauth authentication source",
|
Usage: "Add new Oauth authentication source",
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
Action: runAddOauth,
|
||||||
return newAuthService().runAddOauth(ctx, cmd)
|
Flags: oauthCLIFlags,
|
||||||
},
|
|
||||||
Flags: oauthCLIFlags(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func microcmdAuthUpdateOauth() *cli.Command {
|
microcmdAuthUpdateOauth = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "update-oauth",
|
Name: "update-oauth",
|
||||||
Usage: "Update existing Oauth authentication source",
|
Usage: "Update existing Oauth authentication source",
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
Action: runUpdateOauth,
|
||||||
return newAuthService().runUpdateOauth(ctx, cmd)
|
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
|
||||||
},
|
|
||||||
Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{
|
|
||||||
Name: "id",
|
|
||||||
Usage: "ID of authentication source",
|
|
||||||
}}, oauthCLIFlags()[1:]...)...),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func parseOAuth2Config(c *cli.Command) *oauth2.Source {
|
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
||||||
var customURLMapping *oauth2.CustomURLMapping
|
var customURLMapping *oauth2.CustomURLMapping
|
||||||
if c.IsSet("use-custom-urls") {
|
if c.IsSet("use-custom-urls") {
|
||||||
customURLMapping = &oauth2.CustomURLMapping{
|
customURLMapping = &oauth2.CustomURLMapping{
|
||||||
@@ -185,13 +165,14 @@ func parseOAuth2Config(c *cli.Command) *oauth2.Source {
|
|||||||
RestrictedGroup: c.String("restricted-group"),
|
RestrictedGroup: c.String("restricted-group"),
|
||||||
GroupTeamMap: c.String("group-team-map"),
|
GroupTeamMap: c.String("group-team-map"),
|
||||||
GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
|
GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
|
||||||
SSHPublicKeyClaimName: c.String("ssh-public-key-claim-name"),
|
|
||||||
FullNameClaimName: c.String("full-name-claim-name"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error {
|
func runAddOauth(c *cli.Context) error {
|
||||||
if err := a.initDB(ctx); err != nil {
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +184,7 @@ func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.createAuthSource(ctx, &auth_model.Source{
|
return auth_model.CreateSource(ctx, &auth_model.Source{
|
||||||
Type: auth_model.OAuth2,
|
Type: auth_model.OAuth2,
|
||||||
Name: c.String("name"),
|
Name: c.String("name"),
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
@@ -212,16 +193,19 @@ func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error {
|
func runUpdateOauth(c *cli.Context) error {
|
||||||
if !c.IsSet("id") {
|
if !c.IsSet("id") {
|
||||||
return errors.New("--id flag is missing")
|
return errors.New("--id flag is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.initDB(ctx); err != nil {
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
|
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -278,12 +262,6 @@ func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error
|
|||||||
if c.IsSet("group-team-map-removal") {
|
if c.IsSet("group-team-map-removal") {
|
||||||
oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
|
oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
|
||||||
}
|
}
|
||||||
if c.IsSet("ssh-public-key-claim-name") {
|
|
||||||
oAuth2Config.SSHPublicKeyClaimName = c.String("ssh-public-key-claim-name")
|
|
||||||
}
|
|
||||||
if c.IsSet("full-name-claim-name") {
|
|
||||||
oAuth2Config.FullNameClaimName = c.String("full-name-claim-name")
|
|
||||||
}
|
|
||||||
|
|
||||||
// update custom URL mapping
|
// update custom URL mapping
|
||||||
customURLMapping := &oauth2.CustomURLMapping{}
|
customURLMapping := &oauth2.CustomURLMapping{}
|
||||||
@@ -318,5 +296,5 @@ func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error
|
|||||||
oAuth2Config.CustomURLMapping = customURLMapping
|
oAuth2Config.CustomURLMapping = customURLMapping
|
||||||
source.Cfg = oAuth2Config
|
source.Cfg = oAuth2Config
|
||||||
source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
|
source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
|
||||||
return a.updateAuthSource(ctx, source)
|
return auth_model.UpdateSource(ctx, source)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,343 +0,0 @@
|
|||||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
|
||||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/urfave/cli/v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAddOauth(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
source *auth_model.Source
|
|
||||||
errMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid config",
|
|
||||||
args: []string{
|
|
||||||
"--name", "test",
|
|
||||||
"--provider", "github",
|
|
||||||
"--key", "some_key",
|
|
||||||
"--secret", "some_secret",
|
|
||||||
},
|
|
||||||
source: &auth_model.Source{
|
|
||||||
Type: auth_model.OAuth2,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &oauth2.Source{
|
|
||||||
Scopes: []string{},
|
|
||||||
Provider: "github",
|
|
||||||
ClientID: "some_key",
|
|
||||||
ClientSecret: "some_secret",
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid config with openid connect",
|
|
||||||
args: []string{
|
|
||||||
"--name", "test",
|
|
||||||
"--provider", "openidConnect",
|
|
||||||
"--key", "some_key",
|
|
||||||
"--secret", "some_secret",
|
|
||||||
"--auto-discover-url", "https://example.com",
|
|
||||||
},
|
|
||||||
source: &auth_model.Source{
|
|
||||||
Type: auth_model.OAuth2,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &oauth2.Source{
|
|
||||||
Scopes: []string{},
|
|
||||||
Provider: "openidConnect",
|
|
||||||
ClientID: "some_key",
|
|
||||||
ClientSecret: "some_secret",
|
|
||||||
OpenIDConnectAutoDiscoveryURL: "https://example.com",
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid config with options",
|
|
||||||
args: []string{
|
|
||||||
"--name", "test",
|
|
||||||
"--provider", "gitlab",
|
|
||||||
"--key", "some_key",
|
|
||||||
"--secret", "some_secret",
|
|
||||||
"--use-custom-urls", "true",
|
|
||||||
"--custom-token-url", "https://example.com/token",
|
|
||||||
"--custom-auth-url", "https://example.com/auth",
|
|
||||||
"--custom-profile-url", "https://example.com/profile",
|
|
||||||
"--custom-email-url", "https://example.com/email",
|
|
||||||
"--custom-tenant-id", "some_tenant",
|
|
||||||
"--icon-url", "https://example.com/icon",
|
|
||||||
"--scopes", "scope1,scope2",
|
|
||||||
"--skip-local-2fa", "true",
|
|
||||||
"--required-claim-name", "claim_name",
|
|
||||||
"--required-claim-value", "claim_value",
|
|
||||||
"--group-claim-name", "group_name",
|
|
||||||
"--admin-group", "admin",
|
|
||||||
"--restricted-group", "restricted",
|
|
||||||
"--group-team-map", `{"group1": [1,2]}`,
|
|
||||||
"--group-team-map-removal=true",
|
|
||||||
"--ssh-public-key-claim-name", "attr_ssh_pub_key",
|
|
||||||
"--full-name-claim-name", "attr_full_name",
|
|
||||||
},
|
|
||||||
source: &auth_model.Source{
|
|
||||||
Type: auth_model.OAuth2,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &oauth2.Source{
|
|
||||||
Provider: "gitlab",
|
|
||||||
ClientID: "some_key",
|
|
||||||
ClientSecret: "some_secret",
|
|
||||||
CustomURLMapping: &oauth2.CustomURLMapping{
|
|
||||||
TokenURL: "https://example.com/token",
|
|
||||||
AuthURL: "https://example.com/auth",
|
|
||||||
ProfileURL: "https://example.com/profile",
|
|
||||||
EmailURL: "https://example.com/email",
|
|
||||||
Tenant: "some_tenant",
|
|
||||||
},
|
|
||||||
IconURL: "https://example.com/icon",
|
|
||||||
Scopes: []string{"scope1", "scope2"},
|
|
||||||
RequiredClaimName: "claim_name",
|
|
||||||
RequiredClaimValue: "claim_value",
|
|
||||||
GroupClaimName: "group_name",
|
|
||||||
AdminGroup: "admin",
|
|
||||||
RestrictedGroup: "restricted",
|
|
||||||
GroupTeamMap: `{"group1": [1,2]}`,
|
|
||||||
GroupTeamMapRemoval: true,
|
|
||||||
SSHPublicKeyClaimName: "attr_ssh_pub_key",
|
|
||||||
FullNameClaimName: "attr_full_name",
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "skip",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
var createdSource *auth_model.Source
|
|
||||||
a := &authService{
|
|
||||||
initDB: func(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
createAuthSource: func(ctx context.Context, source *auth_model.Source) error {
|
|
||||||
createdSource = source
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
app := &cli.Command{
|
|
||||||
Flags: microcmdAuthAddOauth().Flags,
|
|
||||||
Action: a.runAddOauth,
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{"oauth-test"}
|
|
||||||
args = append(args, tc.args...)
|
|
||||||
|
|
||||||
err := app.Run(t.Context(), args)
|
|
||||||
|
|
||||||
if tc.errMsg != "" {
|
|
||||||
assert.EqualError(t, err, tc.errMsg)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, tc.source, createdSource)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateOauth(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
id int64
|
|
||||||
existingAuthSource *auth_model.Source
|
|
||||||
authSource *auth_model.Source
|
|
||||||
errMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "missing id",
|
|
||||||
args: []string{
|
|
||||||
"--name", "test",
|
|
||||||
},
|
|
||||||
errMsg: "--id flag is missing",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid config",
|
|
||||||
id: 1,
|
|
||||||
existingAuthSource: &auth_model.Source{
|
|
||||||
ID: 1,
|
|
||||||
Type: auth_model.OAuth2,
|
|
||||||
Name: "old name",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &oauth2.Source{
|
|
||||||
Provider: "github",
|
|
||||||
ClientID: "old_key",
|
|
||||||
ClientSecret: "old_secret",
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "",
|
|
||||||
},
|
|
||||||
args: []string{
|
|
||||||
"--id", "1",
|
|
||||||
"--name", "test",
|
|
||||||
"--provider", "gitlab",
|
|
||||||
"--key", "new_key",
|
|
||||||
"--secret", "new_secret",
|
|
||||||
},
|
|
||||||
authSource: &auth_model.Source{
|
|
||||||
ID: 1,
|
|
||||||
Type: auth_model.OAuth2,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &oauth2.Source{
|
|
||||||
Provider: "gitlab",
|
|
||||||
ClientID: "new_key",
|
|
||||||
ClientSecret: "new_secret",
|
|
||||||
CustomURLMapping: &oauth2.CustomURLMapping{},
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid config with options",
|
|
||||||
id: 1,
|
|
||||||
existingAuthSource: &auth_model.Source{
|
|
||||||
ID: 1,
|
|
||||||
Type: auth_model.OAuth2,
|
|
||||||
Name: "old name",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &oauth2.Source{
|
|
||||||
Provider: "gitlab",
|
|
||||||
ClientID: "old_key",
|
|
||||||
ClientSecret: "old_secret",
|
|
||||||
CustomURLMapping: &oauth2.CustomURLMapping{
|
|
||||||
TokenURL: "https://old.example.com/token",
|
|
||||||
AuthURL: "https://old.example.com/auth",
|
|
||||||
ProfileURL: "https://old.example.com/profile",
|
|
||||||
EmailURL: "https://old.example.com/email",
|
|
||||||
Tenant: "old_tenant",
|
|
||||||
},
|
|
||||||
IconURL: "https://old.example.com/icon",
|
|
||||||
Scopes: []string{"old_scope1", "old_scope2"},
|
|
||||||
RequiredClaimName: "old_claim_name",
|
|
||||||
RequiredClaimValue: "old_claim_value",
|
|
||||||
GroupClaimName: "old_group_name",
|
|
||||||
AdminGroup: "old_admin",
|
|
||||||
RestrictedGroup: "old_restricted",
|
|
||||||
GroupTeamMap: `{"old_group1": [1,2]}`,
|
|
||||||
GroupTeamMapRemoval: true,
|
|
||||||
SSHPublicKeyClaimName: "old_ssh_pub_key",
|
|
||||||
FullNameClaimName: "old_full_name",
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "",
|
|
||||||
},
|
|
||||||
args: []string{
|
|
||||||
"--id", "1",
|
|
||||||
"--name", "test",
|
|
||||||
"--provider", "github",
|
|
||||||
"--key", "new_key",
|
|
||||||
"--secret", "new_secret",
|
|
||||||
"--use-custom-urls", "true",
|
|
||||||
"--custom-token-url", "https://example.com/token",
|
|
||||||
"--custom-auth-url", "https://example.com/auth",
|
|
||||||
"--custom-profile-url", "https://example.com/profile",
|
|
||||||
"--custom-email-url", "https://example.com/email",
|
|
||||||
"--custom-tenant-id", "new_tenant",
|
|
||||||
"--icon-url", "https://example.com/icon",
|
|
||||||
"--scopes", "scope1,scope2",
|
|
||||||
"--skip-local-2fa=true",
|
|
||||||
"--required-claim-name", "claim_name",
|
|
||||||
"--required-claim-value", "claim_value",
|
|
||||||
"--group-claim-name", "group_name",
|
|
||||||
"--admin-group", "admin",
|
|
||||||
"--restricted-group", "restricted",
|
|
||||||
"--group-team-map", `{"group1": [1,2]}`,
|
|
||||||
"--group-team-map-removal=false",
|
|
||||||
"--ssh-public-key-claim-name", "new_ssh_pub_key",
|
|
||||||
"--full-name-claim-name", "new_full_name",
|
|
||||||
},
|
|
||||||
authSource: &auth_model.Source{
|
|
||||||
ID: 1,
|
|
||||||
Type: auth_model.OAuth2,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &oauth2.Source{
|
|
||||||
Provider: "github",
|
|
||||||
ClientID: "new_key",
|
|
||||||
ClientSecret: "new_secret",
|
|
||||||
CustomURLMapping: &oauth2.CustomURLMapping{
|
|
||||||
TokenURL: "https://example.com/token",
|
|
||||||
AuthURL: "https://example.com/auth",
|
|
||||||
ProfileURL: "https://example.com/profile",
|
|
||||||
EmailURL: "https://example.com/email",
|
|
||||||
Tenant: "new_tenant",
|
|
||||||
},
|
|
||||||
IconURL: "https://example.com/icon",
|
|
||||||
Scopes: []string{"scope1", "scope2"},
|
|
||||||
RequiredClaimName: "claim_name",
|
|
||||||
RequiredClaimValue: "claim_value",
|
|
||||||
GroupClaimName: "group_name",
|
|
||||||
AdminGroup: "admin",
|
|
||||||
RestrictedGroup: "restricted",
|
|
||||||
GroupTeamMap: `{"group1": [1,2]}`,
|
|
||||||
GroupTeamMapRemoval: false,
|
|
||||||
SSHPublicKeyClaimName: "new_ssh_pub_key",
|
|
||||||
FullNameClaimName: "new_full_name",
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "skip",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
a := &authService{
|
|
||||||
initDB: func(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) {
|
|
||||||
return &auth_model.Source{
|
|
||||||
ID: 1,
|
|
||||||
Type: auth_model.OAuth2,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &oauth2.Source{
|
|
||||||
CustomURLMapping: &oauth2.CustomURLMapping{},
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "skip",
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
updateAuthSource: func(ctx context.Context, source *auth_model.Source) error {
|
|
||||||
assert.Equal(t, tc.authSource, source)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
app := &cli.Command{
|
|
||||||
Flags: microcmdAuthUpdateOauth().Flags,
|
|
||||||
Action: a.runUpdateOauth,
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{"oauth-test"}
|
|
||||||
args = append(args, tc.args...)
|
|
||||||
|
|
||||||
err := app.Run(t.Context(), args)
|
|
||||||
|
|
||||||
if tc.errMsg != "" {
|
|
||||||
assert.EqualError(t, err, tc.errMsg)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
|
||||||
"code.gitea.io/gitea/services/auth/source/smtp"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/urfave/cli/v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAddSMTP(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
source *auth_model.Source
|
|
||||||
errMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "missing name",
|
|
||||||
args: []string{
|
|
||||||
"--host", "localhost",
|
|
||||||
"--port", "25",
|
|
||||||
},
|
|
||||||
errMsg: "name must be set",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "missing host",
|
|
||||||
args: []string{
|
|
||||||
"--name", "test",
|
|
||||||
"--port", "25",
|
|
||||||
},
|
|
||||||
errMsg: "host must be set",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "missing port",
|
|
||||||
args: []string{
|
|
||||||
"--name", "test",
|
|
||||||
"--host", "localhost",
|
|
||||||
},
|
|
||||||
errMsg: "port must be set",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid config",
|
|
||||||
args: []string{
|
|
||||||
"--name", "test",
|
|
||||||
"--host", "localhost",
|
|
||||||
"--port", "25",
|
|
||||||
},
|
|
||||||
source: &auth_model.Source{
|
|
||||||
Type: auth_model.SMTP,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &smtp.Source{
|
|
||||||
Auth: "PLAIN",
|
|
||||||
Host: "localhost",
|
|
||||||
Port: 25,
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid config with options",
|
|
||||||
args: []string{
|
|
||||||
"--name", "test",
|
|
||||||
"--host", "localhost",
|
|
||||||
"--port", "25",
|
|
||||||
"--auth-type", "LOGIN",
|
|
||||||
"--force-smtps",
|
|
||||||
"--skip-verify",
|
|
||||||
"--helo-hostname", "example.com",
|
|
||||||
"--disable-helo=true",
|
|
||||||
"--allowed-domains", "example.com,example.org",
|
|
||||||
"--skip-local-2fa",
|
|
||||||
"--active=false",
|
|
||||||
},
|
|
||||||
source: &auth_model.Source{
|
|
||||||
Type: auth_model.SMTP,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: false,
|
|
||||||
Cfg: &smtp.Source{
|
|
||||||
Auth: "LOGIN",
|
|
||||||
Host: "localhost",
|
|
||||||
Port: 25,
|
|
||||||
ForceSMTPS: true,
|
|
||||||
SkipVerify: true,
|
|
||||||
HeloHostname: "example.com",
|
|
||||||
DisableHelo: true,
|
|
||||||
AllowedDomains: "example.com,example.org",
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "skip",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
a := &authService{
|
|
||||||
initDB: func(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
createAuthSource: func(ctx context.Context, source *auth_model.Source) error {
|
|
||||||
assert.Equal(t, tc.source, source)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := &cli.Command{
|
|
||||||
Flags: microcmdAuthAddSMTP().Flags,
|
|
||||||
Action: a.runAddSMTP,
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{"smtp-test"}
|
|
||||||
args = append(args, tc.args...)
|
|
||||||
|
|
||||||
t.Log(args)
|
|
||||||
err := cmd.Run(t.Context(), args)
|
|
||||||
|
|
||||||
if tc.errMsg != "" {
|
|
||||||
assert.EqualError(t, err, tc.errMsg)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateSMTP(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
existingAuthSource *auth_model.Source
|
|
||||||
authSource *auth_model.Source
|
|
||||||
errMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "missing id",
|
|
||||||
args: []string{
|
|
||||||
"--name", "test",
|
|
||||||
"--host", "localhost",
|
|
||||||
"--port", "25",
|
|
||||||
},
|
|
||||||
errMsg: "--id flag is missing",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid config",
|
|
||||||
existingAuthSource: &auth_model.Source{
|
|
||||||
ID: 1,
|
|
||||||
Type: auth_model.SMTP,
|
|
||||||
Name: "old name",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &smtp.Source{
|
|
||||||
Auth: "PLAIN",
|
|
||||||
Host: "old host",
|
|
||||||
Port: 26,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
args: []string{
|
|
||||||
"--id", "1",
|
|
||||||
"--name", "test",
|
|
||||||
"--host", "localhost",
|
|
||||||
"--port", "25",
|
|
||||||
},
|
|
||||||
authSource: &auth_model.Source{
|
|
||||||
ID: 1,
|
|
||||||
Type: auth_model.SMTP,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &smtp.Source{
|
|
||||||
Auth: "PLAIN",
|
|
||||||
Host: "localhost",
|
|
||||||
Port: 25,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid config with options",
|
|
||||||
existingAuthSource: &auth_model.Source{
|
|
||||||
ID: 1,
|
|
||||||
Type: auth_model.SMTP,
|
|
||||||
Name: "old name",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &smtp.Source{
|
|
||||||
Auth: "PLAIN",
|
|
||||||
Host: "old host",
|
|
||||||
Port: 26,
|
|
||||||
HeloHostname: "old.example.com",
|
|
||||||
AllowedDomains: "old.example.com",
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "",
|
|
||||||
},
|
|
||||||
args: []string{
|
|
||||||
"--id", "1",
|
|
||||||
"--name", "test",
|
|
||||||
"--host", "localhost",
|
|
||||||
"--port", "25",
|
|
||||||
"--auth-type", "LOGIN",
|
|
||||||
"--force-smtps",
|
|
||||||
"--skip-verify",
|
|
||||||
"--helo-hostname", "example.com",
|
|
||||||
"--disable-helo",
|
|
||||||
"--allowed-domains", "example.com,example.org",
|
|
||||||
"--skip-local-2fa",
|
|
||||||
"--active=false",
|
|
||||||
},
|
|
||||||
authSource: &auth_model.Source{
|
|
||||||
ID: 1,
|
|
||||||
Type: auth_model.SMTP,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: false,
|
|
||||||
Cfg: &smtp.Source{
|
|
||||||
Auth: "LOGIN",
|
|
||||||
Host: "localhost",
|
|
||||||
Port: 25,
|
|
||||||
ForceSMTPS: true,
|
|
||||||
SkipVerify: true,
|
|
||||||
HeloHostname: "example.com",
|
|
||||||
DisableHelo: true,
|
|
||||||
AllowedDomains: "example.com,example.org",
|
|
||||||
},
|
|
||||||
TwoFactorPolicy: "skip",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
a := &authService{
|
|
||||||
initDB: func(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) {
|
|
||||||
return &auth_model.Source{
|
|
||||||
ID: 1,
|
|
||||||
Type: auth_model.SMTP,
|
|
||||||
Name: "test",
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: &smtp.Source{
|
|
||||||
Auth: "PLAIN",
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
|
|
||||||
updateAuthSource: func(ctx context.Context, source *auth_model.Source) error {
|
|
||||||
assert.Equal(t, tc.authSource, source)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
app := &cli.Command{
|
|
||||||
Flags: microcmdAuthUpdateSMTP().Flags,
|
|
||||||
Action: a.runUpdateSMTP,
|
|
||||||
}
|
|
||||||
args := []string{"smtp-tests"}
|
|
||||||
args = append(args, tc.args...)
|
|
||||||
|
|
||||||
err := app.Run(t.Context(), args)
|
|
||||||
|
|
||||||
if tc.errMsg != "" {
|
|
||||||
assert.EqualError(t, err, tc.errMsg)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -12,11 +11,11 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/services/auth/source/smtp"
|
"code.gitea.io/gitea/services/auth/source/smtp"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func smtpCLIFlags() []cli.Flag {
|
var (
|
||||||
return []cli.Flag{
|
smtpCLIFlags = []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Value: "",
|
Value: "",
|
||||||
@@ -68,34 +67,23 @@ func smtpCLIFlags() []cli.Flag {
|
|||||||
Value: true,
|
Value: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func microcmdAuthUpdateSMTP() *cli.Command {
|
microcmdAuthAddSMTP = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "update-smtp",
|
|
||||||
Usage: "Update existing SMTP authentication source",
|
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
|
||||||
return newAuthService().runUpdateSMTP(ctx, cmd)
|
|
||||||
},
|
|
||||||
Flags: append(smtpCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{
|
|
||||||
Name: "id",
|
|
||||||
Usage: "ID of authentication source",
|
|
||||||
}}, smtpCLIFlags()[1:]...)...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func microcmdAuthAddSMTP() *cli.Command {
|
|
||||||
return &cli.Command{
|
|
||||||
Name: "add-smtp",
|
Name: "add-smtp",
|
||||||
Usage: "Add new SMTP authentication source",
|
Usage: "Add new SMTP authentication source",
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
Action: runAddSMTP,
|
||||||
return newAuthService().runAddSMTP(ctx, cmd)
|
Flags: smtpCLIFlags,
|
||||||
},
|
|
||||||
Flags: smtpCLIFlags(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSMTPConfig(c *cli.Command, conf *smtp.Source) error {
|
microcmdAuthUpdateSMTP = &cli.Command{
|
||||||
|
Name: "update-smtp",
|
||||||
|
Usage: "Update existing SMTP authentication source",
|
||||||
|
Action: runUpdateSMTP,
|
||||||
|
Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
|
||||||
if c.IsSet("auth-type") {
|
if c.IsSet("auth-type") {
|
||||||
conf.Auth = c.String("auth-type")
|
conf.Auth = c.String("auth-type")
|
||||||
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
|
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
|
||||||
@@ -128,8 +116,11 @@ func parseSMTPConfig(c *cli.Command, conf *smtp.Source) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *authService) runAddSMTP(ctx context.Context, c *cli.Command) error {
|
func runAddSMTP(c *cli.Context) error {
|
||||||
if err := a.initDB(ctx); err != nil {
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +148,7 @@ func (a *authService) runAddSMTP(ctx context.Context, c *cli.Command) error {
|
|||||||
smtpConfig.Auth = "PLAIN"
|
smtpConfig.Auth = "PLAIN"
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.createAuthSource(ctx, &auth_model.Source{
|
return auth_model.CreateSource(ctx, &auth_model.Source{
|
||||||
Type: auth_model.SMTP,
|
Type: auth_model.SMTP,
|
||||||
Name: c.String("name"),
|
Name: c.String("name"),
|
||||||
IsActive: active,
|
IsActive: active,
|
||||||
@@ -166,16 +157,19 @@ func (a *authService) runAddSMTP(ctx context.Context, c *cli.Command) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *authService) runUpdateSMTP(ctx context.Context, c *cli.Command) error {
|
func runUpdateSMTP(c *cli.Context) error {
|
||||||
if !c.IsSet("id") {
|
if !c.IsSet("id") {
|
||||||
return errors.New("--id flag is missing")
|
return errors.New("--id flag is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.initDB(ctx); err != nil {
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
|
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -196,5 +190,5 @@ func (a *authService) runUpdateSMTP(ctx context.Context, c *cli.Command) error {
|
|||||||
|
|
||||||
source.Cfg = smtpConfig
|
source.Cfg = smtpConfig
|
||||||
source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
|
source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
|
||||||
return a.updateAuthSource(ctx, source)
|
return auth_model.UpdateSource(ctx, source)
|
||||||
}
|
}
|
||||||
@@ -4,13 +4,11 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -27,14 +25,20 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func runRegenerateHooks(ctx context.Context, _ *cli.Command) error {
|
func runRegenerateHooks(_ *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRegenerateKeys(ctx context.Context, _ *cli.Command) error {
|
func runRegenerateKeys(_ *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,18 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var subcmdUser = &cli.Command{
|
var subcmdUser = &cli.Command{
|
||||||
Name: "user",
|
Name: "user",
|
||||||
Usage: "Modify users",
|
Usage: "Modify users",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
microcmdUserCreate(),
|
microcmdUserCreate,
|
||||||
microcmdUserList,
|
microcmdUserList,
|
||||||
microcmdUserChangePassword(),
|
microcmdUserChangePassword,
|
||||||
microcmdUserDelete(),
|
microcmdUserDelete,
|
||||||
microcmdUserGenerateAccessToken,
|
microcmdUserGenerateAccessToken,
|
||||||
microcmdUserMustChangePassword(),
|
microcmdUserMustChangePassword,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@@ -14,11 +13,10 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
user_service "code.gitea.io/gitea/services/user"
|
user_service "code.gitea.io/gitea/services/user"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func microcmdUserChangePassword() *cli.Command {
|
var microcmdUserChangePassword = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "change-password",
|
Name: "change-password",
|
||||||
Usage: "Change a user's password",
|
Usage: "Change a user's password",
|
||||||
Action: runChangePassword,
|
Action: runChangePassword,
|
||||||
@@ -26,14 +24,14 @@ func microcmdUserChangePassword() *cli.Command {
|
|||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
Aliases: []string{"u"},
|
Aliases: []string{"u"},
|
||||||
|
Value: "",
|
||||||
Usage: "The user to change password for",
|
Usage: "The user to change password for",
|
||||||
Required: true,
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "password",
|
Name: "password",
|
||||||
Aliases: []string{"p"},
|
Aliases: []string{"p"},
|
||||||
|
Value: "",
|
||||||
Usage: "New password to set for user",
|
Usage: "New password to set for user",
|
||||||
Required: true,
|
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "must-change-password",
|
Name: "must-change-password",
|
||||||
@@ -42,13 +40,17 @@ func microcmdUserChangePassword() *cli.Command {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func runChangePassword(ctx context.Context, c *cli.Command) error {
|
func runChangePassword(c *cli.Context) error {
|
||||||
if !setting.IsInTesting {
|
if err := argsSet(c, "username", "password"); err != nil {
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := user_model.GetUserByName(ctx, c.String("username"))
|
user, err := user_model.GetUserByName(ctx, c.String("username"))
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
"code.gitea.io/gitea/models/unittest"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestChangePasswordCommand(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
|
|
||||||
}()
|
|
||||||
|
|
||||||
t.Run("change password successfully", func(t *testing.T) {
|
|
||||||
// defer func() {
|
|
||||||
// require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
|
|
||||||
// }()
|
|
||||||
// Prepare test user
|
|
||||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
err := microcmdUserCreate().Run(ctx, []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// load test user
|
|
||||||
userBase := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
|
|
||||||
// Change the password
|
|
||||||
err = microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "newpassword"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Verify the password has been changed
|
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
assert.NotEqual(t, userBase.Passwd, user.Passwd)
|
|
||||||
assert.NotEqual(t, userBase.Salt, user.Salt)
|
|
||||||
|
|
||||||
// Additional check for must-change-password flag
|
|
||||||
require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "anotherpassword", "--must-change-password=false"}))
|
|
||||||
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
assert.False(t, user.MustChangePassword)
|
|
||||||
|
|
||||||
require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "yetanotherpassword", "--must-change-password"}))
|
|
||||||
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
assert.True(t, user.MustChangePassword)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("failure cases", func(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
expectedErr string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "user does not exist",
|
|
||||||
args: []string{"change-password", "--username", "nonexistentuser", "--password", "newpassword"},
|
|
||||||
expectedErr: "user does not exist",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "missing username",
|
|
||||||
args: []string{"change-password", "--password", "newpassword"},
|
|
||||||
expectedErr: `"username" not set`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "missing password",
|
|
||||||
args: []string{"change-password", "--username", "testuser"},
|
|
||||||
expectedErr: `"password" not set`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "too short password",
|
|
||||||
args: []string{"change-password", "--username", "testuser", "--password", "1"},
|
|
||||||
expectedErr: "password is not long enough",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
err := microcmdUserChangePassword().Run(ctx, tc.args)
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Contains(t, err.Error(), tc.expectedErr)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -16,18 +16,14 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func microcmdUserCreate() *cli.Command {
|
var microcmdUserCreate = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "create",
|
Name: "create",
|
||||||
Usage: "Create a new user in database",
|
Usage: "Create a new user in database",
|
||||||
Action: runCreateUser,
|
Action: runCreateUser,
|
||||||
MutuallyExclusiveFlags: []cli.MutuallyExclusiveFlags{
|
Flags: []cli.Flag{
|
||||||
{
|
|
||||||
Flags: [][]cli.Flag{
|
|
||||||
{
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Usage: "Username. DEPRECATED: use username instead",
|
Usage: "Username. DEPRECATED: use username instead",
|
||||||
@@ -36,12 +32,6 @@ func microcmdUserCreate() *cli.Command {
|
|||||||
Name: "username",
|
Name: "username",
|
||||||
Usage: "Username",
|
Usage: "Username",
|
||||||
},
|
},
|
||||||
},
|
|
||||||
},
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "user-type",
|
Name: "user-type",
|
||||||
Usage: "Set user's type: individual or bot",
|
Usage: "Set user's type: individual or bot",
|
||||||
@@ -54,7 +44,6 @@ func microcmdUserCreate() *cli.Command {
|
|||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "email",
|
Name: "email",
|
||||||
Usage: "User email address",
|
Usage: "User email address",
|
||||||
Required: true,
|
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
@@ -67,7 +56,7 @@ func microcmdUserCreate() *cli.Command {
|
|||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "must-change-password",
|
Name: "must-change-password",
|
||||||
Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
|
Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
|
||||||
HideDefault: true,
|
DisableDefaultText: true,
|
||||||
},
|
},
|
||||||
&cli.IntFlag{
|
&cli.IntFlag{
|
||||||
Name: "random-password-length",
|
Name: "random-password-length",
|
||||||
@@ -98,13 +87,16 @@ func microcmdUserCreate() *cli.Command {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func runCreateUser(ctx context.Context, c *cli.Command) error {
|
func runCreateUser(c *cli.Context) error {
|
||||||
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
|
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
|
||||||
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
|
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
|
||||||
setting.LoadSettings()
|
setting.LoadSettings()
|
||||||
|
|
||||||
|
if err := argsSet(c, "email"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
userTypes := map[string]user_model.UserType{
|
userTypes := map[string]user_model.UserType{
|
||||||
"individual": user_model.UserTypeIndividual,
|
"individual": user_model.UserTypeIndividual,
|
||||||
"bot": user_model.UserTypeBot,
|
"bot": user_model.UserTypeBot,
|
||||||
@@ -121,6 +113,12 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
|
|||||||
return errors.New("password can only be set for individual users")
|
return errors.New("password can only be set for individual users")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if c.IsSet("name") && c.IsSet("username") {
|
||||||
|
return errors.New("cannot set both --name and --username flags")
|
||||||
|
}
|
||||||
|
if !c.IsSet("name") && !c.IsSet("username") {
|
||||||
|
return errors.New("one of --name or --username flags must be set")
|
||||||
|
}
|
||||||
|
|
||||||
if c.IsSet("password") && c.IsSet("random-password") {
|
if c.IsSet("password") && c.IsSet("random-password") {
|
||||||
return errors.New("cannot set both -random-password and -password flags")
|
return errors.New("cannot set both -random-password and -password flags")
|
||||||
@@ -131,12 +129,16 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
|
|||||||
username = c.String("username")
|
username = c.String("username")
|
||||||
} else {
|
} else {
|
||||||
username = c.String("name")
|
username = c.String("name")
|
||||||
_, _ = fmt.Fprintf(c.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
|
_, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := c.Context
|
||||||
if !setting.IsInTesting {
|
if !setting.IsInTesting {
|
||||||
// FIXME: need to refactor the "initDB" related code later
|
// FIXME: need to refactor the "installSignals/initDB" related code later
|
||||||
// it doesn't make sense to call it in (almost) every command action function
|
// it doesn't make sense to call it in (almost) every command action function
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = installSignals()
|
||||||
|
defer cancel()
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -151,7 +153,6 @@ 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")
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestAdminUserCreate(t *testing.T) {
|
func TestAdminUserCreate(t *testing.T) {
|
||||||
|
app := NewMainApp(AppVersion{})
|
||||||
|
|
||||||
reset := func() {
|
reset := func() {
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
|
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.EmailAddress{}))
|
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{}))
|
require.NoError(t, db.TruncateBeans(db.DefaultContext, &auth_model.AccessToken{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("MustChangePassword", func(t *testing.T) {
|
t.Run("MustChangePassword", func(t *testing.T) {
|
||||||
@@ -29,9 +31,8 @@ func TestAdminUserCreate(t *testing.T) {
|
|||||||
IsAdmin bool
|
IsAdmin bool
|
||||||
MustChangePassword bool
|
MustChangePassword bool
|
||||||
}
|
}
|
||||||
|
|
||||||
createCheck := func(name, args string) check {
|
createCheck := func(name, args string) check {
|
||||||
require.NoError(t, microcmdUserCreate().Run(t.Context(), strings.Fields(fmt.Sprintf("create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
|
require.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
|
||||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
|
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
|
||||||
return check{IsAdmin: u.IsAdmin, MustChangePassword: u.MustChangePassword}
|
return check{IsAdmin: u.IsAdmin, MustChangePassword: u.MustChangePassword}
|
||||||
}
|
}
|
||||||
@@ -50,7 +51,7 @@ func TestAdminUserCreate(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
createUser := func(name string, args ...string) error {
|
createUser := func(name string, args ...string) error {
|
||||||
return microcmdUserCreate().Run(t.Context(), append([]string{"create", "--username", name, "--email", name + "@gitea.local"}, args...))
|
return app.Run(append([]string{"./gitea", "admin", "user", "create", "--username", name, "--email", name + "@gitea.local"}, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("UserType", func(t *testing.T) {
|
t.Run("UserType", func(t *testing.T) {
|
||||||
|
|||||||
@@ -4,21 +4,18 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
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/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
user_service "code.gitea.io/gitea/services/user"
|
user_service "code.gitea.io/gitea/services/user"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func microcmdUserDelete() *cli.Command {
|
var microcmdUserDelete = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "delete",
|
Name: "delete",
|
||||||
Usage: "Delete specific user by id, name or email",
|
Usage: "Delete specific user by id, name or email",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
@@ -43,18 +40,18 @@ func microcmdUserDelete() *cli.Command {
|
|||||||
},
|
},
|
||||||
Action: runDeleteUser,
|
Action: runDeleteUser,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func runDeleteUser(ctx context.Context, c *cli.Command) error {
|
func runDeleteUser(c *cli.Context) error {
|
||||||
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
|
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
|
||||||
return errors.New("You must provide the id, username or email of a user to delete")
|
return errors.New("You must provide the id, username or email of a user to delete")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !setting.IsInTesting {
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := storage.Init(); err != nil {
|
if err := storage.Init(); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -73,11 +70,11 @@ func runDeleteUser(ctx context.Context, c *cli.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
|
if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
|
||||||
return fmt.Errorf("the user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
|
return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.IsSet("id") && user.ID != c.Int64("id") {
|
if c.IsSet("id") && user.ID != c.Int64("id") {
|
||||||
return fmt.Errorf("the user %s does not match the provided id %d", user.Name, c.Int64("id"))
|
return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return user_service.DeleteUser(ctx, user, c.Bool("purge"))
|
return user_service.DeleteUser(ctx, user, c.Bool("purge"))
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
"code.gitea.io/gitea/models/unittest"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAdminUserDelete(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
defer func() {
|
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
|
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.EmailAddress{}))
|
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{}))
|
|
||||||
}()
|
|
||||||
|
|
||||||
setupTestUser := func(t *testing.T) {
|
|
||||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("delete user by id", func(t *testing.T) {
|
|
||||||
setupTestUser(t)
|
|
||||||
|
|
||||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--id", strconv.FormatInt(u.ID, 10)})
|
|
||||||
require.NoError(t, err)
|
|
||||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
})
|
|
||||||
t.Run("delete user by username", func(t *testing.T) {
|
|
||||||
setupTestUser(t)
|
|
||||||
|
|
||||||
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--username", "testuser"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
})
|
|
||||||
t.Run("delete user by email", func(t *testing.T) {
|
|
||||||
setupTestUser(t)
|
|
||||||
|
|
||||||
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--email", "testuser@gitea.local"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
})
|
|
||||||
t.Run("delete user by all 3 attributes", func(t *testing.T) {
|
|
||||||
setupTestUser(t)
|
|
||||||
|
|
||||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
err := microcmdUserDelete().Run(ctx, []string{"delete", "--id", strconv.FormatInt(u.ID, 10), "--username", "testuser", "--email", "testuser@gitea.local"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAdminUserDeleteFailure(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
expectedErr string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "no user to delete",
|
|
||||||
args: []string{"delete", "--username", "nonexistentuser"},
|
|
||||||
expectedErr: "user does not exist",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "user exists but provided username does not match",
|
|
||||||
args: []string{"delete", "--email", "testuser@gitea.local", "--username", "wrongusername"},
|
|
||||||
expectedErr: "the user testuser who has email testuser@gitea.local does not match the provided username wrongusername",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "user exists but provided id does not match",
|
|
||||||
args: []string{"delete", "--username", "testuser", "--id", "999"},
|
|
||||||
expectedErr: "the user testuser does not match the provided id 999",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no required flags are provided",
|
|
||||||
args: []string{"delete"},
|
|
||||||
expectedErr: "You must provide the id, username or email of a user to delete",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
if strings.Contains(tc.name, "user exists") {
|
|
||||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := microcmdUserDelete().Run(ctx, tc.args)
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Contains(t, err.Error(), tc.expectedErr)
|
|
||||||
})
|
|
||||||
|
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
|
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.EmailAddress{}))
|
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,14 +4,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var microcmdUserGenerateAccessToken = &cli.Command{
|
var microcmdUserGenerateAccessToken = &cli.Command{
|
||||||
@@ -42,11 +41,14 @@ var microcmdUserGenerateAccessToken = &cli.Command{
|
|||||||
Action: runGenerateAccessToken,
|
Action: runGenerateAccessToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
|
func runGenerateAccessToken(c *cli.Context) error {
|
||||||
if !c.IsSet("username") {
|
if !c.IsSet("username") {
|
||||||
return errors.New("you must provide a username to generate a token for")
|
return errors.New("you must provide a username to generate a token for")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var microcmdUserList = &cli.Command{
|
var microcmdUserList = &cli.Command{
|
||||||
@@ -26,7 +25,10 @@ var microcmdUserList = &cli.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func runListUsers(ctx context.Context, c *cli.Command) error {
|
func runListUsers(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,15 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func microcmdUserMustChangePassword() *cli.Command {
|
var microcmdUserMustChangePassword = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "must-change-password",
|
Name: "must-change-password",
|
||||||
Usage: "Set the must change password flag for the provided users or all users",
|
Usage: "Set the must change password flag for the provided users or all users",
|
||||||
Action: runMustChangePassword,
|
Action: runMustChangePassword,
|
||||||
@@ -36,9 +33,11 @@ func microcmdUserMustChangePassword() *cli.Command {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func runMustChangePassword(ctx context.Context, c *cli.Command) error {
|
func runMustChangePassword(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if c.NArg() == 0 && !c.IsSet("all") {
|
if c.NArg() == 0 && !c.IsSet("all") {
|
||||||
return errors.New("either usernames or --all must be provided")
|
return errors.New("either usernames or --all must be provided")
|
||||||
}
|
}
|
||||||
@@ -47,18 +46,15 @@ func runMustChangePassword(ctx context.Context, c *cli.Command) error {
|
|||||||
all := c.Bool("all")
|
all := c.Bool("all")
|
||||||
exclude := c.StringSlice("exclude")
|
exclude := c.StringSlice("exclude")
|
||||||
|
|
||||||
if !setting.IsInTesting {
|
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args().Slice(), exclude)
|
n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args().Slice(), exclude)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
"code.gitea.io/gitea/models/unittest"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMustChangePassword(t *testing.T) {
|
|
||||||
defer func() {
|
|
||||||
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
|
|
||||||
}()
|
|
||||||
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuserexclude", "--email", "testuserexclude@gitea.local", "--random-password"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
// Reset password change flag
|
|
||||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
testUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
assert.False(t, testUser.MustChangePassword)
|
|
||||||
testUserExclude := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
|
||||||
assert.False(t, testUserExclude.MustChangePassword)
|
|
||||||
|
|
||||||
// Make all users change password
|
|
||||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
assert.True(t, testUser.MustChangePassword)
|
|
||||||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
|
||||||
assert.True(t, testUserExclude.MustChangePassword)
|
|
||||||
|
|
||||||
// Reset password change flag but exclude all tested users
|
|
||||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset", "--exclude", "testuser,testuserexclude"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
assert.True(t, testUser.MustChangePassword)
|
|
||||||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
|
||||||
assert.True(t, testUserExclude.MustChangePassword)
|
|
||||||
|
|
||||||
// Reset password change flag by listing multiple users
|
|
||||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser", "testuserexclude"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
assert.False(t, testUser.MustChangePassword)
|
|
||||||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
|
||||||
assert.False(t, testUserExclude.MustChangePassword)
|
|
||||||
|
|
||||||
// Exclude a user from all user
|
|
||||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--exclude", "testuserexclude"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
assert.True(t, testUser.MustChangePassword)
|
|
||||||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
|
||||||
assert.False(t, testUserExclude.MustChangePassword)
|
|
||||||
|
|
||||||
// Unset a flag for single user
|
|
||||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
|
||||||
assert.False(t, testUser.MustChangePassword)
|
|
||||||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
|
||||||
assert.False(t, testUserExclude.MustChangePassword)
|
|
||||||
}
|
|
||||||
62
cmd/cert.go
62
cmd/cert.go
@@ -6,7 +6,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@@ -14,7 +13,6 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
@@ -22,12 +20,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// cmdCert represents the available cert sub-command.
|
// CmdCert represents the available cert sub-command.
|
||||||
func cmdCert() *cli.Command {
|
var CmdCert = &cli.Command{
|
||||||
return &cli.Command{
|
|
||||||
Name: "cert",
|
Name: "cert",
|
||||||
Usage: "Generate self-signed certificate",
|
Usage: "Generate self-signed certificate",
|
||||||
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
||||||
@@ -36,8 +33,8 @@ Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "host",
|
Name: "host",
|
||||||
|
Value: "",
|
||||||
Usage: "Comma-separated hostnames and IPs to generate a certificate for",
|
Usage: "Comma-separated hostnames and IPs to generate a certificate for",
|
||||||
Required: true,
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "ecdsa-curve",
|
Name: "ecdsa-curve",
|
||||||
@@ -63,18 +60,7 @@ Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
|||||||
Name: "ca",
|
Name: "ca",
|
||||||
Usage: "whether this cert should be its own Certificate Authority",
|
Usage: "whether this cert should be its own Certificate Authority",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "out",
|
|
||||||
Value: "cert.pem",
|
|
||||||
Usage: "Path to the file where there certificate will be saved",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "keyout",
|
|
||||||
Value: "key.pem",
|
|
||||||
Usage: "Path to the file where there certificate key will be saved",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func publicKey(priv any) any {
|
func publicKey(priv any) any {
|
||||||
@@ -103,7 +89,11 @@ func pemBlockForKey(priv any) *pem.Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCert(_ context.Context, c *cli.Command) error {
|
func runCert(c *cli.Context) error {
|
||||||
|
if err := argsSet(c, "host"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var priv any
|
var priv any
|
||||||
var err error
|
var err error
|
||||||
switch c.String("ecdsa-curve") {
|
switch c.String("ecdsa-curve") {
|
||||||
@@ -118,17 +108,17 @@ func runCert(_ context.Context, c *cli.Command) error {
|
|||||||
case "P521":
|
case "P521":
|
||||||
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
|
log.Fatalf("Unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to generate private key: %w", err)
|
log.Fatalf("Failed to generate private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var notBefore time.Time
|
var notBefore time.Time
|
||||||
if startDate := c.String("start-date"); startDate != "" {
|
if startDate := c.String("start-date"); startDate != "" {
|
||||||
notBefore, err = time.Parse("Jan 2 15:04:05 2006", startDate)
|
notBefore, err = time.Parse("Jan 2 15:04:05 2006", startDate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse creation date %w", err)
|
log.Fatalf("Failed to parse creation date: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
notBefore = time.Now()
|
notBefore = time.Now()
|
||||||
@@ -139,7 +129,7 @@ func runCert(_ context.Context, c *cli.Command) error {
|
|||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to generate serial number: %w", err)
|
log.Fatalf("Failed to generate serial number: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
template := x509.Certificate{
|
template := x509.Certificate{
|
||||||
@@ -156,8 +146,8 @@ func runCert(_ context.Context, c *cli.Command) error {
|
|||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
hosts := strings.SplitSeq(c.String("host"), ",")
|
hosts := strings.Split(c.String("host"), ",")
|
||||||
for h := range hosts {
|
for _, h := range hosts {
|
||||||
if ip := net.ParseIP(h); ip != nil {
|
if ip := net.ParseIP(h); ip != nil {
|
||||||
template.IPAddresses = append(template.IPAddresses, ip)
|
template.IPAddresses = append(template.IPAddresses, ip)
|
||||||
} else {
|
} else {
|
||||||
@@ -172,35 +162,35 @@ func runCert(_ context.Context, c *cli.Command) error {
|
|||||||
|
|
||||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
|
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create certificate: %w", err)
|
log.Fatalf("Failed to create certificate: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
certOut, err := os.Create(c.String("out"))
|
certOut, err := os.Create("cert.pem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err)
|
log.Fatalf("Failed to open cert.pem for writing: %v", err)
|
||||||
}
|
}
|
||||||
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to encode certificate: %w", err)
|
log.Fatalf("Failed to encode certificate: %v", err)
|
||||||
}
|
}
|
||||||
err = certOut.Close()
|
err = certOut.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to write cert: %w", err)
|
log.Fatalf("Failed to write cert: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(c.Writer, "Written cert to %s\n", c.String("out"))
|
log.Println("Written cert.pem")
|
||||||
|
|
||||||
keyOut, err := os.OpenFile(c.String("keyout"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
|
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err)
|
log.Fatalf("Failed to open key.pem for writing: %v", err)
|
||||||
}
|
}
|
||||||
err = pem.Encode(keyOut, pemBlockForKey(priv))
|
err = pem.Encode(keyOut, pemBlockForKey(priv))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to encode key: %w", err)
|
log.Fatalf("Failed to encode key: %v", err)
|
||||||
}
|
}
|
||||||
err = keyOut.Close()
|
err = keyOut.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to write key: %w", err)
|
log.Fatalf("Failed to write key: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(c.Writer, "Written key to %s\n", c.String("keyout"))
|
log.Println("Written key.pem")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
123
cmd/cert_test.go
123
cmd/cert_test.go
@@ -1,123 +0,0 @@
|
|||||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCertCommand(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "RSA cert generation",
|
|
||||||
args: []string{
|
|
||||||
"cert-test",
|
|
||||||
"--host", "localhost",
|
|
||||||
"--rsa-bits", "2048",
|
|
||||||
"--duration", "1h",
|
|
||||||
"--start-date", "Jan 1 00:00:00 2024",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ECDSA cert generation",
|
|
||||||
args: []string{
|
|
||||||
"cert-test",
|
|
||||||
"--host", "localhost",
|
|
||||||
"--ecdsa-curve", "P256",
|
|
||||||
"--duration", "1h",
|
|
||||||
"--start-date", "Jan 1 00:00:00 2024",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "mixed host, certificate authority",
|
|
||||||
args: []string{
|
|
||||||
"cert-test",
|
|
||||||
"--host", "localhost,127.0.0.1",
|
|
||||||
"--duration", "1h",
|
|
||||||
"--start-date", "Jan 1 00:00:00 2024",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.name, func(t *testing.T) {
|
|
||||||
app := cmdCert()
|
|
||||||
tempDir := t.TempDir()
|
|
||||||
|
|
||||||
certFile := filepath.Join(tempDir, "cert.pem")
|
|
||||||
keyFile := filepath.Join(tempDir, "key.pem")
|
|
||||||
|
|
||||||
err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.FileExists(t, certFile)
|
|
||||||
assert.FileExists(t, keyFile)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCertCommandFailures(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
errMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Start Date Parsing failure",
|
|
||||||
args: []string{
|
|
||||||
"cert-test",
|
|
||||||
"--host", "localhost",
|
|
||||||
"--start-date", "invalid-date",
|
|
||||||
},
|
|
||||||
errMsg: "parsing time",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Unknown curve",
|
|
||||||
args: []string{
|
|
||||||
"cert-test",
|
|
||||||
"--host", "localhost",
|
|
||||||
"--ecdsa-curve", "invalid-curve",
|
|
||||||
},
|
|
||||||
errMsg: "unrecognized elliptic curve",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Key generation failure",
|
|
||||||
args: []string{
|
|
||||||
"cert-test",
|
|
||||||
"--host", "localhost",
|
|
||||||
"--rsa-bits", "invalid-bits",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Missing parameters",
|
|
||||||
args: []string{
|
|
||||||
"cert-test",
|
|
||||||
},
|
|
||||||
errMsg: `"host" not set`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.name, func(t *testing.T) {
|
|
||||||
app := cmdCert()
|
|
||||||
tempDir := t.TempDir()
|
|
||||||
|
|
||||||
certFile := filepath.Join(tempDir, "cert.pem")
|
|
||||||
keyFile := filepath.Join(tempDir, "key.pem")
|
|
||||||
err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile))
|
|
||||||
require.Error(t, err)
|
|
||||||
if c.errMsg != "" {
|
|
||||||
assert.ErrorContains(t, err, c.errMsg)
|
|
||||||
}
|
|
||||||
assert.NoFileExists(t, certFile)
|
|
||||||
assert.NoFileExists(t, keyFile)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
31
cmd/cmd.go
31
cmd/cmd.go
@@ -18,19 +18,20 @@ import (
|
|||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"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"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// argsSet checks that all the required arguments are set. args is a list of
|
// argsSet checks that all the required arguments are set. args is a list of
|
||||||
// arguments that must be set in the passed Context.
|
// arguments that must be set in the passed Context.
|
||||||
func argsSet(c *cli.Command, args ...string) error {
|
func argsSet(c *cli.Context, args ...string) error {
|
||||||
for _, a := range args {
|
for _, a := range args {
|
||||||
if !c.IsSet(a) {
|
if !c.IsSet(a) {
|
||||||
return errors.New(a + " is not set")
|
return errors.New(a + " is not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Value(a) == nil {
|
if util.IsEmptyString(c.String(a)) {
|
||||||
return errors.New(a + " is required")
|
return errors.New(a + " is required")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +109,7 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
|
|||||||
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
|
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func globalBool(c *cli.Command, name string) bool {
|
func globalBool(c *cli.Context, name string) bool {
|
||||||
for _, ctx := range c.Lineage() {
|
for _, ctx := range c.Lineage() {
|
||||||
if ctx.Bool(name) {
|
if ctx.Bool(name) {
|
||||||
return true
|
return true
|
||||||
@@ -119,14 +120,8 @@ func globalBool(c *cli.Command, name string) bool {
|
|||||||
|
|
||||||
// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout.
|
// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout.
|
||||||
// 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(*cli.Context) error {
|
||||||
return func(ctx context.Context, c *cli.Command) (context.Context, error) {
|
return func(c *cli.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
|
||||||
@@ -135,16 +130,6 @@ func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cl
|
|||||||
level = log.TRACE
|
level = log.TRACE
|
||||||
}
|
}
|
||||||
log.SetConsoleLogger(log.DEFAULT, "console-default", level)
|
log.SetConsoleLogger(log.DEFAULT, "console-default", level)
|
||||||
return ctx, nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValidDefaultSubCommand(cmd *cli.Command) (string, bool) {
|
|
||||||
// Dirty patch for urfave/cli's strange design.
|
|
||||||
// "./gitea bad-cmd" should not start the web server.
|
|
||||||
rootArgs := cmd.Root().Args().Slice()
|
|
||||||
if len(rootArgs) != 0 && rootArgs[0] != cmd.Name {
|
|
||||||
return rootArgs[0], false
|
|
||||||
}
|
|
||||||
return "", true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/urfave/cli/v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDefaultCommand(t *testing.T) {
|
|
||||||
test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) {
|
|
||||||
called := false
|
|
||||||
cmd := &cli.Command{
|
|
||||||
DefaultCommand: "test",
|
|
||||||
Commands: []*cli.Command{
|
|
||||||
{
|
|
||||||
Name: "test",
|
|
||||||
Action: func(ctx context.Context, command *cli.Command) error {
|
|
||||||
retName, retValid := isValidDefaultSubCommand(command)
|
|
||||||
assert.Equal(t, expectedRetName, retName)
|
|
||||||
assert.Equal(t, expectedRetValid, retValid)
|
|
||||||
called = true
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
assert.NoError(t, cmd.Run(t.Context(), args))
|
|
||||||
assert.True(t, called)
|
|
||||||
}
|
|
||||||
test(t, []string{"./gitea"}, "", true)
|
|
||||||
test(t, []string{"./gitea", "test"}, "", true)
|
|
||||||
test(t, []string{"./gitea", "other"}, "other", false)
|
|
||||||
}
|
|
||||||
156
cmd/config.go
156
cmd/config.go
@@ -1,156 +0,0 @@
|
|||||||
// 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
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
// 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))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
18
cmd/docs.go
18
cmd/docs.go
@@ -4,13 +4,11 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
cli_docs "github.com/urfave/cli-docs/v3"
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/urfave/cli/v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDocs represents the available docs sub-command.
|
// CmdDocs represents the available docs sub-command.
|
||||||
@@ -32,16 +30,16 @@ var CmdDocs = &cli.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDocs(_ context.Context, cmd *cli.Command) error {
|
func runDocs(ctx *cli.Context) error {
|
||||||
docs, err := cli_docs.ToMarkdown(cmd.Root())
|
docs, err := ctx.App.ToMarkdown()
|
||||||
if cmd.Bool("man") {
|
if ctx.Bool("man") {
|
||||||
docs, err = cli_docs.ToMan(cmd.Root())
|
docs, err = ctx.App.ToMan()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cmd.Bool("man") {
|
if !ctx.Bool("man") {
|
||||||
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
|
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
|
||||||
// It affects markdown output (even though the issue is referring to man pages)
|
// It affects markdown output (even though the issue is referring to man pages)
|
||||||
// https://github.com/urfave/cli/issues/1040
|
// https://github.com/urfave/cli/issues/1040
|
||||||
@@ -53,8 +51,8 @@ func runDocs(_ context.Context, cmd *cli.Command) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
out := os.Stdout
|
out := os.Stdout
|
||||||
if cmd.String("output") != "" {
|
if ctx.String("output") != "" {
|
||||||
fi, err := os.Create(cmd.String("output"))
|
fi, err := os.Create(ctx.String("output"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/services/doctor"
|
"code.gitea.io/gitea/services/doctor"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ var CmdDoctor = &cli.Command{
|
|||||||
Usage: "Diagnose and optionally fix problems, convert or re-create database tables",
|
Usage: "Diagnose and optionally fix problems, convert or re-create database tables",
|
||||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
|
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
|
||||||
|
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
cmdDoctorCheck,
|
cmdDoctorCheck,
|
||||||
cmdRecreateTable,
|
cmdRecreateTable,
|
||||||
cmdDoctorConvert,
|
cmdDoctorConvert,
|
||||||
@@ -93,13 +93,16 @@ You should back-up your database before doing this and ensure that your database
|
|||||||
Action: runRecreateTable,
|
Action: runRecreateTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
|
func runRecreateTable(ctx *cli.Context) error {
|
||||||
|
stdCtx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
// Redirect the default golog to here
|
// Redirect the default golog to here
|
||||||
golog.SetFlags(0)
|
golog.SetFlags(0)
|
||||||
golog.SetPrefix("")
|
golog.SetPrefix("")
|
||||||
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
|
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
|
||||||
|
|
||||||
debug := cmd.Bool("debug")
|
debug := ctx.Bool("debug")
|
||||||
setting.MustInstalled()
|
setting.MustInstalled()
|
||||||
setting.LoadDBSetting()
|
setting.LoadDBSetting()
|
||||||
|
|
||||||
@@ -110,15 +113,15 @@ func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setting.Database.LogSQL = debug
|
setting.Database.LogSQL = debug
|
||||||
if err := db.InitEngine(ctx); err != nil {
|
if err := db.InitEngine(stdCtx); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
|
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
args := cmd.Args()
|
args := ctx.Args()
|
||||||
names := make([]string, 0, cmd.NArg())
|
names := make([]string, 0, ctx.NArg())
|
||||||
for i := 0; i < cmd.NArg(); i++ {
|
for i := 0; i < ctx.NArg(); i++ {
|
||||||
names = append(names, args.Get(i))
|
names = append(names, args.Get(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +131,7 @@ func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
|
|||||||
}
|
}
|
||||||
recreateTables := migrate_base.RecreateTables(beans...)
|
recreateTables := migrate_base.RecreateTables(beans...)
|
||||||
|
|
||||||
return db.InitEngineWithMigration(context.Background(), func(ctx context.Context, x *xorm.Engine) error {
|
return db.InitEngineWithMigration(stdCtx, func(ctx context.Context, x *xorm.Engine) error {
|
||||||
if err := migrations.EnsureUpToDate(ctx, x); err != nil {
|
if err := migrations.EnsureUpToDate(ctx, x); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -136,11 +139,11 @@ func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupDoctorDefaultLogger(cmd *cli.Command, colorize bool) {
|
func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
|
||||||
// Silence the default loggers
|
// Silence the default loggers
|
||||||
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
|
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
|
||||||
|
|
||||||
logFile := cmd.String("log-file")
|
logFile := ctx.String("log-file")
|
||||||
switch logFile {
|
switch logFile {
|
||||||
case "":
|
case "":
|
||||||
return // if no doctor log-file is set, do not show any log from default logger
|
return // if no doctor log-file is set, do not show any log from default logger
|
||||||
@@ -158,20 +161,23 @@ func setupDoctorDefaultLogger(cmd *cli.Command, colorize bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDoctorCheck(ctx context.Context, cmd *cli.Command) error {
|
func runDoctorCheck(ctx *cli.Context) error {
|
||||||
|
stdCtx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
colorize := log.CanColorStdout
|
colorize := log.CanColorStdout
|
||||||
if cmd.IsSet("color") {
|
if ctx.IsSet("color") {
|
||||||
colorize = cmd.Bool("color")
|
colorize = ctx.Bool("color")
|
||||||
}
|
}
|
||||||
|
|
||||||
setupDoctorDefaultLogger(cmd, colorize)
|
setupDoctorDefaultLogger(ctx, colorize)
|
||||||
|
|
||||||
// Finally redirect the default golang's log to here
|
// Finally redirect the default golang's log to here
|
||||||
golog.SetFlags(0)
|
golog.SetFlags(0)
|
||||||
golog.SetPrefix("")
|
golog.SetPrefix("")
|
||||||
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
|
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
|
||||||
|
|
||||||
if cmd.IsSet("list") {
|
if ctx.IsSet("list") {
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
||||||
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
||||||
doctor.SortChecks(doctor.Checks)
|
doctor.SortChecks(doctor.Checks)
|
||||||
@@ -189,12 +195,12 @@ func runDoctorCheck(ctx context.Context, cmd *cli.Command) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var checks []*doctor.Check
|
var checks []*doctor.Check
|
||||||
if cmd.Bool("all") {
|
if ctx.Bool("all") {
|
||||||
checks = make([]*doctor.Check, len(doctor.Checks))
|
checks = make([]*doctor.Check, len(doctor.Checks))
|
||||||
copy(checks, doctor.Checks)
|
copy(checks, doctor.Checks)
|
||||||
} else if cmd.IsSet("run") {
|
} else if ctx.IsSet("run") {
|
||||||
addDefault := cmd.Bool("default")
|
addDefault := ctx.Bool("default")
|
||||||
runNamesSet := container.SetOf(cmd.StringSlice("run")...)
|
runNamesSet := container.SetOf(ctx.StringSlice("run")...)
|
||||||
for _, check := range doctor.Checks {
|
for _, check := range doctor.Checks {
|
||||||
if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
|
if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
|
||||||
checks = append(checks, check)
|
checks = append(checks, check)
|
||||||
@@ -211,5 +217,5 @@ func runDoctorCheck(ctx context.Context, cmd *cli.Command) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return doctor.RunChecks(ctx, colorize, cmd.Bool("fix"), checks)
|
return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// cmdDoctorConvert represents the available convert sub-command.
|
// cmdDoctorConvert represents the available convert sub-command.
|
||||||
@@ -22,8 +21,11 @@ var cmdDoctorConvert = &cli.Command{
|
|||||||
Action: runDoctorConvert,
|
Action: runDoctorConvert,
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDoctorConvert(ctx context.Context, cmd *cli.Command) error {
|
func runDoctorConvert(ctx *cli.Context) error {
|
||||||
if err := initDB(ctx); err != nil {
|
stdCtx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(stdCtx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"code.gitea.io/gitea/services/doctor"
|
"code.gitea.io/gitea/services/doctor"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDoctorRun(t *testing.T) {
|
func TestDoctorRun(t *testing.T) {
|
||||||
@@ -22,13 +22,12 @@ func TestDoctorRun(t *testing.T) {
|
|||||||
|
|
||||||
SkipDatabaseInitialization: true,
|
SkipDatabaseInitialization: true,
|
||||||
})
|
})
|
||||||
app := &cli.Command{
|
app := cli.NewApp()
|
||||||
Commands: []*cli.Command{cmdDoctorCheck},
|
app.Commands = []*cli.Command{cmdDoctorCheck}
|
||||||
}
|
err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
|
||||||
err := app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check"})
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "no-such"})
|
err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
|
||||||
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
|
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
|
||||||
err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check,no-such"})
|
err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"})
|
||||||
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
|
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
|
||||||
}
|
}
|
||||||
|
|||||||
76
cmd/dump.go
76
cmd/dump.go
@@ -5,7 +5,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -20,7 +19,8 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"gitea.com/go-chi/session"
|
"gitea.com/go-chi/session"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/mholt/archiver/v3"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDump represents the available dump sub-command.
|
// CmdDump represents the available dump sub-command.
|
||||||
@@ -101,17 +101,17 @@ func fatal(format string, args ...any) {
|
|||||||
log.Fatal(format, args...)
|
log.Fatal(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDump(ctx context.Context, cmd *cli.Command) error {
|
func runDump(ctx *cli.Context) error {
|
||||||
setting.MustInstalled()
|
setting.MustInstalled()
|
||||||
|
|
||||||
quite := cmd.Bool("quiet")
|
quite := ctx.Bool("quiet")
|
||||||
verbose := cmd.Bool("verbose")
|
verbose := ctx.Bool("verbose")
|
||||||
if verbose && quite {
|
if verbose && quite {
|
||||||
fatal("Option --quiet and --verbose cannot both be set")
|
fatal("Option --quiet and --verbose cannot both be set")
|
||||||
}
|
}
|
||||||
|
|
||||||
// outFileName is either "-" or a file name (will be made absolute)
|
// outFileName is either "-" or a file name (will be made absolute)
|
||||||
outFileName, outType := dump.PrepareFileNameAndType(cmd.String("file"), cmd.String("type"))
|
outFileName, outType := dump.PrepareFileNameAndType(ctx.String("file"), ctx.String("type"))
|
||||||
if outType == "" {
|
if outType == "" {
|
||||||
fatal("Invalid output type")
|
fatal("Invalid output type")
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,10 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
setting.DisableLoggerInit()
|
setting.DisableLoggerInit()
|
||||||
setting.LoadSettings() // cannot access session settings otherwise
|
setting.LoadSettings() // cannot access session settings otherwise
|
||||||
|
|
||||||
err := db.InitEngine(ctx)
|
stdCtx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err := db.InitEngine(stdCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -145,20 +148,24 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
dumper, err := dump.NewDumper(ctx, outType, outFile)
|
archiverGeneric, err := archiver.ByExtension("." + outType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal("Failed to create archive %q: %v", outFile, err)
|
fatal("Unable to get archiver for extension: %v", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
dumper.Verbose = verbose
|
|
||||||
dumper.GlobalExcludeAbsPath(outFileName)
|
|
||||||
defer func() {
|
|
||||||
if err := dumper.Close(); err != nil {
|
|
||||||
fatal("Failed to save archive %q: %v", outFileName, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if cmd.IsSet("skip-repository") && cmd.Bool("skip-repository") {
|
archiverWriter := archiverGeneric.(archiver.Writer)
|
||||||
|
if err := archiverWriter.Create(outFile); err != nil {
|
||||||
|
fatal("Creating archiver.Writer failed: %v", err)
|
||||||
|
}
|
||||||
|
defer archiverWriter.Close()
|
||||||
|
|
||||||
|
dumper := &dump.Dumper{
|
||||||
|
Writer: archiverWriter,
|
||||||
|
Verbose: verbose,
|
||||||
|
}
|
||||||
|
dumper.GlobalExcludeAbsPath(outFileName)
|
||||||
|
|
||||||
|
if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") {
|
||||||
log.Info("Skip dumping local repositories")
|
log.Info("Skip dumping local repositories")
|
||||||
} else {
|
} else {
|
||||||
log.Info("Dumping local repositories... %s", setting.RepoRootPath)
|
log.Info("Dumping local repositories... %s", setting.RepoRootPath)
|
||||||
@@ -166,7 +173,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
fatal("Failed to include repositories: %v", err)
|
fatal("Failed to include repositories: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.IsSet("skip-lfs-data") && cmd.Bool("skip-lfs-data") {
|
if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") {
|
||||||
log.Info("Skip dumping LFS data")
|
log.Info("Skip dumping LFS data")
|
||||||
} else if !setting.LFS.StartServer {
|
} else if !setting.LFS.StartServer {
|
||||||
log.Info("LFS isn't enabled. Skip dumping LFS data")
|
log.Info("LFS isn't enabled. Skip dumping LFS data")
|
||||||
@@ -175,18 +182,18 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dumper.AddFileByReader(object, info, path.Join("data", "lfs", objPath))
|
return dumper.AddReader(object, info, path.Join("data", "lfs", objPath))
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
fatal("Failed to dump LFS objects: %v", err)
|
fatal("Failed to dump LFS objects: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.Bool("skip-db") {
|
if ctx.Bool("skip-db") {
|
||||||
// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
|
// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
|
||||||
dumper.GlobalExcludeAbsPath(setting.Database.Path)
|
dumper.GlobalExcludeAbsPath(setting.Database.Path)
|
||||||
log.Info("Skipping database")
|
log.Info("Skipping database")
|
||||||
} else {
|
} else {
|
||||||
tmpDir := cmd.String("tempdir")
|
tmpDir := ctx.String("tempdir")
|
||||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||||
fatal("Path does not exist: %s", tmpDir)
|
fatal("Path does not exist: %s", tmpDir)
|
||||||
}
|
}
|
||||||
@@ -202,7 +209,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
targetDBType := cmd.String("database")
|
targetDBType := ctx.String("database")
|
||||||
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
|
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
|
||||||
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
|
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
|
||||||
} else {
|
} else {
|
||||||
@@ -213,17 +220,17 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
fatal("Failed to dump database: %v", err)
|
fatal("Failed to dump database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = dumper.AddFileByPath("gitea-db.sql", dbDump.Name()); err != nil {
|
if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
|
||||||
fatal("Failed to include gitea-db.sql: %v", err)
|
fatal("Failed to include gitea-db.sql: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
||||||
if err = dumper.AddFileByPath("app.ini", setting.CustomConf); err != nil {
|
if err = dumper.AddFile("app.ini", setting.CustomConf); err != nil {
|
||||||
fatal("Failed to include specified app.ini: %v", err)
|
fatal("Failed to include specified app.ini: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.IsSet("skip-custom-dir") && cmd.Bool("skip-custom-dir") {
|
if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
|
||||||
log.Info("Skipping custom directory")
|
log.Info("Skipping custom directory")
|
||||||
} else {
|
} else {
|
||||||
customDir, err := os.Stat(setting.CustomPath)
|
customDir, err := os.Stat(setting.CustomPath)
|
||||||
@@ -256,7 +263,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
excludes = append(excludes, opts.ProviderConfig)
|
excludes = append(excludes, opts.ProviderConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.IsSet("skip-index") && cmd.Bool("skip-index") {
|
if ctx.IsSet("skip-index") && ctx.Bool("skip-index") {
|
||||||
excludes = append(excludes, setting.Indexer.RepoPath)
|
excludes = append(excludes, setting.Indexer.RepoPath)
|
||||||
excludes = append(excludes, setting.Indexer.IssuePath)
|
excludes = append(excludes, setting.Indexer.IssuePath)
|
||||||
}
|
}
|
||||||
@@ -265,26 +272,25 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
excludes = append(excludes, setting.LFS.Storage.Path)
|
excludes = append(excludes, setting.LFS.Storage.Path)
|
||||||
excludes = append(excludes, setting.Attachment.Storage.Path)
|
excludes = append(excludes, setting.Attachment.Storage.Path)
|
||||||
excludes = append(excludes, setting.Packages.Storage.Path)
|
excludes = append(excludes, setting.Packages.Storage.Path)
|
||||||
excludes = append(excludes, setting.RepoArchive.Storage.Path)
|
|
||||||
excludes = append(excludes, setting.Log.RootPath)
|
excludes = append(excludes, setting.Log.RootPath)
|
||||||
if err := dumper.AddRecursiveExclude("data", setting.AppDataPath, excludes); err != nil {
|
if err := dumper.AddRecursiveExclude("data", setting.AppDataPath, excludes); err != nil {
|
||||||
fatal("Failed to include data directory: %v", err)
|
fatal("Failed to include data directory: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.IsSet("skip-attachment-data") && cmd.Bool("skip-attachment-data") {
|
if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") {
|
||||||
log.Info("Skip dumping attachment data")
|
log.Info("Skip dumping attachment data")
|
||||||
} else if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error {
|
} else if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error {
|
||||||
info, err := object.Stat()
|
info, err := object.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dumper.AddFileByReader(object, info, path.Join("data", "attachments", objPath))
|
return dumper.AddReader(object, info, path.Join("data", "attachments", objPath))
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
fatal("Failed to dump attachments: %v", err)
|
fatal("Failed to dump attachments: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.IsSet("skip-package-data") && cmd.Bool("skip-package-data") {
|
if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") {
|
||||||
log.Info("Skip dumping package data")
|
log.Info("Skip dumping package data")
|
||||||
} else if !setting.Packages.Enabled {
|
} else if !setting.Packages.Enabled {
|
||||||
log.Info("Packages isn't enabled. Skip dumping package data")
|
log.Info("Packages isn't enabled. Skip dumping package data")
|
||||||
@@ -293,7 +299,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dumper.AddFileByReader(object, info, path.Join("data", "packages", objPath))
|
return dumper.AddReader(object, info, path.Join("data", "packages", objPath))
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
fatal("Failed to dump packages: %v", err)
|
fatal("Failed to dump packages: %v", err)
|
||||||
}
|
}
|
||||||
@@ -301,7 +307,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
|
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
|
||||||
// ensuring that it's clear the dump is skipped whether the directory's initialized
|
// ensuring that it's clear the dump is skipped whether the directory's initialized
|
||||||
// yet or not.
|
// yet or not.
|
||||||
if cmd.IsSet("skip-log") && cmd.Bool("skip-log") {
|
if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
|
||||||
log.Info("Skip dumping log files")
|
log.Info("Skip dumping log files")
|
||||||
} else {
|
} else {
|
||||||
isExist, err := util.IsExist(setting.Log.RootPath)
|
isExist, err := util.IsExist(setting.Log.RootPath)
|
||||||
@@ -318,6 +324,10 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
|||||||
if outFileName == "-" {
|
if outFileName == "-" {
|
||||||
log.Info("Finish dumping to stdout")
|
log.Info("Finish dumping to stdout")
|
||||||
} else {
|
} else {
|
||||||
|
if err = archiverWriter.Close(); err != nil {
|
||||||
|
_ = os.Remove(outFileName)
|
||||||
|
fatal("Failed to save %q: %v", outFileName, err)
|
||||||
|
}
|
||||||
if err = os.Chmod(outFileName, 0o600); err != nil {
|
if err = os.Chmod(outFileName, 0o600); err != nil {
|
||||||
log.Info("Can't change file access permissions mask to 0600: %v", err)
|
log.Info("Can't change file access permissions mask to 0600: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
"code.gitea.io/gitea/services/migrations"
|
"code.gitea.io/gitea/services/migrations"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDumpRepository represents the available dump repository sub-command.
|
// CmdDumpRepository represents the available dump repository sub-command.
|
||||||
@@ -79,18 +79,21 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
|
func runDumpRepository(ctx *cli.Context) error {
|
||||||
setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr)
|
setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr)
|
||||||
|
|
||||||
setting.DisableLoggerInit()
|
setting.DisableLoggerInit()
|
||||||
setting.LoadSettings() // cannot access skip_tls_verify settings otherwise
|
setting.LoadSettings() // cannot access skip_tls_verify settings otherwise
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
stdCtx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(stdCtx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// migrations.GiteaLocalUploader depends on git module
|
// migrations.GiteaLocalUploader depends on git module
|
||||||
if err := git.InitSimple(); err != nil {
|
if err := git.InitSimple(context.Background()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +105,8 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
serviceType structs.GitServiceType
|
serviceType structs.GitServiceType
|
||||||
cloneAddr = cmd.String("clone_addr")
|
cloneAddr = ctx.String("clone_addr")
|
||||||
serviceStr = cmd.String("git_service")
|
serviceStr = ctx.String("git_service")
|
||||||
)
|
)
|
||||||
|
|
||||||
if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
|
if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
|
||||||
@@ -121,13 +124,13 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
|
|||||||
opts := base.MigrateOptions{
|
opts := base.MigrateOptions{
|
||||||
GitServiceType: serviceType,
|
GitServiceType: serviceType,
|
||||||
CloneAddr: cloneAddr,
|
CloneAddr: cloneAddr,
|
||||||
AuthUsername: cmd.String("auth_username"),
|
AuthUsername: ctx.String("auth_username"),
|
||||||
AuthPassword: cmd.String("auth_password"),
|
AuthPassword: ctx.String("auth_password"),
|
||||||
AuthToken: cmd.String("auth_token"),
|
AuthToken: ctx.String("auth_token"),
|
||||||
RepoName: cmd.String("repo_name"),
|
RepoName: ctx.String("repo_name"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cmd.String("units")) == 0 {
|
if len(ctx.String("units")) == 0 {
|
||||||
opts.Wiki = true
|
opts.Wiki = true
|
||||||
opts.Issues = true
|
opts.Issues = true
|
||||||
opts.Milestones = true
|
opts.Milestones = true
|
||||||
@@ -137,8 +140,8 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
|
|||||||
opts.PullRequests = true
|
opts.PullRequests = true
|
||||||
opts.ReleaseAssets = true
|
opts.ReleaseAssets = true
|
||||||
} else {
|
} else {
|
||||||
units := strings.SplitSeq(cmd.String("units"), ",")
|
units := strings.Split(ctx.String("units"), ",")
|
||||||
for unit := range units {
|
for _, unit := range units {
|
||||||
switch strings.ToLower(strings.TrimSpace(unit)) {
|
switch strings.ToLower(strings.TrimSpace(unit)) {
|
||||||
case "":
|
case "":
|
||||||
continue
|
continue
|
||||||
@@ -166,7 +169,7 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
|
|||||||
|
|
||||||
// the repo_dir will be removed if error occurs in DumpRepository
|
// the repo_dir will be removed if error occurs in DumpRepository
|
||||||
// make sure the directory doesn't exist or is empty, prevent from deleting user files
|
// make sure the directory doesn't exist or is empty, prevent from deleting user files
|
||||||
repoDir := cmd.String("repo_dir")
|
repoDir := ctx.String("repo_dir")
|
||||||
if exists, err := util.IsExist(repoDir); err != nil {
|
if exists, err := util.IsExist(repoDir); err != nil {
|
||||||
return fmt.Errorf("unable to stat repo_dir %q: %w", repoDir, err)
|
return fmt.Errorf("unable to stat repo_dir %q: %w", repoDir, err)
|
||||||
} else if exists {
|
} else if exists {
|
||||||
@@ -179,9 +182,9 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := migrations.DumpRepository(
|
if err := migrations.DumpRepository(
|
||||||
ctx,
|
context.Background(),
|
||||||
repoDir,
|
repoDir,
|
||||||
cmd.String("owner_name"),
|
ctx.String("owner_name"),
|
||||||
opts,
|
opts,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
log.Fatal("Failed to dump repository: %v", err)
|
log.Fatal("Failed to dump repository: %v", err)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -12,7 +11,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/assetfs"
|
"code.gitea.io/gitea/modules/assetfs"
|
||||||
"code.gitea.io/gitea/modules/glob"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/options"
|
"code.gitea.io/gitea/modules/options"
|
||||||
"code.gitea.io/gitea/modules/public"
|
"code.gitea.io/gitea/modules/public"
|
||||||
@@ -20,7 +18,8 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/templates"
|
"code.gitea.io/gitea/modules/templates"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/gobwas/glob"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdEmbedded represents the available extract sub-command.
|
// CmdEmbedded represents the available extract sub-command.
|
||||||
@@ -29,7 +28,7 @@ var (
|
|||||||
Name: "embedded",
|
Name: "embedded",
|
||||||
Usage: "Extract embedded resources",
|
Usage: "Extract embedded resources",
|
||||||
Description: "A command for extracting embedded resources, like templates and images",
|
Description: "A command for extracting embedded resources, like templates and images",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
subcmdList,
|
subcmdList,
|
||||||
subcmdView,
|
subcmdView,
|
||||||
subcmdExtract,
|
subcmdExtract,
|
||||||
@@ -101,7 +100,7 @@ type assetFile struct {
|
|||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
func initEmbeddedExtractor(c *cli.Command) error {
|
func initEmbeddedExtractor(c *cli.Context) error {
|
||||||
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
|
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
|
||||||
|
|
||||||
patterns, err := compileCollectPatterns(c.Args().Slice())
|
patterns, err := compileCollectPatterns(c.Args().Slice())
|
||||||
@@ -116,31 +115,31 @@ func initEmbeddedExtractor(c *cli.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runList(_ context.Context, c *cli.Command) error {
|
func runList(c *cli.Context) error {
|
||||||
if err := runListDo(c); err != nil {
|
if err := runListDo(c); err != nil {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runView(_ context.Context, c *cli.Command) error {
|
func runView(c *cli.Context) error {
|
||||||
if err := runViewDo(c); err != nil {
|
if err := runViewDo(c); err != nil {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runExtract(_ context.Context, c *cli.Command) error {
|
func runExtract(c *cli.Context) error {
|
||||||
if err := runExtractDo(c); err != nil {
|
if err := runExtractDo(c); err != nil {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runListDo(c *cli.Command) error {
|
func runListDo(c *cli.Context) error {
|
||||||
if err := initEmbeddedExtractor(c); err != nil {
|
if err := initEmbeddedExtractor(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -152,7 +151,7 @@ func runListDo(c *cli.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runViewDo(c *cli.Command) error {
|
func runViewDo(c *cli.Context) error {
|
||||||
if err := initEmbeddedExtractor(c); err != nil {
|
if err := initEmbeddedExtractor(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -175,7 +174,7 @@ func runViewDo(c *cli.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runExtractDo(c *cli.Command) error {
|
func runExtractDo(c *cli.Context) error {
|
||||||
if err := initEmbeddedExtractor(c); err != nil {
|
if err := initEmbeddedExtractor(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -217,7 +216,7 @@ func runExtractDo(c *cli.Command) error {
|
|||||||
for _, a := range matchedAssetFiles {
|
for _, a := range matchedAssetFiles {
|
||||||
if err := extractAsset(destdir, a, overwrite, rename); err != nil {
|
if err := extractAsset(destdir, a, overwrite, rename); err != nil {
|
||||||
// Non-fatal error
|
// Non-fatal error
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "%s: %v\n", a.path, err)
|
fmt.Fprintf(os.Stderr, "%s: %v", a.path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +271,7 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string, layer *assetfs.Layer) {
|
func collectAssetFilesByPattern(c *cli.Context, globs []glob.Glob, path string, layer *assetfs.Layer) {
|
||||||
fs := assetfs.Layered(layer)
|
fs := assetfs.Layered(layer)
|
||||||
files, err := fs.ListAllFiles(".", true)
|
files, err := fs.ListAllFiles(".", true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -295,14 +294,16 @@ func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileCollectPatterns(args []string) (_ []glob.Glob, err error) {
|
func compileCollectPatterns(args []string) ([]glob.Glob, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
args = []string{"**"}
|
args = []string{"**"}
|
||||||
}
|
}
|
||||||
pat := make([]glob.Glob, len(args))
|
pat := make([]glob.Glob, len(args))
|
||||||
for i := range args {
|
for i := range args {
|
||||||
if pat[i], err = glob.Compile(args[i], '/'); err != nil {
|
if g, err := glob.Compile(args[i], '/'); err != nil {
|
||||||
return nil, fmt.Errorf("invalid glob patterh %q: %w", args[i], err)
|
return nil, fmt.Errorf("'%s': Invalid glob pattern: %w", args[i], err)
|
||||||
|
} else { //nolint:revive
|
||||||
|
pat[i] = g
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pat, nil
|
return pat, nil
|
||||||
|
|||||||
@@ -5,14 +5,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/generate"
|
"code.gitea.io/gitea/modules/generate"
|
||||||
|
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -20,7 +19,7 @@ var (
|
|||||||
CmdGenerate = &cli.Command{
|
CmdGenerate = &cli.Command{
|
||||||
Name: "generate",
|
Name: "generate",
|
||||||
Usage: "Generate Gitea's secrets/keys/tokens",
|
Usage: "Generate Gitea's secrets/keys/tokens",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
subcmdSecret,
|
subcmdSecret,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -28,7 +27,7 @@ var (
|
|||||||
subcmdSecret = &cli.Command{
|
subcmdSecret = &cli.Command{
|
||||||
Name: "secret",
|
Name: "secret",
|
||||||
Usage: "Generate a secret token",
|
Usage: "Generate a secret token",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
microcmdGenerateInternalToken,
|
microcmdGenerateInternalToken,
|
||||||
microcmdGenerateLfsJwtSecret,
|
microcmdGenerateLfsJwtSecret,
|
||||||
microcmdGenerateSecretKey,
|
microcmdGenerateSecretKey,
|
||||||
@@ -55,7 +54,7 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
|
func runGenerateInternalToken(c *cli.Context) error {
|
||||||
internalToken, err := generate.NewInternalToken()
|
internalToken, err := generate.NewInternalToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -70,7 +69,7 @@ func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
|
func runGenerateLfsJwtSecret(c *cli.Context) error {
|
||||||
_, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
|
_, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -85,13 +84,12 @@ func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runGenerateSecretKey(_ context.Context, c *cli.Command) error {
|
func runGenerateSecretKey(c *cli.Context) error {
|
||||||
secretKey, err := generate.NewSecretKey()
|
secretKey, err := generate.NewSecretKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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()) {
|
||||||
|
|||||||
34
cmd/hook.go
34
cmd/hook.go
@@ -15,13 +15,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -33,10 +32,9 @@ var (
|
|||||||
CmdHook = &cli.Command{
|
CmdHook = &cli.Command{
|
||||||
Name: "hook",
|
Name: "hook",
|
||||||
Usage: "(internal) Should only be called by Git",
|
Usage: "(internal) Should only be called by Git",
|
||||||
Hidden: true, // internal commands shouldn't be visible
|
|
||||||
Description: "Delegate commands to corresponding Git hooks",
|
Description: "Delegate commands to corresponding Git hooks",
|
||||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
subcmdHookPreReceive,
|
subcmdHookPreReceive,
|
||||||
subcmdHookUpdate,
|
subcmdHookUpdate,
|
||||||
subcmdHookPostReceive,
|
subcmdHookPostReceive,
|
||||||
@@ -163,10 +161,12 @@ func (n *nilWriter) WriteString(s string) (int, error) {
|
|||||||
return len(s), nil
|
return len(s), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHookPreReceive(ctx context.Context, c *cli.Command) error {
|
func runHookPreReceive(c *cli.Context) error {
|
||||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
|
|
||||||
@@ -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.Atoi(os.Getenv(repo_module.EnvActionPerm))
|
actionPerm, _ := strconv.ParseInt(os.Getenv(repo_module.EnvActionPerm), 10, 64)
|
||||||
|
|
||||||
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: actionPerm,
|
ActionPerm: int(actionPerm),
|
||||||
}
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
@@ -292,7 +292,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
|
|
||||||
// runHookUpdate avoid to do heavy operations on update hook because it will be
|
// runHookUpdate avoid to do heavy operations on update hook because it will be
|
||||||
// invoked for every ref update which does not like pre-receive and post-receive
|
// invoked for every ref update which does not like pre-receive and post-receive
|
||||||
func runHookUpdate(_ context.Context, c *cli.Command) error {
|
func runHookUpdate(c *cli.Context) error {
|
||||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -309,12 +309,15 @@ func runHookUpdate(_ context.Context, c *cli.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHookPostReceive(ctx context.Context, c *cli.Command) error {
|
func runHookPostReceive(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
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); err != nil {
|
if _, _, err := git.NewCommand("update-server-info").RunStdString(ctx, nil); 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now if we're an internal don't do anything else
|
// Now if we're an internal don't do anything else
|
||||||
@@ -482,7 +485,7 @@ func hookPrintResult(output, isCreate bool, branch, url string) {
|
|||||||
func pushOptions() map[string]string {
|
func pushOptions() map[string]string {
|
||||||
opts := make(map[string]string)
|
opts := make(map[string]string)
|
||||||
if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil {
|
if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil {
|
||||||
for idx := range pushCount {
|
for idx := 0; idx < pushCount; idx++ {
|
||||||
opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx))
|
opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx))
|
||||||
kv := strings.SplitN(opt, "=", 2)
|
kv := strings.SplitN(opt, "=", 2)
|
||||||
if len(kv) == 2 {
|
if len(kv) == 2 {
|
||||||
@@ -493,7 +496,10 @@ func pushOptions() map[string]string {
|
|||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHookProcReceive(ctx context.Context, c *cli.Command) error {
|
func runHookProcReceive(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
|
|
||||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||||
@@ -734,7 +740,7 @@ func readPktLine(ctx context.Context, in *bufio.Reader, requestType pktLineType)
|
|||||||
|
|
||||||
// read prefix
|
// read prefix
|
||||||
lengthBytes := make([]byte, 4)
|
lengthBytes := make([]byte, 4)
|
||||||
for i := range 4 {
|
for i := 0; i < 4; i++ {
|
||||||
lengthBytes[i], err = in.ReadByte()
|
lengthBytes[i], err = in.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fail(ctx, "Protocol: stdin error", "Pkt-Line: read stdin failed : %v", err)
|
return nil, fail(ctx, "Protocol: stdin error", "Pkt-Line: read stdin failed : %v", err)
|
||||||
|
|||||||
11
cmd/keys.go
11
cmd/keys.go
@@ -4,7 +4,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -12,14 +11,13 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdKeys represents the available keys sub-command
|
// CmdKeys represents the available keys sub-command
|
||||||
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 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,
|
||||||
@@ -51,7 +49,7 @@ var CmdKeys = &cli.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func runKeys(ctx context.Context, c *cli.Command) error {
|
func runKeys(c *cli.Context) error {
|
||||||
if !c.IsSet("username") {
|
if !c.IsSet("username") {
|
||||||
return errors.New("No username provided")
|
return errors.New("No username provided")
|
||||||
}
|
}
|
||||||
@@ -70,6 +68,9 @@ func runKeys(ctx context.Context, c *cli.Command) error {
|
|||||||
return errors.New("No key type and content provided")
|
return errors.New("No key type and content provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
|
|
||||||
authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content)
|
authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content)
|
||||||
@@ -77,6 +78,6 @@ func runKeys(ctx context.Context, c *cli.Command) error {
|
|||||||
if extra.Error != nil {
|
if extra.Error != nil {
|
||||||
return extra.Error
|
return extra.Error
|
||||||
}
|
}
|
||||||
_, _ = fmt.Fprintln(c.Root().Writer, strings.TrimSpace(authorizedString.Text))
|
_, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString.Text))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,24 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runSendMail(ctx context.Context, c *cli.Command) error {
|
func runSendMail(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setting.MustInstalled()
|
setting.MustInstalled()
|
||||||
|
|
||||||
|
if err := argsSet(c, "title"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
subject := c.String("title")
|
subject := c.String("title")
|
||||||
confirmSkiped := c.Bool("force")
|
confirmSkiped := c.Bool("force")
|
||||||
body := c.String("content")
|
body := c.String("content")
|
||||||
|
|||||||
170
cmd/main.go
170
cmd/main.go
@@ -4,40 +4,36 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cliHelpPrinterOld = cli.HelpPrinter
|
// cmdHelp is our own help subcommand with more information
|
||||||
|
// Keep in mind that the "./gitea help"(subcommand) is different from "./gitea --help"(flag), the flag doesn't parse the config or output "DEFAULT CONFIGURATION:" information
|
||||||
func init() {
|
func cmdHelp() *cli.Command {
|
||||||
cli.HelpPrinter = cliHelpPrinterNew
|
c := &cli.Command{
|
||||||
|
Name: "help",
|
||||||
|
Aliases: []string{"h"},
|
||||||
|
Usage: "Shows a list of commands or help for one command",
|
||||||
|
ArgsUsage: "[command]",
|
||||||
|
Action: func(c *cli.Context) (err error) {
|
||||||
|
lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil}
|
||||||
|
targetCmdIdx := 0
|
||||||
|
if c.Command.Name == "help" {
|
||||||
|
targetCmdIdx = 1
|
||||||
}
|
}
|
||||||
|
if lineage[targetCmdIdx+1].Command != nil {
|
||||||
// cliHelpPrinterNew helps to print "DEFAULT CONFIGURATION" for the following cases ( "-c" can apper in any position):
|
err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name)
|
||||||
// * ./gitea -c /dev/null -h
|
} else {
|
||||||
// * ./gitea -c help /dev/null help
|
err = cli.ShowAppHelp(c)
|
||||||
// * ./gitea help -c /dev/null
|
|
||||||
// * ./gitea help -c /dev/null web
|
|
||||||
// * ./gitea help web -c /dev/null
|
|
||||||
// * ./gitea web help -c /dev/null
|
|
||||||
// * ./gitea web -h -c /dev/null
|
|
||||||
func cliHelpPrinterNew(out io.Writer, templ string, data any) {
|
|
||||||
cmd, _ := data.(*cli.Command)
|
|
||||||
if cmd != nil {
|
|
||||||
prepareWorkPathAndCustomConf(cmd)
|
|
||||||
}
|
}
|
||||||
cliHelpPrinterOld(out, templ, data)
|
_, _ = fmt.Fprintf(c.App.Writer, `
|
||||||
if setting.CustomConf != "" {
|
|
||||||
_, _ = fmt.Fprintf(out, `
|
|
||||||
DEFAULT CONFIGURATION:
|
DEFAULT CONFIGURATION:
|
||||||
AppPath: %s
|
AppPath: %s
|
||||||
WorkPath: %s
|
WorkPath: %s
|
||||||
@@ -45,38 +41,75 @@ DEFAULT CONFIGURATION:
|
|||||||
ConfigFile: %s
|
ConfigFile: %s
|
||||||
|
|
||||||
`, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
|
`, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func appGlobalFlags() []cli.Flag {
|
||||||
|
return []cli.Flag{
|
||||||
|
// make the builtin flags at the top
|
||||||
|
cli.HelpFlag,
|
||||||
|
|
||||||
|
// shared configuration flags, they are for global and for each sub-command at the same time
|
||||||
|
// eg: such command is valid: "./gitea --config /tmp/app.ini web --config /tmp/app.ini", while it's discouraged indeed
|
||||||
|
// keep in mind that the short flags like "-C", "-c" and "-w" are globally polluted, they can't be used for sub-commands anymore.
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "custom-path",
|
||||||
|
Aliases: []string{"C"},
|
||||||
|
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "config",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Value: setting.CustomConf,
|
||||||
|
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "work-path",
|
||||||
|
Aliases: []string{"w"},
|
||||||
|
Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) {
|
func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) {
|
||||||
originBefore := originCmd.Before
|
command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...)
|
||||||
originCmd.Before = func(ctxOrig context.Context, cmd *cli.Command) (ctx context.Context, err error) {
|
command.Action = prepareWorkPathAndCustomConf(command.Action)
|
||||||
ctx = ctxOrig
|
command.HideHelp = true
|
||||||
if originBefore != nil {
|
if command.Name != "help" {
|
||||||
ctx, err = originBefore(ctx, cmd)
|
command.Subcommands = append(command.Subcommands, cmdHelp())
|
||||||
if err != nil {
|
|
||||||
return ctx, err
|
|
||||||
}
|
}
|
||||||
}
|
for i := range command.Subcommands {
|
||||||
prepareWorkPathAndCustomConf(cmd)
|
prepareSubcommandWithConfig(command.Subcommands[i], globalFlags)
|
||||||
return ctx, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareWorkPathAndCustomConf tries to prepare the work path, custom path and custom config from various inputs:
|
// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
|
||||||
// command line flags, environment variables, config file
|
// It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times
|
||||||
func prepareWorkPathAndCustomConf(cmd *cli.Command) {
|
func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) error {
|
||||||
|
return func(ctx *cli.Context) error {
|
||||||
var args setting.ArgWorkPathAndCustomConf
|
var args setting.ArgWorkPathAndCustomConf
|
||||||
if cmd.IsSet("work-path") {
|
// from children to parent, check the global flags
|
||||||
args.WorkPath = cmd.String("work-path")
|
for _, curCtx := range ctx.Lineage() {
|
||||||
|
if curCtx.IsSet("work-path") && args.WorkPath == "" {
|
||||||
|
args.WorkPath = curCtx.String("work-path")
|
||||||
}
|
}
|
||||||
if cmd.IsSet("custom-path") {
|
if curCtx.IsSet("custom-path") && args.CustomPath == "" {
|
||||||
args.CustomPath = cmd.String("custom-path")
|
args.CustomPath = curCtx.String("custom-path")
|
||||||
|
}
|
||||||
|
if curCtx.IsSet("config") && args.CustomConf == "" {
|
||||||
|
args.CustomConf = curCtx.String("config")
|
||||||
}
|
}
|
||||||
if cmd.IsSet("config") {
|
|
||||||
args.CustomConf = cmd.String("config")
|
|
||||||
}
|
}
|
||||||
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
|
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
|
||||||
|
if ctx.Bool("help") || action == nil {
|
||||||
|
// the default behavior of "urfave/cli": "nil action" means "show help"
|
||||||
|
return cmdHelp().Action(ctx)
|
||||||
|
}
|
||||||
|
return action(ctx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppVersion struct {
|
type AppVersion struct {
|
||||||
@@ -84,36 +117,18 @@ type AppVersion struct {
|
|||||||
Extra string
|
Extra string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMainApp(appVer AppVersion) *cli.Command {
|
func NewMainApp(appVer AppVersion) *cli.App {
|
||||||
app := &cli.Command{}
|
app := cli.NewApp()
|
||||||
app.Name = "gitea" // must be lower-cased because it appears in the "USAGE" section like "gitea doctor [command [command options]]"
|
app.Name = "Gitea"
|
||||||
|
app.HelpName = "gitea"
|
||||||
app.Usage = "A painless self-hosted Git service"
|
app.Usage = "A painless self-hosted Git service"
|
||||||
app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
|
app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
|
||||||
app.Version = appVer.Version + appVer.Extra
|
app.Version = appVer.Version + appVer.Extra
|
||||||
app.EnableShellCompletion = true
|
app.EnableBashCompletion = true
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
&cli.StringFlag{
|
// these sub-commands need to use config file
|
||||||
Name: "work-path",
|
|
||||||
Aliases: []string{"w"},
|
|
||||||
TakesFile: true,
|
|
||||||
Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "config",
|
|
||||||
Aliases: []string{"c"},
|
|
||||||
TakesFile: true,
|
|
||||||
Value: setting.CustomConf,
|
|
||||||
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "custom-path",
|
|
||||||
Aliases: []string{"C"},
|
|
||||||
TakesFile: true,
|
|
||||||
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// these sub-commands need to use a config file
|
|
||||||
subCmdWithConfig := []*cli.Command{
|
subCmdWithConfig := []*cli.Command{
|
||||||
|
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
|
||||||
CmdWeb,
|
CmdWeb,
|
||||||
CmdServ,
|
CmdServ,
|
||||||
CmdHook,
|
CmdHook,
|
||||||
@@ -132,19 +147,20 @@ 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we should eventually drop the default command,
|
|
||||||
// but not sure whether it would break Windows users who used to double-click the EXE to run.
|
|
||||||
app.DefaultCommand = CmdWeb.Name
|
app.DefaultCommand = CmdWeb.Name
|
||||||
|
|
||||||
|
globalFlags := appGlobalFlags()
|
||||||
|
app.Flags = append(app.Flags, cli.VersionFlag)
|
||||||
|
app.Flags = append(app.Flags, globalFlags...)
|
||||||
|
app.HideHelp = true // use our own help action to show helps (with more information like default config)
|
||||||
app.Before = PrepareConsoleLoggerLevel(log.INFO)
|
app.Before = PrepareConsoleLoggerLevel(log.INFO)
|
||||||
for i := range subCmdWithConfig {
|
for i := range subCmdWithConfig {
|
||||||
prepareSubcommandWithGlobalFlags(subCmdWithConfig[i])
|
prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags)
|
||||||
}
|
}
|
||||||
app.Commands = append(app.Commands, subCmdWithConfig...)
|
app.Commands = append(app.Commands, subCmdWithConfig...)
|
||||||
app.Commands = append(app.Commands, subCmdStandalone...)
|
app.Commands = append(app.Commands, subCmdStandalone...)
|
||||||
@@ -153,10 +169,8 @@ func NewMainApp(appVer AppVersion) *cli.Command {
|
|||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunMainApp(app *cli.Command, args ...string) error {
|
func RunMainApp(app *cli.App, args ...string) error {
|
||||||
ctx, cancel := installSignals()
|
err := app.Run(args)
|
||||||
defer cancel()
|
|
||||||
err := app.Run(ctx, args)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
107
cmd/main_test.go
107
cmd/main_test.go
@@ -4,7 +4,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -15,10 +14,9 @@ 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/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
@@ -29,11 +27,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(testCmd cli.Command) *cli.Command {
|
func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
|
||||||
app := NewMainApp(AppVersion{})
|
app := NewMainApp(AppVersion{})
|
||||||
testCmd.Name = util.IfZero(testCmd.Name, "test-cmd")
|
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
|
||||||
prepareSubcommandWithGlobalFlags(&testCmd)
|
prepareSubcommandWithConfig(testCmd, appGlobalFlags())
|
||||||
app.Commands = append(app.Commands, &testCmd)
|
app.Commands = append(app.Commands, testCmd)
|
||||||
app.DefaultCommand = testCmd.Name
|
app.DefaultCommand = testCmd.Name
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
@@ -44,7 +42,7 @@ type runResult struct {
|
|||||||
ExitCode int
|
ExitCode int
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTestApp(app *cli.Command, args ...string) (runResult, error) {
|
func runTestApp(app *cli.App, args ...string) (runResult, error) {
|
||||||
outBuf := new(strings.Builder)
|
outBuf := new(strings.Builder)
|
||||||
errBuf := new(strings.Builder)
|
errBuf := new(strings.Builder)
|
||||||
app.Writer = outBuf
|
app.Writer = outBuf
|
||||||
@@ -67,7 +65,7 @@ func TestCliCmd(t *testing.T) {
|
|||||||
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
|
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
|
||||||
|
|
||||||
cli.CommandHelpTemplate = "(command help template)"
|
cli.CommandHelpTemplate = "(command help template)"
|
||||||
cli.RootCommandHelpTemplate = "(app help template)"
|
cli.AppHelpTemplate = "(app help template)"
|
||||||
cli.SubcommandHelpTemplate = "(subcommand help template)"
|
cli.SubcommandHelpTemplate = "(subcommand help template)"
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@@ -75,56 +73,12 @@ func TestCliCmd(t *testing.T) {
|
|||||||
cmd string
|
cmd string
|
||||||
exp string
|
exp string
|
||||||
}{
|
}{
|
||||||
// help commands
|
// main command help
|
||||||
{
|
|
||||||
cmd: "./gitea -h",
|
|
||||||
exp: "DEFAULT CONFIGURATION:",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
cmd: "./gitea help",
|
cmd: "./gitea help",
|
||||||
exp: "DEFAULT CONFIGURATION:",
|
exp: "DEFAULT CONFIGURATION:",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
cmd: "./gitea -c /dev/null -h",
|
|
||||||
exp: "ConfigFile: /dev/null",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
cmd: "./gitea -c /dev/null help",
|
|
||||||
exp: "ConfigFile: /dev/null",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cmd: "./gitea help -c /dev/null",
|
|
||||||
exp: "ConfigFile: /dev/null",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
cmd: "./gitea -c /dev/null test-cmd -h",
|
|
||||||
exp: "ConfigFile: /dev/null",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cmd: "./gitea test-cmd -c /dev/null -h",
|
|
||||||
exp: "ConfigFile: /dev/null",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cmd: "./gitea test-cmd -h -c /dev/null",
|
|
||||||
exp: "ConfigFile: /dev/null",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
cmd: "./gitea -c /dev/null test-cmd help",
|
|
||||||
exp: "ConfigFile: /dev/null",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cmd: "./gitea test-cmd -c /dev/null help",
|
|
||||||
exp: "ConfigFile: /dev/null",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cmd: "./gitea test-cmd help -c /dev/null",
|
|
||||||
exp: "ConfigFile: /dev/null",
|
|
||||||
},
|
|
||||||
|
|
||||||
// parse paths
|
// parse paths
|
||||||
{
|
{
|
||||||
cmd: "./gitea test-cmd",
|
cmd: "./gitea test-cmd",
|
||||||
@@ -155,14 +109,12 @@ func TestCliCmd(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app := newTestApp(func(ctx *cli.Context) error {
|
||||||
|
_, _ = fmt.Fprint(ctx.App.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
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(cli.Command{
|
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
|
||||||
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
for k, v := range c.env {
|
for k, v := range c.env {
|
||||||
t.Setenv(k, v)
|
t.Setenv(k, v)
|
||||||
}
|
}
|
||||||
@@ -176,54 +128,31 @@ func TestCliCmd(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCliCmdError(t *testing.T) {
|
func TestCliCmdError(t *testing.T) {
|
||||||
app := newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") }})
|
app := newTestApp(func(ctx *cli.Context) 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(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) }})
|
app = newTestApp(func(ctx *cli.Context) 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(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }})
|
app = newTestApp(func(ctx *cli.Context) 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.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stdout)
|
||||||
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
|
assert.Empty(t, r.Stderr) // the cli package's strange behavior, the error message is not in stderr ....
|
||||||
|
|
||||||
app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }})
|
app = newTestApp(func(ctx *cli.Context) 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"])
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -19,7 +18,7 @@ var (
|
|||||||
Name: "manager",
|
Name: "manager",
|
||||||
Usage: "Manage the running gitea process",
|
Usage: "Manage the running gitea process",
|
||||||
Description: "This is a command for managing the running gitea process",
|
Description: "This is a command for managing the running gitea process",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
subcmdShutdown,
|
subcmdShutdown,
|
||||||
subcmdRestart,
|
subcmdRestart,
|
||||||
subcmdReloadTemplates,
|
subcmdReloadTemplates,
|
||||||
@@ -109,31 +108,46 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func runShutdown(ctx context.Context, c *cli.Command) error {
|
func runShutdown(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
extra := private.Shutdown(ctx)
|
extra := private.Shutdown(ctx)
|
||||||
return handleCliResponseExtra(extra)
|
return handleCliResponseExtra(extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRestart(ctx context.Context, c *cli.Command) error {
|
func runRestart(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
extra := private.Restart(ctx)
|
extra := private.Restart(ctx)
|
||||||
return handleCliResponseExtra(extra)
|
return handleCliResponseExtra(extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runReloadTemplates(ctx context.Context, c *cli.Command) error {
|
func runReloadTemplates(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
extra := private.ReloadTemplates(ctx)
|
extra := private.ReloadTemplates(ctx)
|
||||||
return handleCliResponseExtra(extra)
|
return handleCliResponseExtra(extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runFlushQueues(ctx context.Context, c *cli.Command) error {
|
func runFlushQueues(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
extra := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking"))
|
extra := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking"))
|
||||||
return handleCliResponseExtra(extra)
|
return handleCliResponseExtra(extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runProcesses(ctx context.Context, c *cli.Command) error {
|
func runProcesses(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
extra := private.Processes(ctx, os.Stdout, c.Bool("flat"), c.Bool("no-system"), c.Bool("stacktraces"), c.Bool("json"), c.String("cancel"))
|
extra := private.Processes(ctx, os.Stdout, c.Bool("flat"), c.Bool("no-system"), c.Bool("stacktraces"), c.Bool("json"), c.String("cancel"))
|
||||||
return handleCliResponseExtra(extra)
|
return handleCliResponseExtra(extra)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -12,7 +11,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -61,7 +60,7 @@ var (
|
|||||||
subcmdLogging = &cli.Command{
|
subcmdLogging = &cli.Command{
|
||||||
Name: "logging",
|
Name: "logging",
|
||||||
Usage: "Adjust logging commands",
|
Usage: "Adjust logging commands",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "pause",
|
Name: "pause",
|
||||||
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
|
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
|
||||||
@@ -105,7 +104,7 @@ var (
|
|||||||
}, {
|
}, {
|
||||||
Name: "add",
|
Name: "add",
|
||||||
Usage: "Add a logger",
|
Usage: "Add a logger",
|
||||||
Commands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "file",
|
Name: "file",
|
||||||
Usage: "Add a file logger",
|
Usage: "Add a file logger",
|
||||||
@@ -193,7 +192,10 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func runRemoveLogger(ctx context.Context, c *cli.Command) error {
|
func runRemoveLogger(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
logger := c.String("logger")
|
logger := c.String("logger")
|
||||||
if len(logger) == 0 {
|
if len(logger) == 0 {
|
||||||
@@ -205,7 +207,10 @@ func runRemoveLogger(ctx context.Context, c *cli.Command) error {
|
|||||||
return handleCliResponseExtra(extra)
|
return handleCliResponseExtra(extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runAddConnLogger(ctx context.Context, c *cli.Command) error {
|
func runAddConnLogger(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
vals := map[string]any{}
|
vals := map[string]any{}
|
||||||
mode := "conn"
|
mode := "conn"
|
||||||
@@ -229,10 +234,13 @@ func runAddConnLogger(ctx context.Context, c *cli.Command) error {
|
|||||||
if c.IsSet("reconnect-on-message") {
|
if c.IsSet("reconnect-on-message") {
|
||||||
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
|
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
|
||||||
}
|
}
|
||||||
return commonAddLogger(ctx, c, mode, vals)
|
return commonAddLogger(c, mode, vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runAddFileLogger(ctx context.Context, c *cli.Command) error {
|
func runAddFileLogger(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
vals := map[string]any{}
|
vals := map[string]any{}
|
||||||
mode := "file"
|
mode := "file"
|
||||||
@@ -259,10 +267,10 @@ func runAddFileLogger(ctx context.Context, c *cli.Command) error {
|
|||||||
if c.IsSet("compression-level") {
|
if c.IsSet("compression-level") {
|
||||||
vals["compressionLevel"] = c.Int("compression-level")
|
vals["compressionLevel"] = c.Int("compression-level")
|
||||||
}
|
}
|
||||||
return commonAddLogger(ctx, c, mode, vals)
|
return commonAddLogger(c, mode, vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[string]any) error {
|
func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
|
||||||
if len(c.String("level")) > 0 {
|
if len(c.String("level")) > 0 {
|
||||||
vals["level"] = log.LevelFromString(c.String("level")).String()
|
vals["level"] = log.LevelFromString(c.String("level")).String()
|
||||||
}
|
}
|
||||||
@@ -289,33 +297,46 @@ func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[
|
|||||||
if c.IsSet("writer") {
|
if c.IsSet("writer") {
|
||||||
writer = c.String("writer")
|
writer = c.String("writer")
|
||||||
}
|
}
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
extra := private.AddLogger(ctx, logger, writer, mode, vals)
|
extra := private.AddLogger(ctx, logger, writer, mode, vals)
|
||||||
return handleCliResponseExtra(extra)
|
return handleCliResponseExtra(extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runPauseLogging(ctx context.Context, c *cli.Command) error {
|
func runPauseLogging(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
userMsg := private.PauseLogging(ctx)
|
userMsg := private.PauseLogging(ctx)
|
||||||
_, _ = fmt.Fprintln(os.Stdout, userMsg)
|
_, _ = fmt.Fprintln(os.Stdout, userMsg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runResumeLogging(ctx context.Context, c *cli.Command) error {
|
func runResumeLogging(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
userMsg := private.ResumeLogging(ctx)
|
userMsg := private.ResumeLogging(ctx)
|
||||||
_, _ = fmt.Fprintln(os.Stdout, userMsg)
|
_, _ = fmt.Fprintln(os.Stdout, userMsg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runReleaseReopenLogging(ctx context.Context, c *cli.Command) error {
|
func runReleaseReopenLogging(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
userMsg := private.ReleaseReopenLogging(ctx)
|
userMsg := private.ReleaseReopenLogging(ctx)
|
||||||
_, _ = fmt.Fprintln(os.Stdout, userMsg)
|
_, _ = fmt.Fprintln(os.Stdout, userMsg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runSetLogSQL(ctx context.Context, c *cli.Command) error {
|
func runSetLogSQL(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
|
|
||||||
extra := private.SetLogSQL(ctx, !c.Bool("off"))
|
extra := private.SetLogSQL(ctx, !c.Bool("off"))
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/services/versioned_migration"
|
"code.gitea.io/gitea/services/versioned_migration"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdMigrate represents the available migrate sub-command.
|
// CmdMigrate represents the available migrate sub-command.
|
||||||
@@ -22,8 +22,11 @@ var CmdMigrate = &cli.Command{
|
|||||||
Action: runMigrate,
|
Action: runMigrate,
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMigrate(ctx context.Context, c *cli.Command) error {
|
func runMigrate(ctx *cli.Context) error {
|
||||||
if err := initDB(ctx); err != nil {
|
stdCtx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(stdCtx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
"code.gitea.io/gitea/services/versioned_migration"
|
"code.gitea.io/gitea/services/versioned_migration"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdMigrateStorage represents the available migrate storage sub-command.
|
// CmdMigrateStorage represents the available migrate storage sub-command.
|
||||||
@@ -213,8 +213,11 @@ func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStora
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
|
func runMigrateStorage(ctx *cli.Context) error {
|
||||||
if err := initDB(ctx); err != nil {
|
stdCtx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(stdCtx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,51 +238,51 @@ func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
|
|||||||
|
|
||||||
var dstStorage storage.ObjectStorage
|
var dstStorage storage.ObjectStorage
|
||||||
var err error
|
var err error
|
||||||
switch strings.ToLower(cmd.String("storage")) {
|
switch strings.ToLower(ctx.String("storage")) {
|
||||||
case "":
|
case "":
|
||||||
fallthrough
|
fallthrough
|
||||||
case string(setting.LocalStorageType):
|
case string(setting.LocalStorageType):
|
||||||
p := cmd.String("path")
|
p := ctx.String("path")
|
||||||
if p == "" {
|
if p == "" {
|
||||||
log.Fatal("Path must be given when storage is local")
|
log.Fatal("Path must be given when storage is local")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
dstStorage, err = storage.NewLocalStorage(
|
dstStorage, err = storage.NewLocalStorage(
|
||||||
ctx,
|
stdCtx,
|
||||||
&setting.Storage{
|
&setting.Storage{
|
||||||
Path: p,
|
Path: p,
|
||||||
})
|
})
|
||||||
case string(setting.MinioStorageType):
|
case string(setting.MinioStorageType):
|
||||||
dstStorage, err = storage.NewMinioStorage(
|
dstStorage, err = storage.NewMinioStorage(
|
||||||
ctx,
|
stdCtx,
|
||||||
&setting.Storage{
|
&setting.Storage{
|
||||||
MinioConfig: setting.MinioStorageConfig{
|
MinioConfig: setting.MinioStorageConfig{
|
||||||
Endpoint: cmd.String("minio-endpoint"),
|
Endpoint: ctx.String("minio-endpoint"),
|
||||||
AccessKeyID: cmd.String("minio-access-key-id"),
|
AccessKeyID: ctx.String("minio-access-key-id"),
|
||||||
SecretAccessKey: cmd.String("minio-secret-access-key"),
|
SecretAccessKey: ctx.String("minio-secret-access-key"),
|
||||||
Bucket: cmd.String("minio-bucket"),
|
Bucket: ctx.String("minio-bucket"),
|
||||||
Location: cmd.String("minio-location"),
|
Location: ctx.String("minio-location"),
|
||||||
BasePath: cmd.String("minio-base-path"),
|
BasePath: ctx.String("minio-base-path"),
|
||||||
UseSSL: cmd.Bool("minio-use-ssl"),
|
UseSSL: ctx.Bool("minio-use-ssl"),
|
||||||
InsecureSkipVerify: cmd.Bool("minio-insecure-skip-verify"),
|
InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"),
|
||||||
ChecksumAlgorithm: cmd.String("minio-checksum-algorithm"),
|
ChecksumAlgorithm: ctx.String("minio-checksum-algorithm"),
|
||||||
BucketLookUpType: cmd.String("minio-bucket-lookup-type"),
|
BucketLookUpType: ctx.String("minio-bucket-lookup-type"),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
case string(setting.AzureBlobStorageType):
|
case string(setting.AzureBlobStorageType):
|
||||||
dstStorage, err = storage.NewAzureBlobStorage(
|
dstStorage, err = storage.NewAzureBlobStorage(
|
||||||
ctx,
|
stdCtx,
|
||||||
&setting.Storage{
|
&setting.Storage{
|
||||||
AzureBlobConfig: setting.AzureBlobStorageConfig{
|
AzureBlobConfig: setting.AzureBlobStorageConfig{
|
||||||
Endpoint: cmd.String("azureblob-endpoint"),
|
Endpoint: ctx.String("azureblob-endpoint"),
|
||||||
AccountName: cmd.String("azureblob-account-name"),
|
AccountName: ctx.String("azureblob-account-name"),
|
||||||
AccountKey: cmd.String("azureblob-account-key"),
|
AccountKey: ctx.String("azureblob-account-key"),
|
||||||
Container: cmd.String("azureblob-container"),
|
Container: ctx.String("azureblob-container"),
|
||||||
BasePath: cmd.String("azureblob-base-path"),
|
BasePath: ctx.String("azureblob-base-path"),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported storage type: %s", cmd.String("storage"))
|
return fmt.Errorf("unsupported storage type: %s", ctx.String("storage"))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -296,14 +299,14 @@ func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
|
|||||||
"actions-artifacts": migrateActionsArtifacts,
|
"actions-artifacts": migrateActionsArtifacts,
|
||||||
}
|
}
|
||||||
|
|
||||||
tp := strings.ToLower(cmd.String("type"))
|
tp := strings.ToLower(ctx.String("type"))
|
||||||
if m, ok := migratedMethods[tp]; ok {
|
if m, ok := migratedMethods[tp]; ok {
|
||||||
if err := m(ctx, dstStorage); err != nil {
|
if err := m(stdCtx, dstStorage); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info("%s files have successfully been copied to the new storage.", tp)
|
log.Info("%s files have successfully been copied to the new storage.", tp)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("unsupported storage: %s", cmd.String("type"))
|
return fmt.Errorf("unsupported storage: %s", ctx.String("type"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/packages"
|
"code.gitea.io/gitea/models/packages"
|
||||||
"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"
|
||||||
@@ -29,7 +30,7 @@ func TestMigratePackages(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
defer buf.Close()
|
defer buf.Close()
|
||||||
|
|
||||||
v, f, err := packages_service.CreatePackageAndAddFile(t.Context(), &packages_service.PackageCreationInfo{
|
v, f, err := packages_service.CreatePackageAndAddFile(db.DefaultContext, &packages_service.PackageCreationInfo{
|
||||||
PackageInfo: packages_service.PackageInfo{
|
PackageInfo: packages_service.PackageInfo{
|
||||||
Owner: creator,
|
Owner: creator,
|
||||||
PackageType: packages.TypeGeneric,
|
PackageType: packages.TypeGeneric,
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdRestoreRepository represents the available restore a repository sub-command.
|
// CmdRestoreRepository represents the available restore a repository sub-command.
|
||||||
@@ -49,7 +48,10 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRestoreRepository(ctx context.Context, c *cli.Command) error {
|
func runRestoreRepository(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
setting.MustInstalled()
|
setting.MustInstalled()
|
||||||
var units []string
|
var units []string
|
||||||
if s := c.String("units"); s != "" {
|
if s := c.String("units"); s != "" {
|
||||||
|
|||||||
57
cmd/serv.go
57
cmd/serv.go
@@ -18,9 +18,8 @@ import (
|
|||||||
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"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
"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/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/lfstransfer"
|
"code.gitea.io/gitea/modules/lfstransfer"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
@@ -32,7 +31,7 @@ import (
|
|||||||
"code.gitea.io/gitea/services/lfs"
|
"code.gitea.io/gitea/services/lfs"
|
||||||
|
|
||||||
"github.com/kballard/go-shellquote"
|
"github.com/kballard/go-shellquote"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdServ represents the available serv sub-command.
|
// CmdServ represents the available serv sub-command.
|
||||||
@@ -40,7 +39,6 @@ var CmdServ = &cli.Command{
|
|||||||
Name: "serv",
|
Name: "serv",
|
||||||
Usage: "(internal) Should only be called by SSH shell",
|
Usage: "(internal) Should only be called by SSH shell",
|
||||||
Description: "Serv provides access auth for repositories",
|
Description: "Serv provides access auth for repositories",
|
||||||
Hidden: true, // Internal commands shouldn't be visible in help
|
|
||||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||||
Action: runServ,
|
Action: runServ,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
@@ -64,7 +62,7 @@ func setup(ctx context.Context, debug bool) {
|
|||||||
_ = fail(ctx, "Unable to access repository path", "Unable to access repository path %q, err: %v", setting.RepoRootPath, err)
|
_ = fail(ctx, "Unable to access repository path", "Unable to access repository path %q, err: %v", setting.RepoRootPath, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := git.InitSimple(); err != nil {
|
if err := git.InitSimple(context.Background()); err != nil {
|
||||||
_ = fail(ctx, "Failed to init git", "Failed to init git, err: %v", err)
|
_ = fail(ctx, "Failed to init git", "Failed to init git, err: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,7 +129,10 @@ func getAccessMode(verb, lfsVerb string) perm.AccessMode {
|
|||||||
return perm.AccessModeNone
|
return perm.AccessModeNone
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServ(ctx context.Context, c *cli.Command) error {
|
func runServ(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
// FIXME: This needs to internationalised
|
// FIXME: This needs to internationalised
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
|
|
||||||
@@ -191,7 +192,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
|||||||
if git.DefaultFeatures().SupportProcReceive {
|
if git.DefaultFeatures().SupportProcReceive {
|
||||||
// for AGit Flow
|
// for AGit Flow
|
||||||
if cmd == "ssh_info" {
|
if cmd == "ssh_info" {
|
||||||
fmt.Print(`{"type":"agit","version":1}`)
|
fmt.Print(`{"type":"gitea","version":1}`)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,7 +208,12 @@ 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_model.IsValidSSHAccessRepoName(reponame) {
|
// LowerCase and trim the repoPath as that's how they are stored.
|
||||||
|
// This should be done after splitting the repoPath into username and reponame
|
||||||
|
// so that username and reponame are not affected.
|
||||||
|
repoPath = strings.ToLower(strings.TrimSpace(repoPath))
|
||||||
|
|
||||||
|
if !repo.IsValidSSHAccessRepoName(reponame) {
|
||||||
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
|
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,13 +259,6 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// because the original repoPath maybe redirected, we need to use the returned actual repository information
|
|
||||||
if results.IsWiki {
|
|
||||||
repoPath = repo_model.RelativeWikiPath(results.OwnerName, results.RepoName)
|
|
||||||
} else {
|
|
||||||
repoPath = repo_model.RelativePath(results.OwnerName, results.RepoName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LFS SSH protocol
|
// LFS SSH protocol
|
||||||
if verb == git.CmdVerbLfsTransfer {
|
if verb == git.CmdVerbLfsTransfer {
|
||||||
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
|
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
|
||||||
@@ -292,8 +291,8 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var command *exec.Cmd
|
var gitcmd *exec.Cmd
|
||||||
gitBinPath := filepath.Dir(gitcmd.GitExecutable) // e.g. /usr/bin
|
gitBinPath := filepath.Dir(git.GitExecutable) // e.g. /usr/bin
|
||||||
gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack
|
gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack
|
||||||
if _, err := os.Stat(gitBinVerb); err != nil {
|
if _, err := os.Stat(gitBinVerb); err != nil {
|
||||||
// if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git
|
// if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git
|
||||||
@@ -301,21 +300,21 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
|||||||
verbFields := strings.SplitN(verb, "-", 2)
|
verbFields := strings.SplitN(verb, "-", 2)
|
||||||
if len(verbFields) == 2 {
|
if len(verbFields) == 2 {
|
||||||
// use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ...
|
// use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ...
|
||||||
command = exec.CommandContext(ctx, gitcmd.GitExecutable, verbFields[1], repoPath)
|
gitcmd = exec.CommandContext(ctx, git.GitExecutable, verbFields[1], repoPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if command == nil {
|
if gitcmd == nil {
|
||||||
// by default, use the verb (it has been checked above by allowedCommands)
|
// by default, use the verb (it has been checked above by allowedCommands)
|
||||||
command = exec.CommandContext(ctx, gitBinVerb, repoPath)
|
gitcmd = exec.CommandContext(ctx, gitBinVerb, repoPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
process.SetSysProcAttribute(command)
|
process.SetSysProcAttribute(gitcmd)
|
||||||
command.Dir = setting.RepoRootPath
|
gitcmd.Dir = setting.RepoRootPath
|
||||||
command.Stdout = os.Stdout
|
gitcmd.Stdout = os.Stdout
|
||||||
command.Stdin = os.Stdin
|
gitcmd.Stdin = os.Stdin
|
||||||
command.Stderr = os.Stderr
|
gitcmd.Stderr = os.Stderr
|
||||||
command.Env = append(command.Env, os.Environ()...)
|
gitcmd.Env = append(gitcmd.Env, os.Environ()...)
|
||||||
command.Env = append(command.Env,
|
gitcmd.Env = append(gitcmd.Env,
|
||||||
repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki),
|
repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki),
|
||||||
repo_module.EnvRepoName+"="+results.RepoName,
|
repo_module.EnvRepoName+"="+results.RepoName,
|
||||||
repo_module.EnvRepoUsername+"="+results.OwnerName,
|
repo_module.EnvRepoUsername+"="+results.OwnerName,
|
||||||
@@ -330,9 +329,9 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
|||||||
)
|
)
|
||||||
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
|
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
|
||||||
// it could be re-considered whether to use the same git.CommonGitCmdEnvs() as "git" command later.
|
// it could be re-considered whether to use the same git.CommonGitCmdEnvs() as "git" command later.
|
||||||
command.Env = append(command.Env, gitcmd.CommonCmdServEnvs()...)
|
gitcmd.Env = append(gitcmd.Env, git.CommonCmdServEnvs()...)
|
||||||
|
|
||||||
if err = command.Run(); err != nil {
|
if err = gitcmd.Run(); err != nil {
|
||||||
return fail(ctx, "Failed to execute git command", "Failed to execute git command: %v", err)
|
return fail(ctx, "Failed to execute git command", "Failed to execute git command: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
41
cmd/web.go
41
cmd/web.go
@@ -28,7 +28,7 @@ import (
|
|||||||
"code.gitea.io/gitea/routers/install"
|
"code.gitea.io/gitea/routers/install"
|
||||||
|
|
||||||
"github.com/felixge/fgprof"
|
"github.com/felixge/fgprof"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PIDFile could be set from build tag
|
// PIDFile could be set from build tag
|
||||||
@@ -130,19 +130,19 @@ func showWebStartupMessage(msg string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveInstall(cmd *cli.Command) error {
|
func serveInstall(ctx *cli.Context) error {
|
||||||
showWebStartupMessage("Prepare to run install page")
|
showWebStartupMessage("Prepare to run install page")
|
||||||
|
|
||||||
routers.InitWebInstallPage(graceful.GetManager().HammerContext())
|
routers.InitWebInstallPage(graceful.GetManager().HammerContext())
|
||||||
|
|
||||||
// Flag for port number in case first time run conflict
|
// Flag for port number in case first time run conflict
|
||||||
if cmd.IsSet("port") {
|
if ctx.IsSet("port") {
|
||||||
if err := setPort(cmd.String("port")); err != nil {
|
if err := setPort(ctx.String("port")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cmd.IsSet("install-port") {
|
if ctx.IsSet("install-port") {
|
||||||
if err := setPort(cmd.String("install-port")); err != nil {
|
if err := setPort(ctx.String("install-port")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,13 +156,14 @@ 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:
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveInstalled(c *cli.Command) error {
|
func serveInstalled(ctx *cli.Context) error {
|
||||||
setting.InitCfgProvider(setting.CustomConf)
|
setting.InitCfgProvider(setting.CustomConf)
|
||||||
setting.LoadCommonSettings()
|
setting.LoadCommonSettings()
|
||||||
setting.MustInstalled()
|
setting.MustInstalled()
|
||||||
@@ -217,8 +218,8 @@ func serveInstalled(c *cli.Command) error {
|
|||||||
setting.AppDataTempDir("").RemoveOutdated(3 * 24 * time.Hour)
|
setting.AppDataTempDir("").RemoveOutdated(3 * 24 * time.Hour)
|
||||||
|
|
||||||
// Override the provided port number within the configuration
|
// Override the provided port number within the configuration
|
||||||
if c.IsSet("port") {
|
if ctx.IsSet("port") {
|
||||||
if err := setPort(c.String("port")); err != nil {
|
if err := setPort(ctx.String("port")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,31 +231,27 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
func servePprof() {
|
func servePprof() {
|
||||||
// FIXME: it shouldn't use the global DefaultServeMux, and it should use a proper context
|
|
||||||
http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
|
http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
|
||||||
_, _, finished := process.GetManager().AddTypedContext(context.TODO(), "Web: PProf Server", process.SystemProcessType, true)
|
_, _, finished := process.GetManager().AddTypedContext(context.Background(), "Web: PProf Server", process.SystemProcessType, true)
|
||||||
// The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment, it's not worth introducing a configurable option for it.
|
// The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment it's not worth to introduce a configurable option for it.
|
||||||
log.Info("Starting pprof server on localhost:6060")
|
log.Info("Starting pprof server on localhost:6060")
|
||||||
log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", nil))
|
log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", nil))
|
||||||
finished()
|
finished()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runWeb(ctx context.Context, cmd *cli.Command) error {
|
func runWeb(ctx *cli.Context) error {
|
||||||
defer func() {
|
defer func() {
|
||||||
if panicked := recover(); panicked != nil {
|
if panicked := recover(); panicked != nil {
|
||||||
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
|
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if subCmdName, valid := isValidDefaultSubCommand(cmd); !valid {
|
managerCtx, cancel := context.WithCancel(context.Background())
|
||||||
return fmt.Errorf("unknown command: %s", subCmdName)
|
|
||||||
}
|
|
||||||
|
|
||||||
managerCtx, cancel := context.WithCancel(ctx)
|
|
||||||
graceful.InitManager(managerCtx)
|
graceful.InitManager(managerCtx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -265,12 +262,12 @@ func runWeb(ctx context.Context, cmd *cli.Command) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set pid file setting
|
// Set pid file setting
|
||||||
if cmd.IsSet("pid") {
|
if ctx.IsSet("pid") {
|
||||||
createPIDFile(cmd.String("pid"))
|
createPIDFile(ctx.String("pid"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !setting.InstallLock {
|
if !setting.InstallLock {
|
||||||
if err := serveInstall(cmd); err != nil {
|
if err := serveInstall(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -281,7 +278,7 @@ func runWeb(ctx context.Context, cmd *cli.Command) error {
|
|||||||
go servePprof()
|
go servePprof()
|
||||||
}
|
}
|
||||||
|
|
||||||
return serveInstalled(cmd)
|
return serveInstalled(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPort(port string) error {
|
func setPort(port string) error {
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ func NoHTTPRedirector() {
|
|||||||
graceful.GetManager().InformCleanup()
|
graceful.GetManager().InformCleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
|
||||||
|
// for our main HTTP/HTTPS service
|
||||||
|
func NoMainListener() {
|
||||||
|
graceful.GetManager().InformCleanup()
|
||||||
|
}
|
||||||
|
|
||||||
// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener
|
// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener
|
||||||
// for our install HTTP/HTTPS service
|
// for our install HTTP/HTTPS service
|
||||||
func NoInstallListener() {
|
func NoInstallListener() {
|
||||||
|
|||||||
17
contrib/autocompletion/README
Normal file
17
contrib/autocompletion/README
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
Bash and Zsh completion
|
||||||
|
=======================
|
||||||
|
|
||||||
|
From within the gitea root run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source contrib/autocompletion/bash_autocomplete
|
||||||
|
```
|
||||||
|
|
||||||
|
or for zsh run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source contrib/autocompletion/zsh_autocomplete
|
||||||
|
```
|
||||||
|
|
||||||
|
These scripts will check if gitea is on the path and if so add autocompletion for `gitea`. Or if not autocompletion will work for `./gitea`.
|
||||||
|
If gitea has been installed as a different program pass in the `PROG` environment variable to set the correct program name.
|
||||||
30
contrib/autocompletion/bash_autocomplete
Executable file
30
contrib/autocompletion/bash_autocomplete
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
# Heavily inspired by https://github.com/urfave/cli
|
||||||
|
|
||||||
|
_cli_bash_autocomplete() {
|
||||||
|
if [[ "${COMP_WORDS[0]}" != "source" ]]; then
|
||||||
|
local cur opts base
|
||||||
|
COMPREPLY=()
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
if [[ "$cur" == "-"* ]]; then
|
||||||
|
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion )
|
||||||
|
else
|
||||||
|
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
||||||
|
fi
|
||||||
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$PROG" ] && [ ! "$(command -v gitea &> /dev/null)" ] ; then
|
||||||
|
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete gitea
|
||||||
|
elif [ -z "$PROG" ]; then
|
||||||
|
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete ./gitea
|
||||||
|
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete "$PWD/gitea"
|
||||||
|
else
|
||||||
|
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete "$PROG"
|
||||||
|
unset PROG
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
30
contrib/autocompletion/zsh_autocomplete
Normal file
30
contrib/autocompletion/zsh_autocomplete
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#compdef ${PROG:=gitea}
|
||||||
|
|
||||||
|
|
||||||
|
# Heavily inspired by https://github.com/urfave/cli
|
||||||
|
|
||||||
|
_cli_zsh_autocomplete() {
|
||||||
|
|
||||||
|
local -a opts
|
||||||
|
local cur
|
||||||
|
cur=${words[-1]}
|
||||||
|
if [[ "$cur" == "-"* ]]; then
|
||||||
|
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
|
||||||
|
else
|
||||||
|
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${opts[1]}" != "" ]]; then
|
||||||
|
_describe 'values' opts
|
||||||
|
else
|
||||||
|
_files
|
||||||
|
fi
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z $PROG ] ; then
|
||||||
|
compdef _cli_zsh_autocomplete gitea
|
||||||
|
else
|
||||||
|
compdef _cli_zsh_autocomplete $(basename $PROG)
|
||||||
|
fi
|
||||||
@@ -11,7 +11,7 @@ The default version will read from `docs/config.yml`. You can override this
|
|||||||
using the option `--version`.
|
using the option `--version`.
|
||||||
|
|
||||||
The upstream branches will be fetched, using the remote `origin`. This can
|
The upstream branches will be fetched, using the remote `origin`. This can
|
||||||
be overridden using `--upstream`, and fetching can be avoided using
|
be overrided using `--upstream`, and fetching can be avoided using
|
||||||
`--no-fetch`.
|
`--no-fetch`.
|
||||||
|
|
||||||
By default the branch created will be called `backport-$PR-$VERSION`. You
|
By default the branch created will be called `backport-$PR-$VERSION`. You
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//nolint:forbidigo // use of print functions is allowed in cli
|
//nolint:forbidigo
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -12,19 +12,21 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/google/go-github/v74/github"
|
"github.com/google/go-github/v71/github"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v2"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultVersion = "v1.18" // to backport to
|
const defaultVersion = "v1.18" // to backport to
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := &cli.Command{}
|
app := cli.NewApp()
|
||||||
app.Name = "backport"
|
app.Name = "backport"
|
||||||
app.Usage = "Backport provided PR-number on to the current or previous released version"
|
app.Usage = "Backport provided PR-number on to the current or previous released version"
|
||||||
app.Description = `Backport will look-up the PR in Gitea's git log and attempt to cherry-pick it on the current version`
|
app.Description = `Backport will look-up the PR in Gitea's git log and attempt to cherry-pick it on the current version`
|
||||||
@@ -89,7 +91,7 @@ func main() {
|
|||||||
Usage: "Set this flag to continue from a git cherry-pick that has broken",
|
Usage: "Set this flag to continue from a git cherry-pick that has broken",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cli.RootCommandHelpTemplate = `NAME:
|
cli.AppHelpTemplate = `NAME:
|
||||||
{{.Name}} - {{.Usage}}
|
{{.Name}} - {{.Usage}}
|
||||||
USAGE:
|
USAGE:
|
||||||
{{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
{{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
||||||
@@ -103,12 +105,16 @@ OPTIONS:
|
|||||||
`
|
`
|
||||||
|
|
||||||
app.Action = runBackport
|
app.Action = runBackport
|
||||||
if err := app.Run(context.Background(), os.Args); err != nil {
|
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runBackport(ctx context.Context, c *cli.Command) error {
|
func runBackport(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
continuing := c.Bool("continue")
|
continuing := c.Bool("continue")
|
||||||
|
|
||||||
var pr string
|
var pr string
|
||||||
@@ -337,8 +343,8 @@ func determineRemote(ctx context.Context, forkUser string) (string, string, erro
|
|||||||
fmt.Fprintf(os.Stderr, "Unable to list git remotes:\n%s\n", string(out))
|
fmt.Fprintf(os.Stderr, "Unable to list git remotes:\n%s\n", string(out))
|
||||||
return "", "", fmt.Errorf("unable to determine forked remote: %w", err)
|
return "", "", fmt.Errorf("unable to determine forked remote: %w", err)
|
||||||
}
|
}
|
||||||
lines := strings.SplitSeq(string(out), "\n")
|
lines := strings.Split(string(out), "\n")
|
||||||
for line := range lines {
|
for _, line := range lines {
|
||||||
fields := strings.Split(line, "\t")
|
fields := strings.Split(line, "\t")
|
||||||
name, remote := fields[0], fields[1]
|
name, remote := fields[0], fields[1]
|
||||||
// only look at pushers
|
// only look at pushers
|
||||||
@@ -356,12 +362,12 @@ func determineRemote(ctx context.Context, forkUser string) (string, string, erro
|
|||||||
if !strings.Contains(remote, forkUser) {
|
if !strings.Contains(remote, forkUser) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if after, ok := strings.CutPrefix(remote, "git@github.com:"); ok {
|
if strings.HasPrefix(remote, "git@github.com:") {
|
||||||
forkUser = after
|
forkUser = strings.TrimPrefix(remote, "git@github.com:")
|
||||||
} else if after, ok := strings.CutPrefix(remote, "https://github.com/"); ok {
|
} else if strings.HasPrefix(remote, "https://github.com/") {
|
||||||
forkUser = after
|
forkUser = strings.TrimPrefix(remote, "https://github.com/")
|
||||||
} else if after, ok := strings.CutPrefix(remote, "https://www.github.com/"); ok {
|
} else if strings.HasPrefix(remote, "https://www.github.com/") {
|
||||||
forkUser = after
|
forkUser = strings.TrimPrefix(remote, "https://www.github.com/")
|
||||||
} else if forkUser == "" {
|
} else if forkUser == "" {
|
||||||
return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote)
|
return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote)
|
||||||
}
|
}
|
||||||
@@ -454,3 +460,25 @@ func determineSHAforPR(ctx context.Context, prStr, accessToken string) (string,
|
|||||||
|
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func installSignals() (context.Context, context.CancelFunc) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
go func() {
|
||||||
|
// install notify
|
||||||
|
signalChannel := make(chan os.Signal, 1)
|
||||||
|
|
||||||
|
signal.Notify(
|
||||||
|
signalChannel,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
)
|
||||||
|
select {
|
||||||
|
case <-signalChannel:
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
signal.Reset()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ctx, cancel
|
||||||
|
}
|
||||||
|
|||||||
47
contrib/environment-to-ini/README
Normal file
47
contrib/environment-to-ini/README
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
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
|
||||||
111
contrib/environment-to-ini/environment-to-ini.go
Normal file
111
contrib/environment-to-ini/environment-to-ini.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := cli.NewApp()
|
||||||
|
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(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to run app with %s: %v", os.Args, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runEnvironmentToIni(c *cli.Context) 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
|
||||||
|
}
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
|
|
||||||
<p>In general, Your Gitea Instance retains User Personal Information for as long as your account is active, or as needed to provide you service.</p>
|
<p>In general, Your Gitea Instance retains User Personal Information for as long as your account is active, or as needed to provide you service.</p>
|
||||||
|
|
||||||
<p>If you would like to cancel your account or delete your User Personal Information, you may do so in your user profile. We retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile (within reason) within 90 days of your request. Feel free to contact our support to request erasure of the data we process on the basis of consent within 30 days.</p>
|
<p>If you would like to cancel your account or delete your User Personal Information, you may do so in your user profile. We retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile (within reason) within 90 days of your request. Feel free to contact our support to request erasure of the data we process on the bassis of consent within 30 days.</p>
|
||||||
|
|
||||||
<p>After an account has been deleted, certain data, such as contributions to other Users' repositories and comments in others' issues, will remain. However, we will delete or de-identify your User Personal Information, including your username and email address, from the author field of issues, pull requests, and comments by associating them with a ghost user.</p>
|
<p>After an account has been deleted, certain data, such as contributions to other Users' repositories and comments in others' issues, will remain. However, we will delete or de-identify your User Personal Information, including your username and email address, from the author field of issues, pull requests, and comments by associating them with a ghost user.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ fi
|
|||||||
# confirm update
|
# confirm update
|
||||||
echo "Checking currently installed version..."
|
echo "Checking currently installed version..."
|
||||||
current=$(giteacmd --version | cut -d ' ' -f 3)
|
current=$(giteacmd --version | cut -d ' ' -f 3)
|
||||||
[[ "$current" == "$giteaversion" ]] && echo "$current is already installed, stopping." && exit 0
|
[[ "$current" == "$giteaversion" ]] && echo "$current is already installed, stopping." && exit 1
|
||||||
if [[ -z "${no_confirm:-}" ]]; then
|
if [[ -z "${no_confirm:-}" ]]; then
|
||||||
echo "Make sure to read the changelog first: https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md"
|
echo "Make sure to read the changelog first: https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md"
|
||||||
echo "Are you ready to update Gitea from ${current} to ${giteaversion}? (y/N)"
|
echo "Are you ready to update Gitea from ${current} to ${giteaversion}? (y/N)"
|
||||||
|
|||||||
@@ -186,13 +186,17 @@ RUN_USER = ; git
|
|||||||
;; If you intend to use the AuthorizedPrincipalsCommand functionality then you should turn this off.
|
;; If you intend to use the AuthorizedPrincipalsCommand functionality then you should turn this off.
|
||||||
;SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE = true
|
;SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE = true
|
||||||
;;
|
;;
|
||||||
;; For the builtin SSH server, choose the supported ciphers/key-exchange-algorithms/MACs for SSH connections.
|
;; For the built-in SSH server, choose the ciphers to support for SSH connections,
|
||||||
;; The supported names are listed in https://github.com/golang/crypto/blob/master/ssh/common.go.
|
;; for system SSH this setting has no effect
|
||||||
;; Leave them empty to use the Golang crypto's recommended default values.
|
;SSH_SERVER_CIPHERS = chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com
|
||||||
;; For system SSH (non-builtin SSH server), this setting has no effect.
|
;;
|
||||||
;SSH_SERVER_CIPHERS =
|
;; For the built-in SSH server, choose the key exchange algorithms to support for SSH connections,
|
||||||
;SSH_SERVER_KEY_EXCHANGES =
|
;; for system SSH this setting has no effect
|
||||||
;SSH_SERVER_MACS =
|
;SSH_SERVER_KEY_EXCHANGES = curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1
|
||||||
|
;;
|
||||||
|
;; For the built-in SSH server, choose the MACs to support for SSH connections,
|
||||||
|
;; for system SSH this setting has no effect
|
||||||
|
;SSH_SERVER_MACS = hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1
|
||||||
;;
|
;;
|
||||||
;; For the built-in SSH server, choose the keypair to offer as the host key
|
;; For the built-in SSH server, choose the keypair to offer as the host key
|
||||||
;; The private key should be at SSH_SERVER_HOST_KEY and the public SSH_SERVER_HOST_KEY.pub
|
;; The private key should be at SSH_SERVER_HOST_KEY and the public SSH_SERVER_HOST_KEY.pub
|
||||||
@@ -1186,24 +1190,17 @@ LEVEL = Info
|
|||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;
|
;;
|
||||||
;; GPG or SSH key to use to sign commits, Defaults to the default - that is the value of git config --get user.signingkey
|
;; GPG key to use to sign commits, Defaults to the default - that is the value of git config --get user.signingkey
|
||||||
;; Depending on the value of SIGNING_FORMAT this is either:
|
|
||||||
;; - openpgp: the GPG key ID
|
|
||||||
;; - ssh: the path to the ssh public key "/path/to/key.pub": where "/path/to/key" is the private key, use ssh-keygen -t ed25519 to generate a new key pair without password
|
|
||||||
;; run in the context of the RUN_USER
|
;; run in the context of the RUN_USER
|
||||||
;; Switch to none to stop signing completely
|
;; Switch to none to stop signing completely
|
||||||
;SIGNING_KEY = default
|
;SIGNING_KEY = default
|
||||||
;;
|
;;
|
||||||
;; If a SIGNING_KEY ID is provided and is not set to default, use the provided Name and Email address as the signer and the signing format.
|
;; If a SIGNING_KEY ID is provided and is not set to default, use the provided Name and Email address as the signer.
|
||||||
;; These should match a publicized name and email address for the key. (When SIGNING_KEY is default these are set to
|
;; These should match a publicized name and email address for the key. (When SIGNING_KEY is default these are set to
|
||||||
;; the results of git config --get user.name, git config --get user.email and git config --default openpgp --get gpg.format respectively and can only be overridden
|
;; the results of git config --get user.name and git config --get user.email respectively and can only be overridden
|
||||||
;; by setting the SIGNING_KEY ID to the correct ID.)
|
;; by setting the SIGNING_KEY ID to the correct ID.)
|
||||||
;SIGNING_NAME =
|
;SIGNING_NAME =
|
||||||
;SIGNING_EMAIL =
|
;SIGNING_EMAIL =
|
||||||
;; SIGNING_FORMAT can be one of:
|
|
||||||
;; - openpgp (default): use GPG to sign commits
|
|
||||||
;; - ssh: use SSH to sign commits
|
|
||||||
;SIGNING_FORMAT = openpgp
|
|
||||||
;;
|
;;
|
||||||
;; Sets the default trust model for repositories. Options are: collaborator, committer, collaboratorcommitter
|
;; Sets the default trust model for repositories. Options are: collaborator, committer, collaboratorcommitter
|
||||||
;DEFAULT_TRUST_MODEL = collaborator
|
;DEFAULT_TRUST_MODEL = collaborator
|
||||||
@@ -1230,13 +1227,6 @@ LEVEL = Info
|
|||||||
;; - commitssigned: require that all the commits in the head branch are signed.
|
;; - commitssigned: require that all the commits in the head branch are signed.
|
||||||
;; - approved: only sign when merging an approved pr to a protected branch
|
;; - approved: only sign when merging an approved pr to a protected branch
|
||||||
;MERGES = pubkey, twofa, basesigned, commitssigned
|
;MERGES = pubkey, twofa, basesigned, commitssigned
|
||||||
;;
|
|
||||||
;; Determines which additional ssh keys are trusted for all signed commits regardless of the user
|
|
||||||
;; This is useful for ssh signing key rotation.
|
|
||||||
;; Exposes the provided SIGNING_NAME and SIGNING_EMAIL as the signer, regardless of the SIGNING_FORMAT value.
|
|
||||||
;; Multiple keys should be comma separated.
|
|
||||||
;; E.g."ssh-<algorithm> <key>". or "ssh-<algorithm> <key1>, ssh-<algorithm> <key2>".
|
|
||||||
;TRUSTED_SSH_KEYS =
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@@ -1343,10 +1333,6 @@ 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
|
||||||
;;
|
;;
|
||||||
@@ -2541,18 +2527,6 @@ LEVEL = Info
|
|||||||
;; * 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
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
exec /app/gitea/gitea config edit-ini --in-place --apply-env "$@"
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
exec /app/gitea/gitea config edit-ini --in-place --apply-env "$@"
|
|
||||||
1028
eslint.config.ts
1028
eslint.config.ts
File diff suppressed because it is too large
Load Diff
40
flake.lock
generated
40
flake.lock
generated
@@ -1,12 +1,30 @@
|
|||||||
{
|
{
|
||||||
"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": 1760038930,
|
"lastModified": 1752480373,
|
||||||
"narHash": "sha256-Oncbh0UmHjSlxO7ErQDM3KM0A5/Znfofj2BSzlHLeVw=",
|
"narHash": "sha256-JHQbm+OcGp32wAsXTE/FLYGNpb+4GLi5oTvCxwSoBOA=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "0b4defa2584313f3b781240b29d61f6f9f7e0df3",
|
"rev": "62e0f05ede1da0d54515d4ea8ce9c733f12d9f08",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -18,8 +36,24 @@
|
|||||||
},
|
},
|
||||||
"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",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user