mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-19 16:25:05 +09:00
Compare commits
446 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 95ba37d9af | |||
| db23633874 | |||
| 2bde4fa5d2 | |||
| 4aa0d704f2 | |||
| b0eaf6c75b | |||
| 42eb7945d1 | |||
| 0b9623e188 | |||
| 6fb96fcfdf | |||
| 99f8b3d9a1 | |||
| f6e2ca4388 | |||
| 61c58b4b65 | |||
| 5facdcc7fd | |||
| bb4cccc6e9 | |||
| 98cc15b307 | |||
| 9b8bfdceb1 | |||
| e156ac8063 | |||
| 4751adf42d | |||
| 3bebddedc0 | |||
| 617b6948b1 | |||
| 1c7b7ea72d | |||
| e107498f3b | |||
| 3b705738ab | |||
| 8f4b7ebbf6 | |||
| 603c8ece00 | |||
| 4a19964921 | |||
| 38711f2696 | |||
| 64ad4bb0ff | |||
| 8bf445e86a | |||
| 094eeee365 | |||
| cc3ee01fd8 | |||
| c044b0f48c | |||
| 10fc85e263 | |||
| bb6ca9da4d | |||
| 53877583f0 | |||
| 2c749ce548 | |||
| f540f57354 | |||
| 1c2d5e9b03 | |||
| a859221a62 | |||
| d37f7b44a9 | |||
| a34eac5ef4 | |||
| 6d2b02dac1 | |||
| 1b70a4451a | |||
| bc29cd0d3d | |||
| edfba678ec | |||
| 9c0ad8291b | |||
| 58597cc30a | |||
| 86cc3e8783 | |||
| 5038561235 | |||
| 1d7b84922f | |||
| 2965b0c08a | |||
| ab0d52b4c7 | |||
| 519b8d6d88 | |||
| 7b82ded82a | |||
| 1d5163133b | |||
| 0e53c41694 | |||
| c7af094b0a | |||
| 28729ef7e3 | |||
| 57dd9f5bab | |||
| 5829522019 | |||
| 5eaa0bc603 | |||
| fb159eae8f | |||
| 631a9b5d16 | |||
| 5636219dbc | |||
| 439984474c | |||
| a55be951e3 | |||
| 65f3feaa84 | |||
| b28c4f2b08 | |||
| 677ab982bf | |||
| e10da87ebe | |||
| 3004c45607 | |||
| 7d77631881 | |||
| 2bafa41554 | |||
| b586d80f97 | |||
| 58a66cae3c | |||
| 356a119f30 | |||
| b79529015e | |||
| eeb4d8ffa2 | |||
| dd78d87dcd | |||
| e2b211f291 | |||
| 8a49e9d346 | |||
| b88bad2a01 | |||
| 5632abff9e | |||
| 74e515623b | |||
| 4ee74d7699 | |||
| c4a1ff7d16 | |||
| 78899832eb | |||
| fb3c1b031d | |||
| cff6eb5661 | |||
| 2a61284ba5 | |||
| 11f77efea5 | |||
| afdbd9b7c5 | |||
| 64d12024d6 | |||
| 6cc1ee9424 | |||
| 5d7768f34c | |||
| 55a6cfe79b | |||
| 1f643072c1 | |||
| 0280455356 | |||
| a8e465e893 | |||
| fc9dfe0e56 | |||
| 0916039c2a | |||
| 291f6cbd3a | |||
| f536bcd508 | |||
| fc4296a21a | |||
| 657ea10cf1 | |||
| ef096b0f90 | |||
| 7bd55deab3 | |||
| e4b7120bc2 | |||
| f0fd185f14 | |||
| adfa535dc2 | |||
| e6691b0e8d | |||
| 82613a40a0 | |||
| ba5117e4e4 | |||
| 9b9d1e31aa | |||
| eb43da41f5 | |||
| 1412009d0a | |||
| 26a618ac1a | |||
| 145898b358 | |||
| b191cf7e77 | |||
| 4adee80f58 | |||
| 4de12baf9b | |||
| 5d852d2d0a | |||
| 2aca966c5f | |||
| 3b253e06a3 | |||
| df0ad4e8c1 | |||
| 68f5e40e46 | |||
| d1be5c3612 | |||
| 8687faaf3a | |||
| 789a3d3a4d | |||
| b37e098ff0 | |||
| f9b808a8d2 | |||
| 0112ec9b34 | |||
| 7a7376dfc8 | |||
| fc5e0ec877 | |||
| d0a39bc3a4 | |||
| 4eca71d6d4 | |||
| a2283a0c03 | |||
| 3e6b9e5312 | |||
| 1ad9e996be | |||
| 73e0e44298 | |||
| 1b200dc3da | |||
| 290edc1614 | |||
| adf440a3b3 | |||
| fc23bd7b3a | |||
| ff777cd2ad | |||
| dc197a0058 | |||
| 423cdd4d94 | |||
| e47c6135dd | |||
| ca51b4f875 | |||
| a8938115d4 | |||
| 5f443184f3 | |||
| c9669594a8 | |||
| f59d1d3cef | |||
| 3c17daf615 | |||
| 2c2d7e6f64 | |||
| f9f9876f2c | |||
| d80640fa5d | |||
| 74060bb849 | |||
| 30c07c20e9 | |||
| f70f2c76cb | |||
| 6eed75af24 | |||
| 7b17234945 | |||
| 4fa319b9dc | |||
| 23c662ebb1 | |||
| 686d10b7f0 | |||
| 2158cf6e12 | |||
| b53f25a30c | |||
| 3a9cab034b | |||
| 3ffccb8fe5 | |||
| ca8c71359c | |||
| 47a0d88056 | |||
| a20e182067 | |||
| 35b654c9d6 | |||
| 0df3213766 | |||
| e2e8509239 | |||
| 4747dd68bd | |||
| e15219d810 | |||
| d8da91a7f2 | |||
| b20b0ed372 | |||
| f8d14b77eb | |||
| d288b4529b | |||
| 6ca5573718 | |||
| daf581fa89 | |||
| 9bb0aa1c49 | |||
| 612ce46cda | |||
| 539654831a | |||
| c31e0cfc1c | |||
| 2633f9677d | |||
| cbea04c1fc | |||
| d7070b8513 | |||
| 50a1dc9486 | |||
| da51d5af1a | |||
| a88449f13f | |||
| 755d200371 | |||
| 0ec66b5380 | |||
| 6288c87181 | |||
| db7eb4d51b | |||
| a1b0bffd0c | |||
| 84daa0b8be | |||
| 487e357ce6 | |||
| 7492251e7e | |||
| b136a66d12 | |||
| 896e4838cb | |||
| 17b802beae | |||
| 74c40d46ee | |||
| de478c4b6f | |||
| b3c6917463 | |||
| 12737883ba | |||
| 8fdd6d1235 | |||
| d5a89805d9 | |||
| 9583e1a65c | |||
| ffa626b585 | |||
| a3cc34472b | |||
| bc5c554072 | |||
| 435123fe65 | |||
| bb1e22bba4 | |||
| e24c3f7a40 | |||
| 943ff75233 | |||
| cfd9008891 | |||
| c96cc70144 | |||
| 66b8178e59 | |||
| c453d09c36 | |||
| c5e196dedb | |||
| 63c2b69259 | |||
| 86401fd5fd | |||
| cf1e4d7c42 | |||
| 4f9f0fc4b8 | |||
| 788200de9f | |||
| ef88cdb7e7 | |||
| 1edbc21fcc | |||
| ae0bc0222a | |||
| 4ba90207cf | |||
| aa9aea2c6e | |||
| 0ab612f5ab | |||
| c8545033cc | |||
| 45809c8f54 | |||
| b22123ef86 | |||
| ee009ebec8 | |||
| 0e0cf7a813 | |||
| 068d7a513a | |||
| 79f96b3e24 | |||
| 00060ff73c | |||
| 18c65965ab | |||
| d6496c6156 | |||
| 455dd20365 | |||
| 89cd3737bc | |||
| b3b2d111da | |||
| 6372cd7c7d | |||
| e29d1b79d8 | |||
| 877f091305 | |||
| 28e09ffc67 | |||
| 2601f50026 | |||
| 538ec6ae6e | |||
| 356f589f0b | |||
| 385994295d | |||
| 6e8f78ae27 | |||
| 8d06a9425e | |||
| 1dfb32a36f | |||
| 47085f3fa0 | |||
| eb020a9d27 | |||
| a52617b816 | |||
| 80c57ec126 | |||
| 23a5bc5e64 | |||
| 6f8ab6aaaf | |||
| 0724344a8a | |||
| 3f1ef703d5 | |||
| a0996cb229 | |||
| 6e7bc1e635 | |||
| 130e34994f | |||
| f250138f57 | |||
| 2ce71629c3 | |||
| f3bdcc58af | |||
| 57b5ed3f25 | |||
| c710ce34fb | |||
| 9c2c9c5a00 | |||
| 833304ac15 | |||
| 9fe5b70e3e | |||
| 99b0bf7324 | |||
| 723ce3579f | |||
| 867c4af481 | |||
| 5d87bb3d45 | |||
| 79ae9ea97b | |||
| 315b947740 | |||
| b874e0d8e5 | |||
| 484eacb7bf | |||
| 93e3be3018 | |||
| 761b9d439b | |||
| 054eb6d8a5 | |||
| 56f23f623a | |||
| 37f6f7f6d4 | |||
| 0e0daa8afe | |||
| 716a800f50 | |||
| 7889b78c87 | |||
| 08254cf126 | |||
| c3b1e7372e | |||
| bc9817b317 | |||
| 553277b0be | |||
| 5b8c8e724f | |||
| 2c624d4deb | |||
| 1592576fa5 | |||
| e3cf360154 | |||
| 3ee7a87c8a | |||
| 649ebeb120 | |||
| f02f419173 | |||
| 48a3a47741 | |||
| dae2d32186 | |||
| 3b250ba04e | |||
| 2e00b2f0bb | |||
| b24780b3a3 | |||
| 50ec48d9fe | |||
| ae2b19849d | |||
| fde7f7db28 | |||
| 619db646f5 | |||
| 72e63eef39 | |||
| b52d745d0a | |||
| f109b97ddd | |||
| f9a2a8ae8d | |||
| f7f55a356f | |||
| 26d83c932a | |||
| d0f92cb0a1 | |||
| 0d006290a7 | |||
| 840cf68c3e | |||
| 9ae28b6f39 | |||
| 0de8a3d3d8 | |||
| 569c49debe | |||
| 577ed107dd | |||
| 2176e84ab9 | |||
| d19d4da5ce | |||
| ed57c70176 | |||
| 75efc51e98 | |||
| 429ba9c010 | |||
| a8505269ca | |||
| 427954ba6e | |||
| 6e7991316c | |||
| 3db3c058b3 | |||
| 8f15f76dd6 | |||
| 1eced4a7c0 | |||
| 5f8e19fcef | |||
| fed2d81e88 | |||
| ad9850391d | |||
| daf10ff84c | |||
| d9ac0636d0 | |||
| eb59b1a24a | |||
| bb41bca739 | |||
| ed587ca71b | |||
| 18e0746b7b | |||
| 86d102494b | |||
| bcd253a310 | |||
| 5ad87616c9 | |||
| aedc564308 | |||
| bbea5e6c2d | |||
| d59df34a7d | |||
| 3830d488d5 | |||
| 91dc737a35 | |||
| 87f7291909 | |||
| 5e9b9b33d1 | |||
| 147bdfce0d | |||
| b9d323c3d8 | |||
| 2cb8f6a9a5 | |||
| 72ab59efdb | |||
| 1ac4ad358a | |||
| e79112170c | |||
| 63266ba036 | |||
| b970cc02c7 | |||
| 318cb85037 | |||
| ddacefa5d6 | |||
| d6be18e870 | |||
| 883af8d42d | |||
| 1b874d1403 | |||
| cfc60b2142 | |||
| 0e99932530 | |||
| a0160694b9 | |||
| d9d66d04d0 | |||
| 8fdda2dd83 | |||
| 258754f299 | |||
| 08d9845635 | |||
| 4ca4217b3d | |||
| 2896dac536 | |||
| 88752bc159 | |||
| 692ef9eca6 | |||
| 838bb1d379 | |||
| 26bb175d69 | |||
| a6282c98d7 | |||
| 2cdf86e184 | |||
| 1d4b8486f0 | |||
| 4805151f56 | |||
| 7a8fe9eb37 | |||
| ce61d6d99d | |||
| afcd11c77f | |||
| 0d8bd7720d | |||
| d69b786097 | |||
| f76f5207a7 | |||
| 4b36f01bf4 | |||
| d582c9c8c0 | |||
| 8d26ea9373 | |||
| 514f322dcf | |||
| 2876800cb2 | |||
| 47b387921a | |||
| 45ee571693 | |||
| 3754e9dd12 | |||
| fd89ceef79 | |||
| 018a88590c | |||
| c17280149f | |||
| 18ccee0f2f | |||
| 2d70d37bff | |||
| 5e5703694d | |||
| 09a88fb17e | |||
| 8cc8150922 | |||
| 269bc1b112 | |||
| f73e45b4ba | |||
| cf7e49ecdd | |||
| 94437eadd9 | |||
| 36ced5dc8c | |||
| 34b34d2328 | |||
| c401cda108 | |||
| 08b7a30867 | |||
| 2ff4f4a909 | |||
| f65df2a69b | |||
| daf0483ef2 | |||
| ef529de0ac | |||
| a60201a071 | |||
| 49e6d5f6d6 | |||
| e2104a1dd5 | |||
| 0dacd956fb | |||
| 915b44810d | |||
| 403a73dca0 | |||
| ef9c19691d | |||
| 50fdd2d49a | |||
| fca94bcdd7 | |||
| 000d7c1ccb | |||
| 65d93d819b | |||
| 288d1f526a | |||
| 7883f6dde9 | |||
| c2dea22926 | |||
| 584d8ef75f | |||
| 9d96039027 | |||
| 072de7d8cd | |||
| e377da989f | |||
| 7ad9bf4523 | |||
| 7292ae1ed5 | |||
| 8c9247e717 | |||
| 0acaad1919 | |||
| 8feabe4160 | |||
| a16ca3c57c | |||
| 2d1306291b | |||
| 208cbd5a6f | |||
| de829c7821 |
@@ -20,7 +20,6 @@
|
|||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"settings": {},
|
"settings": {},
|
||||||
// same extensions as Gitpod, should match /.gitpod.yml
|
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"editorconfig.editorconfig",
|
"editorconfig.editorconfig",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
|
|||||||
+1
-2
@@ -43,7 +43,6 @@ modifies/internal:
|
|||||||
- ".editorconfig"
|
- ".editorconfig"
|
||||||
- ".eslintrc.cjs"
|
- ".eslintrc.cjs"
|
||||||
- ".golangci.yml"
|
- ".golangci.yml"
|
||||||
- ".gitpod.yml"
|
|
||||||
- ".markdownlint.yaml"
|
- ".markdownlint.yaml"
|
||||||
- ".spectral.yaml"
|
- ".spectral.yaml"
|
||||||
- "stylelint.config.*"
|
- "stylelint.config.*"
|
||||||
@@ -84,9 +83,9 @@ docs-update-needed:
|
|||||||
topic/code-linting:
|
topic/code-linting:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
- ".eslintrc.cjs"
|
|
||||||
- ".golangci.yml"
|
- ".golangci.yml"
|
||||||
- ".markdownlint.yaml"
|
- ".markdownlint.yaml"
|
||||||
- ".spectral.yaml"
|
- ".spectral.yaml"
|
||||||
- ".yamllint.yaml"
|
- ".yamllint.yaml"
|
||||||
|
- "eslint*.config.*"
|
||||||
- "stylelint.config.*"
|
- "stylelint.config.*"
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
name: cron-flake-updater
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 0' # runs weekly on Sunday at 00:00
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
nix-flake-update:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: DeterminateSystems/determinate-nix-action@v3
|
||||||
|
- uses: DeterminateSystems/update-flake-lock@main
|
||||||
|
with:
|
||||||
|
pr-title: "Update Nix flake"
|
||||||
|
pr-labels: |
|
||||||
|
dependencies
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
json: ${{ steps.changes.outputs.json }}
|
json: ${{ steps.changes.outputs.json }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: dorny/paths-filter@v3
|
- uses: dorny/paths-filter@v4
|
||||||
id: changes
|
id: changes
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
@@ -85,6 +85,7 @@ jobs:
|
|||||||
- "uv.lock"
|
- "uv.lock"
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
|
- ".github/workflows/pull-docker-dryrun.yml"
|
||||||
- "Dockerfile"
|
- "Dockerfile"
|
||||||
- "Dockerfile.rootless"
|
- "Dockerfile.rootless"
|
||||||
- "docker/**"
|
- "docker/**"
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: astral-sh/setup-uv@v7
|
- uses: astral-sh/setup-uv@v8.0.0
|
||||||
- run: uv python install 3.14
|
- run: uv python install 3.14
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v5
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: astral-sh/setup-uv@v7
|
- uses: astral-sh/setup-uv@v8.0.0
|
||||||
- run: uv python install 3.14
|
- run: uv python install 3.14
|
||||||
- run: make deps-py
|
- run: make deps-py
|
||||||
- run: make lint-yaml
|
- run: make lint-yaml
|
||||||
@@ -71,7 +71,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v5
|
||||||
- uses: actions/setup-node@v5
|
- uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
@@ -86,7 +86,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v5
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
@@ -168,7 +168,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v5
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
@@ -222,7 +222,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v5
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ jobs:
|
|||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
TEST_TAGS: gogit
|
TEST_TAGS: gogit
|
||||||
TEST_LDAP: 1
|
TEST_LDAP: 1
|
||||||
USE_REPO_TEST_DIR: 1
|
|
||||||
|
|
||||||
test-sqlite:
|
test-sqlite:
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
@@ -90,7 +89,6 @@ jobs:
|
|||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
TEST_TAGS: gogit sqlite sqlite_unlock_notify
|
TEST_TAGS: gogit sqlite sqlite_unlock_notify
|
||||||
USE_REPO_TEST_DIR: 1
|
|
||||||
|
|
||||||
test-unit:
|
test-unit:
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
@@ -206,7 +204,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
USE_REPO_TEST_DIR: 1
|
|
||||||
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
|
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
|
||||||
|
|
||||||
test-mssql:
|
test-mssql:
|
||||||
@@ -246,4 +243,3 @@ jobs:
|
|||||||
timeout-minutes: 50
|
timeout-minutes: 50
|
||||||
env:
|
env:
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
USE_REPO_TEST_DIR: 1
|
|
||||||
|
|||||||
@@ -14,24 +14,27 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
container:
|
container:
|
||||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.docker == 'true'
|
||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-qemu-action@v4
|
||||||
|
- uses: docker/setup-buildx-action@v4
|
||||||
- name: Build regular container image
|
- name: Build regular container image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
push: false
|
push: false
|
||||||
tags: gitea/gitea:linux-amd64
|
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
|
||||||
- name: Build rootless container image
|
- name: Build rootless container image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: false
|
push: false
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
file: Dockerfile.rootless
|
file: Dockerfile.rootless
|
||||||
tags: gitea/gitea:linux-amd64
|
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
name: e2e-tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
files-changed:
|
||||||
|
uses: ./.github/workflows/files-changed.yml
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
test-e2e:
|
||||||
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true'
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
check-latest: true
|
||||||
|
- uses: pnpm/action-setup@v5
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: pnpm
|
||||||
|
cache-dependency-path: pnpm-lock.yaml
|
||||||
|
- run: make deps-frontend
|
||||||
|
- run: make frontend
|
||||||
|
- run: make deps-backend
|
||||||
|
- run: make gitea-e2e
|
||||||
|
- run: make playwright
|
||||||
|
- run: make test-e2e
|
||||||
|
timeout-minutes: 10
|
||||||
|
env:
|
||||||
|
FORCE_COLOR: 1
|
||||||
|
GITEA_TEST_E2E_DEBUG: 1
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v5
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
- name: import gpg key
|
- name: import gpg key
|
||||||
id: import_gpg
|
id: import_gpg
|
||||||
uses: crazy-max/ghaction-import-gpg@v6
|
uses: crazy-max/ghaction-import-gpg@v7
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
||||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
echo "Cleaned name is ${REF_NAME}"
|
echo "Cleaned name is ${REF_NAME}"
|
||||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||||
- name: configure aws
|
- name: configure aws
|
||||||
uses: aws-actions/configure-aws-credentials@v5
|
uses: aws-actions/configure-aws-credentials@v6
|
||||||
with:
|
with:
|
||||||
aws-region: ${{ secrets.AWS_REGION }}
|
aws-region: ${{ secrets.AWS_REGION }}
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
@@ -71,14 +71,14 @@ jobs:
|
|||||||
# 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: docker/setup-qemu-action@v3
|
- uses: docker/setup-qemu-action@v4
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v4
|
||||||
- name: Get cleaned branch name
|
- name: Get cleaned branch name
|
||||||
id: clean_name
|
id: clean_name
|
||||||
run: |
|
run: |
|
||||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||||
- uses: docker/metadata-action@v5
|
- uses: docker/metadata-action@v6
|
||||||
id: meta
|
id: meta
|
||||||
with:
|
with:
|
||||||
images: |-
|
images: |-
|
||||||
@@ -88,7 +88,7 @@ jobs:
|
|||||||
type=raw,value=${{ steps.clean_name.outputs.branch }}
|
type=raw,value=${{ steps.clean_name.outputs.branch }}
|
||||||
annotations: |
|
annotations: |
|
||||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||||
- uses: docker/metadata-action@v5
|
- uses: docker/metadata-action@v6
|
||||||
id: meta_rootless
|
id: meta_rootless
|
||||||
with:
|
with:
|
||||||
images: |-
|
images: |-
|
||||||
@@ -102,26 +102,28 @@ jobs:
|
|||||||
annotations: |
|
annotations: |
|
||||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
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@v4
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- name: Login to GHCR using PAT
|
- name: Login to GHCR using PAT
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
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: build regular docker image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
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 }}
|
annotations: ${{ steps.meta.outputs.annotations }}
|
||||||
|
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
|
||||||
|
cache-to: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful,mode=max
|
||||||
- name: build rootless docker image
|
- name: build rootless docker image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
@@ -129,3 +131,5 @@ jobs:
|
|||||||
file: Dockerfile.rootless
|
file: Dockerfile.rootless
|
||||||
tags: ${{ steps.meta_rootless.outputs.tags }}
|
tags: ${{ steps.meta_rootless.outputs.tags }}
|
||||||
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
annotations: ${{ steps.meta_rootless.outputs.annotations }}
|
||||||
|
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless
|
||||||
|
cache-to: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless,mode=max
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v5
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
- name: import gpg key
|
- name: import gpg key
|
||||||
id: import_gpg
|
id: import_gpg
|
||||||
uses: crazy-max/ghaction-import-gpg@v6
|
uses: crazy-max/ghaction-import-gpg@v7
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
||||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
echo "Cleaned name is ${REF_NAME}"
|
echo "Cleaned name is ${REF_NAME}"
|
||||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
- name: configure aws
|
- name: configure aws
|
||||||
uses: aws-actions/configure-aws-credentials@v5
|
uses: aws-actions/configure-aws-credentials@v6
|
||||||
with:
|
with:
|
||||||
aws-region: ${{ secrets.AWS_REGION }}
|
aws-region: ${{ secrets.AWS_REGION }}
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
@@ -81,9 +81,9 @@ jobs:
|
|||||||
# 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: docker/setup-qemu-action@v3
|
- uses: docker/setup-qemu-action@v4
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v4
|
||||||
- uses: docker/metadata-action@v5
|
- uses: docker/metadata-action@v6
|
||||||
id: meta
|
id: meta
|
||||||
with:
|
with:
|
||||||
images: |-
|
images: |-
|
||||||
@@ -96,7 +96,7 @@ jobs:
|
|||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
annotations: |
|
annotations: |
|
||||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||||
- uses: docker/metadata-action@v5
|
- uses: docker/metadata-action@v6
|
||||||
id: meta_rootless
|
id: meta_rootless
|
||||||
with:
|
with:
|
||||||
images: |-
|
images: |-
|
||||||
@@ -112,18 +112,18 @@ jobs:
|
|||||||
annotations: |
|
annotations: |
|
||||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
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@v4
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- name: Login to GHCR using PAT
|
- name: Login to GHCR using PAT
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
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 regular container image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
@@ -131,7 +131,7 @@ jobs:
|
|||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
annotations: ${{ steps.meta.outputs.annotations }}
|
annotations: ${{ steps.meta.outputs.annotations }}
|
||||||
- name: build rootless container image
|
- name: build rootless container image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v5
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
- name: import gpg key
|
- name: import gpg key
|
||||||
id: import_gpg
|
id: import_gpg
|
||||||
uses: crazy-max/ghaction-import-gpg@v6
|
uses: crazy-max/ghaction-import-gpg@v7
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
||||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||||
@@ -56,7 +56,7 @@ jobs:
|
|||||||
echo "Cleaned name is ${REF_NAME}"
|
echo "Cleaned name is ${REF_NAME}"
|
||||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
- name: configure aws
|
- name: configure aws
|
||||||
uses: aws-actions/configure-aws-credentials@v5
|
uses: aws-actions/configure-aws-credentials@v6
|
||||||
with:
|
with:
|
||||||
aws-region: ${{ secrets.AWS_REGION }}
|
aws-region: ${{ secrets.AWS_REGION }}
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
@@ -84,9 +84,9 @@ jobs:
|
|||||||
# 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: docker/setup-qemu-action@v3
|
- uses: docker/setup-qemu-action@v4
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v4
|
||||||
- uses: docker/metadata-action@v5
|
- uses: docker/metadata-action@v6
|
||||||
id: meta
|
id: meta
|
||||||
with:
|
with:
|
||||||
images: |-
|
images: |-
|
||||||
@@ -103,7 +103,7 @@ jobs:
|
|||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
annotations: |
|
annotations: |
|
||||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||||
- uses: docker/metadata-action@v5
|
- uses: docker/metadata-action@v6
|
||||||
id: meta_rootless
|
id: meta_rootless
|
||||||
with:
|
with:
|
||||||
images: |-
|
images: |-
|
||||||
@@ -124,18 +124,18 @@ jobs:
|
|||||||
annotations: |
|
annotations: |
|
||||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
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@v4
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- name: Login to GHCR using PAT
|
- name: Login to GHCR using PAT
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
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 regular container image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
@@ -143,7 +143,7 @@ jobs:
|
|||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
annotations: ${{ steps.meta.outputs.annotations }}
|
annotations: ${{ steps.meta.outputs.annotations }}
|
||||||
- name: build rootless container image
|
- name: build rootless container image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||||
|
|||||||
+3
-10
@@ -55,6 +55,7 @@ cpu.out
|
|||||||
*.log.*.gz
|
*.log.*.gz
|
||||||
|
|
||||||
/gitea
|
/gitea
|
||||||
|
/gitea-e2e
|
||||||
/gitea-vet
|
/gitea-vet
|
||||||
/debug
|
/debug
|
||||||
/integrations.test
|
/integrations.test
|
||||||
@@ -67,13 +68,9 @@ cpu.out
|
|||||||
/indexers
|
/indexers
|
||||||
/log
|
/log
|
||||||
/public/assets/img/avatar
|
/public/assets/img/avatar
|
||||||
|
/tests/e2e-output
|
||||||
/tests/integration/gitea-integration-*
|
/tests/integration/gitea-integration-*
|
||||||
/tests/integration/indexers-*
|
/tests/integration/indexers-*
|
||||||
/tests/e2e/gitea-e2e-*
|
|
||||||
/tests/e2e/indexers-*
|
|
||||||
/tests/e2e/reports
|
|
||||||
/tests/e2e/test-artifacts
|
|
||||||
/tests/e2e/test-snapshots
|
|
||||||
/tests/*.ini
|
/tests/*.ini
|
||||||
/tests/**/*.git/**/*.sample
|
/tests/**/*.git/**/*.sample
|
||||||
/node_modules
|
/node_modules
|
||||||
@@ -82,6 +79,7 @@ cpu.out
|
|||||||
/yarn-error.log
|
/yarn-error.log
|
||||||
/npm-debug.log*
|
/npm-debug.log*
|
||||||
/.pnpm-store
|
/.pnpm-store
|
||||||
|
/public/assets/.vite
|
||||||
/public/assets/js
|
/public/assets/js
|
||||||
/public/assets/css
|
/public/assets/css
|
||||||
/public/assets/fonts
|
/public/assets/fonts
|
||||||
@@ -89,10 +87,7 @@ cpu.out
|
|||||||
/vendor
|
/vendor
|
||||||
/VERSION
|
/VERSION
|
||||||
/.air
|
/.air
|
||||||
/.go-licenses
|
|
||||||
|
|
||||||
# Files and folders that were previously generated
|
|
||||||
/public/assets/img/webpack
|
|
||||||
|
|
||||||
# Snapcraft
|
# Snapcraft
|
||||||
/gitea_a*.txt
|
/gitea_a*.txt
|
||||||
@@ -121,8 +116,6 @@ prime/
|
|||||||
/.goosehints
|
/.goosehints
|
||||||
/.windsurfrules
|
/.windsurfrules
|
||||||
/.github/copilot-instructions.md
|
/.github/copilot-instructions.md
|
||||||
/AGENT.md
|
|
||||||
/CLAUDE.md
|
|
||||||
/llms.txt
|
/llms.txt
|
||||||
|
|
||||||
# Ignore worktrees when working on multiple branches
|
# Ignore worktrees when working on multiple branches
|
||||||
|
|||||||
-51
@@ -1,51 +0,0 @@
|
|||||||
tasks:
|
|
||||||
- name: Setup
|
|
||||||
init: |
|
|
||||||
cp -r contrib/ide/vscode .vscode
|
|
||||||
make deps
|
|
||||||
make build
|
|
||||||
command: |
|
|
||||||
gp sync-done setup
|
|
||||||
exit 0
|
|
||||||
- name: Run backend
|
|
||||||
command: |
|
|
||||||
gp sync-await setup
|
|
||||||
|
|
||||||
# Get the URL and extract the domain
|
|
||||||
url=$(gp url 3000)
|
|
||||||
domain=$(echo $url | awk -F[/:] '{print $4}')
|
|
||||||
|
|
||||||
if [ -f custom/conf/app.ini ]; then
|
|
||||||
sed -i "s|^ROOT_URL =.*|ROOT_URL = ${url}/|" custom/conf/app.ini
|
|
||||||
sed -i "s|^DOMAIN =.*|DOMAIN = ${domain}|" custom/conf/app.ini
|
|
||||||
sed -i "s|^SSH_DOMAIN =.*|SSH_DOMAIN = ${domain}|" custom/conf/app.ini
|
|
||||||
sed -i "s|^NO_REPLY_ADDRESS =.*|SSH_DOMAIN = noreply.${domain}|" custom/conf/app.ini
|
|
||||||
else
|
|
||||||
mkdir -p custom/conf/
|
|
||||||
echo -e "[server]\nROOT_URL = ${url}/" > custom/conf/app.ini
|
|
||||||
echo -e "\n[database]\nDB_TYPE = sqlite3\nPATH = $GITPOD_REPO_ROOT/data/gitea.db" >> custom/conf/app.ini
|
|
||||||
fi
|
|
||||||
export TAGS="sqlite sqlite_unlock_notify"
|
|
||||||
make watch-backend
|
|
||||||
- name: Run frontend
|
|
||||||
command: |
|
|
||||||
gp sync-await setup
|
|
||||||
make watch-frontend
|
|
||||||
openMode: split-right
|
|
||||||
|
|
||||||
vscode:
|
|
||||||
extensions:
|
|
||||||
- editorconfig.editorconfig
|
|
||||||
- dbaeumer.vscode-eslint
|
|
||||||
- golang.go
|
|
||||||
- stylelint.vscode-stylelint
|
|
||||||
- DavidAnson.vscode-markdownlint
|
|
||||||
- Vue.volar
|
|
||||||
- ms-azuretools.vscode-docker
|
|
||||||
- vitest.explorer
|
|
||||||
- cweijan.vscode-database-client2
|
|
||||||
- GitHub.vscode-pull-request-github
|
|
||||||
|
|
||||||
ports:
|
|
||||||
- name: Gitea
|
|
||||||
port: 3000
|
|
||||||
+1
-21
@@ -18,6 +18,7 @@ linters:
|
|||||||
- mirror
|
- mirror
|
||||||
- modernize
|
- modernize
|
||||||
- nakedret
|
- nakedret
|
||||||
|
- nilnil
|
||||||
- nolintlint
|
- nolintlint
|
||||||
- perfsprint
|
- perfsprint
|
||||||
- revive
|
- revive
|
||||||
@@ -50,8 +51,6 @@ linters:
|
|||||||
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
|
||||||
- pkg: github.com/pkg/errors
|
- pkg: github.com/pkg/errors
|
||||||
desc: use builtin errors package instead
|
desc: use builtin errors package instead
|
||||||
- pkg: github.com/go-ap/errors
|
|
||||||
desc: use builtin errors package instead
|
|
||||||
nolintlint:
|
nolintlint:
|
||||||
allow-unused: false
|
allow-unused: false
|
||||||
require-explanation: true
|
require-explanation: true
|
||||||
@@ -66,35 +65,24 @@ linters:
|
|||||||
revive:
|
revive:
|
||||||
severity: error
|
severity: error
|
||||||
rules:
|
rules:
|
||||||
- name: atomic
|
|
||||||
- name: bare-return
|
|
||||||
- name: blank-imports
|
- name: blank-imports
|
||||||
- name: constant-logical-expr
|
- name: constant-logical-expr
|
||||||
- name: context-as-argument
|
- name: context-as-argument
|
||||||
- name: context-keys-type
|
- name: context-keys-type
|
||||||
- name: dot-imports
|
- name: dot-imports
|
||||||
- name: duplicated-imports
|
|
||||||
- name: empty-lines
|
- name: empty-lines
|
||||||
- name: error-naming
|
|
||||||
- name: error-return
|
- name: error-return
|
||||||
- name: error-strings
|
- name: error-strings
|
||||||
- name: errorf
|
|
||||||
- name: exported
|
- name: exported
|
||||||
- name: identical-branches
|
- name: identical-branches
|
||||||
- name: if-return
|
- name: if-return
|
||||||
- name: increment-decrement
|
- name: increment-decrement
|
||||||
- name: indent-error-flow
|
|
||||||
- name: modifies-value-receiver
|
- name: modifies-value-receiver
|
||||||
- name: package-comments
|
- name: package-comments
|
||||||
- name: range
|
|
||||||
- name: receiver-naming
|
|
||||||
- name: redefines-builtin-id
|
- name: redefines-builtin-id
|
||||||
- name: string-of-int
|
|
||||||
- name: superfluous-else
|
- name: superfluous-else
|
||||||
- name: time-naming
|
- name: time-naming
|
||||||
- name: unconditional-recursion
|
|
||||||
- name: unexported-return
|
- name: unexported-return
|
||||||
- name: unreachable-code
|
|
||||||
- name: var-declaration
|
- name: var-declaration
|
||||||
- name: var-naming
|
- name: var-naming
|
||||||
arguments:
|
arguments:
|
||||||
@@ -132,16 +120,12 @@ linters:
|
|||||||
- linters:
|
- linters:
|
||||||
- dupl
|
- dupl
|
||||||
- errcheck
|
- errcheck
|
||||||
- gocyclo
|
|
||||||
- gosec
|
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- unparam
|
- unparam
|
||||||
path: _test\.go
|
path: _test\.go
|
||||||
- linters:
|
- linters:
|
||||||
- dupl
|
- dupl
|
||||||
- errcheck
|
- errcheck
|
||||||
- gocyclo
|
|
||||||
- gosec
|
|
||||||
path: models/migrations/v
|
path: models/migrations/v
|
||||||
- linters:
|
- linters:
|
||||||
- forbidigo
|
- forbidigo
|
||||||
@@ -153,12 +137,8 @@ linters:
|
|||||||
- gocritic
|
- gocritic
|
||||||
text: (?i)`ID' should not be capitalized
|
text: (?i)`ID' should not be capitalized
|
||||||
- linters:
|
- linters:
|
||||||
- deadcode
|
|
||||||
- unused
|
- unused
|
||||||
text: (?i)swagger
|
text: (?i)swagger
|
||||||
- linters:
|
|
||||||
- staticcheck
|
|
||||||
text: (?i)argument x is overwritten before first use
|
|
||||||
- linters:
|
- linters:
|
||||||
- gocritic
|
- gocritic
|
||||||
text: '(?i)commentFormatting: put a space between `//` and comment text'
|
text: '(?i)commentFormatting: put a space between `//` and comment text'
|
||||||
|
|||||||
+1
-3
@@ -21,9 +21,7 @@ rules:
|
|||||||
comments-indentation:
|
comments-indentation:
|
||||||
level: error
|
level: error
|
||||||
|
|
||||||
document-start:
|
document-start: disable
|
||||||
level: error
|
|
||||||
present: false
|
|
||||||
|
|
||||||
document-end:
|
document-end:
|
||||||
present: false
|
present: false
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
- Use `make help` to find available development targets
|
||||||
|
- Run `make fmt` to format `.go` files, and run `make lint-go` to lint them
|
||||||
|
- Run `make lint-js` to lint `.ts` files
|
||||||
|
- Run `make tidy` after any `go.mod` changes
|
||||||
|
- Add the current year into the copyright header of new `.go` files
|
||||||
|
- Ensure no trailing whitespace in edited files
|
||||||
|
- Never force-push, amend, or squash unless asked. Use new commits and normal push for pull request updates
|
||||||
|
- Preserve existing code comments, do not remove or rewrite comments that are still relevant
|
||||||
|
- In TypeScript, use `!` (non-null assertion) instead of `?.`/`??` when a value is known to always exist
|
||||||
|
- Include authorship attribution in issue and pull request comments
|
||||||
|
- Add `Co-Authored-By` lines to all commits, indicating name and model used
|
||||||
+581
@@ -4,6 +4,587 @@ 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.26.4](https://github.com/go-gitea/gitea/releases/tag/1.26.4) - 2026-06-21
|
||||||
|
|
||||||
|
* SECURITY
|
||||||
|
* fix(auth): do not auto-reactivate disabled users on OAuth2 callback (#38009) (#38183)
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* fix: walk git log context error handling (#38182) (#38185)
|
||||||
|
|
||||||
|
## [1.26.3](https://github.com/go-gitea/gitea/releases/tag/1.26.3) - 2026-06-18
|
||||||
|
|
||||||
|
* BREAKING
|
||||||
|
* fix(actions)!: require merged PR to bypass fork PR approval gate (#38010) (#38041)
|
||||||
|
|
||||||
|
* SECURITY
|
||||||
|
* fix(hostmatcher): patch incorrect private list (#38170) (#38173)
|
||||||
|
* fix: Various security fixes (#38103) (#38151)
|
||||||
|
* fix: Various sec fixes (#38108) (#38147)
|
||||||
|
* fix: allow git clone of private repos with anonymous code access (#38074) (#38146)
|
||||||
|
* fix(auth): ignore stale OIDC external login links to organizations (#37875) (#38141)
|
||||||
|
* fix(hostmatcher): block reserved IP ranges from external/private filters (#38039) (#38059)
|
||||||
|
* fix(lfs): require Code-unit access for cross-repo LFS object reuse (#38006) (#38050)
|
||||||
|
* fix(lfs): reject unknown SSH LFS sub-verbs to prevent auth bypass (#38008) (#38015)
|
||||||
|
* fix: bound CODEOWNERS regex match time (#38011) (#38025)
|
||||||
|
* fix: bound debian ParseControlFile to a single control stanza (#38044) (#38055)
|
||||||
|
* fix(deps): update module golang.org/x/net to v0.55.0 [security] (#37813) (#37829)
|
||||||
|
|
||||||
|
* API
|
||||||
|
* feat(api): add Link header in ListForks (#38052) (#38063)
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* fix: Fix the panic when ssh remote lfs endpoint parsing failure (#38026) (#38158)
|
||||||
|
* fix(api): nil pointer panic when filtering tracked times by a non-existent user (#38112) (#38115)
|
||||||
|
* fix: keep literal "false" value displayed in workflow_dispatch choice dropdowns (#38080) (#38096)
|
||||||
|
* fix: parse HEAD ref (#38119)
|
||||||
|
* fix: git cmd (#38084) (#38087)
|
||||||
|
* fix(releases): generate notes for initial tag (#37697) (#37986)
|
||||||
|
* fix(actions): return 404 when job log blob is missing (#38003) (#38004)
|
||||||
|
* fix(actions): exclude `workflow_call` from workflow trigger detection (#37894) (#37899)
|
||||||
|
* fix(actions): keep action run title clickable when commit subject is a URL (#37867) (#37898)
|
||||||
|
* fix(actions): reject workflow_dispatch for workflows without that trigger (#37660) (#37895)
|
||||||
|
* fix(actions): ack re-sent `UpdateLog` finalize idempotently (#37885) (#37892)
|
||||||
|
* fix: http content file render (#37850) (#37856)
|
||||||
|
* fix(issues): clear stale ReviewTypeRequest when submitting pending review (#37809) (#37815)
|
||||||
|
* fix: Fix issue target branch selection for non-collaborators (#36916) (#38164)
|
||||||
|
|
||||||
|
* BUILD
|
||||||
|
* fix(deps): update `@playwright/test` to 1.60.0 (#38144)
|
||||||
|
* ci: add `tools/ci-tools.ts` for the PR labeler workflow (#37831)
|
||||||
|
* fix(build): swagger css import (#37801) (#37803)
|
||||||
|
|
||||||
|
## [1.26.2](https://github.com/go-gitea/gitea/releases/tag/1.26.2) - 2026-05-20
|
||||||
|
|
||||||
|
* SECURITY
|
||||||
|
* fix(permissions): Fix reading permission (#37769)
|
||||||
|
* fix(actions): make artifact signature payloads unambiguous (#37707)
|
||||||
|
* fix: Unify public-only token filtering in API queries and repo access checks (#37118)
|
||||||
|
* fix: Add missed token scope checking (#37735)
|
||||||
|
* fix(oauth): bind token exchanges to the original client request (#37704)
|
||||||
|
* fix(oauth): strengthen PKCE validation and refresh token replay protection (#37706)
|
||||||
|
* fix(web): enforce token scopes on raw, media, and attachment downloads (#37698)
|
||||||
|
* fix(security): enforce wiki git writes and LFS token access at request time (#37695)
|
||||||
|
* feat(api): encrypt AWS creds (#37679)
|
||||||
|
* fix(deps): update dependency mermaid to v11.15.0 [security], add e2e test
|
||||||
|
* fix(packages): Add label for private and internal package and fix composor package source permission check (#37610)
|
||||||
|
* fix(git): Fix smart http request scope bug (#37583)
|
||||||
|
* Fix basic auth bug (#37503)
|
||||||
|
* Fix allow maintainer edit permission check (#37479) (#37484)
|
||||||
|
* Fix URL sanitization to handle schemeless credentials (#37440) (#37471)
|
||||||
|
* Fix attachment Content-Security-Policy (#37455) (#37464)
|
||||||
|
* chore(deps): bump go-git/go-git/v5 to 5.19.0 (#37608)
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* fix(pull): handle empty pull request files view to allow reviews (#37783)
|
||||||
|
* fix(markup): make RenderString never fail (#37779)
|
||||||
|
* fix: add natural sort to sortTreeViewNodes (#37772)
|
||||||
|
* fix: package creation unique conflict (#37774)
|
||||||
|
* fix!: add DEFAULT_TITLE_SOURCE setting for pull request title default behavior (#37465)
|
||||||
|
* fix: Allow direct commits for unprotected files with push restrictions (#37657)
|
||||||
|
* fix(actions): wrong assumption that run id always >= job id (#37737)
|
||||||
|
* fix(auth): set User-Agent on avatar fetch and sync avatar on link-account register (#37564) (#37588)
|
||||||
|
* fix(actions): deadlock between PrepareRunAndInsert and UpdateTaskByState (#37692)
|
||||||
|
* fix(repo): /generate must sync the branch table for the new repo (#37693)
|
||||||
|
* build: Fix snap build (1.26)
|
||||||
|
* fix(actions): run TransferLogs on UpdateLog{Rows:[], NoMore:true} (#37631)
|
||||||
|
* fix show correct mergebase
|
||||||
|
* fix: make clone URL respect public URL detection setting (#37615)
|
||||||
|
* fix: "run as root" check (#37622)
|
||||||
|
* chore(deps): update dependency go to v1.26.3 (#37601)
|
||||||
|
* Compare dropdown fails when selecting branch with no common merge-base (#37470)
|
||||||
|
* fix: treat email addresses case-insensitively (#37600)
|
||||||
|
* fix(actions): fix blank lines after ::endgroup:: (#37597)
|
||||||
|
* fix(actions): report individual step status in workflow job API response (#37592)
|
||||||
|
* fix: Invalid UTF-8 commit messages in JSON API responses (#37542)
|
||||||
|
* fix: use consistent GetUser family functions (#37553)
|
||||||
|
* fix(api): return 409 message instead of empty JSON for wrong commit id (#37572)
|
||||||
|
* fix(actions): prevent panic when workflow contains null jobs (#37570)
|
||||||
|
* Make ServeSetHeaders default to download attachment if filename exists (#37552) (#37555)
|
||||||
|
* Fix(actions): validate workflow param to prevent 500 error (#37546) (#37554)
|
||||||
|
* Don't unblock run-level-concurrency-blocked runs in the resolver (#37461) (#37538)
|
||||||
|
* Fix(packages): use file names for generic web downloads (#37514) (#37520)
|
||||||
|
* Fix merge autodetect can't close other PRs but only the last one when multiple PRs are pushed at once (#37512) (#37516)
|
||||||
|
* Fix update branch protection order (#37508) (#37513)
|
||||||
|
* Fix mCaptcha broken after Vite migration (#37492) (#37509)
|
||||||
|
* Fix review submission from single-commit PR view (#37475) (#37485)
|
||||||
|
* Fix scheduled action panic with null event payload (#37459) (#37466)
|
||||||
|
* Make GetPossibleUserByID can handle deleted user (#37430) (#37431)
|
||||||
|
* Remove excessive quote from terraform instructions (#37424) (#37426)
|
||||||
|
* Fix color regressions, add `priority` color (#37417) (#37421)
|
||||||
|
|
||||||
|
* MISC
|
||||||
|
* Add CurrentURL template variable back (#37444) (#37449)
|
||||||
|
|
||||||
|
## [1.26.1](https://github.com/go-gitea/gitea/releases/tag/v1.26.1) - 2026-04-21
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Add event.schedule context for schedule actions task (#37320) (#37348)
|
||||||
|
* Fix an issue where changing an organization's visibility caused problems when users had forked its repositories. (#37324) (#37344)
|
||||||
|
* Use modern "git update-index --cacheinfo" syntax to support more file names (#37338) (#37343)
|
||||||
|
* Fix URL related escaping for oauth2 (#37334) (#37340)
|
||||||
|
* When the requested arch rpm is missing fall back to noarch (#37236) (#37339)
|
||||||
|
* Fix actions concurrency groups cross-branch leak (#37311) (#37331)
|
||||||
|
* Fix bug when accessing user badges (#37321) (#37329)
|
||||||
|
* Fix AppFullLink (#37325) (#37328)
|
||||||
|
* Fix container auth for public instance (#37290) (#37294)
|
||||||
|
* Enhance GetActionWorkflow to support fallback references (#37189) (#37283)
|
||||||
|
* Fix vite manifest update masking build errors (#37279) (#37310)
|
||||||
|
* Fix Mermaid diagrams failing when node labels contain line breaks (#37296) (#37299)
|
||||||
|
* Use TriggerEvent instead of Event in workflow runs API response for scheduled runs (#37288) #37360
|
||||||
|
* Add URL to Learn more about blocking a user. (#37355) #37367
|
||||||
|
* Fix button layout shift when collapsing file tree in editor (#37363) #37375
|
||||||
|
* Fix org team assignee/reviewer lookups for team member permissions (#37365) #37391
|
||||||
|
* Fix repo init README EOL (#37388) #37399
|
||||||
|
* Fix: dump with default zip type produces uncompressed zip (#37401)#37402
|
||||||
|
|
||||||
|
## [1.26.0](https://github.com/go-gitea/gitea/releases/tag/v1.26.0) - 2026-04-17
|
||||||
|
|
||||||
|
* BREAKING
|
||||||
|
* Correct swagger annotations for enums, status codes, and notification state (#37030)
|
||||||
|
* Remove GET API registration-token (#36801)
|
||||||
|
* Support Actions `concurrency` syntax (#32751)
|
||||||
|
* Make PUBLIC_URL_DETECTION default to "auto" (#36955)
|
||||||
|
* SECURITY
|
||||||
|
* Bound PageSize in `ListUnadoptedRepositories` (#36884)
|
||||||
|
* FEATURES
|
||||||
|
* Support Actions `concurrency` syntax (#32751)
|
||||||
|
* Add terraform state registry (#36710)
|
||||||
|
* Instance-wide (global) info banner and maintenance mode (#36571)
|
||||||
|
* Support rendering OpenAPI spec (#36449)
|
||||||
|
* Add keyboard shortcuts for repository file and code search (#36416)
|
||||||
|
* Add support for archive-upload rpc (#36391)
|
||||||
|
* Add ability to download subpath archive (#36371)
|
||||||
|
* Add workflow dependencies visualization (#26062) (#36248) & Restyle Workflow Graph (#36912)
|
||||||
|
* Automatic generation of release notes (#35977)
|
||||||
|
* Add "Go to file", "Delete Directory" to repo file list page (#35911)
|
||||||
|
* Introduce "config edit-ini" sub command to help maintaining INI config file (#35735)
|
||||||
|
* Add button to re-run failed jobs in Actions (#36924)
|
||||||
|
* Support actions and reusable workflows from private repos (#32562)
|
||||||
|
* Add summary to action runs view (#36883)
|
||||||
|
* Add user badges (#36752)
|
||||||
|
* Add configurable permissions for Actions automatic tokens (#36173)
|
||||||
|
* Add per-runner "Disable/Pause" (#36776)
|
||||||
|
* Feature non-zipped actions artifacts (action v7 / nodejs / npm v6.2.0) (#36786)
|
||||||
|
* PERFORMANCE
|
||||||
|
* WorkflowDispatch API optionally return runid (#36706)
|
||||||
|
* Add render cache for SVG icons (#36863)
|
||||||
|
* Load `mentionValues` asynchronously (#36739)
|
||||||
|
* Lazy-load some Vue components, fix heatmap chunk loading on every page (#36719)
|
||||||
|
* Load heatmap data asynchronously (#36622)
|
||||||
|
* Use prev/next pagination for user profile activities page to speed up (#36642)
|
||||||
|
* Refactor cat-file batch operations and support `--batch-command` approach (#35775)
|
||||||
|
* Use merge tree to detect conflicts when possible (#36400)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Implement logout redirection for reverse proxy auth setups (#36085) (#37171)
|
||||||
|
* Adds option to force update new branch in contents routes (#35592)
|
||||||
|
* Add viewer controller for mermaid (zoom, drag) (#36557)
|
||||||
|
* Add code editor setting dropdowns (#36534)
|
||||||
|
* Add `elk` layout support to mermaid (#36486)
|
||||||
|
* Add resolve/unresolve review comment API endpoints (#36441)
|
||||||
|
* Allow configuring default PR base branch (fixes #36412) (#36425)
|
||||||
|
* Add support for RPM Errata (updateinfo.xml) (#37125)
|
||||||
|
* Require additional user confirmation for making repo private (#36959)
|
||||||
|
* Add `actions.WORKFLOW_DIRS` setting (#36619)
|
||||||
|
* Avoid opening new tab when downloading actions logs (#36740)
|
||||||
|
* Implements OIDC RP-Initiated Logout (#36724)
|
||||||
|
* Show workflow link (#37070)
|
||||||
|
* Desaturate dark theme background colors (#37056)
|
||||||
|
* Refactor "org teams" page and help new users to "add member" to an org (#37051)
|
||||||
|
* Add webhook name field to improve webhook identification (#37025) (#37040)
|
||||||
|
* Make task list checkboxes clickable in the preview tab (#37010)
|
||||||
|
* Improve severity labels in Actions logs and tweak colors (#36993)
|
||||||
|
* Linkify URLs in Actions workflow logs (#36986)
|
||||||
|
* Allow text selection on checkbox labels (#36970)
|
||||||
|
* Support dark/light theme images in markdown (#36922)
|
||||||
|
* Enable native dark mode for swagger-ui (#36899)
|
||||||
|
* Rework checkbox styling, remove `input` border hover effect (#36870)
|
||||||
|
* Refactor storage content-type handling of ServeDirectURL (#36804)
|
||||||
|
* Use "Enable Gravatar" but not "Disable" (#36771)
|
||||||
|
* Use case-insensitive matching for Git error "Not a valid object name" (#36728)
|
||||||
|
* Add "Copy Source" to markup comment menu (#36726)
|
||||||
|
* Change image transparency grid to CSS (#36711)
|
||||||
|
* Add "Run" prefix for unnamed action steps (#36624)
|
||||||
|
* Persist actions log time display settings in `localStorage` (#36623)
|
||||||
|
* Use first commit title for multi-commit PRs and fix auto-focus title field (#36606)
|
||||||
|
* Improve BuildCaseInsensitiveLike with lowercase (#36598)
|
||||||
|
* Improve diff highlighting (#36583)
|
||||||
|
* Exclude cancelled runs from failure-only email notifications (#36569)
|
||||||
|
* Use full-file highlighting for diff sections (#36561)
|
||||||
|
* Color command/error logs in Actions log (#36538)
|
||||||
|
* Add paging headers (#36521)
|
||||||
|
* Improve timeline entries for WIP prefix changes in pull requests (#36518)
|
||||||
|
* Add FOLDER_ICON_THEME configuration option (#36496)
|
||||||
|
* Normalize guessed languages for code highlighting (#36450)
|
||||||
|
* Add chunked transfer encoding support for LFS uploads (#36380)
|
||||||
|
* Indicate when only optional checks failed (#36367)
|
||||||
|
* Add 'allow_maintainer_edit' API option for creating a pull request (#36283)
|
||||||
|
* Support closing keywords with URL references (#36221)
|
||||||
|
* Improve diff file headers (#36215)
|
||||||
|
* Fix and enhance comment editor monospace toggle (#36181)
|
||||||
|
* Add git.DIFF_RENAME_SIMILARITY_THRESHOLD option (#36164)
|
||||||
|
* Add matching pair insertion to markdown textarea (#36121)
|
||||||
|
* Add sorting/filtering to admin user search API endpoint (#36112)
|
||||||
|
* Allow action user have read permission in public repo like other user (#36095)
|
||||||
|
* Disable matchBrackets in monaco (#36089)
|
||||||
|
* Use GitHub-style commit message for squash merge (#35987)
|
||||||
|
* Make composer registry support tar.gz and tar.bz2 and fix bugs (#35958)
|
||||||
|
* Add GITEA_PR_INDEX env variable to githooks (#35938)
|
||||||
|
* Add proper error message if session provider can not be created (#35520)
|
||||||
|
* Add button to copy file name in PR files (#35509)
|
||||||
|
* Move `X_FRAME_OPTIONS` setting from `cors` to `security` section (#30256)
|
||||||
|
* Add placeholder content for empty content page (#37114)
|
||||||
|
* Add `DEFAULT_DELETE_BRANCH_AFTER_MERGE` setting (#36917)
|
||||||
|
* Redirect to the only OAuth2 provider when no other login methods and fix various problems (#36901)
|
||||||
|
* Add admin badge to navbar avatar (#36790)
|
||||||
|
* Add `never` option to `PUBLIC_URL_DETECTION` configuration (#36785)
|
||||||
|
* Add background and run count to actions list page (#36707)
|
||||||
|
* Add icon to buttons "Close with Comment", "Close Pull Request", "Close Issue" (#36654)
|
||||||
|
* Add support for in_progress event in workflow_run webhook (#36979)
|
||||||
|
* Report commit status for pull_request_review events (#36589)
|
||||||
|
* Render merged pull request title as such in dashboard feed (#36479)
|
||||||
|
* Feature to be able to filter project boards by milestones (#36321)
|
||||||
|
* Use user id in noreply emails (#36550)
|
||||||
|
* Enable pagination on GiteaDownloader.getIssueReactions() (#36549)
|
||||||
|
* Remove striped tables in UI (#36509)
|
||||||
|
* Improve control char rendering and escape button styling (#37094)
|
||||||
|
* Support legacy run/job index-based URLs and refactor migration 326 (#37008)
|
||||||
|
* Add date to "No Contributions" tooltip (#36190)
|
||||||
|
* Show edit page confirmation dialog on tree view file change (#36130)
|
||||||
|
* Mention proc-receive in text for dashboard.resync_all_hooks func (#35991)
|
||||||
|
* Reuse selectable style for wiki (#35990)
|
||||||
|
* Support blue yellow colorblind theme (#35910)
|
||||||
|
* Support selecting theme on the footer (#35741)
|
||||||
|
* Improve online runner check (#35722)
|
||||||
|
* Add quick approve button on PR page (#35678)
|
||||||
|
* Enable commenting on expanded lines in PR diffs (#35662)
|
||||||
|
* Print PR-Title into tooltip for actions (#35579)
|
||||||
|
* Use explicit, stronger defaults for newly generated repo signing keys for Debian (#36236)
|
||||||
|
* Improve the compare page (#36261)
|
||||||
|
* Unify repo names in system notices (#36491)
|
||||||
|
* Move package settings to package instead of being tied to version (#37026)
|
||||||
|
* Add Actions API rerun endpoints for runs and jobs (#36768)
|
||||||
|
* Add branch_count to repository API (#35351) (#36743)
|
||||||
|
* Add created_by filter to SearchIssues (#36670)
|
||||||
|
* Allow admins to rename non-local users (#35970)
|
||||||
|
* Support updating branch via API (#35951)
|
||||||
|
* Add an option to automatically verify SSH keys from LDAP (#35927)
|
||||||
|
* Make "update file" API can create a new file when SHA is not set (#35738)
|
||||||
|
* Update issue.go with labels documentation (labels content, not ids) (#35522)
|
||||||
|
* Expose content_version for optimistic locking on issue and PR edits (#37035)
|
||||||
|
* Pass ServeHeaderOptions by value instead of pointer, fine tune httplib tests (#36982)
|
||||||
|
* BUGFIXES
|
||||||
|
* Frontend iframe renderer framework: 3D models, OpenAPI (#37233) (#37273)
|
||||||
|
* Fix CODEOWNERS absolute path matching. (#37244) (#37264)
|
||||||
|
* Swift registry metadata: preserve more JSON fields and accept empty metadata (#37254) (#37261)
|
||||||
|
* Fix user ssh key exporting and tests (#37256) (#37258)
|
||||||
|
* Fix team member avatar size and add tooltip (#37253)
|
||||||
|
* Fix commit title rendering in action run and blame (#37243) (#37251)
|
||||||
|
* Fix corrupted JSON caused by goccy library (#37214) (#37220)
|
||||||
|
* Add test for "fetch redirect", add CSS value validation for external render (#37207) (#37216)
|
||||||
|
* Fix incorrect concurrency check (#37205) (#37215)
|
||||||
|
* Fix handle missing base branch in PR commits API (#37193) (#37203)
|
||||||
|
* Fix encoding for Matrix Webhooks (#37190) (#37201)
|
||||||
|
* Fix handle fork-only commits in compare API (#37185) (#37199)
|
||||||
|
* Indicate form field readonly via background, fix RunUser config (#37175, #37180) (#37178)
|
||||||
|
* Report structurally invalid workflows to users (#37116) (#37164)
|
||||||
|
* Fix API not persisting pull request unit config when has_pull_requests is not set (#36718)
|
||||||
|
* Rename CSS variables and improve colorblind themes (#36353)
|
||||||
|
* Hide `add-matcher` and `remove-matcher` from actions job logs (#36520)
|
||||||
|
* Prevent navigation keys from triggering actions during IME composition (#36540)
|
||||||
|
* Fix vertical alignment of `.commit-sign-badge` children (#36570)
|
||||||
|
* Fix duplicate startup warnings in admin panel (#36641)
|
||||||
|
* Fix CODEOWNERS review request attribution using comment metadata (#36348)
|
||||||
|
* Fix HTML tags appearing in wiki table of contents (#36284)
|
||||||
|
* Fix various bugs (#37096)
|
||||||
|
* Fix various legacy problems (#37092)
|
||||||
|
* Fix RPM Registry 404 when package name contains 'package' (#37087)
|
||||||
|
* Merge some standalone Vite entries into index.js (#37085)
|
||||||
|
* Fix various problems (#37077)
|
||||||
|
* Fix issue label deletion with Actions tokens (#37013)
|
||||||
|
* Hide delete branch or tag buttons in mirror or archived repositories. (#37006)
|
||||||
|
* Fix org contact email not clearable once set (#36975)
|
||||||
|
* Fix a bug when forking a repository in an organization (#36950)
|
||||||
|
* Preserve sort order of exclusive labels from template repo (#36931)
|
||||||
|
* Make container registry support Apple Container (basic auth) (#36920)
|
||||||
|
* Fix the wrong push commits in the pull request when force push (#36914)
|
||||||
|
* Add class "list-header-filters" to the div for projects (#36889)
|
||||||
|
* Fix dbfs error handling (#36844)
|
||||||
|
* Fix incorrect viewed files counter if reverted change was viewed (#36819)
|
||||||
|
* Refactor avatar package, support default avatar fallback (#36788)
|
||||||
|
* Fix README symlink resolution in subdirectories like .github (#36775)
|
||||||
|
* Fix CSS stacking context issue in actions log (#36749)
|
||||||
|
* Add gpg signing for merge rebase and update by rebase (#36701)
|
||||||
|
* Delete non-exist branch should return 404 (#36694)
|
||||||
|
* Fix `TestActionsCollaborativeOwner` (#36657)
|
||||||
|
* Fix multi-arch Docker build SIGILL by splitting frontend stage (#36646)
|
||||||
|
* Fix linguist-detectable attribute being ignored for configuration files (#36640)
|
||||||
|
* Fix state desync in ComboMarkdownEditor (#36625)
|
||||||
|
* Unify DEFAULT_SHOW_FULL_NAME output in templates and dropdown (#36597)
|
||||||
|
* Pull Request Pusher should be the author of the merge (#36581)
|
||||||
|
* Fix various version parsing problems (#36553)
|
||||||
|
* Fix highlight diff result (#36539)
|
||||||
|
* Fix mirror sync parser and fix mirror messages (#36504)
|
||||||
|
* Fix bug when list pull request commits (#36485)
|
||||||
|
* Fix various bugs (#36446)
|
||||||
|
* Fix issue filter menu layout (#36426)
|
||||||
|
* Restrict branch naming when new change matches with protection rules (#36405)
|
||||||
|
* Fix link/origin referrer and login redirect (#36279)
|
||||||
|
* Generate IDs for HTML headings without id attribute (#36233)
|
||||||
|
* Use a migration test instead of a wrong test which populated the meta test repositories and fix a migration bug (#36160)
|
||||||
|
* Fix issue close timeline icon (#36138)
|
||||||
|
* Fix diff blob excerpt expansion (#35922)
|
||||||
|
* Fix external render (#35727)
|
||||||
|
* Fix review request webhook bug (#35339) (#35723)
|
||||||
|
* Fix shutdown waitgroup panic (#35676)
|
||||||
|
* Cleanup ActionRun creation (#35624)
|
||||||
|
* Fix possible bug when migrating issues/pull requests (#33487)
|
||||||
|
* Various fixes (#36697)
|
||||||
|
* Apply notify/register mail flags during install load (#37120)
|
||||||
|
* Repair duration display for bad stopped timestamps (#37121)
|
||||||
|
* Fix(upgrade.sh): use HTTPS for GPG key import and restore SELinux context after upgrade (#36930)
|
||||||
|
* Fix various trivial problems (#36921)
|
||||||
|
* Fix various trivial problems (#36953)
|
||||||
|
* Fix NuGet package upload error handling (#37074)
|
||||||
|
* Fix CodeQL code scanning alerts (#36858)
|
||||||
|
* Refactor issue sidebar and fix various problems (#37045)
|
||||||
|
* Fix various problems (#37029)
|
||||||
|
* Fix relative-time RangeError (#37021)
|
||||||
|
* Fix chroma lexer mapping (#36629)
|
||||||
|
* Fix typos and grammar in English locale (#36751)
|
||||||
|
* Fix milestone/project text overflow in issue sidebar (#36741)
|
||||||
|
* Fix `no-content` message not rendering after comment edit (#36733)
|
||||||
|
* Fix theme loading in development (#36605)
|
||||||
|
* Fix workflow run jobs API returning null steps (#36603)
|
||||||
|
* Fix timeline event layout overflow with long content (#36595)
|
||||||
|
* Fix minor UI issues in runner edit page (#36590)
|
||||||
|
* Fix incorrect vendored detections (#36508)
|
||||||
|
* Fix editorconfig not respected in PR Conversation view (#36492)
|
||||||
|
* Don't create self-references in merged PRs (#36490)
|
||||||
|
* Fix potential incorrect runID in run status update (#36437)
|
||||||
|
* Fix file-tree ui error when adding files to repo without commits (#36312)
|
||||||
|
* Improve image captcha contrast for dark mode (#36265)
|
||||||
|
* Fix panic in blame view when a file has only a single commit (#36230)
|
||||||
|
* Fix spelling error in migrate-storage cmd utility (#36226)
|
||||||
|
* Fix code highlighting on blame page (#36157)
|
||||||
|
* Fix nilnil in onedev downloader (#36154)
|
||||||
|
* Fix actions lint (#36029)
|
||||||
|
* Fix oauth2 session gob register (#36017)
|
||||||
|
* Fix Arch repo pacman.conf snippet (#35825)
|
||||||
|
* Fix a number of `strictNullChecks`-related issues (#35795)
|
||||||
|
* Fix URLJoin, markup render link reoslving, sign-in/up/linkaccount page common data (#36861)
|
||||||
|
* Hide delete directory button for mirror or archive repository and disable the menu item if user have no permission (#36384)
|
||||||
|
* Update message severity colors, fix navbar double border (#37019)
|
||||||
|
* Inline and lazy-load EasyMDE CSS, fix border colors (#36714)
|
||||||
|
* Closed milestones with no issues now show as 100% completed (#36220)
|
||||||
|
* Add test for ExtendCommentTreePathLength migration and fix bugs (#35791)
|
||||||
|
* Only turn links to current instance into hash links (#36237)
|
||||||
|
* Fix typos in code comments: doesnt, dont, wont (#36890)
|
||||||
|
* REFACTOR
|
||||||
|
* Clean up and improve non-gitea js error filter (#37148) (#37155)
|
||||||
|
* Always show owner/repo name in compare page dropdowns (#37172) (#37200)
|
||||||
|
* Remove dead CSS rules (#37173) (#37177)
|
||||||
|
* Replace Monaco with CodeMirror (#36764)
|
||||||
|
* Replace CSRF cookie with `CrossOriginProtection` (#36183)
|
||||||
|
* Replace index with id in actions routes (#36842)
|
||||||
|
* Remove unnecessary function parameter (#35765)
|
||||||
|
* Move jobparser from act repository to Gitea (#36699)
|
||||||
|
* Refactor compare router param parse (#36105)
|
||||||
|
* Optimize 'refreshAccesses' to perform update without removing then adding (#35702)
|
||||||
|
* Clean up checkbox cursor styles (#37016)
|
||||||
|
* Remove undocumented support of signing key in the repository git configuration file (#36143)
|
||||||
|
* Switch `cmd/` to use constructor functions. (#36962)
|
||||||
|
* Use `relative-time` to render absolute dates (#36238)
|
||||||
|
* Some refactors about GetMergeBase (#36186)
|
||||||
|
* Some small refactors (#36163)
|
||||||
|
* Use gitRepo as parameter instead of repopath when invoking sign functions (#36162)
|
||||||
|
* Move blame to gitrepo (#36161)
|
||||||
|
* Move some functions to gitrepo package to reduce RepoPath reference directly (#36126)
|
||||||
|
* Use gitrepo's clone and push when possible (#36093)
|
||||||
|
* Remove mermaid margin workaround (#35732)
|
||||||
|
* Move some functions to gitrepo package (#35543)
|
||||||
|
* Move GetDiverging functions to gitrepo (#35524)
|
||||||
|
* Use global lock instead of status pool for cron lock (#35507)
|
||||||
|
* Use explicit mux instead of DefaultServeMux (#36276)
|
||||||
|
* Use gitrepo's push function (#36245)
|
||||||
|
* Pass request context to generateAdditionalHeadersForIssue (#36274)
|
||||||
|
* Move assign project when creating pull request to the same database transaction (#36244)
|
||||||
|
* Move catfile batch to a sub package of git module (#36232)
|
||||||
|
* Use gitrepo.Repository instead of wikipath (#35398)
|
||||||
|
* Use experimental go json v2 library (#35392)
|
||||||
|
* Refactor template render (#36438)
|
||||||
|
* Refactor GetRepoRawDiffForFile to avoid unnecessary pipe or goroutine (#36434)
|
||||||
|
* Refactor text utility classes to Tailwind CSS (#36703)
|
||||||
|
* Refactor git command stdio pipe (#36422)
|
||||||
|
* Refactor git command context & pipeline (#36406)
|
||||||
|
* Refactor git command stdio pipe (#36393)
|
||||||
|
* Remove unused functions (#36672)
|
||||||
|
* Refactor Actions Token Access (#35688)
|
||||||
|
* Move commit related functions to gitrepo package (#35600)
|
||||||
|
* Move archive function to repo_model and gitrepo (#35514)
|
||||||
|
* Move some functions to gitrepo package (#35503)
|
||||||
|
* Use git model to detect whether branch exist instead of gitrepo method (#35459)
|
||||||
|
* Some refactor for repo path (#36251)
|
||||||
|
* Extract helper functions from SearchIssues (#36158)
|
||||||
|
* Refactor merge conan and container auth preserve actions taskID (#36560)
|
||||||
|
* Refactor Nuget Auth to reuse Basic Auth Token Validation (#36558)
|
||||||
|
* Refactor ActionsTaskID (#36503)
|
||||||
|
* Refactor auth middleware (#36848)
|
||||||
|
* Refactor code render and render control chars (#37078)
|
||||||
|
* Clean up AppURL, remove legacy origin-url webcomponent (#37090)
|
||||||
|
* Remove `util.URLJoin` and replace all callers with direct path concatenation (#36867)
|
||||||
|
* Replace legacy tw-flex utility classes with flex-text-block/inline (#36778)
|
||||||
|
* Mark unused&immature activitypub as "not implemented" (#36789)
|
||||||
|
* TESTING
|
||||||
|
* Add e2e tests for server push events (#36879)
|
||||||
|
* Rework e2e tests (#36634)
|
||||||
|
* Add e2e reaction test, improve accessibility, enable parallel testing (#37081)
|
||||||
|
* Increase e2e test timeouts on CI to fix flaky tests (#37053)
|
||||||
|
* BUILD
|
||||||
|
* Upgrade go-git to v5.18.0 (#37269)
|
||||||
|
* Replace rollup-plugin-license with rolldown-license-plugin (#37130) (#37158)
|
||||||
|
* Bump min go version to 1.26.2 (#37139) (#37143)
|
||||||
|
* Convert locale files from ini to json format (#35489)
|
||||||
|
* Bump golangci-lint to 2.7.2, enable modernize stringsbuilder (#36180)
|
||||||
|
* Port away from `flake-utils` (#35675)
|
||||||
|
* Remove nolint (#36252)
|
||||||
|
* Update the Unlicense copy to latest version (#36636)
|
||||||
|
* Update to go 1.26.0 and golangci-lint 2.9.0 (#36588)
|
||||||
|
* Replace `google/go-licenses` with custom generation (#36575)
|
||||||
|
* Update go dependencies (#36548)
|
||||||
|
* Bump appleboy/git-push-action from 1.0.0 to 1.2.0 (#36306)
|
||||||
|
* Remove fomantic form module (#36222)
|
||||||
|
* Bump setup-node to v6, re-enable cache (#36207)
|
||||||
|
* Bump crowdin/github-action from 1 to 2 (#36204)
|
||||||
|
* Revert "Bump alpine to 3.23 (#36185)" (#36202)
|
||||||
|
* Update chroma to v2.21.1 (#36201)
|
||||||
|
* Bump astral-sh/setup-uv from 6 to 7 (#36198)
|
||||||
|
* Bump docker/build-push-action from 5 to 6 (#36197)
|
||||||
|
* Bump aws-actions/configure-aws-credentials from 4 to 5 (#36196)
|
||||||
|
* Bump dev-hanz-ops/install-gh-cli-action from 0.1.0 to 0.2.1 (#36195)
|
||||||
|
* Add JSON linting (#36192)
|
||||||
|
* Enable dependabot for actions (#36191)
|
||||||
|
* Bump alpine to 3.23 (#36185)
|
||||||
|
* Update chroma to v2.21.0 (#36171)
|
||||||
|
* Update JS deps and eslint enhancements (#36147)
|
||||||
|
* Update JS deps (#36091)
|
||||||
|
* update golangci-lint to v2.7.0 (#36079)
|
||||||
|
* Update JS deps, fix deprecations (#36040)
|
||||||
|
* Update JS deps (#35978)
|
||||||
|
* Add toolchain directive to go.mod (#35901)
|
||||||
|
* Move `gitea-vet` to use `go tool` (#35878)
|
||||||
|
* Update to go 1.25.4 (#35877)
|
||||||
|
* Enable TypeScript `strictNullChecks` (#35843)
|
||||||
|
* Enable `vue/require-typed-ref` eslint rule (#35764)
|
||||||
|
* Update JS dependencies (#35759)
|
||||||
|
* Move `codeformat` folder to tools (#35758)
|
||||||
|
* Update dependencies (#35733)
|
||||||
|
* Bump happy-dom from 20.0.0 to 20.0.2 (#35677)
|
||||||
|
* Bump setup-go to v6 (#35660)
|
||||||
|
* Update JS deps, misc tweaks (#35643)
|
||||||
|
* Bump happy-dom from 19.0.2 to 20.0.0 (#35625)
|
||||||
|
* Use bundled version of spectral (#35573)
|
||||||
|
* Update JS and PY deps (#35565)
|
||||||
|
* Bump github.com/wneessen/go-mail from 0.6.2 to 0.7.1 (#35557)
|
||||||
|
* Migrate from webpack to vite (#37002)
|
||||||
|
* Update JS dependencies and misc tweaks (#37064)
|
||||||
|
* Update to eslint 10 (#36925)
|
||||||
|
* Optimize Docker build with dependency layer caching (#36864)
|
||||||
|
* Update JS deps (#36850)
|
||||||
|
* Update tool dependencies and fix new lint issues (#36702)
|
||||||
|
* Remove redundant linter rules (#36658)
|
||||||
|
* Move Fomantic dropdown CSS to custom module (#36530)
|
||||||
|
* Remove and forbid `@ts-expect-error` (#36513)
|
||||||
|
* Refactor git command stderr handling (#36402)
|
||||||
|
* Enable gocheckcompilerdirectives linter (#36156)
|
||||||
|
* Replace `lint-go-gopls` with additional `govet` linters (#36028)
|
||||||
|
* Update golangci-lint to v2.6.0 (#35801)
|
||||||
|
* Misc tool tweaks (#35734)
|
||||||
|
* Add cache to container build (#35697)
|
||||||
|
* Upgrade vite (#37126)
|
||||||
|
* Update `setup-uv` to v8.0.0 (#37101)
|
||||||
|
* Upgrade `go-git` to v5.17.2 and related dependencies (#37060)
|
||||||
|
* Raise minimum Node.js version to 22.18.0 (#37058)
|
||||||
|
* Upgrade `golang.org/x/image` to v0.38.0 (#37054)
|
||||||
|
* Update minimum go version to 1.26.1, golangci-lint to 2.11.2, fix test style (#36876)
|
||||||
|
* Enable eslint concurrency (#36878)
|
||||||
|
* Vendor relative-time-element as local web component (#36853)
|
||||||
|
* Update material-icon-theme v5.32.0 (#36832)
|
||||||
|
* Update Go dependencies (#36781)
|
||||||
|
* Upgrade minimatch (#36760)
|
||||||
|
* Remove i18n backport tool at the moment because of translation format changed (#36643)
|
||||||
|
* Update emoji data for Unicode 16 (#36596)
|
||||||
|
* Update JS dependencies, adjust webpack config, misc fixes (#36431)
|
||||||
|
* Update material-icon-theme to v5.31.0 (#36427)
|
||||||
|
* Update JS and PY deps (#36383)
|
||||||
|
* Bump alpine to 3.23, add platforms to `docker-dryrun` (#36379)
|
||||||
|
* Update JS deps (#36354)
|
||||||
|
* Update goldmark to v1.7.16 (#36343)
|
||||||
|
* Update chroma to v2.22.0 (#36342)
|
||||||
|
* DOCS
|
||||||
|
* Update AI Contribution Policy (#37022)
|
||||||
|
* Update AGENTS.md with additional guidelines (#37018)
|
||||||
|
* Add missing cron tasks to example ini (#37012)
|
||||||
|
* Add AI Contribution Policy to CONTRIBUTING.md (#36651)
|
||||||
|
* Minor punctuation improvement in CONTRIBUTING.md (#36291)
|
||||||
|
* Add documentation for markdown anchor post-processing (#36443)
|
||||||
|
* MISC
|
||||||
|
* Correct spelling (#36783)
|
||||||
|
* Update Nix flake (#37110)
|
||||||
|
* Update Nix flake (#37024)
|
||||||
|
* Add valid github scopes (#36977)
|
||||||
|
* Update Nix flake (#36943)
|
||||||
|
* Update Nix flake (#36902)
|
||||||
|
* Update Nix flake (#36857)
|
||||||
|
* Update Nix flake (#36787)
|
||||||
|
|
||||||
|
## [1.25.5](https://github.com/go-gitea/gitea/releases/tag/v1.25.5) - 2026-03-10
|
||||||
|
|
||||||
|
* SECURITY
|
||||||
|
* Toolchain Update to Go 1.25.6 (#36480) (#36487)
|
||||||
|
* Adjust the toolchain version (#36537) (#36542)
|
||||||
|
* Update toolchain to 1.25.8 for v1.25 (#36888)
|
||||||
|
* Prevent redirect bypasses via backslash-encoded paths (#36660) (#36716)
|
||||||
|
* Fix get release draft permission check (#36659) (#36715)
|
||||||
|
* Fix a bug user could change another user's primary email (#36586) (#36607)
|
||||||
|
* Fix OAuth2 authorization code expiry and reuse handling (#36797) (#36851)
|
||||||
|
* Add validation constraints for repository creation fields (#36671) (#36757)
|
||||||
|
* Fix bug to check whether user can update pull request branch or rebase branch (#36465) (#36838)
|
||||||
|
* Add migration http transport for push/sync mirror lfs (#36665) (#36691)
|
||||||
|
* Fix track time list permission check (#36662) (#36744)
|
||||||
|
* Fix track time issue id (#36664) (#36689)
|
||||||
|
* Fix path resolving (#36734) (#36746)
|
||||||
|
* Fix dump release asset bug (#36799) (#36839)
|
||||||
|
* Fix org permission API visibility checks for hidden members and private orgs (#36798) (#36841)
|
||||||
|
* Fix forwarded proto handling for public URL detection (#36810) (#36836)
|
||||||
|
* Add a git grep search timeout (#36809) (#36835)
|
||||||
|
* Fix oauth2 s256 (#36462) (#36477)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Make `security-check` informational only (#36681) (#36852)
|
||||||
|
* Upgrade to github.com/cloudflare/circl 1.6.3, svgo 4.0.1, markdownlint-cli 0.48.0 (#36840)
|
||||||
|
* Add some validation on values provided to USER_DISABLED_FEATURES and EXTERNAL_USER_DISABLED_FEATURES (#36688) (#36692)
|
||||||
|
* Upgrade gogit to 5.16.5 (#36687)
|
||||||
|
* Add wrap to runner label list (#36565) (#36574)
|
||||||
|
* Add dnf5 command for Fedora in RPM package instructions (#36527) (#36572)
|
||||||
|
* Allow scroll propagation outside code editor (#36502) (#36510)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix non-admins unable to automerge PRs from forks (#36833) (#36843)
|
||||||
|
* Fix bug when pushing mirror with wiki (#36795) (#36807)
|
||||||
|
* Fix artifacts v4 backend upload problems (#36805) (#36834)
|
||||||
|
* Fix CRAN package version validation to allow more than 4 version components (#36813) (#36821)
|
||||||
|
* Fix force push time-line commit comments of pull request (#36653) (#36717)
|
||||||
|
* Fix SVG height calculation in diff viewer (#36748) (#36750)
|
||||||
|
* Fix push time bug (#36693) (#36713)
|
||||||
|
* Fix bug the protected branch rule name is conflicted with renamed branch name (#36650) (#36661)
|
||||||
|
* Fix bug when do LFS GC (#36500) (#36608)
|
||||||
|
* Fix focus lost bugs in the Monaco editor (#36609)
|
||||||
|
* Reprocess htmx content after loading more files (#36568) (#36577)
|
||||||
|
* Fix assignee sidebar links and empty placeholder (#36559) (#36563)
|
||||||
|
* Fix issues filter dropdown showing empty label scope section (#36535) (#36544)
|
||||||
|
* Fix various mermaid bugs (#36547) (#36552)
|
||||||
|
* Fix data race when uploading container blobs concurrently (#36524) (#36526)
|
||||||
|
* Correct spacing between username and bot label (#36473) (#36484)
|
||||||
|
|
||||||
## [1.25.4](https://github.com/go-gitea/gitea/releases/tag/v1.25.4) - 2026-01-15
|
## [1.25.4](https://github.com/go-gitea/gitea/releases/tag/v1.25.4) - 2026-01-15
|
||||||
|
|
||||||
* SECURITY
|
* SECURITY
|
||||||
|
|||||||
+26
-2
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
- [Contribution Guidelines](#contribution-guidelines)
|
- [Contribution Guidelines](#contribution-guidelines)
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
|
- [AI Contribution Policy](#ai-contribution-policy)
|
||||||
- [Issues](#issues)
|
- [Issues](#issues)
|
||||||
- [How to report issues](#how-to-report-issues)
|
- [How to report issues](#how-to-report-issues)
|
||||||
- [Types of issues](#types-of-issues)
|
- [Types of issues](#types-of-issues)
|
||||||
@@ -67,6 +68,21 @@ Sensitive security-related issues should be reported to [security@gitea.io](mail
|
|||||||
|
|
||||||
For configuring IDEs for Gitea development, see the [contributed IDE configurations](contrib/ide/).
|
For configuring IDEs for Gitea development, see the [contributed IDE configurations](contrib/ide/).
|
||||||
|
|
||||||
|
## AI Contribution Policy
|
||||||
|
|
||||||
|
Contributions made with the assistance of AI tools are welcome, but contributors must use them responsibly and disclose that use clearly.
|
||||||
|
|
||||||
|
1. Review AI-generated code closely before marking a pull request ready for review.
|
||||||
|
2. Manually test the changes and add appropriate automated tests where feasible.
|
||||||
|
3. Only use AI to assist in contributions that you understand well enough to explain, defend, and revise yourself during review.
|
||||||
|
4. Disclose AI-assisted content clearly.
|
||||||
|
5. Do not use AI to reply to questions about your issue or pull request. The questions are for you, not an AI model.
|
||||||
|
6. AI may be used to help draft issues and pull requests, but contributors remain responsible for the accuracy, completeness, and intent of what they submit.
|
||||||
|
|
||||||
|
Maintainers reserve the right to close pull requests and issues that do not disclose AI assistance, that appear to be low-quality AI-generated content, or where the contributor cannot explain or defend the proposed changes themselves.
|
||||||
|
|
||||||
|
We welcome new contributors, but cannot sustain the effort of supporting contributors who primarily defer to AI rather than engaging substantively with the review process.
|
||||||
|
|
||||||
## Issues
|
## Issues
|
||||||
|
|
||||||
### How to report issues
|
### How to report issues
|
||||||
@@ -178,12 +194,20 @@ Here's how to run the test suite:
|
|||||||
| :------------------------------------------ | :------------------------------------------------------- | ------------------------------------------- |
|
| :------------------------------------------ | :------------------------------------------------------- | ------------------------------------------- |
|
||||||
|``make test[\#SpecificTestName]`` | run unit test(s) | |
|
|``make test[\#SpecificTestName]`` | run unit test(s) | |
|
||||||
|``make test-sqlite[\#SpecificTestName]`` | run [integration](tests/integration) test(s) for SQLite | [More details](tests/integration/README.md) |
|
|``make test-sqlite[\#SpecificTestName]`` | run [integration](tests/integration) test(s) for SQLite | [More details](tests/integration/README.md) |
|
||||||
|``make test-e2e-sqlite[\#SpecificTestName]`` | run [end-to-end](tests/e2e) test(s) for SQLite | [More details](tests/e2e/README.md) |
|
|``make test-e2e`` | run [end-to-end](tests/e2e) test(s) using Playwright | |
|
||||||
|
|
||||||
|
- E2E test environment variables
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
| :-------------------------------- | :---------------------------------------------------------- |
|
||||||
|
| ``GITEA_TEST_E2E_DEBUG`` | When set, show Gitea server output |
|
||||||
|
| ``GITEA_TEST_E2E_FLAGS`` | Additional flags passed to Playwright, for example ``--ui`` |
|
||||||
|
| ``GITEA_TEST_E2E_TIMEOUT_FACTOR`` | Timeout multiplier (default: 3 on CI, 1 locally) |
|
||||||
|
|
||||||
## Translation
|
## Translation
|
||||||
|
|
||||||
All translation work happens on [Crowdin](https://translate.gitea.com).
|
All translation work happens on [Crowdin](https://translate.gitea.com).
|
||||||
The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.ini).
|
The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.json).
|
||||||
It is synced regularly with Crowdin. \
|
It is synced regularly with Crowdin. \
|
||||||
Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \
|
Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \
|
||||||
Once a language has reached a **satisfactory percentage** of translated keys (~25%), it will be synced back into this repo and included in the next released version.
|
Once a language has reached a **satisfactory percentage** of translated keys (~25%), it will be synced back into this repo and included in the next released version.
|
||||||
|
|||||||
+19
-14
@@ -1,8 +1,15 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
# Build stage
|
# Build frontend on the native platform to avoid QEMU-related issues with nodejs ecosystem
|
||||||
FROM docker.io/library/golang:1.25-alpine3.22 AS build-env
|
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26-alpine3.23 AS frontend-build
|
||||||
|
RUN apk --no-cache add build-base git nodejs pnpm
|
||||||
|
WORKDIR /src
|
||||||
|
COPY package.json pnpm-lock.yaml .npmrc ./
|
||||||
|
RUN --mount=type=cache,target=/root/.local/share/pnpm/store pnpm install --frozen-lockfile
|
||||||
|
COPY --exclude=.git/ . .
|
||||||
|
RUN make frontend
|
||||||
|
|
||||||
ARG GOPROXY=direct
|
# Build backend for each target platform
|
||||||
|
FROM docker.io/library/golang:1.26-alpine3.23 AS build-env
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
ARG GITEA_VERSION
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
@@ -12,22 +19,20 @@ ARG CGO_EXTRA_CFLAGS
|
|||||||
# Build deps
|
# Build deps
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add \
|
||||||
build-base \
|
build-base \
|
||||||
git \
|
git
|
||||||
nodejs \
|
|
||||||
pnpm
|
|
||||||
|
|
||||||
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.
|
COPY go.mod go.sum ./
|
||||||
# ".git" directory will be mounted later separately for getting version data.
|
RUN go mod download
|
||||||
# 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.
|
# Use COPY instead of bind mount as read-only one breaks makefile state tracking and read-write one needs binary to be moved as it's discarded.
|
||||||
|
# ".git" directory is mounted separately later only for version data extraction.
|
||||||
COPY --exclude=.git/ . .
|
COPY --exclude=.git/ . .
|
||||||
|
COPY --from=frontend-build /src/public/assets public/assets
|
||||||
|
|
||||||
# Build gitea, .git mount is required for version data
|
# Build gitea, .git mount is required for version data
|
||||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
RUN --mount=type=cache,target="/root/.cache/go-build" \
|
||||||
--mount=type=cache,target="/root/.cache/go-build" \
|
|
||||||
--mount=type=cache,target=/root/.local/share/pnpm/store \
|
|
||||||
--mount=type=bind,source=".git/",target=".git/" \
|
--mount=type=bind,source=".git/",target=".git/" \
|
||||||
make
|
make backend
|
||||||
|
|
||||||
COPY docker/root /tmp/local
|
COPY docker/root /tmp/local
|
||||||
|
|
||||||
@@ -39,7 +44,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
|
|||||||
/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
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.22 AS gitea
|
FROM docker.io/library/alpine:3.23 AS gitea
|
||||||
|
|
||||||
EXPOSE 22 3000
|
EXPOSE 22 3000
|
||||||
|
|
||||||
|
|||||||
+17
-11
@@ -1,8 +1,15 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
# Build stage
|
# Build frontend on the native platform to avoid QEMU-related issues with nodejs ecosystem
|
||||||
FROM docker.io/library/golang:1.25-alpine3.22 AS build-env
|
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26-alpine3.23 AS frontend-build
|
||||||
|
RUN apk --no-cache add build-base git nodejs pnpm
|
||||||
|
WORKDIR /src
|
||||||
|
COPY package.json pnpm-lock.yaml .npmrc ./
|
||||||
|
RUN --mount=type=cache,target=/root/.local/share/pnpm/store pnpm install --frozen-lockfile
|
||||||
|
COPY --exclude=.git/ . .
|
||||||
|
RUN make frontend
|
||||||
|
|
||||||
ARG GOPROXY=direct
|
# Build backend for each target platform
|
||||||
|
FROM docker.io/library/golang:1.26-alpine3.23 AS build-env
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
ARG GITEA_VERSION
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
@@ -12,20 +19,19 @@ ARG CGO_EXTRA_CFLAGS
|
|||||||
# Build deps
|
# Build deps
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add \
|
||||||
build-base \
|
build-base \
|
||||||
git \
|
git
|
||||||
nodejs \
|
|
||||||
pnpm
|
|
||||||
|
|
||||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
# See the comments in Dockerfile
|
# See the comments in Dockerfile
|
||||||
COPY --exclude=.git/ . .
|
COPY --exclude=.git/ . .
|
||||||
|
COPY --from=frontend-build /src/public/assets public/assets
|
||||||
|
|
||||||
# Build gitea, .git mount is required for version data
|
# Build gitea, .git mount is required for version data
|
||||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
RUN --mount=type=cache,target="/root/.cache/go-build" \
|
||||||
--mount=type=cache,target="/root/.cache/go-build" \
|
|
||||||
--mount=type=cache,target=/root/.local/share/pnpm/store \
|
|
||||||
--mount=type=bind,source=".git/",target=".git/" \
|
--mount=type=bind,source=".git/",target=".git/" \
|
||||||
make
|
make backend
|
||||||
|
|
||||||
COPY docker/rootless /tmp/local
|
COPY docker/rootless /tmp/local
|
||||||
|
|
||||||
@@ -33,7 +39,7 @@ COPY docker/rootless /tmp/local
|
|||||||
RUN chmod 755 /tmp/local/usr/local/bin/* \
|
RUN chmod 755 /tmp/local/usr/local/bin/* \
|
||||||
/go/src/code.gitea.io/gitea/gitea
|
/go/src/code.gitea.io/gitea/gitea
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.22 AS gitea-rootless
|
FROM docker.io/library/alpine:3.23 AS gitea-rootless
|
||||||
|
|
||||||
EXPOSE 2222 3000
|
EXPOSE 2222 3000
|
||||||
|
|
||||||
|
|||||||
@@ -64,3 +64,4 @@ 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)
|
Tobias Balle-Petersen <tobiasbp@gmail.com> (@tobiasbp)
|
||||||
TheFox <thefox0x7@gmail.com> (@TheFox0x7)
|
TheFox <thefox0x7@gmail.com> (@TheFox0x7)
|
||||||
|
Nicolas <bircni@icloud.com> (@bircni)
|
||||||
@@ -1,22 +1,5 @@
|
|||||||
ifeq ($(USE_REPO_TEST_DIR),1)
|
|
||||||
|
|
||||||
# This rule replaces the whole Makefile when we're trying to use /tmp repository temporary files
|
|
||||||
location = $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
|
|
||||||
self := $(location)
|
|
||||||
|
|
||||||
%:
|
|
||||||
@tmpdir=`mktemp --tmpdir -d` ; \
|
|
||||||
echo Using temporary directory $$tmpdir for test repositories ; \
|
|
||||||
USE_REPO_TEST_DIR= $(MAKE) -f $(self) --no-print-directory REPO_TEST_DIR=$$tmpdir/ $@ ; \
|
|
||||||
STATUS=$$? ; rm -r "$$tmpdir" ; exit $$STATUS
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
# This is the "normal" part of the Makefile
|
|
||||||
|
|
||||||
DIST := dist
|
DIST := dist
|
||||||
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
||||||
IMPORT := code.gitea.io/gitea
|
|
||||||
|
|
||||||
# By default use go's 1.25 experimental json v2 library when building
|
# By default use go's 1.25 experimental json v2 library when building
|
||||||
# TODO: remove when no longer experimental
|
# TODO: remove when no longer experimental
|
||||||
@@ -32,14 +15,13 @@ XGO_VERSION := go-1.25.x
|
|||||||
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
||||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3
|
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3
|
||||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.2
|
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.2
|
||||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.8.0
|
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4
|
||||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15
|
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15
|
||||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.7.0
|
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.8.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.33.1
|
||||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
|
|
||||||
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.7.10
|
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.11
|
||||||
|
|
||||||
DOCKER_IMAGE ?= gitea/gitea
|
DOCKER_IMAGE ?= gitea/gitea
|
||||||
DOCKER_TAG ?= latest
|
DOCKER_TAG ?= latest
|
||||||
@@ -71,9 +53,11 @@ endif
|
|||||||
ifeq ($(IS_WINDOWS),yes)
|
ifeq ($(IS_WINDOWS),yes)
|
||||||
GOFLAGS := -v -buildmode=exe
|
GOFLAGS := -v -buildmode=exe
|
||||||
EXECUTABLE ?= gitea.exe
|
EXECUTABLE ?= gitea.exe
|
||||||
|
EXECUTABLE_E2E ?= gitea-e2e.exe
|
||||||
else
|
else
|
||||||
GOFLAGS := -v
|
GOFLAGS := -v
|
||||||
EXECUTABLE ?= gitea
|
EXECUTABLE ?= gitea
|
||||||
|
EXECUTABLE_E2E ?= gitea-e2e
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu)
|
ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu)
|
||||||
@@ -84,7 +68,6 @@ endif
|
|||||||
|
|
||||||
EXTRA_GOFLAGS ?=
|
EXTRA_GOFLAGS ?=
|
||||||
|
|
||||||
MAKE_VERSION := $(shell "$(MAKE)" -v | cat | head -n 1)
|
|
||||||
MAKE_EVIDENCE_DIR := .make_evidence
|
MAKE_EVIDENCE_DIR := .make_evidence
|
||||||
|
|
||||||
GOTESTFLAGS ?=
|
GOTESTFLAGS ?=
|
||||||
@@ -98,15 +81,6 @@ STORED_VERSION_FILE := VERSION
|
|||||||
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- | sed 's/-.*//' | 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)
|
||||||
@@ -130,17 +104,18 @@ ifeq ($(VERSION),main)
|
|||||||
VERSION := main-nightly
|
VERSION := main-nightly
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
LDFLAGS := $(LDFLAGS) -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
||||||
|
|
||||||
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/riscv64
|
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/riscv64
|
||||||
|
|
||||||
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
|
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||||
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)
|
FRONTEND_SOURCES := $(shell find web_src/js web_src/css -type f)
|
||||||
WEBPACK_CONFIGS := webpack.config.ts tailwind.config.ts
|
FRONTEND_CONFIGS := vite.config.ts tailwind.config.ts
|
||||||
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
|
FRONTEND_DEST := public/assets/.vite/manifest.json
|
||||||
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
|
FRONTEND_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts public/assets/.vite
|
||||||
|
FRONTEND_DEV_LOG_LEVEL ?= warn
|
||||||
|
|
||||||
BINDATA_DEST_WILDCARD := modules/migration/bindata.* modules/public/bindata.* modules/options/bindata.* modules/templates/bindata.*
|
BINDATA_DEST_WILDCARD := modules/migration/bindata.* modules/public/bindata.* modules/options/bindata.* modules/templates/bindata.*
|
||||||
|
|
||||||
@@ -150,7 +125,6 @@ SVG_DEST_DIR := public/assets/img/svg
|
|||||||
|
|
||||||
AIR_TMP_DIR := .air
|
AIR_TMP_DIR := .air
|
||||||
|
|
||||||
GO_LICENSE_TMP_DIR := .go-licenses
|
|
||||||
GO_LICENSE_FILE := assets/go-licenses.json
|
GO_LICENSE_FILE := assets/go-licenses.json
|
||||||
|
|
||||||
TAGS ?=
|
TAGS ?=
|
||||||
@@ -159,7 +133,7 @@ TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
|||||||
|
|
||||||
TEST_TAGS ?= $(TAGS_SPLIT) sqlite sqlite_unlock_notify
|
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_DIRS := build cmd models modules routers services tests tools
|
GO_DIRS := build cmd models modules routers services tests tools
|
||||||
WEB_DIRS := web_src/js web_src/css
|
WEB_DIRS := web_src/js web_src/css
|
||||||
@@ -173,10 +147,7 @@ GO_SOURCES := $(wildcard *.go)
|
|||||||
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go")
|
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go")
|
||||||
GO_SOURCES += $(GENERATED_GO_DEST)
|
GO_SOURCES += $(GENERATED_GO_DEST)
|
||||||
|
|
||||||
# Force installation of playwright dependencies by setting this flag
|
ESLINT_CONCURRENCY ?= 2
|
||||||
ifdef DEPS_PLAYWRIGHT
|
|
||||||
PLAYWRIGHT_FLAGS += --with-deps
|
|
||||||
endif
|
|
||||||
|
|
||||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
||||||
SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json
|
SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json
|
||||||
@@ -207,7 +178,7 @@ all: build
|
|||||||
.PHONY: help
|
.PHONY: help
|
||||||
help: Makefile ## print Makefile help information.
|
help: Makefile ## print Makefile help information.
|
||||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m[TARGETS] default target: build\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 }' Makefile #$(MAKEFILE_LIST)
|
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m[TARGETS] default target: build\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 }' Makefile #$(MAKEFILE_LIST)
|
||||||
@printf " \033[36m%-46s\033[0m %s\n" "test-e2e[#TestSpecificName]" "test end to end using playwright"
|
@printf " \033[36m%-46s\033[0m %s\n" "test-e2e" "test end to end using playwright"
|
||||||
@printf " \033[36m%-46s\033[0m %s\n" "test[#TestSpecificName]" "run unit test"
|
@printf " \033[36m%-46s\033[0m %s\n" "test[#TestSpecificName]" "run unit test"
|
||||||
@printf " \033[36m%-46s\033[0m %s\n" "test-sqlite[#TestSpecificName]" "run integration test for sqlite"
|
@printf " \033[36m%-46s\033[0m %s\n" "test-sqlite[#TestSpecificName]" "run integration test for sqlite"
|
||||||
|
|
||||||
@@ -220,16 +191,15 @@ git-check:
|
|||||||
|
|
||||||
.PHONY: clean-all
|
.PHONY: clean-all
|
||||||
clean-all: clean ## delete backend, frontend and integration files
|
clean-all: clean ## delete backend, frontend and integration files
|
||||||
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
|
rm -rf $(FRONTEND_DEST_ENTRIES) node_modules
|
||||||
|
|
||||||
.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) $(EXECUTABLE_E2E) $(DIST) $(BINDATA_DEST_WILDCARD) \
|
||||||
integrations*.test \
|
integrations*.test \
|
||||||
e2e*.test \
|
|
||||||
tests/integration/gitea-integration-* \
|
tests/integration/gitea-integration-* \
|
||||||
tests/integration/indexers-* \
|
tests/integration/indexers-* \
|
||||||
tests/mysql.ini tests/pgsql.ini tests/mssql.ini man/ \
|
tests/sqlite.ini tests/mysql.ini tests/pgsql.ini tests/mssql.ini man/ \
|
||||||
tests/e2e/gitea-e2e-*/ \
|
tests/e2e/gitea-e2e-*/ \
|
||||||
tests/e2e/indexers-*/ \
|
tests/e2e/indexers-*/ \
|
||||||
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
|
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
|
||||||
@@ -314,44 +284,42 @@ lint-backend: lint-go lint-go-gitea-vet lint-editorconfig ## lint backend files
|
|||||||
lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
|
lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
|
||||||
|
|
||||||
.PHONY: lint-js
|
.PHONY: lint-js
|
||||||
lint-js: node_modules ## lint js files
|
lint-js: node_modules ## lint js and ts files
|
||||||
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 $(ESLINT_FILES)
|
pnpm exec eslint --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) $(ESLINT_FILES)
|
||||||
$(NODE_VARS) pnpm exec vue-tsc
|
pnpm exec vue-tsc
|
||||||
$(NODE_VARS) pnpm exec knip --no-progress --cache
|
|
||||||
|
|
||||||
.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 and ts files and fix issues
|
||||||
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 $(ESLINT_FILES) --fix
|
pnpm exec eslint --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) $(ESLINT_FILES) --fix
|
||||||
$(NODE_VARS) pnpm exec vue-tsc
|
pnpm exec vue-tsc
|
||||||
$(NODE_VARS) pnpm exec knip --no-progress --cache --fix
|
|
||||||
|
|
||||||
.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)
|
pnpm exec 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
|
pnpm exec 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)
|
pnpm exec 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
|
pnpm exec markdownlint *.md
|
||||||
|
|
||||||
.PHONY: lint-md-fix
|
.PHONY: lint-md-fix
|
||||||
lint-md-fix: node_modules ## lint markdown files and fix issues
|
lint-md-fix: node_modules ## lint markdown files and fix issues
|
||||||
$(NODE_VARS) pnpm exec markdownlint --fix *.md
|
pnpm exec markdownlint --fix *.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)
|
@git ls-files $(SPELLCHECK_FILES) | xargs go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -error
|
||||||
|
|
||||||
.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)
|
@git ls-files $(SPELLCHECK_FILES) | xargs go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -w
|
||||||
|
|
||||||
.PHONY: lint-go
|
.PHONY: lint-go
|
||||||
lint-go: ## lint go files
|
lint-go: ## lint go files
|
||||||
@@ -393,20 +361,19 @@ lint-yaml: .venv ## lint yaml files
|
|||||||
|
|
||||||
.PHONY: lint-json
|
.PHONY: lint-json
|
||||||
lint-json: node_modules ## lint json files
|
lint-json: node_modules ## lint json files
|
||||||
$(NODE_VARS) pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0
|
pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY)
|
||||||
|
|
||||||
.PHONY: lint-json-fix
|
.PHONY: lint-json-fix
|
||||||
lint-json-fix: node_modules ## lint and fix json files
|
lint-json-fix: node_modules ## lint and fix json files
|
||||||
$(NODE_VARS) pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0 --fix
|
pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) --fix
|
||||||
|
|
||||||
.PHONY: watch
|
.PHONY: watch
|
||||||
watch: ## watch everything and continuously rebuild
|
watch: ## watch everything and continuously rebuild
|
||||||
@bash tools/watch.sh
|
@bash tools/watch.sh
|
||||||
|
|
||||||
.PHONY: watch-frontend
|
.PHONY: watch-frontend
|
||||||
watch-frontend: node_modules ## watch frontend files and continuously rebuild
|
watch-frontend: node_modules ## start vite dev server for frontend
|
||||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
NODE_ENV=development pnpm exec vite --logLevel $(FRONTEND_DEV_LOG_LEVEL)
|
||||||
NODE_ENV=development $(NODE_VARS) pnpm exec webpack --watch --progress --disable-interpret
|
|
||||||
|
|
||||||
.PHONY: watch-backend
|
.PHONY: watch-backend
|
||||||
watch-backend: ## watch backend files and continuously rebuild
|
watch-backend: ## watch backend files and continuously rebuild
|
||||||
@@ -422,7 +389,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
|
pnpm exec vitest
|
||||||
|
|
||||||
.PHONY: test-check
|
.PHONY: test-check
|
||||||
test-check:
|
test-check:
|
||||||
@@ -475,16 +442,11 @@ tidy-check: tidy
|
|||||||
go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses
|
go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses
|
||||||
|
|
||||||
$(GO_LICENSE_FILE): go.mod go.sum
|
$(GO_LICENSE_FILE): go.mod go.sum
|
||||||
@rm -rf $(GO_LICENSE_FILE)
|
GO=$(GO) $(GO) run build/generate-go-licenses.go $(GO_LICENSE_FILE)
|
||||||
$(GO) install $(GO_LICENSES_PACKAGE)
|
|
||||||
-GOOS=linux CGO_ENABLED=1 go-licenses save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
|
|
||||||
$(GO) run build/generate-go-licenses.go $(GO_LICENSE_TMP_DIR) $(GO_LICENSE_FILE)
|
|
||||||
@rm -rf $(GO_LICENSE_TMP_DIR)
|
|
||||||
|
|
||||||
generate-ini-sqlite:
|
generate-ini-sqlite:
|
||||||
sed -e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
sed -e 's|{{WORK_PATH}}|$(CURDIR)/tests/$(or $(TEST_TYPE),integration)/gitea-$(or $(TEST_TYPE),integration)-sqlite|g' \
|
||||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
|
||||||
tests/sqlite.ini.tmpl > tests/sqlite.ini
|
tests/sqlite.ini.tmpl > tests/sqlite.ini
|
||||||
|
|
||||||
.PHONY: test-sqlite
|
.PHONY: test-sqlite
|
||||||
@@ -503,9 +465,8 @@ generate-ini-mysql:
|
|||||||
-e 's|{{TEST_MYSQL_DBNAME}}|${TEST_MYSQL_DBNAME}|g' \
|
-e 's|{{TEST_MYSQL_DBNAME}}|${TEST_MYSQL_DBNAME}|g' \
|
||||||
-e 's|{{TEST_MYSQL_USERNAME}}|${TEST_MYSQL_USERNAME}|g' \
|
-e 's|{{TEST_MYSQL_USERNAME}}|${TEST_MYSQL_USERNAME}|g' \
|
||||||
-e 's|{{TEST_MYSQL_PASSWORD}}|${TEST_MYSQL_PASSWORD}|g' \
|
-e 's|{{TEST_MYSQL_PASSWORD}}|${TEST_MYSQL_PASSWORD}|g' \
|
||||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
-e 's|{{WORK_PATH}}|$(CURDIR)/tests/$(or $(TEST_TYPE),integration)/gitea-$(or $(TEST_TYPE),integration)-mysql|g' \
|
||||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
|
||||||
tests/mysql.ini.tmpl > tests/mysql.ini
|
tests/mysql.ini.tmpl > tests/mysql.ini
|
||||||
|
|
||||||
.PHONY: test-mysql
|
.PHONY: test-mysql
|
||||||
@@ -526,9 +487,8 @@ generate-ini-pgsql:
|
|||||||
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
||||||
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
|
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
|
||||||
-e 's|{{TEST_MINIO_ENDPOINT}}|${TEST_MINIO_ENDPOINT}|g' \
|
-e 's|{{TEST_MINIO_ENDPOINT}}|${TEST_MINIO_ENDPOINT}|g' \
|
||||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
-e 's|{{WORK_PATH}}|$(CURDIR)/tests/$(or $(TEST_TYPE),integration)/gitea-$(or $(TEST_TYPE),integration)-pgsql|g' \
|
||||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
|
||||||
tests/pgsql.ini.tmpl > tests/pgsql.ini
|
tests/pgsql.ini.tmpl > tests/pgsql.ini
|
||||||
|
|
||||||
.PHONY: test-pgsql
|
.PHONY: test-pgsql
|
||||||
@@ -547,9 +507,8 @@ generate-ini-mssql:
|
|||||||
-e 's|{{TEST_MSSQL_DBNAME}}|${TEST_MSSQL_DBNAME}|g' \
|
-e 's|{{TEST_MSSQL_DBNAME}}|${TEST_MSSQL_DBNAME}|g' \
|
||||||
-e 's|{{TEST_MSSQL_USERNAME}}|${TEST_MSSQL_USERNAME}|g' \
|
-e 's|{{TEST_MSSQL_USERNAME}}|${TEST_MSSQL_USERNAME}|g' \
|
||||||
-e 's|{{TEST_MSSQL_PASSWORD}}|${TEST_MSSQL_PASSWORD}|g' \
|
-e 's|{{TEST_MSSQL_PASSWORD}}|${TEST_MSSQL_PASSWORD}|g' \
|
||||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
-e 's|{{WORK_PATH}}|$(CURDIR)/tests/$(or $(TEST_TYPE),integration)/gitea-$(or $(TEST_TYPE),integration)-mssql|g' \
|
||||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
|
||||||
tests/mssql.ini.tmpl > tests/mssql.ini
|
tests/mssql.ini.tmpl > tests/mssql.ini
|
||||||
|
|
||||||
.PHONY: test-mssql
|
.PHONY: test-mssql
|
||||||
@@ -565,47 +524,12 @@ 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)
|
@# on GitHub Actions VMs, playwright's system deps are pre-installed
|
||||||
|
@pnpm exec playwright install $(if $(GITHUB_ACTIONS),,--with-deps) chromium firefox $(PLAYWRIGHT_FLAGS)
|
||||||
.PHONY: test-e2e%
|
|
||||||
test-e2e%: TEST_TYPE ?= e2e
|
|
||||||
# Clear display env variable. Otherwise, chromium tests can fail.
|
|
||||||
DISPLAY=
|
|
||||||
|
|
||||||
.PHONY: test-e2e
|
.PHONY: test-e2e
|
||||||
test-e2e: test-e2e-sqlite
|
test-e2e: playwright $(EXECUTABLE_E2E)
|
||||||
|
@EXECUTABLE=$(EXECUTABLE_E2E) ./tools/test-e2e.sh $(GITEA_TEST_E2E_FLAGS)
|
||||||
.PHONY: test-e2e-sqlite
|
|
||||||
test-e2e-sqlite: playwright e2e.sqlite.test generate-ini-sqlite
|
|
||||||
GITEA_TEST_CONF=tests/sqlite.ini ./e2e.sqlite.test
|
|
||||||
|
|
||||||
.PHONY: test-e2e-sqlite\#%
|
|
||||||
test-e2e-sqlite\#%: playwright e2e.sqlite.test generate-ini-sqlite
|
|
||||||
GITEA_TEST_CONF=tests/sqlite.ini ./e2e.sqlite.test -test.run TestE2e/$*
|
|
||||||
|
|
||||||
.PHONY: test-e2e-mysql
|
|
||||||
test-e2e-mysql: playwright e2e.mysql.test generate-ini-mysql
|
|
||||||
GITEA_TEST_CONF=tests/mysql.ini ./e2e.mysql.test
|
|
||||||
|
|
||||||
.PHONY: test-e2e-mysql\#%
|
|
||||||
test-e2e-mysql\#%: playwright e2e.mysql.test generate-ini-mysql
|
|
||||||
GITEA_TEST_CONF=tests/mysql.ini ./e2e.mysql.test -test.run TestE2e/$*
|
|
||||||
|
|
||||||
.PHONY: test-e2e-pgsql
|
|
||||||
test-e2e-pgsql: playwright e2e.pgsql.test generate-ini-pgsql
|
|
||||||
GITEA_TEST_CONF=tests/pgsql.ini ./e2e.pgsql.test
|
|
||||||
|
|
||||||
.PHONY: test-e2e-pgsql\#%
|
|
||||||
test-e2e-pgsql\#%: playwright e2e.pgsql.test generate-ini-pgsql
|
|
||||||
GITEA_TEST_CONF=tests/pgsql.ini ./e2e.pgsql.test -test.run TestE2e/$*
|
|
||||||
|
|
||||||
.PHONY: test-e2e-mssql
|
|
||||||
test-e2e-mssql: playwright e2e.mssql.test generate-ini-mssql
|
|
||||||
GITEA_TEST_CONF=tests/mssql.ini ./e2e.mssql.test
|
|
||||||
|
|
||||||
.PHONY: test-e2e-mssql\#%
|
|
||||||
test-e2e-mssql\#%: playwright e2e.mssql.test generate-ini-mssql
|
|
||||||
GITEA_TEST_CONF=tests/mssql.ini ./e2e.mssql.test -test.run TestE2e/$*
|
|
||||||
|
|
||||||
.PHONY: bench-sqlite
|
.PHONY: bench-sqlite
|
||||||
bench-sqlite: integrations.sqlite.test generate-ini-sqlite
|
bench-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||||
@@ -670,7 +594,7 @@ migrations.sqlite.test: $(GO_SOURCES) generate-ini-sqlite
|
|||||||
GITEA_TEST_CONF=tests/sqlite.ini ./migrations.sqlite.test
|
GITEA_TEST_CONF=tests/sqlite.ini ./migrations.sqlite.test
|
||||||
|
|
||||||
.PHONY: migrations.individual.mysql.test
|
.PHONY: migrations.individual.mysql.test
|
||||||
migrations.individual.mysql.test: $(GO_SOURCES)
|
migrations.individual.mysql.test: $(GO_SOURCES) generate-ini-mysql
|
||||||
GITEA_TEST_CONF=tests/mysql.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES)
|
GITEA_TEST_CONF=tests/mysql.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES)
|
||||||
|
|
||||||
.PHONY: migrations.individual.sqlite.test\#%
|
.PHONY: migrations.individual.sqlite.test\#%
|
||||||
@@ -678,7 +602,7 @@ migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite
|
|||||||
GITEA_TEST_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$*
|
GITEA_TEST_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$*
|
||||||
|
|
||||||
.PHONY: migrations.individual.pgsql.test
|
.PHONY: migrations.individual.pgsql.test
|
||||||
migrations.individual.pgsql.test: $(GO_SOURCES)
|
migrations.individual.pgsql.test: $(GO_SOURCES) generate-ini-pgsql
|
||||||
GITEA_TEST_CONF=tests/pgsql.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES)
|
GITEA_TEST_CONF=tests/pgsql.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES)
|
||||||
|
|
||||||
.PHONY: migrations.individual.pgsql.test\#%
|
.PHONY: migrations.individual.pgsql.test\#%
|
||||||
@@ -701,18 +625,6 @@ migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite
|
|||||||
migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite
|
migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite
|
||||||
GITEA_TEST_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$*
|
GITEA_TEST_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$*
|
||||||
|
|
||||||
e2e.mysql.test: $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mysql.test
|
|
||||||
|
|
||||||
e2e.pgsql.test: $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.pgsql.test
|
|
||||||
|
|
||||||
e2e.mssql.test: $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mssql.test
|
|
||||||
|
|
||||||
e2e.sqlite.test: $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.sqlite.test -tags '$(TEST_TAGS)'
|
|
||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: test
|
check: test
|
||||||
|
|
||||||
@@ -724,7 +636,7 @@ install: $(wildcard *.go)
|
|||||||
build: frontend backend ## build everything
|
build: frontend backend ## build everything
|
||||||
|
|
||||||
.PHONY: frontend
|
.PHONY: frontend
|
||||||
frontend: $(WEBPACK_DEST) ## build frontend files
|
frontend: $(FRONTEND_DEST) ## build frontend files
|
||||||
|
|
||||||
.PHONY: backend
|
.PHONY: backend
|
||||||
backend: generate-backend $(EXECUTABLE) ## build backend files
|
backend: generate-backend $(EXECUTABLE) ## build backend files
|
||||||
@@ -743,7 +655,7 @@ generate-go: $(TAGS_PREREQ)
|
|||||||
|
|
||||||
.PHONY: security-check
|
.PHONY: security-check
|
||||||
security-check:
|
security-check:
|
||||||
GOEXPERIMENT= go run $(GOVULNCHECK_PACKAGE) -show color ./...
|
GOEXPERIMENT= go run $(GOVULNCHECK_PACKAGE) -show color ./... || true
|
||||||
|
|
||||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||||
ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)
|
ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)
|
||||||
@@ -751,6 +663,9 @@ ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)
|
|||||||
endif
|
endif
|
||||||
CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@
|
CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@
|
||||||
|
|
||||||
|
$(EXECUTABLE_E2E): $(GO_SOURCES) $(FRONTEND_DEST)
|
||||||
|
CGO_ENABLED=1 $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TEST_TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-check
|
release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-check
|
||||||
|
|
||||||
@@ -821,13 +736,12 @@ deps-tools: ## install tool dependencies
|
|||||||
$(GO) install $(MISSPELL_PACKAGE) & \
|
$(GO) install $(MISSPELL_PACKAGE) & \
|
||||||
$(GO) install $(SWAGGER_PACKAGE) & \
|
$(GO) install $(SWAGGER_PACKAGE) & \
|
||||||
$(GO) install $(XGO_PACKAGE) & \
|
$(GO) install $(XGO_PACKAGE) & \
|
||||||
$(GO) install $(GO_LICENSES_PACKAGE) & \
|
|
||||||
$(GO) install $(GOVULNCHECK_PACKAGE) & \
|
$(GO) install $(GOVULNCHECK_PACKAGE) & \
|
||||||
$(GO) install $(ACTIONLINT_PACKAGE) & \
|
$(GO) install $(ACTIONLINT_PACKAGE) & \
|
||||||
wait
|
wait
|
||||||
|
|
||||||
node_modules: pnpm-lock.yaml
|
node_modules: pnpm-lock.yaml
|
||||||
$(NODE_VARS) pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
@touch node_modules
|
@touch node_modules
|
||||||
|
|
||||||
.venv: uv.lock
|
.venv: uv.lock
|
||||||
@@ -835,33 +749,38 @@ node_modules: pnpm-lock.yaml
|
|||||||
@touch .venv
|
@touch .venv
|
||||||
|
|
||||||
.PHONY: update
|
.PHONY: update
|
||||||
update: update-js update-py ## update js and py dependencies
|
update: update-go update-js update-py ## update dependencies
|
||||||
|
|
||||||
|
.PHONY: update-go
|
||||||
|
update-go: ## update go dependencies
|
||||||
|
$(GO) get -u ./...
|
||||||
|
$(MAKE) tidy
|
||||||
|
|
||||||
.PHONY: update-js
|
.PHONY: update-js
|
||||||
update-js: node_modules ## update js dependencies
|
update-js: node_modules ## update js dependencies
|
||||||
$(NODE_VARS) pnpm exec updates -u -f package.json
|
pnpm exec updates -u -f package.json
|
||||||
rm -rf node_modules pnpm-lock.yaml
|
rm -rf node_modules pnpm-lock.yaml
|
||||||
$(NODE_VARS) pnpm install
|
pnpm install
|
||||||
$(NODE_VARS) pnpm exec nolyfill install
|
pnpm exec nolyfill install
|
||||||
$(NODE_VARS) pnpm install
|
pnpm install
|
||||||
@touch node_modules
|
@touch node_modules
|
||||||
|
|
||||||
.PHONY: update-py
|
.PHONY: update-py
|
||||||
update-py: node_modules ## update py dependencies
|
update-py: node_modules ## update py dependencies
|
||||||
$(NODE_VARS) pnpm exec updates -u -f pyproject.toml
|
pnpm exec updates -u -f pyproject.toml
|
||||||
rm -rf .venv uv.lock
|
rm -rf .venv uv.lock
|
||||||
uv sync
|
uv sync
|
||||||
@touch .venv
|
@touch .venv
|
||||||
|
|
||||||
.PHONY: webpack
|
.PHONY: vite
|
||||||
webpack: $(WEBPACK_DEST) ## build webpack files
|
vite: $(FRONTEND_DEST) ## build vite files
|
||||||
|
|
||||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) pnpm-lock.yaml
|
$(FRONTEND_DEST): $(FRONTEND_SOURCES) $(FRONTEND_CONFIGS) pnpm-lock.yaml
|
||||||
@$(MAKE) -s node_modules
|
@$(MAKE) -s node_modules
|
||||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
@rm -rf $(FRONTEND_DEST_ENTRIES)
|
||||||
@echo "Running webpack..."
|
@echo "Running vite build..."
|
||||||
@BROWSERSLIST_IGNORE_OLD_DATA=true $(NODE_VARS) pnpm exec webpack --disable-interpret
|
@pnpm exec vite build
|
||||||
@touch $(WEBPACK_DEST)
|
@touch $(FRONTEND_DEST)
|
||||||
|
|
||||||
.PHONY: svg
|
.PHONY: svg
|
||||||
svg: node_modules ## build svg files
|
svg: node_modules ## build svg files
|
||||||
@@ -880,7 +799,7 @@ svg-check: svg
|
|||||||
|
|
||||||
.PHONY: lockfile-check
|
.PHONY: lockfile-check
|
||||||
lockfile-check:
|
lockfile-check:
|
||||||
$(NODE_VARS) pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
@diff=$$(git diff --color=always pnpm-lock.yaml); \
|
@diff=$$(git diff --color=always pnpm-lock.yaml); \
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "pnpm-lock.yaml is inconsistent with package.json"; \
|
echo "pnpm-lock.yaml is inconsistent with package.json"; \
|
||||||
@@ -910,9 +829,6 @@ docker:
|
|||||||
docker build --disable-content-trust=false -t $(DOCKER_REF) .
|
docker build --disable-content-trust=false -t $(DOCKER_REF) .
|
||||||
# support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify" .
|
# support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify" .
|
||||||
|
|
||||||
# This endif closes the if at the top of the file
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Disable parallel execution because it would break some targets that don't
|
# Disable parallel execution because it would break some targets that don't
|
||||||
# specify exact dependencies like 'backend' which does currently not depend
|
# specify exact dependencies like 'backend' which does currently not depend
|
||||||
# on 'frontend' to enable Node.js-less builds from source tarballs.
|
# on 'frontend' to enable Node.js-less builds from source tarballs.
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
|
[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
|
||||||
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
||||||
[](https://opensource.org/licenses/MIT "License: MIT")
|
[](https://opensource.org/licenses/MIT "License: MIT")
|
||||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
|
||||||
[](https://translate.gitea.com "Crowdin")
|
[](https://translate.gitea.com "Crowdin")
|
||||||
|
|
||||||
[繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
|
[繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
|
[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
|
||||||
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
||||||
[](https://opensource.org/licenses/MIT "License: MIT")
|
[](https://opensource.org/licenses/MIT "License: MIT")
|
||||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
|
||||||
[](https://translate.gitea.com "Crowdin")
|
[](https://translate.gitea.com "Crowdin")
|
||||||
|
|
||||||
[English](./README.md) | [繁體中文](./README.zh-tw.md)
|
[English](./README.md) | [繁體中文](./README.zh-tw.md)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
|
[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
|
||||||
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
||||||
[](https://opensource.org/licenses/MIT "License: MIT")
|
[](https://opensource.org/licenses/MIT "License: MIT")
|
||||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
|
||||||
[](https://translate.gitea.com "Crowdin")
|
[](https://translate.gitea.com "Crowdin")
|
||||||
|
|
||||||
[English](./README.md) | [简体中文](./README.zh-cn.md)
|
[English](./README.md) | [简体中文](./README.zh-cn.md)
|
||||||
|
|||||||
Generated
+11483
-1
File diff suppressed because one or more lines are too long
Generated
+159
-134
File diff suppressed because one or more lines are too long
@@ -1,115 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/container"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) != 2 {
|
|
||||||
println("usage: backport-locales <to-ref>")
|
|
||||||
println("eg: backport-locales release/v1.19")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
mustNoErr := func(err error) {
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
collectInis := func(ref string) map[string]setting.ConfigProvider {
|
|
||||||
inis := map[string]setting.ConfigProvider{}
|
|
||||||
err := filepath.WalkDir("options/locale", func(path string, d os.DirEntry, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if d.IsDir() || !strings.HasSuffix(d.Name(), ".ini") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cfg, err := setting.NewConfigProviderForLocale(path)
|
|
||||||
mustNoErr(err)
|
|
||||||
inis[path] = cfg
|
|
||||||
fmt.Printf("collecting: %s @ %s\n", path, ref)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
mustNoErr(err)
|
|
||||||
return inis
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect new locales from current working directory
|
|
||||||
inisNew := collectInis("HEAD")
|
|
||||||
|
|
||||||
// switch to the target ref, and collect the old locales
|
|
||||||
cmd := exec.Command("git", "checkout", os.Args[1])
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
mustNoErr(cmd.Run())
|
|
||||||
inisOld := collectInis(os.Args[1])
|
|
||||||
|
|
||||||
// use old en-US as the base, and copy the new translations to the old locales
|
|
||||||
enUsOld := inisOld["options/locale/locale_en-US.ini"]
|
|
||||||
brokenWarned := make(container.Set[string])
|
|
||||||
for path, iniOld := range inisOld {
|
|
||||||
if iniOld == enUsOld {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
iniNew := inisNew[path]
|
|
||||||
if iniNew == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, secEnUS := range enUsOld.Sections() {
|
|
||||||
secOld := iniOld.Section(secEnUS.Name())
|
|
||||||
secNew := iniNew.Section(secEnUS.Name())
|
|
||||||
for _, keyEnUs := range secEnUS.Keys() {
|
|
||||||
if secNew.HasKey(keyEnUs.Name()) {
|
|
||||||
oldStr := secOld.Key(keyEnUs.Name()).String()
|
|
||||||
newStr := secNew.Key(keyEnUs.Name()).String()
|
|
||||||
broken := oldStr != "" && strings.Count(oldStr, "%") != strings.Count(newStr, "%")
|
|
||||||
broken = broken || strings.Contains(oldStr, "\n") || strings.Contains(oldStr, "\n")
|
|
||||||
if broken {
|
|
||||||
brokenWarned.Add(secOld.Name() + "." + keyEnUs.Name())
|
|
||||||
fmt.Println("----")
|
|
||||||
fmt.Printf("WARNING: skip broken locale: %s , [%s] %s\n", path, secEnUS.Name(), keyEnUs.Name())
|
|
||||||
fmt.Printf("\told: %s\n", strings.ReplaceAll(oldStr, "\n", "\\n"))
|
|
||||||
fmt.Printf("\tnew: %s\n", strings.ReplaceAll(newStr, "\n", "\\n"))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
secOld.Key(keyEnUs.Name()).SetValue(newStr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mustNoErr(iniOld.SaveTo(path))
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("========")
|
|
||||||
|
|
||||||
for path, iniNew := range inisNew {
|
|
||||||
for _, sec := range iniNew.Sections() {
|
|
||||||
for _, key := range sec.Keys() {
|
|
||||||
str := sec.Key(key.Name()).String()
|
|
||||||
broken := strings.Contains(str, "\n")
|
|
||||||
broken = broken || strings.HasPrefix(str, "`") != strings.HasSuffix(str, "`")
|
|
||||||
broken = broken || strings.HasPrefix(str, "\"`")
|
|
||||||
broken = broken || strings.HasPrefix(str, "`\"")
|
|
||||||
broken = broken || strings.Count(str, `"`)%2 == 1
|
|
||||||
broken = broken || strings.Count(str, "`")%2 == 1
|
|
||||||
if broken && !brokenWarned.Contains(sec.Name()+"."+key.Name()) {
|
|
||||||
fmt.Printf("WARNING: found broken locale: %s , [%s] %s\n", path, sec.Name(), key.Name())
|
|
||||||
fmt.Printf("\tstr: %s\n", strings.ReplaceAll(str, "\n", "\\n"))
|
|
||||||
fmt.Println("----")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,8 +24,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
gemojiURL = "https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json"
|
gemojiURL = "https://raw.githubusercontent.com/rhysd/gemoji/537ff2d7e0496e9964824f7f73ec7ece88c9765a/db/emoji.json"
|
||||||
maxUnicodeVersion = 15
|
maxUnicodeVersion = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
||||||
@@ -149,8 +149,8 @@ func generate() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write a JSON file to use with tribute (write before adding skin tones since we can't support them there yet)
|
// write a JSON file to use with tribute (write before adding skin tones since we can't support them there yet)
|
||||||
file, _ := json.Marshal(data)
|
file, _ := json.MarshalIndent(data, "", " ")
|
||||||
_ = os.WriteFile("assets/emoji.json", file, 0o644)
|
_ = os.WriteFile("assets/emoji.json", append(file, '\n'), 0o644)
|
||||||
|
|
||||||
// Add skin tones to emoji that support it
|
// Add skin tones to emoji that support it
|
||||||
var (
|
var (
|
||||||
|
|||||||
+180
-59
@@ -8,99 +8,220 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/container"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// regexp is based on go-license, excluding README and NOTICE
|
// regexp is based on go-license, excluding README and NOTICE
|
||||||
// https://github.com/google/go-licenses/blob/master/licenses/find.go
|
// https://github.com/google/go-licenses/blob/master/licenses/find.go
|
||||||
|
// also defined in vite.config.ts
|
||||||
var licenseRe = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING).*$`)
|
var licenseRe = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING).*$`)
|
||||||
|
|
||||||
|
// primaryLicenseRe matches exact primary license filenames without suffixes.
|
||||||
|
// When a directory has both primary and variant files (e.g. LICENSE and
|
||||||
|
// LICENSE.docs), only the primary files are kept.
|
||||||
|
var primaryLicenseRe = regexp.MustCompile(`^(?i)(LICEN[SC]E|COPYING)$`)
|
||||||
|
|
||||||
|
// ignoredNames are LicenseEntry.Name values to exclude from the output.
|
||||||
|
var ignoredNames = map[string]bool{
|
||||||
|
"code.gitea.io/gitea": true,
|
||||||
|
"code.gitea.io/gitea/options/license": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var excludedExt = map[string]bool{
|
||||||
|
".gitignore": true,
|
||||||
|
".go": true,
|
||||||
|
".mod": true,
|
||||||
|
".sum": true,
|
||||||
|
".toml": true,
|
||||||
|
".yaml": true,
|
||||||
|
".yml": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModuleInfo struct {
|
||||||
|
Path string
|
||||||
|
Dir string
|
||||||
|
PkgDirs []string // directories of packages imported from this module
|
||||||
|
}
|
||||||
|
|
||||||
type LicenseEntry struct {
|
type LicenseEntry struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
LicenseText string `json:"licenseText"`
|
LicenseText string `json:"licenseText"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
// getModules returns all dependency modules with their local directory paths
|
||||||
if len(os.Args) != 3 {
|
// and the package directories used from each module.
|
||||||
fmt.Println("usage: go run generate-go-licenses.go <base-dir> <out-json-file>")
|
func getModules(goCmd string) []ModuleInfo {
|
||||||
|
cmd := exec.Command(goCmd, "list", "-deps", "-f",
|
||||||
|
"{{if .Module}}{{.Module.Path}}\t{{.Module.Dir}}\t{{.Dir}}{{end}}", "./...")
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
// Use GOOS=linux with CGO to ensure we capture all platform-specific
|
||||||
|
// dependencies, matching the CI environment.
|
||||||
|
cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=amd64", "CGO_ENABLED=1")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to run 'go list -deps': %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
base, out := os.Args[1], os.Args[2]
|
var modules []ModuleInfo
|
||||||
|
seen := make(map[string]int) // module path -> index in modules
|
||||||
// Add ext for excluded files because license_test.go will be included for some reason.
|
for _, line := range strings.Split(string(output), "\n") {
|
||||||
// And there are more files that should be excluded, check with:
|
line = strings.TrimSpace(line)
|
||||||
//
|
if line == "" {
|
||||||
// go run github.com/google/go-licenses@v1.6.0 save . --force --save_path=.go-licenses 2>/dev/null
|
continue
|
||||||
// find .go-licenses -type f | while read FILE; do echo "${$(basename $FILE)##*.}"; done | sort -u
|
|
||||||
// AUTHORS
|
|
||||||
// COPYING
|
|
||||||
// LICENSE
|
|
||||||
// Makefile
|
|
||||||
// NOTICE
|
|
||||||
// gitignore
|
|
||||||
// go
|
|
||||||
// md
|
|
||||||
// mod
|
|
||||||
// sum
|
|
||||||
// toml
|
|
||||||
// txt
|
|
||||||
// yml
|
|
||||||
//
|
|
||||||
// It could be removed once we have a better regex.
|
|
||||||
excludedExt := container.SetOf(".gitignore", ".go", ".mod", ".sum", ".toml", ".yml")
|
|
||||||
|
|
||||||
var paths []string
|
|
||||||
err := filepath.WalkDir(base, func(path string, entry fs.DirEntry, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
if entry.IsDir() || !licenseRe.MatchString(entry.Name()) || excludedExt.Contains(filepath.Ext(entry.Name())) {
|
parts := strings.Split(line, "\t")
|
||||||
return nil
|
if len(parts) != 3 {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
paths = append(paths, path)
|
modPath, modDir, pkgDir := parts[0], parts[1], parts[2]
|
||||||
return nil
|
if idx, ok := seen[modPath]; ok {
|
||||||
})
|
modules[idx].PkgDirs = append(modules[idx].PkgDirs, pkgDir)
|
||||||
if err != nil {
|
} else {
|
||||||
panic(err)
|
seen[modPath] = len(modules)
|
||||||
|
modules = append(modules, ModuleInfo{
|
||||||
|
Path: modPath,
|
||||||
|
Dir: modDir,
|
||||||
|
PkgDirs: []string{pkgDir},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modules
|
||||||
|
}
|
||||||
|
|
||||||
|
// findLicenseFiles scans a module's root directory and its used package
|
||||||
|
// directories for license files. It also walks up from each package directory
|
||||||
|
// to the module root, scanning intermediate directories. Subdirectory licenses
|
||||||
|
// are only included if their text differs from the root license(s).
|
||||||
|
func findLicenseFiles(mod ModuleInfo) []LicenseEntry {
|
||||||
|
var entries []LicenseEntry
|
||||||
|
seenTexts := make(map[string]bool)
|
||||||
|
|
||||||
|
// First, collect root-level license files.
|
||||||
|
entries = append(entries, scanDirForLicenses(mod.Dir, mod.Path, "")...)
|
||||||
|
for _, e := range entries {
|
||||||
|
seenTexts[e.LicenseText] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(paths)
|
// Then check each package directory and all intermediate parent directories
|
||||||
|
// up to the module root for license files with unique text.
|
||||||
|
seenDirs := map[string]bool{mod.Dir: true}
|
||||||
|
for _, pkgDir := range mod.PkgDirs {
|
||||||
|
for dir := pkgDir; dir != mod.Dir && strings.HasPrefix(dir, mod.Dir); dir = filepath.Dir(dir) {
|
||||||
|
if seenDirs[dir] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seenDirs[dir] = true
|
||||||
|
for _, e := range scanDirForLicenses(dir, mod.Path, mod.Dir) {
|
||||||
|
if !seenTexts[e.LicenseText] {
|
||||||
|
seenTexts[e.LicenseText] = true
|
||||||
|
entries = append(entries, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanDirForLicenses reads a single directory for license files and returns entries.
|
||||||
|
// If moduleRoot is non-empty, paths are made relative to it.
|
||||||
|
func scanDirForLicenses(dir, modulePath, moduleRoot string) []LicenseEntry {
|
||||||
|
dirEntries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var entries []LicenseEntry
|
var entries []LicenseEntry
|
||||||
for _, filePath := range paths {
|
for _, entry := range dirEntries {
|
||||||
licenseText, err := os.ReadFile(filePath)
|
if entry.IsDir() {
|
||||||
if err != nil {
|
continue
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
name := entry.Name()
|
||||||
pkgPath := filepath.ToSlash(filePath)
|
if !licenseRe.MatchString(name) {
|
||||||
pkgPath = strings.TrimPrefix(pkgPath, base+"/")
|
continue
|
||||||
pkgName := path.Dir(pkgPath)
|
}
|
||||||
|
if excludedExt[strings.ToLower(filepath.Ext(name))] {
|
||||||
// There might be a bug somewhere in go-licenses that sometimes interprets the
|
|
||||||
// root package as "." and sometimes as "code.gitea.io/gitea". Workaround by
|
|
||||||
// removing both of them for the sake of stable output.
|
|
||||||
if pkgName == "." || pkgName == "code.gitea.io/gitea" {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content, err := os.ReadFile(filepath.Join(dir, name))
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
entryName := modulePath
|
||||||
|
entryPath := modulePath + "/" + name
|
||||||
|
if moduleRoot != "" {
|
||||||
|
rel, _ := filepath.Rel(moduleRoot, dir)
|
||||||
|
if rel != "." {
|
||||||
|
relSlash := filepath.ToSlash(rel)
|
||||||
|
entryName = modulePath + "/" + relSlash
|
||||||
|
entryPath = modulePath + "/" + relSlash + "/" + name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
entries = append(entries, LicenseEntry{
|
entries = append(entries, LicenseEntry{
|
||||||
Name: pkgName,
|
Name: entryName,
|
||||||
Path: pkgPath,
|
Path: entryPath,
|
||||||
LicenseText: string(licenseText),
|
LicenseText: string(content),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When multiple license files exist, prefer primary files (e.g. LICENSE)
|
||||||
|
// over variants with suffixes (e.g. LICENSE.docs, LICENSE-2.0.txt).
|
||||||
|
// If no primary file exists, keep only the first variant.
|
||||||
|
if len(entries) > 1 {
|
||||||
|
var primary []LicenseEntry
|
||||||
|
for _, e := range entries {
|
||||||
|
fileName := e.Path[strings.LastIndex(e.Path, "/")+1:]
|
||||||
|
if primaryLicenseRe.MatchString(fileName) {
|
||||||
|
primary = append(primary, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(primary) > 0 {
|
||||||
|
return primary
|
||||||
|
}
|
||||||
|
return entries[:1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) != 2 {
|
||||||
|
fmt.Println("usage: go run generate-go-licenses.go <out-json-file>")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := os.Args[1]
|
||||||
|
|
||||||
|
goCmd := "go"
|
||||||
|
if env := os.Getenv("GO"); env != "" {
|
||||||
|
goCmd = env
|
||||||
|
}
|
||||||
|
|
||||||
|
modules := getModules(goCmd)
|
||||||
|
|
||||||
|
var entries []LicenseEntry
|
||||||
|
for _, mod := range modules {
|
||||||
|
entries = append(entries, findLicenseFiles(mod)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
entries = slices.DeleteFunc(entries, func(e LicenseEntry) bool {
|
||||||
|
return ignoredNames[e.Name]
|
||||||
|
})
|
||||||
|
|
||||||
|
sort.Slice(entries, func(i, j int) bool {
|
||||||
|
return entries[i].Path < entries[j].Path
|
||||||
|
})
|
||||||
|
|
||||||
jsonBytes, err := json.MarshalIndent(entries, "", " ")
|
jsonBytes, err := json.MarshalIndent(entries, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
+7
-6
@@ -13,17 +13,18 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func newActionsCommand() *cli.Command {
|
||||||
// CmdActions represents the available actions sub-commands.
|
return &cli.Command{
|
||||||
CmdActions = &cli.Command{
|
|
||||||
Name: "actions",
|
Name: "actions",
|
||||||
Usage: "Manage Gitea Actions",
|
Usage: "Manage Gitea Actions",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
subcmdActionsGenRunnerToken,
|
newActionsGenerateRunnerTokenCommand(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdActionsGenRunnerToken = &cli.Command{
|
func newActionsGenerateRunnerTokenCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "generate-runner-token",
|
Name: "generate-runner-token",
|
||||||
Usage: "Generate a new token for a runner to use to register with the server",
|
Usage: "Generate a new token for a runner to use to register with the server",
|
||||||
Action: runGenerateActionsRunnerToken,
|
Action: runGenerateActionsRunnerToken,
|
||||||
@@ -37,7 +38,7 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error {
|
func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error {
|
||||||
setting.MustInstalled()
|
setting.MustInstalled()
|
||||||
|
|||||||
+25
-18
@@ -18,36 +18,41 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func newAdminCommand() *cli.Command {
|
||||||
// CmdAdmin represents the available admin sub-command.
|
return &cli.Command{
|
||||||
CmdAdmin = &cli.Command{
|
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Usage: "Perform common administrative operations",
|
Usage: "Perform common administrative operations",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
subcmdUser,
|
newUserCommand(),
|
||||||
subcmdRepoSyncReleases,
|
newRepoSyncReleasesCommand(),
|
||||||
subcmdRegenerate,
|
newRegenerateCommand(),
|
||||||
subcmdAuth,
|
newAuthCommand(),
|
||||||
subcmdSendMail,
|
newSendMailCommand(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdRepoSyncReleases = &cli.Command{
|
func newRepoSyncReleasesCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "repo-sync-releases",
|
Name: "repo-sync-releases",
|
||||||
Usage: "Synchronize repository releases with tags",
|
Usage: "Synchronize repository releases with tags",
|
||||||
Action: runRepoSyncReleases,
|
Action: runRepoSyncReleases,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdRegenerate = &cli.Command{
|
func newRegenerateCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "regenerate",
|
Name: "regenerate",
|
||||||
Usage: "Regenerate specific files",
|
Usage: "Regenerate specific files",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
microcmdRegenHooks,
|
newRegenerateHooksCommand(),
|
||||||
microcmdRegenKeys,
|
newRegenerateKeysCommand(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdAuth = &cli.Command{
|
func newAuthCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "auth",
|
Name: "auth",
|
||||||
Usage: "Modify external auth providers",
|
Usage: "Modify external auth providers",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
@@ -59,12 +64,14 @@ var (
|
|||||||
microcmdAuthUpdateLdapSimpleAuth(),
|
microcmdAuthUpdateLdapSimpleAuth(),
|
||||||
microcmdAuthAddSMTP(),
|
microcmdAuthAddSMTP(),
|
||||||
microcmdAuthUpdateSMTP(),
|
microcmdAuthUpdateSMTP(),
|
||||||
microcmdAuthList,
|
newAuthListCommand(),
|
||||||
microcmdAuthDelete,
|
newAuthDeleteCommand(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdSendMail = &cli.Command{
|
func newSendMailCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "sendmail",
|
Name: "sendmail",
|
||||||
Usage: "Send a message to all users",
|
Usage: "Send a message to all users",
|
||||||
Action: runSendMail,
|
Action: runSendMail,
|
||||||
@@ -86,7 +93,7 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
func idFlag() *cli.Int64Flag {
|
func idFlag() *cli.Int64Flag {
|
||||||
return &cli.Int64Flag{
|
return &cli.Int64Flag{
|
||||||
@@ -134,7 +141,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
|
|||||||
}
|
}
|
||||||
log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum)
|
log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum)
|
||||||
|
|
||||||
if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
|
if _, err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
|
||||||
log.Warn(" SyncReleasesWithTags: %v", err)
|
log.Warn(" SyncReleasesWithTags: %v", err)
|
||||||
gitRepo.Close()
|
gitRepo.Close()
|
||||||
continue
|
continue
|
||||||
|
|||||||
+7
-4
@@ -17,14 +17,17 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func newAuthDeleteCommand() *cli.Command {
|
||||||
microcmdAuthDelete = &cli.Command{
|
return &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{
|
}
|
||||||
|
|
||||||
|
func newAuthListCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Usage: "List auth sources",
|
Usage: "List auth sources",
|
||||||
Action: runListAuth,
|
Action: runListAuth,
|
||||||
@@ -55,7 +58,7 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
func runListAuth(ctx context.Context, c *cli.Command) error {
|
func runListAuth(ctx context.Context, c *cli.Command) error {
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
|
|||||||
@@ -13,19 +13,21 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func newRegenerateHooksCommand() *cli.Command {
|
||||||
microcmdRegenHooks = &cli.Command{
|
return &cli.Command{
|
||||||
Name: "hooks",
|
Name: "hooks",
|
||||||
Usage: "Regenerate git-hooks",
|
Usage: "Regenerate git-hooks",
|
||||||
Action: runRegenerateHooks,
|
Action: runRegenerateHooks,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
microcmdRegenKeys = &cli.Command{
|
func newRegenerateKeysCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "keys",
|
Name: "keys",
|
||||||
Usage: "Regenerate authorized_keys file",
|
Usage: "Regenerate authorized_keys file",
|
||||||
Action: runRegenerateKeys,
|
Action: runRegenerateKeys,
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
func runRegenerateHooks(ctx context.Context, _ *cli.Command) error {
|
func runRegenerateHooks(ctx context.Context, _ *cli.Command) error {
|
||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
|
|||||||
+13
-11
@@ -7,15 +7,17 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var subcmdUser = &cli.Command{
|
func newUserCommand() *cli.Command {
|
||||||
Name: "user",
|
return &cli.Command{
|
||||||
Usage: "Modify users",
|
Name: "user",
|
||||||
Commands: []*cli.Command{
|
Usage: "Modify users",
|
||||||
microcmdUserCreate(),
|
Commands: []*cli.Command{
|
||||||
microcmdUserList,
|
microcmdUserCreate(),
|
||||||
microcmdUserChangePassword(),
|
newUserListCommand(),
|
||||||
microcmdUserDelete(),
|
microcmdUserChangePassword(),
|
||||||
microcmdUserGenerateAccessToken,
|
microcmdUserDelete(),
|
||||||
microcmdUserMustChangePassword(),
|
newUserGenerateAccessTokenCommand(),
|
||||||
},
|
microcmdUserMustChangePassword(),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,32 +14,34 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var microcmdUserGenerateAccessToken = &cli.Command{
|
func newUserGenerateAccessTokenCommand() *cli.Command {
|
||||||
Name: "generate-access-token",
|
return &cli.Command{
|
||||||
Usage: "Generate an access token for a specific user",
|
Name: "generate-access-token",
|
||||||
Flags: []cli.Flag{
|
Usage: "Generate an access token for a specific user",
|
||||||
&cli.StringFlag{
|
Flags: []cli.Flag{
|
||||||
Name: "username",
|
&cli.StringFlag{
|
||||||
Aliases: []string{"u"},
|
Name: "username",
|
||||||
Usage: "Username",
|
Aliases: []string{"u"},
|
||||||
|
Usage: "Username",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "token-name",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Usage: "Token name",
|
||||||
|
Value: "gitea-admin",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "raw",
|
||||||
|
Usage: "Display only the token value",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "scopes",
|
||||||
|
Value: "all",
|
||||||
|
Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
Action: runGenerateAccessToken,
|
||||||
Name: "token-name",
|
}
|
||||||
Aliases: []string{"t"},
|
|
||||||
Usage: "Token name",
|
|
||||||
Value: "gitea-admin",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "raw",
|
|
||||||
Usage: "Display only the token value",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "scopes",
|
|
||||||
Value: "all",
|
|
||||||
Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: runGenerateAccessToken,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
|
func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
|
||||||
|
|||||||
+11
-9
@@ -14,16 +14,18 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var microcmdUserList = &cli.Command{
|
func newUserListCommand() *cli.Command {
|
||||||
Name: "list",
|
return &cli.Command{
|
||||||
Usage: "List users",
|
Name: "list",
|
||||||
Action: runListUsers,
|
Usage: "List users",
|
||||||
Flags: []cli.Flag{
|
Action: runListUsers,
|
||||||
&cli.BoolFlag{
|
Flags: []cli.Flag{
|
||||||
Name: "admin",
|
&cli.BoolFlag{
|
||||||
Usage: "List only admin users",
|
Name: "admin",
|
||||||
|
Usage: "List only admin users",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runListUsers(ctx context.Context, c *cli.Command) error {
|
func runListUsers(ctx context.Context, c *cli.Command) error {
|
||||||
|
|||||||
+17
-16
@@ -13,23 +13,24 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDocs represents the available docs sub-command.
|
func newDocsCommand() *cli.Command {
|
||||||
var CmdDocs = &cli.Command{
|
return &cli.Command{
|
||||||
Name: "docs",
|
Name: "docs",
|
||||||
Usage: "Output CLI documentation",
|
Usage: "Output CLI documentation",
|
||||||
Description: "A command to output Gitea's CLI documentation, optionally to a file.",
|
Description: "A command to output Gitea's CLI documentation, optionally to a file.",
|
||||||
Action: runDocs,
|
Action: runDocs,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "man",
|
Name: "man",
|
||||||
Usage: "Output man pages instead",
|
Usage: "Output man pages instead",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "output",
|
||||||
|
Aliases: []string{"o"},
|
||||||
|
Usage: "Path to output to instead of stdout (will overwrite if exists)",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
}
|
||||||
Name: "output",
|
|
||||||
Aliases: []string{"o"},
|
|
||||||
Usage: "Path to output to instead of stdout (will overwrite if exists)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDocs(_ context.Context, cmd *cli.Command) error {
|
func runDocs(_ context.Context, cmd *cli.Command) error {
|
||||||
|
|||||||
+61
-57
@@ -24,73 +24,77 @@ import (
|
|||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDoctor represents the available doctor sub-command.
|
func newDoctorCommand() *cli.Command {
|
||||||
var CmdDoctor = &cli.Command{
|
return &cli.Command{
|
||||||
Name: "doctor",
|
Name: "doctor",
|
||||||
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{
|
||||||
Commands: []*cli.Command{
|
newDoctorCheckCommand(),
|
||||||
cmdDoctorCheck,
|
newRecreateTableCommand(),
|
||||||
cmdRecreateTable,
|
newDoctorConvertCommand(),
|
||||||
cmdDoctorConvert,
|
},
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmdDoctorCheck = &cli.Command{
|
func newDoctorCheckCommand() *cli.Command {
|
||||||
Name: "check",
|
return &cli.Command{
|
||||||
Usage: "Diagnose and optionally fix problems",
|
Name: "check",
|
||||||
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.",
|
Usage: "Diagnose and optionally fix problems",
|
||||||
Action: runDoctorCheck,
|
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.",
|
||||||
Flags: []cli.Flag{
|
Action: runDoctorCheck,
|
||||||
&cli.BoolFlag{
|
Flags: []cli.Flag{
|
||||||
Name: "list",
|
&cli.BoolFlag{
|
||||||
Usage: "List the available checks",
|
Name: "list",
|
||||||
|
Usage: "List the available checks",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "default",
|
||||||
|
Usage: "Run the default checks (if neither --run or --all is set, this is the default behaviour)",
|
||||||
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "run",
|
||||||
|
Usage: "Run the provided checks - (if --default is set, the default checks will also run)",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "all",
|
||||||
|
Usage: "Run all the available checks",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "fix",
|
||||||
|
Usage: "Automatically fix what we can",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "log-file",
|
||||||
|
Usage: `Name of the log file (no verbose log output by default). Set to "-" to output to stdout`,
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "color",
|
||||||
|
Aliases: []string{"H"},
|
||||||
|
Usage: "Use color for outputted information",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
}
|
||||||
Name: "default",
|
|
||||||
Usage: "Run the default checks (if neither --run or --all is set, this is the default behaviour)",
|
|
||||||
},
|
|
||||||
&cli.StringSliceFlag{
|
|
||||||
Name: "run",
|
|
||||||
Usage: "Run the provided checks - (if --default is set, the default checks will also run)",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "all",
|
|
||||||
Usage: "Run all the available checks",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "fix",
|
|
||||||
Usage: "Automatically fix what we can",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "log-file",
|
|
||||||
Usage: `Name of the log file (no verbose log output by default). Set to "-" to output to stdout`,
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "color",
|
|
||||||
Aliases: []string{"H"},
|
|
||||||
Usage: "Use color for outputted information",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmdRecreateTable = &cli.Command{
|
func newRecreateTableCommand() *cli.Command {
|
||||||
Name: "recreate-table",
|
return &cli.Command{
|
||||||
Usage: "Recreate tables from XORM definitions and copy the data.",
|
Name: "recreate-table",
|
||||||
ArgsUsage: "[TABLE]... : (TABLEs to recreate - leave blank for all)",
|
Usage: "Recreate tables from XORM definitions and copy the data.",
|
||||||
Flags: []cli.Flag{
|
ArgsUsage: "[TABLE]... : (TABLEs to recreate - leave blank for all)",
|
||||||
&cli.BoolFlag{
|
Flags: []cli.Flag{
|
||||||
Name: "debug",
|
&cli.BoolFlag{
|
||||||
Usage: "Print SQL commands sent",
|
Name: "debug",
|
||||||
|
Usage: "Print SQL commands sent",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
Description: `The database definitions Gitea uses change across versions, sometimes changing default values and leaving old unused columns.
|
||||||
Description: `The database definitions Gitea uses change across versions, sometimes changing default values and leaving old unused columns.
|
|
||||||
|
|
||||||
This command will cause Xorm to recreate tables, copying over the data and deleting the old table.
|
This command will cause Xorm to recreate tables, copying over the data and deleting the old table.
|
||||||
|
|
||||||
You should back-up your database before doing this and ensure that your database is up-to-date first.`,
|
You should back-up your database before doing this and ensure that your database is up-to-date first.`,
|
||||||
Action: runRecreateTable,
|
Action: runRecreateTable,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
|
func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// cmdDoctorConvert represents the available convert sub-command.
|
func newDoctorConvertCommand() *cli.Command {
|
||||||
var cmdDoctorConvert = &cli.Command{
|
return &cli.Command{
|
||||||
Name: "convert",
|
Name: "convert",
|
||||||
Usage: "Convert the database",
|
Usage: "Convert the database",
|
||||||
Description: "A command to convert an existing MySQL database from utf8 to utf8mb4 or MSSQL database from varchar to nvarchar",
|
Description: "A command to convert an existing MySQL database from utf8 to utf8mb4 or MSSQL database from varchar to nvarchar",
|
||||||
Action: runDoctorConvert,
|
Action: runDoctorConvert,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDoctorConvert(ctx context.Context, cmd *cli.Command) error {
|
func runDoctorConvert(ctx context.Context, cmd *cli.Command) error {
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ func TestDoctorRun(t *testing.T) {
|
|||||||
SkipDatabaseInitialization: true,
|
SkipDatabaseInitialization: true,
|
||||||
})
|
})
|
||||||
app := &cli.Command{
|
app := &cli.Command{
|
||||||
Commands: []*cli.Command{cmdDoctorCheck},
|
Commands: []*cli.Command{newDoctorCheckCommand()},
|
||||||
}
|
}
|
||||||
err := app.Run(t.Context(), []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)
|
||||||
|
|||||||
+72
-71
@@ -23,78 +23,79 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDump represents the available dump sub-command.
|
func newDumpCommand() *cli.Command {
|
||||||
var CmdDump = &cli.Command{
|
return &cli.Command{
|
||||||
Name: "dump",
|
Name: "dump",
|
||||||
Usage: "Dump Gitea files and database",
|
Usage: "Dump Gitea files and database",
|
||||||
Description: `Dump compresses all related files and database into zip file. It can be used for backup and capture Gitea server image to send to maintainer`,
|
Description: `Dump compresses all related files and database into zip file. It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||||
Action: runDump,
|
Action: runDump,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "file",
|
Name: "file",
|
||||||
Aliases: []string{"f"},
|
Aliases: []string{"f"},
|
||||||
Usage: `Name of the dump file which will be created, default to "gitea-dump-{time}.zip". Supply '-' for stdout. See type for available types.`,
|
Usage: `Name of the dump file which will be created, default to "gitea-dump-{time}.zip". Supply '-' for stdout. See type for available types.`,
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "verbose",
|
||||||
|
Aliases: []string{"V"},
|
||||||
|
Usage: "Show process details",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "quiet",
|
||||||
|
Aliases: []string{"q"},
|
||||||
|
Usage: "Only display warnings and errors",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "tempdir",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Value: os.TempDir(),
|
||||||
|
Usage: "Temporary dir path",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "database",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Usage: "Specify the database SQL syntax: sqlite3, mysql, mssql, postgres",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "skip-repository",
|
||||||
|
Aliases: []string{"R"},
|
||||||
|
Usage: "Skip the repository dumping",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "skip-log",
|
||||||
|
Aliases: []string{"L"},
|
||||||
|
Usage: "Skip the log dumping",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "skip-custom-dir",
|
||||||
|
Usage: "Skip custom directory",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "skip-lfs-data",
|
||||||
|
Usage: "Skip LFS data",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "skip-attachment-data",
|
||||||
|
Usage: "Skip attachment data",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "skip-package-data",
|
||||||
|
Usage: "Skip package data",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "skip-index",
|
||||||
|
Usage: "Skip bleve index data",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "skip-db",
|
||||||
|
Usage: "Skip database",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "type",
|
||||||
|
Usage: `Dump output format, default to "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
}
|
||||||
Name: "verbose",
|
|
||||||
Aliases: []string{"V"},
|
|
||||||
Usage: "Show process details",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "quiet",
|
|
||||||
Aliases: []string{"q"},
|
|
||||||
Usage: "Only display warnings and errors",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "tempdir",
|
|
||||||
Aliases: []string{"t"},
|
|
||||||
Value: os.TempDir(),
|
|
||||||
Usage: "Temporary dir path",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "database",
|
|
||||||
Aliases: []string{"d"},
|
|
||||||
Usage: "Specify the database SQL syntax: sqlite3, mysql, mssql, postgres",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-repository",
|
|
||||||
Aliases: []string{"R"},
|
|
||||||
Usage: "Skip the repository dumping",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-log",
|
|
||||||
Aliases: []string{"L"},
|
|
||||||
Usage: "Skip the log dumping",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-custom-dir",
|
|
||||||
Usage: "Skip custom directory",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-lfs-data",
|
|
||||||
Usage: "Skip LFS data",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-attachment-data",
|
|
||||||
Usage: "Skip attachment data",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-package-data",
|
|
||||||
Usage: "Skip package data",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-index",
|
|
||||||
Usage: "Skip bleve index data",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-db",
|
|
||||||
Usage: "Skip database",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "type",
|
|
||||||
Usage: `Dump output format, default to "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fatal(format string, args ...any) {
|
func fatal(format string, args ...any) {
|
||||||
|
|||||||
+54
-53
@@ -22,61 +22,62 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDumpRepository represents the available dump repository sub-command.
|
func newDumpRepositoryCommand() *cli.Command {
|
||||||
var CmdDumpRepository = &cli.Command{
|
return &cli.Command{
|
||||||
Name: "dump-repo",
|
Name: "dump-repo",
|
||||||
Usage: "Dump the repository from git/github/gitea/gitlab",
|
Usage: "Dump the repository from git/github/gitea/gitlab",
|
||||||
Description: "This is a command for dumping the repository data.",
|
Description: "This is a command for dumping the repository data.",
|
||||||
Action: runDumpRepository,
|
Action: runDumpRepository,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "git_service",
|
Name: "git_service",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
|
Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "repo_dir",
|
Name: "repo_dir",
|
||||||
Aliases: []string{"r"},
|
Aliases: []string{"r"},
|
||||||
Value: "./data",
|
Value: "./data",
|
||||||
Usage: "Repository dir path to store the data",
|
Usage: "Repository dir path to store the data",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "clone_addr",
|
Name: "clone_addr",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL",
|
Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "auth_username",
|
Name: "auth_username",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The username to visit the clone_addr",
|
Usage: "The username to visit the clone_addr",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "auth_password",
|
Name: "auth_password",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The password to visit the clone_addr",
|
Usage: "The password to visit the clone_addr",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "auth_token",
|
Name: "auth_token",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The personal token to visit the clone_addr",
|
Usage: "The personal token to visit the clone_addr",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "owner_name",
|
Name: "owner_name",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The data will be stored on a directory with owner name if not empty",
|
Usage: "The data will be stored on a directory with owner name if not empty",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "repo_name",
|
Name: "repo_name",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The data will be stored on a directory with repository name if not empty",
|
Usage: "The data will be stored on a directory with repository name if not empty",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "units",
|
Name: "units",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: `Which items will be migrated, one or more units should be separated as comma.
|
Usage: `Which items will be migrated, one or more units should be separated as comma.
|
||||||
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
|
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
|
func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
|
||||||
|
|||||||
+17
-12
@@ -23,20 +23,23 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdEmbedded represents the available extract sub-command.
|
var matchedAssetFiles []assetFile
|
||||||
var (
|
|
||||||
CmdEmbedded = &cli.Command{
|
func newEmbeddedCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
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{
|
Commands: []*cli.Command{
|
||||||
subcmdList,
|
newEmbeddedListCommand(),
|
||||||
subcmdView,
|
newEmbeddedViewCommand(),
|
||||||
subcmdExtract,
|
newEmbeddedExtractCommand(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdList = &cli.Command{
|
func newEmbeddedListCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Usage: "List files matching the given pattern",
|
Usage: "List files matching the given pattern",
|
||||||
Action: runList,
|
Action: runList,
|
||||||
@@ -48,8 +51,10 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdView = &cli.Command{
|
func newEmbeddedViewCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "view",
|
Name: "view",
|
||||||
Usage: "View a file matching the given pattern",
|
Usage: "View a file matching the given pattern",
|
||||||
Action: runView,
|
Action: runView,
|
||||||
@@ -61,8 +66,10 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdExtract = &cli.Command{
|
func newEmbeddedExtractCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "extract",
|
Name: "extract",
|
||||||
Usage: "Extract resources",
|
Usage: "Extract resources",
|
||||||
Action: runExtract,
|
Action: runExtract,
|
||||||
@@ -91,9 +98,7 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
matchedAssetFiles []assetFile
|
|
||||||
)
|
|
||||||
|
|
||||||
type assetFile struct {
|
type assetFile struct {
|
||||||
fs *assetfs.LayeredFS
|
fs *assetfs.LayeredFS
|
||||||
|
|||||||
+20
-17
@@ -15,45 +15,52 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func newGenerateCommand() *cli.Command {
|
||||||
// CmdGenerate represents the available generate sub-command.
|
return &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{
|
Commands: []*cli.Command{
|
||||||
subcmdSecret,
|
newGenerateSecretCommand(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdSecret = &cli.Command{
|
func newGenerateSecretCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "secret",
|
Name: "secret",
|
||||||
Usage: "Generate a secret token",
|
Usage: "Generate a secret token",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
microcmdGenerateInternalToken,
|
newGenerateInternalTokenCommand(),
|
||||||
microcmdGenerateLfsJwtSecret,
|
newGenerateLfsJWTSecretCommand(),
|
||||||
microcmdGenerateSecretKey,
|
newGenerateSecretKeyCommand(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
microcmdGenerateInternalToken = &cli.Command{
|
func newGenerateInternalTokenCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "INTERNAL_TOKEN",
|
Name: "INTERNAL_TOKEN",
|
||||||
Usage: "Generate a new INTERNAL_TOKEN",
|
Usage: "Generate a new INTERNAL_TOKEN",
|
||||||
Action: runGenerateInternalToken,
|
Action: runGenerateInternalToken,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
microcmdGenerateLfsJwtSecret = &cli.Command{
|
func newGenerateLfsJWTSecretCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "JWT_SECRET",
|
Name: "JWT_SECRET",
|
||||||
Aliases: []string{"LFS_JWT_SECRET"},
|
Aliases: []string{"LFS_JWT_SECRET"},
|
||||||
Usage: "Generate a new JWT_SECRET",
|
Usage: "Generate a new JWT_SECRET",
|
||||||
Action: runGenerateLfsJwtSecret,
|
Action: runGenerateLfsJwtSecret,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
microcmdGenerateSecretKey = &cli.Command{
|
func newGenerateSecretKeyCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "SECRET_KEY",
|
Name: "SECRET_KEY",
|
||||||
Usage: "Generate a new SECRET_KEY",
|
Usage: "Generate a new SECRET_KEY",
|
||||||
Action: runGenerateSecretKey,
|
Action: runGenerateSecretKey,
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
|
func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
|
||||||
internalToken, err := generate.NewInternalToken()
|
internalToken, err := generate.NewInternalToken()
|
||||||
@@ -71,11 +78,7 @@ func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
|
func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
|
||||||
_, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
|
_, jwtSecretBase64 := generate.NewJwtSecretWithBase64()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s", jwtSecretBase64)
|
fmt.Printf("%s", jwtSecretBase64)
|
||||||
|
|
||||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||||
|
|||||||
+37
-15
@@ -28,23 +28,24 @@ const (
|
|||||||
hookBatchSize = 500
|
hookBatchSize = 500
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func newHookCommand() *cli.Command {
|
||||||
// CmdHook represents the available hooks sub-command.
|
return &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
|
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{
|
Commands: []*cli.Command{
|
||||||
subcmdHookPreReceive,
|
newHookPreReceiveCommand(),
|
||||||
subcmdHookUpdate,
|
newHookUpdateCommand(),
|
||||||
subcmdHookPostReceive,
|
newHookPostReceiveCommand(),
|
||||||
subcmdHookProcReceive,
|
newHookProcReceiveCommand(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdHookPreReceive = &cli.Command{
|
func newHookPreReceiveCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "pre-receive",
|
Name: "pre-receive",
|
||||||
Usage: "Delegate pre-receive Git hook",
|
Usage: "Delegate pre-receive Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
@@ -55,7 +56,10 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
subcmdHookUpdate = &cli.Command{
|
}
|
||||||
|
|
||||||
|
func newHookUpdateCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "update",
|
Name: "update",
|
||||||
Usage: "Delegate update Git hook",
|
Usage: "Delegate update Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
@@ -66,7 +70,10 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
subcmdHookPostReceive = &cli.Command{
|
}
|
||||||
|
|
||||||
|
func newHookPostReceiveCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "post-receive",
|
Name: "post-receive",
|
||||||
Usage: "Delegate post-receive Git hook",
|
Usage: "Delegate post-receive Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
@@ -77,8 +84,11 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Note: new hook since git 2.29
|
}
|
||||||
subcmdHookProcReceive = &cli.Command{
|
|
||||||
|
// Note: new hook since git 2.29
|
||||||
|
func newHookProcReceiveCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "proc-receive",
|
Name: "proc-receive",
|
||||||
Usage: "Delegate proc-receive Git hook",
|
Usage: "Delegate proc-receive Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
@@ -89,7 +99,7 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
type delayWriter struct {
|
type delayWriter struct {
|
||||||
internal io.Writer
|
internal io.Writer
|
||||||
@@ -194,7 +204,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))
|
actionsTaskID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvActionsTaskID), 10, 64)
|
||||||
|
|
||||||
hookOptions := private.HookOptions{
|
hookOptions := private.HookOptions{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
@@ -204,7 +214,8 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
GitPushOptions: pushOptions(),
|
GitPushOptions: pushOptions(),
|
||||||
PullRequestID: prID,
|
PullRequestID: prID,
|
||||||
DeployKeyID: deployKeyID,
|
DeployKeyID: deployKeyID,
|
||||||
ActionPerm: actionPerm,
|
ActionsTaskID: actionsTaskID,
|
||||||
|
IsWiki: isWiki,
|
||||||
}
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
@@ -275,6 +286,9 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
lastline = 0
|
lastline = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return fail(ctx, "Hook failed: stdin read error", "scanner error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
hookOptions.OldCommitIDs = oldCommitIDs[:count]
|
hookOptions.OldCommitIDs = oldCommitIDs[:count]
|
||||||
@@ -366,6 +380,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
GitPushOptions: pushOptions(),
|
GitPushOptions: pushOptions(),
|
||||||
PullRequestID: prID,
|
PullRequestID: prID,
|
||||||
PushTrigger: repo_module.PushTrigger(os.Getenv(repo_module.EnvPushTrigger)),
|
PushTrigger: repo_module.PushTrigger(os.Getenv(repo_module.EnvPushTrigger)),
|
||||||
|
IsWiki: isWiki,
|
||||||
}
|
}
|
||||||
oldCommitIDs := make([]string, hookBatchSize)
|
oldCommitIDs := make([]string, hookBatchSize)
|
||||||
newCommitIDs := make([]string, hookBatchSize)
|
newCommitIDs := make([]string, hookBatchSize)
|
||||||
@@ -413,6 +428,11 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
count = 0
|
count = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
_ = dWriter.Close()
|
||||||
|
hookPrintResults(results)
|
||||||
|
return fail(ctx, "Hook failed: stdin read error", "scanner error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
if wasEmpty && masterPushed {
|
if wasEmpty && masterPushed {
|
||||||
@@ -513,6 +533,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
repoUser := os.Getenv(repo_module.EnvRepoUsername)
|
repoUser := os.Getenv(repo_module.EnvRepoUsername)
|
||||||
|
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
|
||||||
repoName := os.Getenv(repo_module.EnvRepoName)
|
repoName := os.Getenv(repo_module.EnvRepoName)
|
||||||
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||||
pusherName := os.Getenv(repo_module.EnvPusherName)
|
pusherName := os.Getenv(repo_module.EnvPusherName)
|
||||||
@@ -590,6 +611,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
UserName: pusherName,
|
UserName: pusherName,
|
||||||
UserID: pusherID,
|
UserID: pusherID,
|
||||||
GitPushOptions: make(map[string]string),
|
GitPushOptions: make(map[string]string),
|
||||||
|
IsWiki: isWiki,
|
||||||
}
|
}
|
||||||
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
|
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
|
||||||
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
|
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
|
||||||
|
|||||||
+35
-33
@@ -15,40 +15,42 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdKeys represents the available keys sub-command
|
// NewKeysCommand returns the internal SSH key lookup sub-command.
|
||||||
var CmdKeys = &cli.Command{
|
func NewKeysCommand() *cli.Command {
|
||||||
Name: "keys",
|
return &cli.Command{
|
||||||
Usage: "(internal) Should only be called by SSH server",
|
Name: "keys",
|
||||||
Hidden: true, // internal commands shouldn't be visible
|
Usage: "(internal) Should only be called by SSH server",
|
||||||
Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint",
|
Hidden: true, // internal commands shouldn't be visible
|
||||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint",
|
||||||
Action: runKeys,
|
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||||
Flags: []cli.Flag{
|
Action: runKeys,
|
||||||
&cli.StringFlag{
|
Flags: []cli.Flag{
|
||||||
Name: "expected",
|
&cli.StringFlag{
|
||||||
Aliases: []string{"e"},
|
Name: "expected",
|
||||||
Value: "git",
|
Aliases: []string{"e"},
|
||||||
Usage: "Expected user for whom provide key commands",
|
Value: "git",
|
||||||
|
Usage: "Expected user for whom provide key commands",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "Username trying to log in by SSH",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "type",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "Type of the SSH key provided to the SSH Server (requires content to be provided too)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "content",
|
||||||
|
Aliases: []string{"k"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
}
|
||||||
Name: "username",
|
|
||||||
Aliases: []string{"u"},
|
|
||||||
Value: "",
|
|
||||||
Usage: "Username trying to log in by SSH",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "type",
|
|
||||||
Aliases: []string{"t"},
|
|
||||||
Value: "",
|
|
||||||
Usage: "Type of the SSH key provided to the SSH Server (requires content to be provided too)",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "content",
|
|
||||||
Aliases: []string{"k"},
|
|
||||||
Value: "",
|
|
||||||
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runKeys(ctx context.Context, c *cli.Command) error {
|
func runKeys(ctx context.Context, c *cli.Command) error {
|
||||||
|
|||||||
+2
-2
@@ -17,10 +17,10 @@ func runSendMail(ctx context.Context, c *cli.Command) error {
|
|||||||
setting.MustInstalled()
|
setting.MustInstalled()
|
||||||
|
|
||||||
subject := c.String("title")
|
subject := c.String("title")
|
||||||
confirmSkiped := c.Bool("force")
|
confirmSkipped := c.Bool("force")
|
||||||
body := c.String("content")
|
body := c.String("content")
|
||||||
|
|
||||||
if !confirmSkiped {
|
if !confirmSkipped {
|
||||||
if len(body) == 0 {
|
if len(body) == 0 {
|
||||||
fmt.Print("warning: Content is empty")
|
fmt.Print("warning: Content is empty")
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-17
@@ -112,35 +112,36 @@ func NewMainApp(appVer AppVersion) *cli.Command {
|
|||||||
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
|
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
webCmd := newWebCommand()
|
||||||
// these sub-commands need to use a config file
|
// these sub-commands need to use a config file
|
||||||
subCmdWithConfig := []*cli.Command{
|
subCmdWithConfig := []*cli.Command{
|
||||||
CmdWeb,
|
webCmd,
|
||||||
CmdServ,
|
newServCommand(),
|
||||||
CmdHook,
|
newHookCommand(),
|
||||||
CmdKeys,
|
NewKeysCommand(),
|
||||||
CmdDump,
|
newDumpCommand(),
|
||||||
CmdAdmin,
|
newAdminCommand(),
|
||||||
CmdMigrate,
|
newMigrateCommand(),
|
||||||
CmdDoctor,
|
newDoctorCommand(),
|
||||||
CmdManager,
|
newManagerCommand(),
|
||||||
CmdEmbedded,
|
newEmbeddedCommand(),
|
||||||
CmdMigrateStorage,
|
newMigrateStorageCommand(),
|
||||||
CmdDumpRepository,
|
newDumpRepositoryCommand(),
|
||||||
CmdRestoreRepository,
|
newRestoreRepositoryCommand(),
|
||||||
CmdActions,
|
newActionsCommand(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(),
|
cmdConfig(),
|
||||||
cmdCert(),
|
cmdCert(),
|
||||||
CmdGenerate,
|
newGenerateCommand(),
|
||||||
CmdDocs,
|
newDocsCommand(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we should eventually drop the default command,
|
// 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.
|
// but not sure whether it would break Windows users who used to double-click the EXE to run.
|
||||||
app.DefaultCommand = CmdWeb.Name
|
app.DefaultCommand = webCmd.Name
|
||||||
|
|
||||||
app.Before = PrepareConsoleLoggerLevel(log.INFO)
|
app.Before = PrepareConsoleLoggerLevel(log.INFO)
|
||||||
for i := range subCmdWithConfig {
|
for i := range subCmdWithConfig {
|
||||||
|
|||||||
+29
-15
@@ -13,22 +13,24 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func newManagerCommand() *cli.Command {
|
||||||
// CmdManager represents the manager command
|
return &cli.Command{
|
||||||
CmdManager = &cli.Command{
|
|
||||||
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{
|
Commands: []*cli.Command{
|
||||||
subcmdShutdown,
|
newShutdownCommand(),
|
||||||
subcmdRestart,
|
newRestartCommand(),
|
||||||
subcmdReloadTemplates,
|
newReloadTemplatesCommand(),
|
||||||
subcmdFlushQueues,
|
newFlushQueuesCommand(),
|
||||||
subcmdLogging,
|
newLoggingCommand(),
|
||||||
subCmdProcesses,
|
newProcessesCommand(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
subcmdShutdown = &cli.Command{
|
}
|
||||||
|
|
||||||
|
func newShutdownCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "shutdown",
|
Name: "shutdown",
|
||||||
Usage: "Gracefully shutdown the running process",
|
Usage: "Gracefully shutdown the running process",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
@@ -38,7 +40,10 @@ var (
|
|||||||
},
|
},
|
||||||
Action: runShutdown,
|
Action: runShutdown,
|
||||||
}
|
}
|
||||||
subcmdRestart = &cli.Command{
|
}
|
||||||
|
|
||||||
|
func newRestartCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "restart",
|
Name: "restart",
|
||||||
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
@@ -48,7 +53,10 @@ var (
|
|||||||
},
|
},
|
||||||
Action: runRestart,
|
Action: runRestart,
|
||||||
}
|
}
|
||||||
subcmdReloadTemplates = &cli.Command{
|
}
|
||||||
|
|
||||||
|
func newReloadTemplatesCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "reload-templates",
|
Name: "reload-templates",
|
||||||
Usage: "Reload template files in the running process",
|
Usage: "Reload template files in the running process",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
@@ -58,7 +66,10 @@ var (
|
|||||||
},
|
},
|
||||||
Action: runReloadTemplates,
|
Action: runReloadTemplates,
|
||||||
}
|
}
|
||||||
subcmdFlushQueues = &cli.Command{
|
}
|
||||||
|
|
||||||
|
func newFlushQueuesCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "flush-queues",
|
Name: "flush-queues",
|
||||||
Usage: "Flush queues in the running process",
|
Usage: "Flush queues in the running process",
|
||||||
Action: runFlushQueues,
|
Action: runFlushQueues,
|
||||||
@@ -77,7 +88,10 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
subCmdProcesses = &cli.Command{
|
}
|
||||||
|
|
||||||
|
func newProcessesCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "processes",
|
Name: "processes",
|
||||||
Usage: "Display running processes within the current process",
|
Usage: "Display running processes within the current process",
|
||||||
Action: runProcesses,
|
Action: runProcesses,
|
||||||
@@ -107,7 +121,7 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
func runShutdown(ctx context.Context, c *cli.Command) error {
|
func runShutdown(ctx context.Context, c *cli.Command) error {
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func defaultLoggingFlags() []cli.Flag {
|
||||||
defaultLoggingFlags = []cli.Flag{
|
return []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "logger",
|
Name: "logger",
|
||||||
Usage: `Logger name - will default to "default"`,
|
Usage: `Logger name - will default to "default"`,
|
||||||
@@ -57,8 +57,10 @@ var (
|
|||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subcmdLogging = &cli.Command{
|
func newLoggingCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
Name: "logging",
|
Name: "logging",
|
||||||
Usage: "Adjust logging commands",
|
Usage: "Adjust logging commands",
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
@@ -109,7 +111,7 @@ var (
|
|||||||
{
|
{
|
||||||
Name: "file",
|
Name: "file",
|
||||||
Usage: "Add a file logger",
|
Usage: "Add a file logger",
|
||||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
Flags: append(defaultLoggingFlags(), []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "filename",
|
Name: "filename",
|
||||||
Aliases: []string{"f"},
|
Aliases: []string{"f"},
|
||||||
@@ -150,7 +152,7 @@ var (
|
|||||||
}, {
|
}, {
|
||||||
Name: "conn",
|
Name: "conn",
|
||||||
Usage: "Add a net conn logger",
|
Usage: "Add a net conn logger",
|
||||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
Flags: append(defaultLoggingFlags(), []cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "reconnect-on-message",
|
Name: "reconnect-on-message",
|
||||||
Aliases: []string{"R"},
|
Aliases: []string{"R"},
|
||||||
@@ -191,7 +193,7 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
func runRemoveLogger(ctx context.Context, c *cli.Command) error {
|
func runRemoveLogger(ctx context.Context, c *cli.Command) error {
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
|
|||||||
+7
-6
@@ -14,12 +14,13 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdMigrate represents the available migrate sub-command.
|
func newMigrateCommand() *cli.Command {
|
||||||
var CmdMigrate = &cli.Command{
|
return &cli.Command{
|
||||||
Name: "migrate",
|
Name: "migrate",
|
||||||
Usage: "Migrate the database",
|
Usage: "Migrate the database",
|
||||||
Description: `This is a command for migrating the database, so that you can run "gitea admin create user" before starting the server.`,
|
Description: `This is a command for migrating the database, so that you can run "gitea admin create user" before starting the server.`,
|
||||||
Action: runMigrate,
|
Action: runMigrate,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMigrate(ctx context.Context, c *cli.Command) error {
|
func runMigrate(ctx context.Context, c *cli.Command) error {
|
||||||
|
|||||||
+101
-100
@@ -25,107 +25,108 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdMigrateStorage represents the available migrate storage sub-command.
|
func newMigrateStorageCommand() *cli.Command {
|
||||||
var CmdMigrateStorage = &cli.Command{
|
return &cli.Command{
|
||||||
Name: "migrate-storage",
|
Name: "migrate-storage",
|
||||||
Usage: "Migrate the storage",
|
Usage: "Migrate the storage",
|
||||||
Description: "Copies stored files from storage configured in app.ini to parameter-configured storage",
|
Description: "Copies stored files from storage configured in app.ini to parameter-configured storage",
|
||||||
Action: runMigrateStorage,
|
Action: runMigrateStorage,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "type",
|
Name: "type",
|
||||||
Aliases: []string{"t"},
|
Aliases: []string{"t"},
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts'",
|
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts'",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "storage",
|
||||||
|
Aliases: []string{"s"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "New storage type: local (default), minio or azureblob",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "path",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "New storage placement if store is local (leave blank for default)",
|
||||||
|
},
|
||||||
|
// Minio Storage special configurations
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "minio-endpoint",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Minio storage endpoint",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "minio-access-key-id",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Minio storage accessKeyID",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "minio-secret-access-key",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Minio storage secretAccessKey",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "minio-bucket",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Minio storage bucket",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "minio-location",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Minio storage location to create bucket",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "minio-base-path",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Minio storage base path on the bucket",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "minio-use-ssl",
|
||||||
|
Usage: "Enable SSL for minio",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "minio-insecure-skip-verify",
|
||||||
|
Usage: "Skip SSL verification",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "minio-checksum-algorithm",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Minio checksum algorithm (default/md5)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "minio-bucket-lookup-type",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Minio bucket lookup type",
|
||||||
|
},
|
||||||
|
// Azure Blob Storage special configurations
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "azureblob-endpoint",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Azure Blob storage endpoint",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "azureblob-account-name",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Azure Blob storage account name",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "azureblob-account-key",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Azure Blob storage account key",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "azureblob-container",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Azure Blob storage container",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "azureblob-base-path",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Azure Blob storage base path",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
}
|
||||||
Name: "storage",
|
|
||||||
Aliases: []string{"s"},
|
|
||||||
Value: "",
|
|
||||||
Usage: "New storage type: local (default), minio or azureblob",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "path",
|
|
||||||
Aliases: []string{"p"},
|
|
||||||
Value: "",
|
|
||||||
Usage: "New storage placement if store is local (leave blank for default)",
|
|
||||||
},
|
|
||||||
// Minio Storage special configurations
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "minio-endpoint",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage endpoint",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "minio-access-key-id",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage accessKeyID",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "minio-secret-access-key",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage secretAccessKey",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "minio-bucket",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage bucket",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "minio-location",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage location to create bucket",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "minio-base-path",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage base path on the bucket",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "minio-use-ssl",
|
|
||||||
Usage: "Enable SSL for minio",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "minio-insecure-skip-verify",
|
|
||||||
Usage: "Skip SSL verification",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "minio-checksum-algorithm",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio checksum algorithm (default/md5)",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "minio-bucket-lookup-type",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio bucket lookup type",
|
|
||||||
},
|
|
||||||
// Azure Blob Storage special configurations
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "azureblob-endpoint",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Azure Blob storage endpoint",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "azureblob-account-name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Azure Blob storage account name",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "azureblob-account-key",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Azure Blob storage account key",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "azureblob-container",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Azure Blob storage container",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "azureblob-base-path",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Azure Blob storage base path",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||||
|
|||||||
+33
-32
@@ -13,40 +13,41 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdRestoreRepository represents the available restore a repository sub-command.
|
func newRestoreRepositoryCommand() *cli.Command {
|
||||||
var CmdRestoreRepository = &cli.Command{
|
return &cli.Command{
|
||||||
Name: "restore-repo",
|
Name: "restore-repo",
|
||||||
Usage: "Restore the repository from disk",
|
Usage: "Restore the repository from disk",
|
||||||
Description: "This is a command for restoring the repository data.",
|
Description: "This is a command for restoring the repository data.",
|
||||||
Action: runRestoreRepository,
|
Action: runRestoreRepository,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "repo_dir",
|
Name: "repo_dir",
|
||||||
Aliases: []string{"r"},
|
Aliases: []string{"r"},
|
||||||
Value: "./data",
|
Value: "./data",
|
||||||
Usage: "Repository dir path to restore from",
|
Usage: "Repository dir path to restore from",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "owner_name",
|
Name: "owner_name",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Restore destination owner name",
|
Usage: "Restore destination owner name",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "repo_name",
|
Name: "repo_name",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Restore destination repository name",
|
Usage: "Restore destination repository name",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "units",
|
Name: "units",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: `Which items will be restored, one or more units should be separated as comma.
|
Usage: `Which items will be restored, one or more units should be separated as comma.
|
||||||
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
|
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "validation",
|
||||||
|
Usage: "Sanity check the content of the files before trying to load them",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
}
|
||||||
Name: "validation",
|
|
||||||
Usage: "Sanity check the content of the files before trying to load them",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRestoreRepository(ctx context.Context, c *cli.Command) error {
|
func runRestoreRepository(ctx context.Context, c *cli.Command) error {
|
||||||
|
|||||||
+30
-24
@@ -35,22 +35,23 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdServ represents the available serv sub-command.
|
func newServCommand() *cli.Command {
|
||||||
var CmdServ = &cli.Command{
|
return &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
|
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{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "enable-pprof",
|
Name: "enable-pprof",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
}
|
||||||
Name: "debug",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(ctx context.Context, debug bool) {
|
func setup(ctx context.Context, debug bool) {
|
||||||
@@ -112,23 +113,25 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAccessMode(verb, lfsVerb string) perm.AccessMode {
|
// getAccessMode maps an SSH git/LFS verb to the access mode it requires, with
|
||||||
|
// ok=false for an unrecognised verb. Callers MUST reject the request when ok is
|
||||||
|
// false: AccessModeNone would otherwise pass the `userMode < mode` permission
|
||||||
|
// check in routers/private/serv.go and grant access.
|
||||||
|
func getAccessMode(verb, lfsVerb string) (mode perm.AccessMode, ok bool) {
|
||||||
switch verb {
|
switch verb {
|
||||||
case git.CmdVerbUploadPack, git.CmdVerbUploadArchive:
|
case git.CmdVerbUploadPack, git.CmdVerbUploadArchive:
|
||||||
return perm.AccessModeRead
|
return perm.AccessModeRead, true
|
||||||
case git.CmdVerbReceivePack:
|
case git.CmdVerbReceivePack:
|
||||||
return perm.AccessModeWrite
|
return perm.AccessModeWrite, true
|
||||||
case git.CmdVerbLfsAuthenticate, git.CmdVerbLfsTransfer:
|
case git.CmdVerbLfsAuthenticate, git.CmdVerbLfsTransfer:
|
||||||
switch lfsVerb {
|
switch lfsVerb {
|
||||||
case git.CmdSubVerbLfsUpload:
|
case git.CmdSubVerbLfsUpload:
|
||||||
return perm.AccessModeWrite
|
return perm.AccessModeWrite, true
|
||||||
case git.CmdSubVerbLfsDownload:
|
case git.CmdSubVerbLfsDownload:
|
||||||
return perm.AccessModeRead
|
return perm.AccessModeRead, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// should be unreachable
|
return perm.AccessModeNone, false
|
||||||
setting.PanicInDevOrTesting("unknown verb: %s %s", verb, lfsVerb)
|
|
||||||
return perm.AccessModeNone
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServ(ctx context.Context, c *cli.Command) error {
|
func runServ(ctx context.Context, c *cli.Command) error {
|
||||||
@@ -246,7 +249,10 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedMode := getAccessMode(verb, lfsVerb)
|
requestedMode, ok := getAccessMode(verb, lfsVerb)
|
||||||
|
if !ok {
|
||||||
|
return fail(ctx, "Unknown git command", "Unknown git command %s %s", verb, lfsVerb)
|
||||||
|
}
|
||||||
|
|
||||||
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
|
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
|
||||||
if extra.HasError() {
|
if extra.HasError() {
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/perm"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetAccessMode(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
verb, lfsVerb string
|
||||||
|
expected perm.AccessMode
|
||||||
|
}{
|
||||||
|
{git.CmdVerbUploadPack, "", perm.AccessModeRead},
|
||||||
|
{git.CmdVerbUploadArchive, "", perm.AccessModeRead},
|
||||||
|
{git.CmdVerbReceivePack, "", perm.AccessModeWrite},
|
||||||
|
{git.CmdVerbLfsAuthenticate, git.CmdSubVerbLfsUpload, perm.AccessModeWrite},
|
||||||
|
{git.CmdVerbLfsAuthenticate, git.CmdSubVerbLfsDownload, perm.AccessModeRead},
|
||||||
|
{git.CmdVerbLfsTransfer, git.CmdSubVerbLfsUpload, perm.AccessModeWrite},
|
||||||
|
{git.CmdVerbLfsTransfer, git.CmdSubVerbLfsDownload, perm.AccessModeRead},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.verb+"/"+tc.lfsVerb, func(t *testing.T) {
|
||||||
|
mode, ok := getAccessMode(tc.verb, tc.lfsVerb)
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, tc.expected, mode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetAccessModeUnknownVerb locks in the invariant that getAccessMode reports
|
||||||
|
// ok=false for unrecognised verbs and LFS sub-verbs, so runServ rejects them. An
|
||||||
|
// unknown verb has no valid access mode; if it were treated as AccessModeNone (0)
|
||||||
|
// it would pass the `userMode < mode` permission check in routers/private/serv.go
|
||||||
|
// and hand out valid LFS JWTs for any private repository.
|
||||||
|
func TestGetAccessModeUnknownVerb(t *testing.T) {
|
||||||
|
cases := []struct{ verb, lfsVerb string }{
|
||||||
|
{git.CmdVerbLfsAuthenticate, ""},
|
||||||
|
{git.CmdVerbLfsAuthenticate, "badverb"},
|
||||||
|
{git.CmdVerbLfsTransfer, "badverb"},
|
||||||
|
{"git-unknown-verb", ""},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.verb+"/"+tc.lfsVerb, func(t *testing.T) {
|
||||||
|
mode, ok := getAccessMode(tc.verb, tc.lfsVerb)
|
||||||
|
assert.False(t, ok)
|
||||||
|
assert.Equal(t, perm.AccessModeNone, mode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
-36
@@ -34,42 +34,43 @@ import (
|
|||||||
// PIDFile could be set from build tag
|
// PIDFile could be set from build tag
|
||||||
var PIDFile = "/run/gitea.pid"
|
var PIDFile = "/run/gitea.pid"
|
||||||
|
|
||||||
// CmdWeb represents the available web sub-command.
|
func newWebCommand() *cli.Command {
|
||||||
var CmdWeb = &cli.Command{
|
return &cli.Command{
|
||||||
Name: "web",
|
Name: "web",
|
||||||
Usage: "Start Gitea web server",
|
Usage: "Start Gitea web server",
|
||||||
Description: `Gitea web server is the only thing you need to run,
|
Description: `Gitea web server is the only thing you need to run,
|
||||||
and it takes care of all the other things for you`,
|
and it takes care of all the other things for you`,
|
||||||
Before: PrepareConsoleLoggerLevel(log.INFO),
|
Before: PrepareConsoleLoggerLevel(log.INFO),
|
||||||
Action: runWeb,
|
Action: runWeb,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "port",
|
Name: "port",
|
||||||
Aliases: []string{"p"},
|
Aliases: []string{"p"},
|
||||||
Value: "3000",
|
Value: "3000",
|
||||||
Usage: "Temporary port number to prevent conflict",
|
Usage: "Temporary port number to prevent conflict",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "install-port",
|
||||||
|
Value: "3000",
|
||||||
|
Usage: "Temporary port number to run the install page on to prevent conflict",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "pid",
|
||||||
|
Aliases: []string{"P"},
|
||||||
|
Value: PIDFile,
|
||||||
|
Usage: "Custom pid file path",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "quiet",
|
||||||
|
Aliases: []string{"q"},
|
||||||
|
Usage: "Only display Fatal logging errors until logging is set-up",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "verbose",
|
||||||
|
Usage: "Set initial logging to TRACE level until logging is properly set-up",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
}
|
||||||
Name: "install-port",
|
|
||||||
Value: "3000",
|
|
||||||
Usage: "Temporary port number to run the install page on to prevent conflict",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "pid",
|
|
||||||
Aliases: []string{"P"},
|
|
||||||
Value: PIDFile,
|
|
||||||
Usage: "Custom pid file path",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "quiet",
|
|
||||||
Aliases: []string{"q"},
|
|
||||||
Usage: "Only display Fatal logging errors until logging is set-up",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "verbose",
|
|
||||||
Usage: "Set initial logging to TRACE level until logging is properly set-up",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHTTPRedirector() {
|
func runHTTPRedirector() {
|
||||||
@@ -163,8 +164,6 @@ func serveInstall(cmd *cli.Command) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func serveInstalled(c *cli.Command) error {
|
func serveInstalled(c *cli.Command) error {
|
||||||
setting.InitCfgProvider(setting.CustomConf)
|
|
||||||
setting.LoadCommonSettings()
|
|
||||||
setting.MustInstalled()
|
setting.MustInstalled()
|
||||||
|
|
||||||
showWebStartupMessage("Prepare to run web server")
|
showWebStartupMessage("Prepare to run web server")
|
||||||
|
|||||||
+3
-2
@@ -7,6 +7,7 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -124,8 +125,8 @@ func runACME(listenAddr string, m http.Handler) error {
|
|||||||
defer finished()
|
defer finished()
|
||||||
|
|
||||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
// all traffic coming into HTTP will be redirected to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||||
err := runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)), setting.RedirectorUseProxyProtocol)
|
err := runHTTP("tcp", net.JoinHostPort(setting.HTTPAddr, setting.PortToRedirect), "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)), setting.RedirectorUseProxyProtocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/go-github/v74/github"
|
"github.com/google/go-github/v84/github"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|||||||
+5
-1
@@ -108,7 +108,9 @@ curl --connect-timeout 10 --silent --show-error --fail --location -O "$binurl{,.
|
|||||||
sha256sum -c "${binname}.xz.sha256"
|
sha256sum -c "${binname}.xz.sha256"
|
||||||
if [[ -z "${ignore_gpg:-}" ]]; then
|
if [[ -z "${ignore_gpg:-}" ]]; then
|
||||||
require gpg
|
require gpg
|
||||||
gpg --keyserver keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
|
# try to use curl first, it uses standard tcp 443 port and works better behind strict firewall rules
|
||||||
|
curl -fsSL --connect-timeout 10 "https://keys.openpgp.org/vks/v1/by-fingerprint/7C9E68152594688862D62AF62D9AE806EC1592E2" | gpg --import \
|
||||||
|
|| gpg --keyserver keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
|
||||||
gpg --verify "${binname}.xz.asc" "${binname}.xz" || { echo 'Signature does not match'; exit 1; }
|
gpg --verify "${binname}.xz.asc" "${binname}.xz" || { echo 'Signature does not match'; exit 1; }
|
||||||
fi
|
fi
|
||||||
rm "${binname}".xz.{sha256,asc}
|
rm "${binname}".xz.{sha256,asc}
|
||||||
@@ -127,6 +129,8 @@ echo "Creating backup in $giteahome"
|
|||||||
giteacmd dump $backupopts
|
giteacmd dump $backupopts
|
||||||
echo "Updating binary at $giteabin"
|
echo "Updating binary at $giteabin"
|
||||||
cp -f "$giteabin" "$giteabin.bak" && mv -f "$binname" "$giteabin"
|
cp -f "$giteabin" "$giteabin.bak" && mv -f "$binname" "$giteabin"
|
||||||
|
# Restore SELinux context if applicable (e.g. RHEL/Fedora)
|
||||||
|
command -v restorecon &>/dev/null && restorecon -v "$giteabin" || true
|
||||||
$service_start
|
$service_start
|
||||||
$service_status
|
$service_status
|
||||||
|
|
||||||
|
|||||||
+146
-28
@@ -41,10 +41,10 @@
|
|||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;
|
;;
|
||||||
;; App name that shows in every page title
|
;; App name that shows in every page title
|
||||||
APP_NAME = ; Gitea: Git with a cup of tea
|
;APP_NAME = Gitea: Git with a cup of tea
|
||||||
;;
|
;;
|
||||||
;; RUN_USER will automatically detect the current user - but you can set it here change it if you run locally
|
;; RUN_USER will automatically detect the current user - but you can set it here change it if you run locally
|
||||||
RUN_USER = ; git
|
;RUN_USER =
|
||||||
;;
|
;;
|
||||||
;; Application run mode, affects performance and debugging: "dev" or "prod", default is "prod"
|
;; Application run mode, affects performance and debugging: "dev" or "prod", default is "prod"
|
||||||
;; Mode "dev" makes Gitea easier to develop and debug, values other than "dev" are treated as "prod" which is for production use.
|
;; Mode "dev" makes Gitea easier to develop and debug, values other than "dev" are treated as "prod" which is for production use.
|
||||||
@@ -69,12 +69,12 @@ RUN_USER = ; git
|
|||||||
;; Most users should set it to the real website URL of their Gitea instance when there is a reverse proxy.
|
;; Most users should set it to the real website URL of their Gitea instance when there is a reverse proxy.
|
||||||
;ROOT_URL =
|
;ROOT_URL =
|
||||||
;;
|
;;
|
||||||
;; Controls how to detect the public URL.
|
;; Controls how to detect the public URL. Most instances should use the "auto" behavior,
|
||||||
;; Although it defaults to "legacy" (to avoid breaking existing users), most instances should use the "auto" behavior,
|
|
||||||
;; especially when the Gitea instance needs to be accessed in a container network.
|
;; especially when the Gitea instance needs to be accessed in a container network.
|
||||||
;; * legacy: detect the public URL from "Host" header if "X-Forwarded-Proto" header exists, otherwise use "ROOT_URL".
|
;; * legacy: (default <= 1.25) detect the public URL from "Host" header if "X-Forwarded-Proto" header exists, otherwise use "ROOT_URL".
|
||||||
;; * auto: always use "Host" header, and also use "X-Forwarded-Proto" header if it exists. If no "Host" header, use "ROOT_URL".
|
;; * auto: (default >= 1.26) always use "Host" header, and also use "X-Forwarded-Proto" header if it exists. If no "Host" header, use "ROOT_URL".
|
||||||
;PUBLIC_URL_DETECTION = legacy
|
;; * never: always use "ROOT_URL", never detect from request headers.
|
||||||
|
;PUBLIC_URL_DETECTION = auto
|
||||||
;;
|
;;
|
||||||
;; For development purpose only. It makes Gitea handle sub-path ("/sub-path/owner/repo/...") directly when debugging without a reverse proxy.
|
;; For development purpose only. It makes Gitea handle sub-path ("/sub-path/owner/repo/...") directly when debugging without a reverse proxy.
|
||||||
;; DO NOT USE IT IN PRODUCTION!!!
|
;; DO NOT USE IT IN PRODUCTION!!!
|
||||||
@@ -175,14 +175,15 @@ RUN_USER = ; git
|
|||||||
;; The port number the builtin SSH server should listen on, defaults to SSH_PORT
|
;; The port number the builtin SSH server should listen on, defaults to SSH_PORT
|
||||||
;SSH_LISTEN_PORT =
|
;SSH_LISTEN_PORT =
|
||||||
;;
|
;;
|
||||||
;; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
|
;; Root path of SSH user directory for the system's standalone SSH server if Gitea is not using its builtin SSH server.
|
||||||
|
;; Default is the '.ssh' directory in the run user's home directory.
|
||||||
;SSH_ROOT_PATH =
|
;SSH_ROOT_PATH =
|
||||||
;;
|
;;
|
||||||
;; Gitea will create a authorized_keys file by default when it is not using the internal ssh server
|
;; Gitea will create an authorized_keys file by default when it is not using the builtin SSH server
|
||||||
;; If you intend to use the AuthorizedKeysCommand functionality then you should turn this off.
|
;; If you intend to use the AuthorizedKeysCommand functionality then you should turn this off.
|
||||||
;SSH_CREATE_AUTHORIZED_KEYS_FILE = true
|
;SSH_CREATE_AUTHORIZED_KEYS_FILE = true
|
||||||
;;
|
;;
|
||||||
;; Gitea will create a authorized_principals file by default when it is not using the internal ssh server
|
;; Gitea will create an authorized_principals file by default when it is not using the builtin SSH server
|
||||||
;; 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
|
||||||
;;
|
;;
|
||||||
@@ -238,9 +239,6 @@ RUN_USER = ; git
|
|||||||
;; Indicate whether to check minimum key size with corresponding type
|
;; Indicate whether to check minimum key size with corresponding type
|
||||||
;MINIMUM_KEY_SIZE_CHECK = false
|
;MINIMUM_KEY_SIZE_CHECK = false
|
||||||
;;
|
;;
|
||||||
;; Disable CDN even in "prod" mode
|
|
||||||
;OFFLINE_MODE = true
|
|
||||||
;;
|
|
||||||
;; TLS Settings: Either ACME or manual
|
;; TLS Settings: Either ACME or manual
|
||||||
;; (Other common TLS configuration are found before)
|
;; (Other common TLS configuration are found before)
|
||||||
;ENABLE_ACME = false
|
;ENABLE_ACME = false
|
||||||
@@ -463,6 +461,11 @@ INTERNAL_TOKEN =
|
|||||||
;; Name of cookie used to store authentication information.
|
;; Name of cookie used to store authentication information.
|
||||||
;COOKIE_REMEMBER_NAME = gitea_incredible
|
;COOKIE_REMEMBER_NAME = gitea_incredible
|
||||||
;;
|
;;
|
||||||
|
;; URL or path that Gitea should redirect users to *after* performing its own logout.
|
||||||
|
;; Use this, if needed, when authentication is handled by a reverse proxy or SSO.
|
||||||
|
;; For example: "/my-sso/logout?return=/my-sso/home"
|
||||||
|
;REVERSE_PROXY_LOGOUT_REDIRECT =
|
||||||
|
;;
|
||||||
;; Reverse proxy authentication header name of user name, email, and full name
|
;; Reverse proxy authentication header name of user name, email, and full name
|
||||||
;REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
|
;REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
|
||||||
;REVERSE_PROXY_AUTHENTICATION_EMAIL = X-WEBAUTH-EMAIL
|
;REVERSE_PROXY_AUTHENTICATION_EMAIL = X-WEBAUTH-EMAIL
|
||||||
@@ -521,6 +524,9 @@ INTERNAL_TOKEN =
|
|||||||
;; Set the two-factor auth behavior.
|
;; Set the two-factor auth behavior.
|
||||||
;; Set to "enforced", to force users to enroll into Two-Factor Authentication, users without 2FA have no access to repositories via API or web.
|
;; Set to "enforced", to force users to enroll into Two-Factor Authentication, users without 2FA have no access to repositories via API or web.
|
||||||
;TWO_FACTOR_AUTH =
|
;TWO_FACTOR_AUTH =
|
||||||
|
;;
|
||||||
|
;; The value of the X-Frame-Options HTTP header for HTML responses. Use "unset" to remove the header.
|
||||||
|
;X_FRAME_OPTIONS = SAMEORIGIN
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@@ -1152,15 +1158,20 @@ LEVEL = Info
|
|||||||
;; Add co-authored-by and co-committed-by trailers if committer does not match author
|
;; Add co-authored-by and co-committed-by trailers if committer does not match author
|
||||||
;ADD_CO_COMMITTER_TRAILERS = true
|
;ADD_CO_COMMITTER_TRAILERS = true
|
||||||
;;
|
;;
|
||||||
;; In addition to testing patches using the three-way merge method, re-test conflicting patches with git apply
|
|
||||||
;TEST_CONFLICTING_PATCHES_WITH_GIT_APPLY = false
|
|
||||||
;;
|
|
||||||
;; Retarget child pull requests to the parent pull request branch target on merge of parent pull request. It only works on merged PRs where the head and base branch target the same repo.
|
;; Retarget child pull requests to the parent pull request branch target on merge of parent pull request. It only works on merged PRs where the head and base branch target the same repo.
|
||||||
;RETARGET_CHILDREN_ON_MERGE = true
|
;RETARGET_CHILDREN_ON_MERGE = true
|
||||||
;;
|
;;
|
||||||
|
;; Default source for the pull request title when opening a new PR.
|
||||||
|
;; "first-commit" uses the oldest commit's summary.
|
||||||
|
;; "auto" uses commit's summary if the PR only has one commit, normalizes the branch name if multiple commits.
|
||||||
|
;DEFAULT_TITLE_SOURCE = auto
|
||||||
|
;;
|
||||||
;; Delay mergeable check until page view or API access, for pull requests that have not been updated in the specified days when their base branches get updated.
|
;; Delay mergeable check until page view or API access, for pull requests that have not been updated in the specified days when their base branches get updated.
|
||||||
;; Use "-1" to always check all pull requests (old behavior). Use "0" to always delay the checks.
|
;; Use "-1" to always check all pull requests (old behavior). Use "0" to always delay the checks.
|
||||||
;DELAY_CHECK_FOR_INACTIVE_DAYS = 7
|
;DELAY_CHECK_FOR_INACTIVE_DAYS = 7
|
||||||
|
;;
|
||||||
|
;; Set the default value for "Delete pull request branch after merge by default" for new repositories
|
||||||
|
;DEFAULT_DELETE_BRANCH_AFTER_MERGE = false
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@@ -1178,9 +1189,17 @@ LEVEL = Info
|
|||||||
;[repository.release]
|
;[repository.release]
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
;; Comma-separated list of allowed release attachment file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
||||||
;ALLOWED_TYPES =
|
;ALLOWED_TYPES =
|
||||||
|
;;
|
||||||
|
;; Number of releases that are displayed on release page
|
||||||
;DEFAULT_PAGING_NUM = 10
|
;DEFAULT_PAGING_NUM = 10
|
||||||
|
;;
|
||||||
|
;; Max size of each release attachment file in megabytes. Defaults to 2GB
|
||||||
|
;FILE_MAX_SIZE = 2048
|
||||||
|
;;
|
||||||
|
;; Max number of release attachment files per upload. Defaults to 5
|
||||||
|
;MAX_FILES = 5
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@@ -1282,9 +1301,6 @@ LEVEL = Info
|
|||||||
;;
|
;;
|
||||||
;; headers to permit
|
;; headers to permit
|
||||||
;HEADERS = Content-Type,User-Agent
|
;HEADERS = Content-Type,User-Agent
|
||||||
;;
|
|
||||||
;; set X-FRAME-OPTIONS header
|
|
||||||
;X_FRAME_OPTIONS = SAMEORIGIN
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@@ -1329,9 +1345,12 @@ LEVEL = Info
|
|||||||
;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
|
;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
|
||||||
;THEMES =
|
;THEMES =
|
||||||
;;
|
;;
|
||||||
;; The icons for file list (basic/material), this is a temporary option which will be replaced by a user setting in the future.
|
;; The icon theme for files (basic/material)
|
||||||
;FILE_ICON_THEME = material
|
;FILE_ICON_THEME = material
|
||||||
;;
|
;;
|
||||||
|
;; The icon theme for folders (basic/material)
|
||||||
|
;FOLDER_ICON_THEME = basic
|
||||||
|
;;
|
||||||
;; All available reactions users can choose on issues/prs and comments.
|
;; All available reactions users can choose on issues/prs and comments.
|
||||||
;; Values can be emoji alias (:smile:) or a unicode emoji.
|
;; Values can be emoji alias (:smile:) or a unicode emoji.
|
||||||
;; For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png
|
;; For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png
|
||||||
@@ -1972,12 +1991,12 @@ LEVEL = Info
|
|||||||
;; or a custom avatar source, like: http://cn.gravatar.com/avatar/
|
;; or a custom avatar source, like: http://cn.gravatar.com/avatar/
|
||||||
;GRAVATAR_SOURCE = gravatar
|
;GRAVATAR_SOURCE = gravatar
|
||||||
;;
|
;;
|
||||||
;; This value will always be true in offline mode.
|
;; Deprecated, see Web UI Admin Panel -> Config -> Settings
|
||||||
;DISABLE_GRAVATAR = false
|
;DISABLE_GRAVATAR = false
|
||||||
;;
|
;;
|
||||||
;; Federated avatar lookup uses DNS to discover avatar associated
|
;; Federated avatar lookup uses DNS to discover avatar associated
|
||||||
;; with emails, see https://www.libravatar.org
|
;; with emails, see https://www.libravatar.org
|
||||||
;; This value will always be false in offline mode or when Gravatar is disabled.
|
;; Deprecated, see Web UI Admin Panel -> Config -> Settings
|
||||||
;ENABLE_FEDERATED_AVATAR = false
|
;ENABLE_FEDERATED_AVATAR = false
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@@ -1986,16 +2005,18 @@ LEVEL = Info
|
|||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;
|
;;
|
||||||
;; Whether issue and pull request attachments are enabled. Defaults to `true`
|
;; Whether issue, pull-request and release attachments are enabled. Defaults to `true`
|
||||||
|
;; ALLOWED_TYPES/MAX_SIZE/MAX_FILES in this section only affect issue and pull-request attachments, not release attachments.
|
||||||
|
;; Release attachment has its own config options in [repository.release] section.
|
||||||
;ENABLED = true
|
;ENABLED = true
|
||||||
;;
|
;;
|
||||||
;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
;; Comma-separated list of allowed issue/pull-request attachment file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
||||||
;ALLOWED_TYPES = .avif,.cpuprofile,.csv,.dmp,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.json,.jsonc,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.webp,.xls,.xlsx,.zip
|
;ALLOWED_TYPES = .avif,.cpuprofile,.csv,.dmp,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.json,.jsonc,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.webp,.xls,.xlsx,.zip
|
||||||
;;
|
;;
|
||||||
;; Max size of each file. Defaults to 2048MB
|
;; Max size of each issue/pull-request attachment file. Defaults to 100MB
|
||||||
;MAX_SIZE = 2048
|
;MAX_SIZE = 100
|
||||||
;;
|
;;
|
||||||
;; Max number of files per upload. Defaults to 5
|
;; Max number of issue/pull-request attachment files per upload. Defaults to 5
|
||||||
;MAX_FILES = 5
|
;MAX_FILES = 5
|
||||||
;;
|
;;
|
||||||
;; Storage type for attachments, `local` for local disk or `minio` for s3 compatible
|
;; Storage type for attachments, `local` for local disk or `minio` for s3 compatible
|
||||||
@@ -2268,6 +2289,22 @@ LEVEL = Info
|
|||||||
;; Unreferenced blobs created more than OLDER_THAN ago are subject to deletion
|
;; Unreferenced blobs created more than OLDER_THAN ago are subject to deletion
|
||||||
;OLDER_THAN = 24h
|
;OLDER_THAN = 24h
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Synchronize repository licenses
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[cron.sync_repo_licenses]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Whether to enable the job
|
||||||
|
;ENABLED = false
|
||||||
|
;; Whether to always run at least once at start up time (if ENABLED)
|
||||||
|
;RUN_AT_START = false
|
||||||
|
;; Whether to emit notice on successful execution too
|
||||||
|
;NOTICE_ON_SUCCESS = false
|
||||||
|
;; Time interval for job to run
|
||||||
|
;SCHEDULE = @annually
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@@ -2329,6 +2366,18 @@ LEVEL = Info
|
|||||||
;NOTICE_ON_SUCCESS = false
|
;NOTICE_ON_SUCCESS = false
|
||||||
;SCHEDULE = @every 72h
|
;SCHEDULE = @every 72h
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Update the '.ssh/authorized_principals' file
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[cron.resync_all_sshprincipals]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;ENABLED = false
|
||||||
|
;RUN_AT_START = false
|
||||||
|
;NOTICE_ON_SUCCESS = false
|
||||||
|
;SCHEDULE = @every 72h
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Resynchronize git hooks of all repositories (pre-receive, update, post-receive, proc-receive, ...)
|
;; Resynchronize git hooks of all repositories (pre-receive, update, post-receive, proc-receive, ...)
|
||||||
@@ -2437,6 +2486,70 @@ LEVEL = Info
|
|||||||
;Check at least this proportion of LFSMetaObjects per repo. (This may cause all stale LFSMetaObjects to be checked.)
|
;Check at least this proportion of LFSMetaObjects per repo. (This may cause all stale LFSMetaObjects to be checked.)
|
||||||
;PROPORTION_TO_CHECK_PER_REPO = 0.6
|
;PROPORTION_TO_CHECK_PER_REPO = 0.6
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Rebuild issue index
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[cron.rebuild_issue_indexer]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;ENABLED = false
|
||||||
|
;RUN_AT_START = false
|
||||||
|
;NO_SUCCESS_NOTICE = false
|
||||||
|
;SCHEDULE = @annually
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Actions cron tasks
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Stop running tasks which haven't been updated for a long time
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[cron.stop_zombie_tasks]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;ENABLED = true
|
||||||
|
;RUN_AT_START = true
|
||||||
|
;NO_SUCCESS_NOTICE = false
|
||||||
|
;SCHEDULE = @every 5m
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Stop running tasks which have running status and continuous updates but don't end for a long time
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[cron.stop_endless_tasks]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;ENABLED = true
|
||||||
|
;RUN_AT_START = true
|
||||||
|
;NO_SUCCESS_NOTICE = false
|
||||||
|
;SCHEDULE = @every 30m
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Cancel jobs which haven't been picked up for a long time
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[cron.cancel_abandoned_jobs]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;ENABLED = true
|
||||||
|
;RUN_AT_START = false
|
||||||
|
;NO_SUCCESS_NOTICE = false
|
||||||
|
;SCHEDULE = @every 6h
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Start cron based actions
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[cron.start_schedule_tasks]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;ENABLED = true
|
||||||
|
;RUN_AT_START = false
|
||||||
|
;NO_SUCCESS_NOTICE = false
|
||||||
|
;SCHEDULE = @every 1m
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;[mirror]
|
;[mirror]
|
||||||
@@ -2690,6 +2803,8 @@ LEVEL = Info
|
|||||||
;LIMIT_SIZE_SWIFT = -1
|
;LIMIT_SIZE_SWIFT = -1
|
||||||
;; Maximum size of a Vagrant upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
;; Maximum size of a Vagrant upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
;LIMIT_SIZE_VAGRANT = -1
|
;LIMIT_SIZE_VAGRANT = -1
|
||||||
|
;; Maximum size of a Terraform state upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
|
;LIMIT_SIZE_TERRAFORM_STATE = -1
|
||||||
;; Enable RPM re-signing by default. (It will overwrite the old signature ,using v4 format, not compatible with CentOS 6 or older)
|
;; Enable RPM re-signing by default. (It will overwrite the old signature ,using v4 format, not compatible with CentOS 6 or older)
|
||||||
;DEFAULT_RPM_SIGN_ENABLED = false
|
;DEFAULT_RPM_SIGN_ENABLED = false
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@@ -2855,6 +2970,9 @@ LEVEL = Info
|
|||||||
;ABANDONED_JOB_TIMEOUT = 24h
|
;ABANDONED_JOB_TIMEOUT = 24h
|
||||||
;; Strings committers can place inside a commit message or PR title to skip executing the corresponding actions workflow
|
;; Strings committers can place inside a commit message or PR title to skip executing the corresponding actions workflow
|
||||||
;SKIP_WORKFLOW_STRINGS = [skip ci],[ci skip],[no ci],[skip actions],[actions skip]
|
;SKIP_WORKFLOW_STRINGS = [skip ci],[ci skip],[no ci],[skip actions],[actions skip]
|
||||||
|
;; Comma-separated list of workflow directories, the first one to exist
|
||||||
|
;; in a repo is used to find Actions workflow files
|
||||||
|
;WORKFLOW_DIRS = .gitea/workflows,.github/workflows
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# $1 = exit code of the run script, $2 = signal
|
||||||
|
if [ "$1" -ne 0 ]; then
|
||||||
|
# avoid immediately restarting the sshd service, which may cause CPU 100% if the error (permission, configuration) is not fixed
|
||||||
|
echo "openssh failed with exit code $1 - waiting a short delay before attempting a restart"
|
||||||
|
sleep 3
|
||||||
|
fi
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ ROOT_PATH = /data/gitea/log
|
|||||||
[security]
|
[security]
|
||||||
INSTALL_LOCK = $INSTALL_LOCK
|
INSTALL_LOCK = $INSTALL_LOCK
|
||||||
SECRET_KEY = $SECRET_KEY
|
SECRET_KEY = $SECRET_KEY
|
||||||
REVERSE_PROXY_LIMIT = 1
|
|
||||||
REVERSE_PROXY_TRUSTED_PROXIES = *
|
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
||||||
|
|||||||
@@ -48,8 +48,6 @@ ROOT_PATH = $GITEA_WORK_DIR/data/log
|
|||||||
[security]
|
[security]
|
||||||
INSTALL_LOCK = $INSTALL_LOCK
|
INSTALL_LOCK = $INSTALL_LOCK
|
||||||
SECRET_KEY = $SECRET_KEY
|
SECRET_KEY = $SECRET_KEY
|
||||||
REVERSE_PROXY_LIMIT = 1
|
|
||||||
REVERSE_PROXY_TRUSTED_PROXIES = *
|
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
||||||
|
|||||||
+57
-37
@@ -1,5 +1,6 @@
|
|||||||
import arrayFunc from 'eslint-plugin-array-func';
|
import arrayFunc from 'eslint-plugin-array-func';
|
||||||
import comments from '@eslint-community/eslint-plugin-eslint-comments';
|
import comments from '@eslint-community/eslint-plugin-eslint-comments';
|
||||||
|
import deMorgan from 'eslint-plugin-de-morgan';
|
||||||
import github from 'eslint-plugin-github';
|
import github from 'eslint-plugin-github';
|
||||||
import globals from 'globals';
|
import globals from 'globals';
|
||||||
import importPlugin from 'eslint-plugin-import-x';
|
import importPlugin from 'eslint-plugin-import-x';
|
||||||
@@ -15,6 +16,7 @@ import vue from 'eslint-plugin-vue';
|
|||||||
import vueScopedCss from 'eslint-plugin-vue-scoped-css';
|
import vueScopedCss from 'eslint-plugin-vue-scoped-css';
|
||||||
import wc from 'eslint-plugin-wc';
|
import wc from 'eslint-plugin-wc';
|
||||||
import {defineConfig, globalIgnores} from 'eslint/config';
|
import {defineConfig, globalIgnores} from 'eslint/config';
|
||||||
|
import type {ESLint} from 'eslint';
|
||||||
|
|
||||||
const jsExts = ['js', 'mjs', 'cjs'] as const;
|
const jsExts = ['js', 'mjs', 'cjs'] as const;
|
||||||
const tsExts = ['ts', 'mts', 'cts'] as const;
|
const tsExts = ['ts', 'mts', 'cts'] as const;
|
||||||
@@ -62,8 +64,8 @@ export default defineConfig([
|
|||||||
'@stylistic': stylistic,
|
'@stylistic': stylistic,
|
||||||
'@typescript-eslint': typescriptPlugin.plugin,
|
'@typescript-eslint': typescriptPlugin.plugin,
|
||||||
'array-func': arrayFunc,
|
'array-func': arrayFunc,
|
||||||
// @ts-expect-error -- https://github.com/un-ts/eslint-plugin-import-x/issues/203
|
'de-morgan': deMorgan,
|
||||||
'import-x': importPlugin,
|
'import-x': importPlugin as unknown as ESLint.Plugin, // https://github.com/un-ts/eslint-plugin-import-x/issues/203
|
||||||
regexp,
|
regexp,
|
||||||
sonarjs,
|
sonarjs,
|
||||||
unicorn,
|
unicorn,
|
||||||
@@ -156,7 +158,7 @@ export default defineConfig([
|
|||||||
'@typescript-eslint/adjacent-overload-signatures': [0],
|
'@typescript-eslint/adjacent-overload-signatures': [0],
|
||||||
'@typescript-eslint/array-type': [0],
|
'@typescript-eslint/array-type': [0],
|
||||||
'@typescript-eslint/await-thenable': [2],
|
'@typescript-eslint/await-thenable': [2],
|
||||||
'@typescript-eslint/ban-ts-comment': [2, {'ts-expect-error': false, 'ts-ignore': true, 'ts-nocheck': false, 'ts-check': false}],
|
'@typescript-eslint/ban-ts-comment': [2, {'ts-expect-error': true, 'ts-ignore': true, 'ts-nocheck': false, 'ts-check': false}],
|
||||||
'@typescript-eslint/ban-tslint-comment': [0],
|
'@typescript-eslint/ban-tslint-comment': [0],
|
||||||
'@typescript-eslint/class-literal-property-style': [0],
|
'@typescript-eslint/class-literal-property-style': [0],
|
||||||
'@typescript-eslint/class-methods-use-this': [0],
|
'@typescript-eslint/class-methods-use-this': [0],
|
||||||
@@ -179,7 +181,7 @@ export default defineConfig([
|
|||||||
'@typescript-eslint/naming-convention': [0],
|
'@typescript-eslint/naming-convention': [0],
|
||||||
'@typescript-eslint/no-array-constructor': [2],
|
'@typescript-eslint/no-array-constructor': [2],
|
||||||
'@typescript-eslint/no-array-delete': [2],
|
'@typescript-eslint/no-array-delete': [2],
|
||||||
'@typescript-eslint/no-base-to-string': [0],
|
'@typescript-eslint/no-base-to-string': [2],
|
||||||
'@typescript-eslint/no-confusing-non-null-assertion': [2],
|
'@typescript-eslint/no-confusing-non-null-assertion': [2],
|
||||||
'@typescript-eslint/no-confusing-void-expression': [0],
|
'@typescript-eslint/no-confusing-void-expression': [0],
|
||||||
'@typescript-eslint/no-deprecated': [2],
|
'@typescript-eslint/no-deprecated': [2],
|
||||||
@@ -211,7 +213,7 @@ export default defineConfig([
|
|||||||
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': [0],
|
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': [0],
|
||||||
'@typescript-eslint/no-non-null-asserted-optional-chain': [2],
|
'@typescript-eslint/no-non-null-asserted-optional-chain': [2],
|
||||||
'@typescript-eslint/no-non-null-assertion': [0],
|
'@typescript-eslint/no-non-null-assertion': [0],
|
||||||
'@typescript-eslint/no-redeclare': [0],
|
'@typescript-eslint/no-redeclare': [2],
|
||||||
'@typescript-eslint/no-redundant-type-constituents': [2],
|
'@typescript-eslint/no-redundant-type-constituents': [2],
|
||||||
'@typescript-eslint/no-require-imports': [2],
|
'@typescript-eslint/no-require-imports': [2],
|
||||||
'@typescript-eslint/no-restricted-imports': [0],
|
'@typescript-eslint/no-restricted-imports': [0],
|
||||||
@@ -254,10 +256,10 @@ export default defineConfig([
|
|||||||
'@typescript-eslint/prefer-function-type': [2],
|
'@typescript-eslint/prefer-function-type': [2],
|
||||||
'@typescript-eslint/prefer-includes': [2],
|
'@typescript-eslint/prefer-includes': [2],
|
||||||
'@typescript-eslint/prefer-literal-enum-member': [0],
|
'@typescript-eslint/prefer-literal-enum-member': [0],
|
||||||
'@typescript-eslint/prefer-namespace-keyword': [0],
|
'@typescript-eslint/prefer-namespace-keyword': [2],
|
||||||
'@typescript-eslint/prefer-nullish-coalescing': [0],
|
'@typescript-eslint/prefer-nullish-coalescing': [0],
|
||||||
'@typescript-eslint/prefer-optional-chain': [2, {requireNullish: true}],
|
'@typescript-eslint/prefer-optional-chain': [2, {requireNullish: true}],
|
||||||
'@typescript-eslint/prefer-promise-reject-errors': [0],
|
'@typescript-eslint/prefer-promise-reject-errors': [2],
|
||||||
'@typescript-eslint/prefer-readonly': [0],
|
'@typescript-eslint/prefer-readonly': [0],
|
||||||
'@typescript-eslint/prefer-readonly-parameter-types': [0],
|
'@typescript-eslint/prefer-readonly-parameter-types': [0],
|
||||||
'@typescript-eslint/prefer-reduce-type-parameter': [0],
|
'@typescript-eslint/prefer-reduce-type-parameter': [0],
|
||||||
@@ -268,7 +270,7 @@ export default defineConfig([
|
|||||||
'@typescript-eslint/require-array-sort-compare': [0],
|
'@typescript-eslint/require-array-sort-compare': [0],
|
||||||
'@typescript-eslint/require-await': [0],
|
'@typescript-eslint/require-await': [0],
|
||||||
'@typescript-eslint/restrict-plus-operands': [2],
|
'@typescript-eslint/restrict-plus-operands': [2],
|
||||||
'@typescript-eslint/restrict-template-expressions': [0],
|
'@typescript-eslint/restrict-template-expressions': [2],
|
||||||
'@typescript-eslint/return-await': [0],
|
'@typescript-eslint/return-await': [0],
|
||||||
'@typescript-eslint/strict-boolean-expressions': [0],
|
'@typescript-eslint/strict-boolean-expressions': [0],
|
||||||
'@typescript-eslint/strict-void-return': [0],
|
'@typescript-eslint/strict-void-return': [0],
|
||||||
@@ -295,6 +297,8 @@ export default defineConfig([
|
|||||||
'consistent-this': [0],
|
'consistent-this': [0],
|
||||||
'constructor-super': [2],
|
'constructor-super': [2],
|
||||||
'curly': [0],
|
'curly': [0],
|
||||||
|
'de-morgan/no-negated-conjunction': [2],
|
||||||
|
'de-morgan/no-negated-disjunction': [2],
|
||||||
'default-case-last': [2],
|
'default-case-last': [2],
|
||||||
'default-case': [0],
|
'default-case': [0],
|
||||||
'default-param-last': [0],
|
'default-param-last': [0],
|
||||||
@@ -325,7 +329,7 @@ export default defineConfig([
|
|||||||
'github/no-innerText': [2],
|
'github/no-innerText': [2],
|
||||||
'github/no-then': [2],
|
'github/no-then': [2],
|
||||||
'github/no-useless-passive': [2],
|
'github/no-useless-passive': [2],
|
||||||
'github/prefer-observers': [2],
|
'github/prefer-observers': [0],
|
||||||
'github/require-passive-events': [2],
|
'github/require-passive-events': [2],
|
||||||
'github/unescaped-html-literal': [2],
|
'github/unescaped-html-literal': [2],
|
||||||
'grouped-accessor-pairs': [2],
|
'grouped-accessor-pairs': [2],
|
||||||
@@ -342,7 +346,7 @@ export default defineConfig([
|
|||||||
'import-x/first': [2],
|
'import-x/first': [2],
|
||||||
'import-x/group-exports': [0],
|
'import-x/group-exports': [0],
|
||||||
'import-x/max-dependencies': [0],
|
'import-x/max-dependencies': [0],
|
||||||
'import-x/named': [2],
|
'import-x/named': [0],
|
||||||
'import-x/namespace': [0],
|
'import-x/namespace': [0],
|
||||||
'import-x/newline-after-import': [0],
|
'import-x/newline-after-import': [0],
|
||||||
'import-x/no-absolute-path': [0],
|
'import-x/no-absolute-path': [0],
|
||||||
@@ -437,7 +441,7 @@ export default defineConfig([
|
|||||||
'no-import-assign': [2],
|
'no-import-assign': [2],
|
||||||
'no-inline-comments': [0],
|
'no-inline-comments': [0],
|
||||||
'no-inner-declarations': [2],
|
'no-inner-declarations': [2],
|
||||||
'no-invalid-regexp': [2],
|
'no-invalid-regexp': [0], // handled by regexp/no-invalid-regexp
|
||||||
'no-invalid-this': [0],
|
'no-invalid-this': [0],
|
||||||
'no-irregular-whitespace': [2],
|
'no-irregular-whitespace': [2],
|
||||||
'no-iterator': [2],
|
'no-iterator': [2],
|
||||||
@@ -551,7 +555,7 @@ export default defineConfig([
|
|||||||
'no-new-func': [0], // handled by @typescript-eslint/no-implied-eval
|
'no-new-func': [0], // handled by @typescript-eslint/no-implied-eval
|
||||||
'no-new-native-nonconstructor': [2],
|
'no-new-native-nonconstructor': [2],
|
||||||
'no-new-object': [2],
|
'no-new-object': [2],
|
||||||
'no-new-symbol': [2],
|
'no-new-symbol': [0], // handled by no-new-native-nonconstructor
|
||||||
'no-new-wrappers': [2],
|
'no-new-wrappers': [2],
|
||||||
'no-new': [0],
|
'no-new': [0],
|
||||||
'no-nonoctal-decimal-escape': [2],
|
'no-nonoctal-decimal-escape': [2],
|
||||||
@@ -566,9 +570,11 @@ export default defineConfig([
|
|||||||
'no-redeclare': [0], // must be disabled for typescript overloads
|
'no-redeclare': [0], // must be disabled for typescript overloads
|
||||||
'no-regex-spaces': [2],
|
'no-regex-spaces': [2],
|
||||||
'no-restricted-exports': [0],
|
'no-restricted-exports': [0],
|
||||||
'no-restricted-globals': [2, ...restrictedGlobals],
|
'no-restricted-imports': [2, {paths: [
|
||||||
'no-restricted-properties': [2, ...restrictedProperties],
|
{name: 'jquery', message: 'Use the global $ instead', allowTypeImports: true},
|
||||||
'no-restricted-imports': [0],
|
{name: 'htmx.org', message: 'Use the global htmx instead', allowTypeImports: true},
|
||||||
|
{name: 'idiomorph/htmx', message: 'Loaded in globals.ts', allowTypeImports: true},
|
||||||
|
]}],
|
||||||
'no-restricted-syntax': [2, 'WithStatement', 'ForInStatement', 'LabeledStatement', 'SequenceExpression'],
|
'no-restricted-syntax': [2, 'WithStatement', 'ForInStatement', 'LabeledStatement', 'SequenceExpression'],
|
||||||
'no-return-assign': [0],
|
'no-return-assign': [0],
|
||||||
'no-script-url': [2],
|
'no-script-url': [2],
|
||||||
@@ -582,10 +588,11 @@ export default defineConfig([
|
|||||||
'no-template-curly-in-string': [2],
|
'no-template-curly-in-string': [2],
|
||||||
'no-ternary': [0],
|
'no-ternary': [0],
|
||||||
'no-this-before-super': [2],
|
'no-this-before-super': [2],
|
||||||
'no-throw-literal': [2],
|
'no-throw-literal': [0], // handled by @typescript-eslint/only-throw-error
|
||||||
'no-undef-init': [2],
|
'no-undef-init': [2],
|
||||||
'no-undef': [2], // it is still needed by eslint & IDE to prompt undefined names in real time
|
'no-undef': [2], // it is still needed by eslint & IDE to prompt undefined names in real time
|
||||||
'no-undefined': [0],
|
'no-undefined': [0],
|
||||||
|
'no-unassigned-vars': [2],
|
||||||
'no-underscore-dangle': [0],
|
'no-underscore-dangle': [0],
|
||||||
'no-unexpected-multiline': [2],
|
'no-unexpected-multiline': [2],
|
||||||
'no-unmodified-loop-condition': [2],
|
'no-unmodified-loop-condition': [2],
|
||||||
@@ -600,7 +607,7 @@ export default defineConfig([
|
|||||||
'no-unused-vars': [0], // handled by @typescript-eslint/no-unused-vars
|
'no-unused-vars': [0], // handled by @typescript-eslint/no-unused-vars
|
||||||
'no-use-before-define': [0], // handled by @typescript-eslint/no-use-before-define
|
'no-use-before-define': [0], // handled by @typescript-eslint/no-use-before-define
|
||||||
'no-useless-assignment': [2],
|
'no-useless-assignment': [2],
|
||||||
'no-useless-backreference': [2],
|
'no-useless-backreference': [0], // handled by regexp/no-useless-backreference
|
||||||
'no-useless-call': [2],
|
'no-useless-call': [2],
|
||||||
'no-useless-catch': [2],
|
'no-useless-catch': [2],
|
||||||
'no-useless-computed-key': [2],
|
'no-useless-computed-key': [2],
|
||||||
@@ -608,7 +615,7 @@ export default defineConfig([
|
|||||||
'no-useless-constructor': [2],
|
'no-useless-constructor': [2],
|
||||||
'no-useless-escape': [2],
|
'no-useless-escape': [2],
|
||||||
'no-useless-rename': [2],
|
'no-useless-rename': [2],
|
||||||
'no-useless-return': [2],
|
'no-useless-return': [0], // handled by sonarjs/no-redundant-jump
|
||||||
'no-var': [2],
|
'no-var': [2],
|
||||||
'no-void': [2],
|
'no-void': [2],
|
||||||
'no-warning-comments': [0],
|
'no-warning-comments': [0],
|
||||||
@@ -617,7 +624,7 @@ export default defineConfig([
|
|||||||
'one-var-declaration-per-line': [0],
|
'one-var-declaration-per-line': [0],
|
||||||
'one-var': [0],
|
'one-var': [0],
|
||||||
'operator-assignment': [2, 'always'],
|
'operator-assignment': [2, 'always'],
|
||||||
'operator-linebreak': [2, 'after'],
|
'operator-linebreak': [0], // handled by @stylistic/operator-linebreak
|
||||||
'prefer-arrow-callback': [2, {allowNamedFunctions: true, allowUnboundThis: true}],
|
'prefer-arrow-callback': [2, {allowNamedFunctions: true, allowUnboundThis: true}],
|
||||||
'prefer-const': [2, {destructuring: 'all', ignoreReadBeforeAssign: true}],
|
'prefer-const': [2, {destructuring: 'all', ignoreReadBeforeAssign: true}],
|
||||||
'prefer-destructuring': [0],
|
'prefer-destructuring': [0],
|
||||||
@@ -626,19 +633,20 @@ export default defineConfig([
|
|||||||
'prefer-numeric-literals': [2],
|
'prefer-numeric-literals': [2],
|
||||||
'prefer-object-has-own': [2],
|
'prefer-object-has-own': [2],
|
||||||
'prefer-object-spread': [2],
|
'prefer-object-spread': [2],
|
||||||
'prefer-promise-reject-errors': [2, {allowEmptyReject: false}],
|
'prefer-promise-reject-errors': [0], // handled by @typescript-eslint/prefer-promise-reject-errors
|
||||||
'prefer-regex-literals': [2],
|
'prefer-regex-literals': [2],
|
||||||
'prefer-rest-params': [2],
|
'prefer-rest-params': [2],
|
||||||
'prefer-spread': [2],
|
'prefer-spread': [2],
|
||||||
'prefer-template': [2],
|
'prefer-template': [2],
|
||||||
'radix': [2, 'as-needed'],
|
'preserve-caught-error': [0],
|
||||||
|
'radix': [0],
|
||||||
'regexp/confusing-quantifier': [2],
|
'regexp/confusing-quantifier': [2],
|
||||||
'regexp/control-character-escape': [2],
|
'regexp/control-character-escape': [2],
|
||||||
'regexp/hexadecimal-escape': [0],
|
'regexp/hexadecimal-escape': [0],
|
||||||
'regexp/letter-case': [0],
|
'regexp/letter-case': [0],
|
||||||
'regexp/match-any': [2],
|
'regexp/match-any': [2],
|
||||||
'regexp/negation': [2],
|
'regexp/negation': [2],
|
||||||
'regexp/no-contradiction-with-assertion': [0],
|
'regexp/no-contradiction-with-assertion': [2],
|
||||||
'regexp/no-control-character': [0],
|
'regexp/no-control-character': [0],
|
||||||
'regexp/no-dupe-characters-character-class': [2],
|
'regexp/no-dupe-characters-character-class': [2],
|
||||||
'regexp/no-dupe-disjunctions': [2],
|
'regexp/no-dupe-disjunctions': [2],
|
||||||
@@ -654,8 +662,8 @@ export default defineConfig([
|
|||||||
'regexp/no-invisible-character': [2],
|
'regexp/no-invisible-character': [2],
|
||||||
'regexp/no-lazy-ends': [2],
|
'regexp/no-lazy-ends': [2],
|
||||||
'regexp/no-legacy-features': [2],
|
'regexp/no-legacy-features': [2],
|
||||||
'regexp/no-misleading-capturing-group': [0],
|
'regexp/no-misleading-capturing-group': [2],
|
||||||
'regexp/no-misleading-unicode-character': [0],
|
'regexp/no-misleading-unicode-character': [2],
|
||||||
'regexp/no-missing-g-flag': [2],
|
'regexp/no-missing-g-flag': [2],
|
||||||
'regexp/no-non-standard-flag': [2],
|
'regexp/no-non-standard-flag': [2],
|
||||||
'regexp/no-obscure-range': [2],
|
'regexp/no-obscure-range': [2],
|
||||||
@@ -697,7 +705,7 @@ export default defineConfig([
|
|||||||
'regexp/prefer-question-quantifier': [2],
|
'regexp/prefer-question-quantifier': [2],
|
||||||
'regexp/prefer-range': [2],
|
'regexp/prefer-range': [2],
|
||||||
'regexp/prefer-regexp-exec': [2],
|
'regexp/prefer-regexp-exec': [2],
|
||||||
'regexp/prefer-regexp-test': [2],
|
'regexp/prefer-regexp-test': [0], // handled by unicorn/prefer-regexp-test
|
||||||
'regexp/prefer-result-array-groups': [0],
|
'regexp/prefer-result-array-groups': [0],
|
||||||
'regexp/prefer-set-operation': [2],
|
'regexp/prefer-set-operation': [2],
|
||||||
'regexp/prefer-star-quantifier': [2],
|
'regexp/prefer-star-quantifier': [2],
|
||||||
@@ -727,7 +735,7 @@ export default defineConfig([
|
|||||||
'sonarjs/no-empty-collection': [2],
|
'sonarjs/no-empty-collection': [2],
|
||||||
'sonarjs/no-extra-arguments': [2],
|
'sonarjs/no-extra-arguments': [2],
|
||||||
'sonarjs/no-gratuitous-expressions': [2],
|
'sonarjs/no-gratuitous-expressions': [2],
|
||||||
'sonarjs/no-identical-conditions': [2],
|
'sonarjs/no-identical-conditions': [0], // handled by no-dupe-else-if
|
||||||
'sonarjs/no-identical-expressions': [2],
|
'sonarjs/no-identical-expressions': [2],
|
||||||
'sonarjs/no-identical-functions': [2, 5],
|
'sonarjs/no-identical-functions': [2, 5],
|
||||||
'sonarjs/no-ignored-return': [2],
|
'sonarjs/no-ignored-return': [2],
|
||||||
@@ -740,7 +748,7 @@ export default defineConfig([
|
|||||||
'sonarjs/no-small-switch': [0],
|
'sonarjs/no-small-switch': [0],
|
||||||
'sonarjs/no-unused-collection': [2],
|
'sonarjs/no-unused-collection': [2],
|
||||||
'sonarjs/no-use-of-empty-return-value': [2],
|
'sonarjs/no-use-of-empty-return-value': [2],
|
||||||
'sonarjs/no-useless-catch': [2],
|
'sonarjs/no-useless-catch': [0], // handled by no-useless-catch
|
||||||
'sonarjs/non-existent-operator': [2],
|
'sonarjs/non-existent-operator': [2],
|
||||||
'sonarjs/prefer-immediate-return': [0],
|
'sonarjs/prefer-immediate-return': [0],
|
||||||
'sonarjs/prefer-object-literal': [0],
|
'sonarjs/prefer-object-literal': [0],
|
||||||
@@ -756,6 +764,7 @@ export default defineConfig([
|
|||||||
'unicorn/catch-error-name': [0],
|
'unicorn/catch-error-name': [0],
|
||||||
'unicorn/consistent-destructuring': [2],
|
'unicorn/consistent-destructuring': [2],
|
||||||
'unicorn/consistent-empty-array-spread': [2],
|
'unicorn/consistent-empty-array-spread': [2],
|
||||||
|
'unicorn/consistent-template-literal-escape': [2],
|
||||||
'unicorn/consistent-existence-index-check': [0],
|
'unicorn/consistent-existence-index-check': [0],
|
||||||
'unicorn/consistent-function-scoping': [0],
|
'unicorn/consistent-function-scoping': [0],
|
||||||
'unicorn/custom-error-definition': [0],
|
'unicorn/custom-error-definition': [0],
|
||||||
@@ -767,6 +776,7 @@ export default defineConfig([
|
|||||||
'unicorn/filename-case': [0],
|
'unicorn/filename-case': [0],
|
||||||
'unicorn/import-index': [0],
|
'unicorn/import-index': [0],
|
||||||
'unicorn/import-style': [0],
|
'unicorn/import-style': [0],
|
||||||
|
'unicorn/isolated-functions': [2, {functions: []}],
|
||||||
'unicorn/new-for-builtins': [2],
|
'unicorn/new-for-builtins': [2],
|
||||||
'unicorn/no-abusive-eslint-disable': [0],
|
'unicorn/no-abusive-eslint-disable': [0],
|
||||||
'unicorn/no-anonymous-default-export': [0],
|
'unicorn/no-anonymous-default-export': [0],
|
||||||
@@ -806,10 +816,11 @@ export default defineConfig([
|
|||||||
'unicorn/no-unnecessary-await': [2],
|
'unicorn/no-unnecessary-await': [2],
|
||||||
'unicorn/no-unnecessary-polyfills': [2],
|
'unicorn/no-unnecessary-polyfills': [2],
|
||||||
'unicorn/no-unreadable-array-destructuring': [0],
|
'unicorn/no-unreadable-array-destructuring': [0],
|
||||||
'unicorn/no-unreadable-iife': [2],
|
'unicorn/no-unreadable-iife': [0],
|
||||||
'unicorn/no-unused-properties': [2],
|
'unicorn/no-unused-properties': [2],
|
||||||
'unicorn/no-useless-collection-argument': [2],
|
'unicorn/no-useless-collection-argument': [2],
|
||||||
'unicorn/no-useless-fallback-in-spread': [2],
|
'unicorn/no-useless-fallback-in-spread': [2],
|
||||||
|
'unicorn/no-useless-iterator-to-array': [2],
|
||||||
'unicorn/no-useless-length-check': [2],
|
'unicorn/no-useless-length-check': [2],
|
||||||
'unicorn/no-useless-promise-resolve-reject': [2],
|
'unicorn/no-useless-promise-resolve-reject': [2],
|
||||||
'unicorn/no-useless-spread': [2],
|
'unicorn/no-useless-spread': [2],
|
||||||
@@ -819,7 +830,7 @@ export default defineConfig([
|
|||||||
'unicorn/number-literal-case': [0],
|
'unicorn/number-literal-case': [0],
|
||||||
'unicorn/numeric-separators-style': [0],
|
'unicorn/numeric-separators-style': [0],
|
||||||
'unicorn/prefer-add-event-listener': [2],
|
'unicorn/prefer-add-event-listener': [2],
|
||||||
'unicorn/prefer-array-find': [2],
|
'unicorn/prefer-array-find': [0], // handled by @typescript-eslint/prefer-find
|
||||||
'unicorn/prefer-array-flat': [2],
|
'unicorn/prefer-array-flat': [2],
|
||||||
'unicorn/prefer-array-flat-map': [2],
|
'unicorn/prefer-array-flat-map': [2],
|
||||||
'unicorn/prefer-array-index-of': [2],
|
'unicorn/prefer-array-index-of': [2],
|
||||||
@@ -836,7 +847,7 @@ export default defineConfig([
|
|||||||
'unicorn/prefer-event-target': [2],
|
'unicorn/prefer-event-target': [2],
|
||||||
'unicorn/prefer-export-from': [0],
|
'unicorn/prefer-export-from': [0],
|
||||||
'unicorn/prefer-global-this': [0],
|
'unicorn/prefer-global-this': [0],
|
||||||
'unicorn/prefer-includes': [2],
|
'unicorn/prefer-includes': [0], // handled by @typescript-eslint/prefer-includes
|
||||||
'unicorn/prefer-json-parse-buffer': [0],
|
'unicorn/prefer-json-parse-buffer': [0],
|
||||||
'unicorn/prefer-keyboard-event-key': [2],
|
'unicorn/prefer-keyboard-event-key': [2],
|
||||||
'unicorn/prefer-logical-operator-over-ternary': [2],
|
'unicorn/prefer-logical-operator-over-ternary': [2],
|
||||||
@@ -859,11 +870,12 @@ export default defineConfig([
|
|||||||
'unicorn/prefer-response-static-json': [2],
|
'unicorn/prefer-response-static-json': [2],
|
||||||
'unicorn/prefer-set-has': [0],
|
'unicorn/prefer-set-has': [0],
|
||||||
'unicorn/prefer-set-size': [2],
|
'unicorn/prefer-set-size': [2],
|
||||||
|
'unicorn/prefer-simple-condition-first': [0],
|
||||||
'unicorn/prefer-spread': [0],
|
'unicorn/prefer-spread': [0],
|
||||||
'unicorn/prefer-string-raw': [0],
|
'unicorn/prefer-string-raw': [0],
|
||||||
'unicorn/prefer-string-replace-all': [0],
|
'unicorn/prefer-string-replace-all': [0],
|
||||||
'unicorn/prefer-string-slice': [0],
|
'unicorn/prefer-string-slice': [0],
|
||||||
'unicorn/prefer-string-starts-ends-with': [2],
|
'unicorn/prefer-string-starts-ends-with': [0], // handled by @typescript-eslint/prefer-string-starts-ends-with
|
||||||
'unicorn/prefer-string-trim-start-end': [2],
|
'unicorn/prefer-string-trim-start-end': [2],
|
||||||
'unicorn/prefer-structured-clone': [2],
|
'unicorn/prefer-structured-clone': [2],
|
||||||
'unicorn/prefer-switch': [0],
|
'unicorn/prefer-switch': [0],
|
||||||
@@ -877,6 +889,7 @@ export default defineConfig([
|
|||||||
'unicorn/require-post-message-target-origin': [0],
|
'unicorn/require-post-message-target-origin': [0],
|
||||||
'unicorn/string-content': [0],
|
'unicorn/string-content': [0],
|
||||||
'unicorn/switch-case-braces': [0],
|
'unicorn/switch-case-braces': [0],
|
||||||
|
'unicorn/switch-case-break-position': [2],
|
||||||
'unicorn/template-indent': [2],
|
'unicorn/template-indent': [2],
|
||||||
'unicorn/text-encoding-identifier-case': [0],
|
'unicorn/text-encoding-identifier-case': [0],
|
||||||
'unicorn/throw-new-error': [2],
|
'unicorn/throw-new-error': [2],
|
||||||
@@ -910,9 +923,11 @@ export default defineConfig([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
...playwright.configs['flat/recommended'],
|
...playwright.configs['flat/recommended'],
|
||||||
files: ['tests/e2e/**'],
|
files: ['tests/e2e/**/*.test.ts'],
|
||||||
|
languageOptions: {globals: {...globals.nodeBuiltin, ...globals.browser}},
|
||||||
rules: {
|
rules: {
|
||||||
...playwright.configs['flat/recommended'].rules,
|
...playwright.configs['flat/recommended'].rules,
|
||||||
|
'playwright/expect-expect': [0],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -924,8 +939,7 @@ export default defineConfig([
|
|||||||
},
|
},
|
||||||
extends: [
|
extends: [
|
||||||
vue.configs['flat/recommended'],
|
vue.configs['flat/recommended'],
|
||||||
// @ts-expect-error
|
vueScopedCss.configs.recommended as any,
|
||||||
vueScopedCss.configs['flat/recommended'],
|
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
'vue/attributes-order': [0],
|
'vue/attributes-order': [0],
|
||||||
@@ -960,6 +974,7 @@ export default defineConfig([
|
|||||||
'vitest/no-interpolation-in-snapshots': [0],
|
'vitest/no-interpolation-in-snapshots': [0],
|
||||||
'vitest/no-large-snapshots': [0],
|
'vitest/no-large-snapshots': [0],
|
||||||
'vitest/no-mocks-import': [0],
|
'vitest/no-mocks-import': [0],
|
||||||
|
'vitest/no-importing-vitest-globals': [2],
|
||||||
'vitest/no-restricted-matchers': [0],
|
'vitest/no-restricted-matchers': [0],
|
||||||
'vitest/no-restricted-vi-methods': [0],
|
'vitest/no-restricted-vi-methods': [0],
|
||||||
'vitest/no-standalone-expect': [0],
|
'vitest/no-standalone-expect': [0],
|
||||||
@@ -988,7 +1003,8 @@ export default defineConfig([
|
|||||||
'vitest/require-to-throw-message': [0],
|
'vitest/require-to-throw-message': [0],
|
||||||
'vitest/require-top-level-describe': [0],
|
'vitest/require-top-level-describe': [0],
|
||||||
'vitest/valid-describe-callback': [2],
|
'vitest/valid-describe-callback': [2],
|
||||||
'vitest/valid-expect': [2],
|
'vitest/valid-expect': [2, {maxArgs: 2}],
|
||||||
|
'vitest/valid-expect-in-promise': [2],
|
||||||
'vitest/valid-title': [2],
|
'vitest/valid-title': [2],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1000,11 +1016,15 @@ export default defineConfig([
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ['*', 'tools/**/*'],
|
files: ['*', 'tools/**/*', 'tests/**/*'],
|
||||||
languageOptions: {globals: globals.nodeBuiltin},
|
languageOptions: {globals: globals.nodeBuiltin},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ['web_src/**/*'],
|
files: ['web_src/**/*'],
|
||||||
languageOptions: {globals: {...globals.browser, ...globals.webpack}},
|
languageOptions: {globals: {...globals.browser, ...globals.jquery, htmx: false}},
|
||||||
|
rules: {
|
||||||
|
'no-restricted-globals': [2, ...restrictedGlobals],
|
||||||
|
'no-restricted-properties': [2, ...restrictedProperties],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
Generated
+3
-3
@@ -2,11 +2,11 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1760038930,
|
"lastModified": 1775036866,
|
||||||
"narHash": "sha256-Oncbh0UmHjSlxO7ErQDM3KM0A5/Znfofj2BSzlHLeVw=",
|
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "0b4defa2584313f3b781240b29d61f6f9f7e0df3",
|
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -33,9 +33,9 @@
|
|||||||
inherit (pkgs) lib;
|
inherit (pkgs) lib;
|
||||||
|
|
||||||
# only bump toolchain versions here
|
# only bump toolchain versions here
|
||||||
go = pkgs.go_1_25;
|
go = pkgs.go_1_26;
|
||||||
nodejs = pkgs.nodejs_24;
|
nodejs = pkgs.nodejs_24;
|
||||||
python3 = pkgs.python312;
|
python3 = pkgs.python314;
|
||||||
pnpm = pkgs.pnpm_10;
|
pnpm = pkgs.pnpm_10;
|
||||||
|
|
||||||
# Platform-specific dependencies
|
# Platform-specific dependencies
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
module code.gitea.io/gitea
|
module code.gitea.io/gitea
|
||||||
|
|
||||||
go 1.25.0
|
go 1.26.3
|
||||||
|
|
||||||
toolchain go1.25.6
|
|
||||||
|
|
||||||
// rfc5280 said: "The serial number is an integer assigned by the CA to each certificate."
|
// rfc5280 said: "The serial number is an integer assigned by the CA to each certificate."
|
||||||
// But some CAs use negative serial number, just relax the check. related:
|
// But some CAs use negative serial number, just relax the check. related:
|
||||||
@@ -11,204 +9,201 @@ godebug x509negativeserial=1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
code.gitea.io/actions-proto-go v0.4.1
|
code.gitea.io/actions-proto-go v0.4.1
|
||||||
code.gitea.io/sdk/gitea v0.22.0
|
code.gitea.io/sdk/gitea v0.24.1
|
||||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||||
connectrpc.com/connect v1.18.1
|
connectrpc.com/connect v1.19.1
|
||||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
|
gitea.com/go-chi/binding v0.0.0-20260414111559-654cea7ac60a
|
||||||
gitea.com/go-chi/cache v0.2.1
|
gitea.com/go-chi/cache v0.2.1
|
||||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
|
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
|
||||||
gitea.com/go-chi/session v0.0.0-20251124165456-68e0254e989e
|
gitea.com/go-chi/session v0.0.0-20251124165456-68e0254e989e
|
||||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||||
github.com/42wim/httpsig v1.2.3
|
github.com/42wim/httpsig v1.2.4
|
||||||
github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920
|
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
github.com/Azure/go-ntlmssp v0.1.0
|
||||||
github.com/ProtonMail/go-crypto v1.3.0
|
github.com/ProtonMail/go-crypto v1.4.1
|
||||||
github.com/PuerkitoBio/goquery v1.10.3
|
github.com/PuerkitoBio/goquery v1.12.0
|
||||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0
|
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0
|
||||||
github.com/alecthomas/chroma/v2 v2.23.0
|
github.com/alecthomas/chroma/v2 v2.23.1
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.10
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.13
|
||||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.32.2
|
github.com/aws/aws-sdk-go-v2/service/codecommit v1.33.12
|
||||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||||
github.com/blevesearch/bleve/v2 v2.5.3
|
github.com/blevesearch/bleve/v2 v2.5.7
|
||||||
github.com/bohde/codel v0.2.0
|
github.com/bohde/codel v0.2.0
|
||||||
github.com/buildkite/terminal-to-html/v3 v3.16.8
|
github.com/buildkite/terminal-to-html/v3 v3.16.8
|
||||||
github.com/caddyserver/certmagic v0.24.0
|
github.com/caddyserver/certmagic v0.25.2
|
||||||
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20251013092601-6327009efd21
|
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20251013092601-6327009efd21
|
||||||
github.com/chi-middleware/proxy v1.1.1
|
github.com/chi-middleware/proxy v1.1.1
|
||||||
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
||||||
|
github.com/dlclark/regexp2 v1.11.5
|
||||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
|
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
|
||||||
github.com/dustin/go-humanize v1.0.1
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
|
github.com/editorconfig/editorconfig-core-go/v2 v2.6.4
|
||||||
github.com/emersion/go-imap v1.2.1
|
github.com/emersion/go-imap v1.2.1
|
||||||
github.com/emirpasic/gods v1.18.1
|
github.com/emirpasic/gods v1.18.1
|
||||||
github.com/ethantkoenig/rupture v1.0.1
|
github.com/ethantkoenig/rupture v1.0.1
|
||||||
github.com/felixge/fgprof v0.9.5
|
github.com/felixge/fgprof v0.9.5
|
||||||
github.com/fsnotify/fsnotify v1.9.0
|
github.com/fsnotify/fsnotify v1.9.0
|
||||||
github.com/gliderlabs/ssh v0.3.8
|
github.com/gliderlabs/ssh v0.3.8
|
||||||
github.com/go-ap/activitypub v0.0.0-20250810115208-cb73b20a1742
|
github.com/go-chi/chi/v5 v5.2.5
|
||||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
|
|
||||||
github.com/go-chi/chi/v5 v5.2.3
|
|
||||||
github.com/go-chi/cors v1.2.2
|
github.com/go-chi/cors v1.2.2
|
||||||
github.com/go-co-op/gocron v1.37.0
|
github.com/go-co-op/gocron/v2 v2.19.1
|
||||||
github.com/go-enry/go-enry/v2 v2.9.2
|
github.com/go-enry/go-enry/v2 v2.9.5
|
||||||
github.com/go-git/go-billy/v5 v5.6.2
|
github.com/go-git/go-billy/v5 v5.9.0
|
||||||
github.com/go-git/go-git/v5 v5.16.3
|
github.com/go-git/go-git/v5 v5.19.0
|
||||||
github.com/go-ldap/ldap/v3 v3.4.11
|
github.com/go-ldap/ldap/v3 v3.4.13
|
||||||
github.com/go-redsync/redsync/v4 v4.13.0
|
github.com/go-redsync/redsync/v4 v4.16.0
|
||||||
github.com/go-sql-driver/mysql v1.9.3
|
github.com/go-sql-driver/mysql v1.9.3
|
||||||
github.com/go-webauthn/webauthn v0.13.4
|
github.com/go-webauthn/webauthn v0.16.1
|
||||||
github.com/goccy/go-json v0.10.5
|
|
||||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
github.com/google/go-github/v74 v74.0.0
|
github.com/google/go-github/v84 v84.0.0
|
||||||
github.com/google/licenseclassifier/v2 v2.0.0
|
github.com/google/licenseclassifier/v2 v2.0.0
|
||||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6
|
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/feeds v1.2.0
|
github.com/gorilla/feeds v1.2.0
|
||||||
github.com/gorilla/sessions v1.4.0
|
github.com/gorilla/sessions v1.4.0
|
||||||
github.com/hashicorp/go-version v1.7.0
|
github.com/hashicorp/go-version v1.9.0
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||||
github.com/huandu/xstrings v1.5.0
|
github.com/huandu/xstrings v1.5.0
|
||||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
||||||
github.com/jhillyerd/enmime v1.3.0
|
github.com/jhillyerd/enmime/v2 v2.3.0
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/klauspost/compress v1.18.0
|
github.com/klauspost/compress v1.18.5
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0
|
github.com/klauspost/cpuid/v2 v2.3.0
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.12.1
|
||||||
github.com/markbates/goth v1.82.0
|
github.com/markbates/goth v1.82.0
|
||||||
github.com/mattn/go-isatty v0.0.20
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/mattn/go-sqlite3 v1.14.32
|
github.com/mattn/go-sqlite3 v1.14.38
|
||||||
github.com/meilisearch/meilisearch-go v0.33.2
|
github.com/meilisearch/meilisearch-go v0.36.1
|
||||||
github.com/mholt/archives v0.0.0-20251009205813-e30ac6010726
|
github.com/mholt/archives v0.1.5
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27
|
github.com/microcosm-cc/bluemonday v1.0.27
|
||||||
github.com/microsoft/go-mssqldb v1.9.3
|
github.com/microsoft/go-mssqldb v1.9.6
|
||||||
github.com/minio/minio-go/v7 v7.0.95
|
github.com/minio/minio-go/v7 v7.0.99
|
||||||
github.com/msteinert/pam v1.2.0
|
github.com/msteinert/pam/v2 v2.1.0
|
||||||
github.com/nektos/act v0.2.63
|
github.com/nektos/act v0.2.63
|
||||||
github.com/niklasfasching/go-org v1.9.1
|
github.com/niklasfasching/go-org v1.9.1
|
||||||
github.com/olivere/elastic/v7 v7.0.32
|
github.com/olivere/elastic/v7 v7.0.32
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/opencontainers/image-spec v1.1.1
|
github.com/opencontainers/image-spec v1.1.1
|
||||||
github.com/pquerna/otp v1.5.0
|
github.com/pquerna/otp v1.5.0
|
||||||
github.com/prometheus/client_golang v1.23.0
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/quasoft/websspi v1.1.2
|
github.com/quasoft/websspi v1.1.2
|
||||||
github.com/redis/go-redis/v9 v9.12.1
|
github.com/redis/go-redis/v9 v9.18.0
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
|
||||||
github.com/sassoftware/go-rpmutils v0.4.0
|
github.com/sassoftware/go-rpmutils v0.4.0
|
||||||
github.com/sergi/go-diff v1.4.0
|
github.com/sergi/go-diff v1.4.0
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/syndtr/goleveldb v1.0.0
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
github.com/tstranex/u2f v1.0.0
|
github.com/tstranex/u2f v1.0.0
|
||||||
github.com/ulikunitz/xz v0.5.15
|
github.com/ulikunitz/xz v0.5.15
|
||||||
github.com/urfave/cli-docs/v3 v3.0.0-alpha6
|
github.com/urfave/cli-docs/v3 v3.1.0
|
||||||
github.com/urfave/cli/v3 v3.4.1
|
github.com/urfave/cli/v3 v3.4.1
|
||||||
github.com/wneessen/go-mail v0.7.2
|
github.com/wneessen/go-mail v0.7.2
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0
|
github.com/xeipuuv/gojsonschema v1.2.0
|
||||||
github.com/yohcop/openid-go v1.0.1
|
github.com/yohcop/openid-go v1.0.1
|
||||||
github.com/yuin/goldmark v1.7.16
|
github.com/yuin/goldmark v1.8.2
|
||||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||||
github.com/yuin/goldmark-meta v1.1.0
|
gitlab.com/gitlab-org/api/client-go v1.46.0
|
||||||
gitlab.com/gitlab-org/api/client-go v0.142.4
|
go.yaml.in/yaml/v4 v4.0.0-rc.3
|
||||||
golang.org/x/crypto v0.45.0
|
golang.org/x/crypto v0.52.0
|
||||||
golang.org/x/image v0.30.0
|
golang.org/x/image v0.40.0
|
||||||
golang.org/x/net v0.47.0
|
golang.org/x/net v0.55.0
|
||||||
golang.org/x/oauth2 v0.30.0
|
golang.org/x/oauth2 v0.36.0
|
||||||
golang.org/x/sync v0.18.0
|
golang.org/x/sync v0.20.0
|
||||||
golang.org/x/sys v0.38.0
|
golang.org/x/sys v0.45.0
|
||||||
golang.org/x/text v0.31.0
|
golang.org/x/text v0.37.0
|
||||||
google.golang.org/grpc v1.75.0
|
google.golang.org/grpc v1.79.3
|
||||||
google.golang.org/protobuf v1.36.8
|
google.golang.org/protobuf v1.36.11
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.1
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
mvdan.cc/xurls/v2 v2.6.0
|
mvdan.cc/xurls/v2 v2.6.0
|
||||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
strk.kbt.io/projects/go/libravatar v0.0.0-20260301104140-add494e31dab
|
||||||
xorm.io/builder v0.3.13
|
xorm.io/builder v0.3.13
|
||||||
xorm.io/xorm v1.3.10
|
xorm.io/xorm v1.3.11
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||||
code.gitea.io/gitea-vet v0.2.3 // indirect
|
code.gitea.io/gitea-vet v0.2.3 // indirect
|
||||||
dario.cat/mergo v1.0.2 // indirect
|
dario.cat/mergo v1.0.2 // indirect
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.2.0 // indirect
|
||||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||||
github.com/DataDog/zstd v1.5.7 // indirect
|
github.com/DataDog/zstd v1.5.7 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/RoaringBitmap/roaring/v2 v2.10.0 // indirect
|
github.com/RoaringBitmap/roaring/v2 v2.16.0 // indirect
|
||||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.1 // indirect
|
||||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.38.3 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.6 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.6 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
|
||||||
github.com/aws/smithy-go v1.23.0 // indirect
|
github.com/aws/smithy-go v1.24.2 // indirect
|
||||||
github.com/aymerick/douceur v0.2.0 // indirect
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bits-and-blooms/bitset v1.24.0 // indirect
|
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||||
github.com/blevesearch/bleve_index_api v1.2.9 // indirect
|
github.com/blevesearch/bleve_index_api v1.3.7 // indirect
|
||||||
github.com/blevesearch/geo v0.2.4 // indirect
|
github.com/blevesearch/geo v0.2.5 // indirect
|
||||||
github.com/blevesearch/go-faiss v1.0.25 // indirect
|
github.com/blevesearch/go-faiss v1.0.30 // indirect
|
||||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
github.com/blevesearch/mmap-go v1.2.0 // indirect
|
||||||
github.com/blevesearch/scorch_segment_api/v2 v2.3.11 // indirect
|
github.com/blevesearch/scorch_segment_api/v2 v2.4.5 // indirect
|
||||||
github.com/blevesearch/segment v0.9.1 // indirect
|
github.com/blevesearch/segment v0.9.1 // indirect
|
||||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||||
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
||||||
github.com/blevesearch/vellum v1.1.0 // indirect
|
github.com/blevesearch/vellum v1.2.0 // indirect
|
||||||
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
|
github.com/blevesearch/zapx/v11 v11.4.3 // indirect
|
||||||
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
|
github.com/blevesearch/zapx/v12 v12.4.3 // indirect
|
||||||
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
|
github.com/blevesearch/zapx/v13 v13.4.3 // indirect
|
||||||
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
|
github.com/blevesearch/zapx/v14 v14.4.3 // indirect
|
||||||
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
|
github.com/blevesearch/zapx/v15 v15.4.3 // indirect
|
||||||
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
|
github.com/blevesearch/zapx/v16 v16.3.2 // indirect
|
||||||
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
|
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||||
github.com/bodgit/sevenzip v1.6.1 // indirect
|
github.com/bodgit/sevenzip v1.6.1 // indirect
|
||||||
github.com/bodgit/windows v1.0.1 // indirect
|
github.com/bodgit/windows v1.0.1 // indirect
|
||||||
github.com/boombuler/barcode v1.1.0 // indirect
|
github.com/boombuler/barcode v1.1.0 // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
|
||||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
github.com/caddyserver/zerossl v0.1.5 // indirect
|
||||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/cloudflare/circl v1.6.1 // indirect
|
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
|
github.com/cloudflare/circl v1.6.3 // indirect
|
||||||
github.com/couchbase/go-couchbase v0.1.1 // indirect
|
github.com/couchbase/go-couchbase v0.1.1 // indirect
|
||||||
github.com/couchbase/gomemcached v0.3.3 // indirect
|
github.com/couchbase/gomemcached v0.3.4 // indirect
|
||||||
github.com/couchbase/goutils v0.1.2 // indirect
|
github.com/couchbase/goutils v0.3.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
|
||||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
|
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.19.0 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.9.1 // indirect
|
||||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
|
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
|
||||||
github.com/go-ap/errors v0.0.0-20250527110557-c8db454e53fd // indirect
|
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||||
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
|
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/go-ini/ini v1.67.0 // indirect
|
github.com/go-ini/ini v1.67.0 // indirect
|
||||||
github.com/go-webauthn/x v0.1.24 // indirect
|
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
github.com/go-webauthn/x v0.2.2 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.6 // indirect
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
|
||||||
github.com/golang/snappy v1.0.0 // indirect
|
github.com/golang/snappy v1.0.0 // indirect
|
||||||
github.com/google/btree v1.1.3 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/flatbuffers v25.2.10+incompatible // indirect
|
github.com/google/flatbuffers v25.12.19+incompatible // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.2.0 // indirect
|
||||||
github.com/google/go-tpm v0.9.5 // indirect
|
github.com/google/go-tpm v0.9.8 // indirect
|
||||||
github.com/gorilla/css v1.0.1 // indirect
|
github.com/gorilla/css v1.0.1 // indirect
|
||||||
github.com/gorilla/mux v1.8.1 // indirect
|
github.com/gorilla/mux v1.8.1 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
@@ -216,55 +211,56 @@ require (
|
|||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||||
|
github.com/inbucket/html2text v1.0.0 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
|
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
||||||
|
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||||
github.com/libdns/libdns v1.1.1 // indirect
|
github.com/libdns/libdns v1.1.1 // indirect
|
||||||
github.com/mailru/easyjson v0.9.0 // indirect
|
github.com/mailru/easyjson v0.9.2 // indirect
|
||||||
github.com/markbates/going v1.0.3 // indirect
|
github.com/markbates/going v1.0.3 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||||
github.com/mholt/acmez/v3 v3.1.2 // indirect
|
github.com/mholt/acmez/v3 v3.1.6 // indirect
|
||||||
github.com/miekg/dns v1.1.68 // indirect
|
github.com/miekg/dns v1.1.72 // indirect
|
||||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/minio/minlz v1.0.1 // indirect
|
github.com/minio/minlz v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||||
github.com/mschoch/smat v0.2.0 // indirect
|
github.com/mschoch/smat v0.2.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
|
github.com/nwaples/rardecode/v2 v2.2.2 // indirect
|
||||||
github.com/olekukonko/cat v0.0.0-20250817074551-3280053e4e00 // indirect
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||||
github.com/olekukonko/errors v1.1.0 // indirect
|
github.com/olekukonko/errors v1.2.0 // indirect
|
||||||
github.com/olekukonko/ll v0.1.0 // indirect
|
github.com/olekukonko/ll v0.1.8 // indirect
|
||||||
github.com/olekukonko/tablewriter v1.0.9 // indirect
|
github.com/olekukonko/tablewriter v1.1.4 // indirect
|
||||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||||
github.com/philhofer/fwd v1.2.0 // indirect
|
github.com/philhofer/fwd v1.2.0 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
github.com/pierrec/lz4/v4 v4.1.26 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.4.0 // indirect
|
github.com/pjbgf/sha1cd v0.6.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.65.0 // indirect
|
github.com/prometheus/common v0.67.5 // indirect
|
||||||
github.com/prometheus/procfs v0.17.0 // indirect
|
github.com/prometheus/procfs v0.20.1 // indirect
|
||||||
github.com/rhysd/actionlint v1.7.7 // indirect
|
github.com/rhysd/actionlint v1.7.12 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
|
||||||
github.com/rs/xid v1.6.0 // indirect
|
github.com/rs/xid v1.6.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||||
|
github.com/skeema/knownhosts v1.3.2 // indirect
|
||||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||||
github.com/spf13/afero v1.15.0 // indirect
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||||
github.com/tinylib/msgp v1.4.0 // indirect
|
github.com/tinylib/msgp v1.6.3 // indirect
|
||||||
github.com/unknwon/com v1.0.1 // indirect
|
github.com/unknwon/com v1.0.1 // indirect
|
||||||
github.com/valyala/fastjson v1.6.4 // indirect
|
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
@@ -275,16 +271,16 @@ require (
|
|||||||
go.etcd.io/bbolt v1.4.3 // indirect
|
go.etcd.io/bbolt v1.4.3 // indirect
|
||||||
go.uber.org/atomic v1.11.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.27.0 // indirect
|
go.uber.org/zap v1.27.1 // indirect
|
||||||
go.uber.org/zap/exp v0.3.0 // indirect
|
go.uber.org/zap/exp v0.3.0 // indirect
|
||||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/mod v0.29.0 // indirect
|
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||||
golang.org/x/time v0.12.0 // indirect
|
golang.org/x/mod v0.35.0 // indirect
|
||||||
golang.org/x/tools v0.38.0 // indirect
|
golang.org/x/time v0.15.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
|
golang.org/x/tools v0.44.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401020348-3a24fdc17823 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ignore (
|
ignore (
|
||||||
@@ -292,20 +288,22 @@ ignore (
|
|||||||
./node_modules
|
./node_modules
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/jaytaylor/html2text => github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347
|
// When doing "go get -u ./...", Golang will try to update all dependencies
|
||||||
|
// But not all latest versions of dependencies are compatible with other packages or our codebase, so we need to pin some dependencies to specific versions
|
||||||
|
// Need to regularly maintain this list to try to update them to latest versions, especially the TODO ones
|
||||||
|
|
||||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
replace github.com/jaytaylor/html2text => github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347 // jaytaylor/html2text is unmaintained
|
||||||
|
|
||||||
replace github.com/nektos/act => gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763
|
replace github.com/nektos/act => gitea.com/gitea/act v0.261.10 // gitea maintains its own package
|
||||||
|
|
||||||
replace git.sr.ht/~mariusor/go-xsd-duration => gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078
|
replace github.com/urfave/cli/v3 => github.com/urfave/cli/v3 v3.4.1 // v3.6.2 breaks -c flag parsing in help commands
|
||||||
|
|
||||||
exclude github.com/gofrs/uuid v3.2.0+incompatible
|
replace go.yaml.in/yaml/v4 => go.yaml.in/yaml/v4 v4.0.0-rc.3 // rc.4 changes block scalar serialization, wait for stable release
|
||||||
|
|
||||||
exclude github.com/gofrs/uuid v4.0.0+incompatible
|
replace github.com/Azure/azure-sdk-for-go/sdk/azcore => github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0 // v1.21.0+ uses API version unsupported by Azurite in CI
|
||||||
|
|
||||||
exclude github.com/goccy/go-json v0.4.11
|
replace github.com/Azure/azure-sdk-for-go/sdk/storage/azblob => github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 // v1.6.4+ uses API version unsupported by Azurite in CI
|
||||||
|
|
||||||
exclude github.com/satori/go.uuid v1.2.0
|
replace github.com/microsoft/go-mssqldb => github.com/microsoft/go-mssqldb v1.9.7 // downgraded with Azure SDK
|
||||||
|
|
||||||
tool code.gitea.io/gitea-vet
|
tool code.gitea.io/gitea-vet
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import type {KnipConfig} from 'knip';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
entry: [
|
|
||||||
'*.ts',
|
|
||||||
'tools/**/*.ts',
|
|
||||||
'tests/e2e/**/*.ts',
|
|
||||||
],
|
|
||||||
ignoreDependencies: [
|
|
||||||
// dependencies used in Makefile or tools
|
|
||||||
'@primer/octicons',
|
|
||||||
'markdownlint-cli',
|
|
||||||
'nolyfill',
|
|
||||||
'spectral-cli-bundle',
|
|
||||||
'vue-tsc',
|
|
||||||
'webpack-cli',
|
|
||||||
],
|
|
||||||
} satisfies KnipConfig;
|
|
||||||
@@ -26,9 +26,8 @@ import (
|
|||||||
|
|
||||||
// these flags will be set by the build flags
|
// these flags will be set by the build flags
|
||||||
var (
|
var (
|
||||||
Version = "development" // program version for this build
|
Version = "development" // program version for this build
|
||||||
Tags = "" // the Golang build tags
|
Tags = "" // the Golang build tags
|
||||||
MakeVersion = "" // "make" program version if built with make
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -50,9 +49,6 @@ func main() {
|
|||||||
|
|
||||||
func formatBuiltWith() string {
|
func formatBuiltWith() string {
|
||||||
version := runtime.Version()
|
version := runtime.Version()
|
||||||
if len(MakeVersion) > 0 {
|
|
||||||
version = MakeVersion + ", " + runtime.Version()
|
|
||||||
}
|
|
||||||
if len(Tags) == 0 {
|
if len(Tags) == 0 {
|
||||||
return " built with " + version
|
return " built with " + version
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-13
@@ -53,6 +53,11 @@ func init() {
|
|||||||
db.RegisterModel(new(ActionArtifact))
|
db.RegisterModel(new(ActionArtifact))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ContentEncodingV3Gzip = "gzip"
|
||||||
|
ContentTypeZip = "application/zip"
|
||||||
|
)
|
||||||
|
|
||||||
// ActionArtifact is a file that is stored in the artifact storage.
|
// ActionArtifact is a file that is stored in the artifact storage.
|
||||||
type ActionArtifact struct {
|
type ActionArtifact struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
@@ -61,16 +66,26 @@ type ActionArtifact struct {
|
|||||||
RepoID int64 `xorm:"index"`
|
RepoID int64 `xorm:"index"`
|
||||||
OwnerID int64
|
OwnerID int64
|
||||||
CommitSHA string
|
CommitSHA string
|
||||||
StoragePath string // The path to the artifact in the storage
|
StoragePath string // The path to the artifact in the storage
|
||||||
FileSize int64 // The size of the artifact in bytes
|
FileSize int64 // The size of the artifact in bytes
|
||||||
FileCompressedSize int64 // The size of the artifact in bytes after gzip compression
|
FileCompressedSize int64 // The size of the artifact in bytes after gzip compression
|
||||||
ContentEncoding string // The content encoding of the artifact
|
|
||||||
ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
|
// The content encoding or content type of the artifact
|
||||||
ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it
|
// * empty or null: legacy (v3) uncompressed content
|
||||||
Status ArtifactStatus `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
|
// * magic string "gzip" (ContentEncodingV3Gzip): v3 gzip compressed content
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
// * requires gzip decoding before storing in a zip for download
|
||||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated index"`
|
// * requires gzip content-encoding header when downloaded single files within a workflow
|
||||||
ExpiredUnix timeutil.TimeStamp `xorm:"index"` // The time when the artifact will be expired
|
// * mime type for "Content-Type":
|
||||||
|
// * "application/zip" (ContentTypeZip), seems to be an abuse, fortunately there is no conflict, and it won't cause problems?
|
||||||
|
// * "application/pdf", "text/html", etc.: real content type of the artifact
|
||||||
|
ContentEncodingOrType string `xorm:"content_encoding"`
|
||||||
|
|
||||||
|
ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
|
||||||
|
ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it
|
||||||
|
Status ArtifactStatus `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
|
||||||
|
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||||
|
UpdatedUnix timeutil.TimeStamp `xorm:"updated index"`
|
||||||
|
ExpiredUnix timeutil.TimeStamp `xorm:"index"` // The time when the artifact will be expired
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPath string, expiredDays int64) (*ActionArtifact, error) {
|
func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPath string, expiredDays int64) (*ActionArtifact, error) {
|
||||||
@@ -156,7 +171,8 @@ func (opts FindArtifactsOptions) ToConds() builder.Cond {
|
|||||||
}
|
}
|
||||||
if opts.FinalizedArtifactsV4 {
|
if opts.FinalizedArtifactsV4 {
|
||||||
cond = cond.And(builder.Eq{"status": ArtifactStatusUploadConfirmed}.Or(builder.Eq{"status": ArtifactStatusExpired}))
|
cond = cond.And(builder.Eq{"status": ArtifactStatusUploadConfirmed}.Or(builder.Eq{"status": ArtifactStatusExpired}))
|
||||||
cond = cond.And(builder.Eq{"content_encoding": "application/zip"})
|
// see the comment of ActionArtifact.ContentEncodingOrType: "*/*" means the field is a content type
|
||||||
|
cond = cond.And(builder.Like{"content_encoding", "%/%"})
|
||||||
}
|
}
|
||||||
|
|
||||||
return cond
|
return cond
|
||||||
@@ -170,10 +186,10 @@ type ActionArtifactMeta struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run
|
// ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run
|
||||||
func ListUploadedArtifactsMeta(ctx context.Context, runID int64) ([]*ActionArtifactMeta, error) {
|
func ListUploadedArtifactsMeta(ctx context.Context, repoID, runID int64) ([]*ActionArtifactMeta, error) {
|
||||||
arts := make([]*ActionArtifactMeta, 0, 10)
|
arts := make([]*ActionArtifactMeta, 0, 10)
|
||||||
return arts, db.GetEngine(ctx).Table("action_artifact").
|
return arts, db.GetEngine(ctx).Table("action_artifact").
|
||||||
Where("run_id=? AND (status=? OR status=?)", runID, ArtifactStatusUploadConfirmed, ArtifactStatusExpired).
|
Where("repo_id=? AND run_id=? AND (status=? OR status=?)", repoID, runID, ArtifactStatusUploadConfirmed, ArtifactStatusExpired).
|
||||||
GroupBy("artifact_name").
|
GroupBy("artifact_name").
|
||||||
Select("artifact_name, sum(file_size) as file_size, max(status) as status").
|
Select("artifact_name, sum(file_size) as file_size, max(status) as status").
|
||||||
Find(&arts)
|
Find(&arts)
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/perm"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
"xorm.io/xorm/convert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OwnerActionsConfig defines the Actions configuration for a user or organization
|
||||||
|
type OwnerActionsConfig struct {
|
||||||
|
// TokenPermissionMode defines the default permission mode (permissive, restricted)
|
||||||
|
TokenPermissionMode repo_model.ActionsTokenPermissionMode `json:"token_permission_mode,omitempty"`
|
||||||
|
|
||||||
|
// MaxTokenPermissions defines the absolute maximum permissions any token can have in this context.
|
||||||
|
MaxTokenPermissions *repo_model.ActionsTokenPermissions `json:"max_token_permissions,omitempty"`
|
||||||
|
|
||||||
|
// AllowedCrossRepoIDs is a list of specific repo IDs that can be accessed cross-repo
|
||||||
|
AllowedCrossRepoIDs []int64 `json:"allowed_cross_repo_ids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ convert.ConversionFrom = (*OwnerActionsConfig)(nil)
|
||||||
|
|
||||||
|
func (cfg *OwnerActionsConfig) FromDB(bytes []byte) error {
|
||||||
|
_ = json.Unmarshal(bytes, cfg)
|
||||||
|
cfg.TokenPermissionMode, _ = util.EnumValue(cfg.TokenPermissionMode)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOwnerActionsConfig loads the OwnerActionsConfig for a user or organization from user settings
|
||||||
|
// It returns a default config if no setting is found
|
||||||
|
func GetOwnerActionsConfig(ctx context.Context, userID int64) (ret OwnerActionsConfig, err error) {
|
||||||
|
return user_model.GetUserSettingJSON(ctx, userID, user_model.SettingsKeyActionsConfig, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOwnerActionsConfig saves the OwnerActionsConfig for a user or organization to user settings
|
||||||
|
func SetOwnerActionsConfig(ctx context.Context, userID int64, cfg OwnerActionsConfig) error {
|
||||||
|
return user_model.SetUserSettingJSON(ctx, userID, user_model.SettingsKeyActionsConfig, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultTokenPermissions returns the default token permissions by its TokenPermissionMode.
|
||||||
|
func (cfg *OwnerActionsConfig) GetDefaultTokenPermissions() repo_model.ActionsTokenPermissions {
|
||||||
|
switch cfg.TokenPermissionMode {
|
||||||
|
case repo_model.ActionsTokenPermissionModeRestricted:
|
||||||
|
return repo_model.MakeRestrictedPermissions()
|
||||||
|
case repo_model.ActionsTokenPermissionModePermissive:
|
||||||
|
return repo_model.MakeActionsTokenPermissions(perm.AccessModeWrite)
|
||||||
|
default:
|
||||||
|
return repo_model.MakeActionsTokenPermissions(perm.AccessModeNone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMaxTokenPermissions returns the maximum allowed permissions
|
||||||
|
func (cfg *OwnerActionsConfig) GetMaxTokenPermissions() repo_model.ActionsTokenPermissions {
|
||||||
|
if cfg.MaxTokenPermissions != nil {
|
||||||
|
return *cfg.MaxTokenPermissions
|
||||||
|
}
|
||||||
|
// Default max is write for everything
|
||||||
|
return repo_model.MakeActionsTokenPermissions(perm.AccessModeWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClampPermissions ensures that the given permissions don't exceed the maximum
|
||||||
|
func (cfg *OwnerActionsConfig) ClampPermissions(perms repo_model.ActionsTokenPermissions) repo_model.ActionsTokenPermissions {
|
||||||
|
maxPerms := cfg.GetMaxTokenPermissions()
|
||||||
|
return repo_model.ClampActionsTokenPermissions(perms, maxPerms)
|
||||||
|
}
|
||||||
+42
-48
@@ -7,7 +7,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -70,14 +69,14 @@ func (run *ActionRun) HTMLURL() string {
|
|||||||
if run.Repo == nil {
|
if run.Repo == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s/actions/runs/%d", run.Repo.HTMLURL(), run.Index)
|
return fmt.Sprintf("%s/actions/runs/%d", run.Repo.HTMLURL(), run.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (run *ActionRun) Link() string {
|
func (run *ActionRun) Link() string {
|
||||||
if run.Repo == nil {
|
if run.Repo == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s/actions/runs/%d", run.Repo.Link(), run.Index)
|
return fmt.Sprintf("%s/actions/runs/%d", run.Repo.Link(), run.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (run *ActionRun) WorkflowLink() string {
|
func (run *ActionRun) WorkflowLink() string {
|
||||||
@@ -115,7 +114,7 @@ func (run *ActionRun) RefTooltip() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadAttributes load Repo TriggerUser if not loaded
|
// LoadAttributes load Repo TriggerUser if not loaded
|
||||||
func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
func (run *ActionRun) LoadAttributes(ctx context.Context) (err error) {
|
||||||
if run == nil {
|
if run == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -129,11 +128,10 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if run.TriggerUser == nil {
|
if run.TriggerUser == nil {
|
||||||
u, err := user_model.GetPossibleUserByID(ctx, run.TriggerUserID)
|
run.TriggerUserID, run.TriggerUser, err = user_model.GetPossibleUserByID(ctx, run.TriggerUserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
run.TriggerUser = u
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -153,7 +151,11 @@ func (run *ActionRun) LoadRepo(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (run *ActionRun) Duration() time.Duration {
|
func (run *ActionRun) Duration() time.Duration {
|
||||||
return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration
|
d := calculateDuration(run.Started, run.Stopped, run.Status, run.Updated) + run.PreviousDuration
|
||||||
|
if d < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
|
func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
|
||||||
@@ -168,7 +170,7 @@ func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, error) {
|
func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, error) {
|
||||||
if run.Event.IsPullRequest() {
|
if run.Event.IsPullRequest() || run.Event.IsPullRequestReview() {
|
||||||
var payload api.PullRequestPayload
|
var payload api.PullRequestPayload
|
||||||
if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
|
if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -194,30 +196,34 @@ func (run *ActionRun) IsSchedule() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRepoRunsNumbers updates the number of runs and closed runs of a repository.
|
// UpdateRepoRunsNumbers updates the number of runs and closed runs of a repository.
|
||||||
func UpdateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
|
// Callers MUST invoke this from outside any transaction that has X-locked action_run rows for the same repo, otherwise, transaction deadlock
|
||||||
_, err := db.GetEngine(ctx).ID(repo.ID).
|
func UpdateRepoRunsNumbers(ctx context.Context, repoID int64) {
|
||||||
NoAutoTime().
|
if db.InTransaction(ctx) {
|
||||||
Cols("num_action_runs", "num_closed_action_runs").
|
setting.PanicInDevOrTesting("UpdateRepoRunsNumbers must not be called inside a transaction")
|
||||||
SetExpr("num_action_runs",
|
}
|
||||||
builder.Select("count(*)").From("action_run").
|
|
||||||
Where(builder.Eq{"repo_id": repo.ID}),
|
e := db.GetEngine(ctx)
|
||||||
).
|
|
||||||
SetExpr("num_closed_action_runs",
|
numActionRuns, err := e.Where("repo_id = ?", repoID).Count(new(ActionRun))
|
||||||
builder.Select("count(*)").From("action_run").
|
if err != nil {
|
||||||
Where(builder.Eq{
|
log.Error("UpdateRepoRunsNumbers count num_action_runs for repo %d: %v", repoID, err)
|
||||||
"repo_id": repo.ID,
|
return
|
||||||
}.And(
|
}
|
||||||
builder.In("status",
|
|
||||||
StatusSuccess,
|
numClosedActionRuns, err := e.Where("repo_id = ?", repoID).
|
||||||
StatusFailure,
|
In("status", StatusSuccess, StatusFailure, StatusCancelled, StatusSkipped).
|
||||||
StatusCancelled,
|
Count(new(ActionRun))
|
||||||
StatusSkipped,
|
if err != nil {
|
||||||
),
|
log.Error("UpdateRepoRunsNumbers count num_closed_action_runs for repo %d: %v", repoID, err)
|
||||||
),
|
return
|
||||||
),
|
}
|
||||||
).
|
|
||||||
Update(repo)
|
if _, err := e.ID(repoID).Cols("num_action_runs", "num_closed_action_runs").NoAutoTime().Update(&repo_model.Repository{
|
||||||
return err
|
NumActionRuns: int(numActionRuns),
|
||||||
|
NumClosedActionRuns: int(numClosedActionRuns),
|
||||||
|
}); err != nil {
|
||||||
|
log.Error("UpdateRepoRunsNumbers update repo %d: %v", repoID, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CancelPreviousJobs cancels all previous jobs of the same repository, reference, workflow, and event.
|
// CancelPreviousJobs cancels all previous jobs of the same repository, reference, workflow, and event.
|
||||||
@@ -299,7 +305,7 @@ func CancelJobs(ctx context.Context, jobs []*ActionRunJob) ([]*ActionRunJob, err
|
|||||||
if err := StopTask(ctx, job.TaskID, StatusCancelled); err != nil {
|
if err := StopTask(ctx, job.TaskID, StatusCancelled); err != nil {
|
||||||
return cancelledJobs, err
|
return cancelledJobs, err
|
||||||
}
|
}
|
||||||
updatedJob, err := GetRunJobByID(ctx, job.ID)
|
updatedJob, err := GetRunJobByRunAndID(ctx, job.RunID, job.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cancelledJobs, fmt.Errorf("get job: %w", err)
|
return cancelledJobs, fmt.Errorf("get job: %w", err)
|
||||||
}
|
}
|
||||||
@@ -322,16 +328,16 @@ func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, er
|
|||||||
return &run, nil
|
return &run, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRunByIndex(ctx context.Context, repoID, index int64) (*ActionRun, error) {
|
func GetRunByRepoAndIndex(ctx context.Context, repoID, runIndex int64) (*ActionRun, error) {
|
||||||
run := &ActionRun{
|
run := &ActionRun{
|
||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
Index: index,
|
Index: runIndex,
|
||||||
}
|
}
|
||||||
has, err := db.GetEngine(ctx).Get(run)
|
has, err := db.GetEngine(ctx).Get(run)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return nil, fmt.Errorf("run with index %d %d: %w", repoID, index, util.ErrNotExist)
|
return nil, fmt.Errorf("run with repo_id %d and index %d: %w", repoID, runIndex, util.ErrNotExist)
|
||||||
}
|
}
|
||||||
|
|
||||||
return run, nil
|
return run, nil
|
||||||
@@ -385,18 +391,6 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error {
|
|||||||
// It's impossible that the run is not found, since Gitea never deletes runs.
|
// It's impossible that the run is not found, since Gitea never deletes runs.
|
||||||
}
|
}
|
||||||
|
|
||||||
if run.Status != 0 || slices.Contains(cols, "status") {
|
|
||||||
if run.RepoID == 0 {
|
|
||||||
setting.PanicInDevOrTesting("RepoID should not be 0")
|
|
||||||
}
|
|
||||||
if err = run.LoadRepo(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := UpdateRepoRunsNumbers(ctx, run.Repo); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,18 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/modules/actions/jobparser"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/nektos/act/pkg/jobparser"
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MaxJobNumPerRun is the maximum number of jobs in a single run.
|
||||||
|
// https://docs.github.com/en/actions/reference/limits#existing-system-limits
|
||||||
|
// TODO: check this limit when creating jobs
|
||||||
|
const MaxJobNumPerRun = 256
|
||||||
|
|
||||||
// ActionRunJob represents a job of a run
|
// ActionRunJob represents a job of a run
|
||||||
type ActionRunJob struct {
|
type ActionRunJob struct {
|
||||||
ID int64
|
ID int64
|
||||||
@@ -51,6 +56,11 @@ type ActionRunJob struct {
|
|||||||
ConcurrencyGroup string `xorm:"index(repo_concurrency) NOT NULL DEFAULT ''"` // evaluated concurrency.group
|
ConcurrencyGroup string `xorm:"index(repo_concurrency) NOT NULL DEFAULT ''"` // evaluated concurrency.group
|
||||||
ConcurrencyCancel bool `xorm:"NOT NULL DEFAULT FALSE"` // evaluated concurrency.cancel-in-progress
|
ConcurrencyCancel bool `xorm:"NOT NULL DEFAULT FALSE"` // evaluated concurrency.cancel-in-progress
|
||||||
|
|
||||||
|
// TokenPermissions stores the explicit permissions from workflow/job YAML (no org/repo clamps applied).
|
||||||
|
// Org/repo clamps are enforced when the token is used at runtime.
|
||||||
|
// It is JSON-encoded repo_model.ActionsTokenPermissions and may be empty if not specified.
|
||||||
|
TokenPermissions *repo_model.ActionsTokenPermissions `xorm:"JSON TEXT"`
|
||||||
|
|
||||||
Started timeutil.TimeStamp
|
Started timeutil.TimeStamp
|
||||||
Stopped timeutil.TimeStamp
|
Stopped timeutil.TimeStamp
|
||||||
Created timeutil.TimeStamp `xorm:"created"`
|
Created timeutil.TimeStamp `xorm:"created"`
|
||||||
@@ -62,7 +72,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (job *ActionRunJob) Duration() time.Duration {
|
func (job *ActionRunJob) Duration() time.Duration {
|
||||||
return calculateDuration(job.Started, job.Stopped, job.Status)
|
return calculateDuration(job.Started, job.Stopped, job.Status, job.Updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (job *ActionRunJob) LoadRun(ctx context.Context) error {
|
func (job *ActionRunJob) LoadRun(ctx context.Context) error {
|
||||||
@@ -118,13 +128,25 @@ func (job *ActionRunJob) ParseJob() (*jobparser.Job, error) {
|
|||||||
return workflowJob, nil
|
return workflowJob, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) {
|
func GetRunJobByRepoAndID(ctx context.Context, repoID, jobID int64) (*ActionRunJob, error) {
|
||||||
var job ActionRunJob
|
var job ActionRunJob
|
||||||
has, err := db.GetEngine(ctx).Where("id=?", id).Get(&job)
|
has, err := db.GetEngine(ctx).Where("id=? AND repo_id=?", jobID, repoID).Get(&job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return nil, fmt.Errorf("run job with id %d: %w", id, util.ErrNotExist)
|
return nil, fmt.Errorf("run job with id %d: %w", jobID, util.ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &job, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRunJobByRunAndID(ctx context.Context, runID, jobID int64) (*ActionRunJob, error) {
|
||||||
|
var job ActionRunJob
|
||||||
|
has, err := db.GetEngine(ctx).Where("id=? AND run_id=?", jobID, runID).Get(&job)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, fmt.Errorf("run job with id %d: %w", jobID, util.ErrNotExist)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &job, nil
|
return &job, nil
|
||||||
@@ -168,7 +190,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
|
|||||||
|
|
||||||
if job.RunID == 0 {
|
if job.RunID == 0 {
|
||||||
var err error
|
var err error
|
||||||
if job, err = GetRunJobByID(ctx, job.ID); err != nil {
|
if job, err = GetRunJobByRepoAndID(ctx, job.RepoID, job.ID); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ type FindRunOptions struct {
|
|||||||
Ref string // the commit/tag/… that caused this workflow
|
Ref string // the commit/tag/… that caused this workflow
|
||||||
TriggerUserID int64
|
TriggerUserID int64
|
||||||
TriggerEvent webhook_module.HookEventType
|
TriggerEvent webhook_module.HookEventType
|
||||||
Approved bool // not util.OptionalBool, it works only when it's true
|
|
||||||
Status []Status
|
Status []Status
|
||||||
ConcurrencyGroup string
|
ConcurrencyGroup string
|
||||||
CommitSHA string
|
CommitSHA string
|
||||||
@@ -87,9 +86,6 @@ func (opts FindRunOptions) ToConds() builder.Cond {
|
|||||||
if opts.TriggerUserID > 0 {
|
if opts.TriggerUserID > 0 {
|
||||||
cond = cond.And(builder.Eq{"`action_run`.trigger_user_id": opts.TriggerUserID})
|
cond = cond.And(builder.Eq{"`action_run`.trigger_user_id": opts.TriggerUserID})
|
||||||
}
|
}
|
||||||
if opts.Approved {
|
|
||||||
cond = cond.And(builder.Gt{"`action_run`.approved_by": 0})
|
|
||||||
}
|
|
||||||
if len(opts.Status) > 0 {
|
if len(opts.Status) > 0 {
|
||||||
cond = cond.And(builder.In("`action_run`.status", opts.Status))
|
cond = cond.And(builder.In("`action_run`.status", opts.Status))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ package actions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@@ -27,9 +29,18 @@ func TestUpdateRepoRunsNumbers(t *testing.T) {
|
|||||||
assert.Equal(t, 2, repo.NumClosedActionRuns)
|
assert.Equal(t, 2, repo.NumClosedActionRuns)
|
||||||
|
|
||||||
// now update will correct them, only num_actionr_runs and num_closed_action_runs should be updated
|
// now update will correct them, only num_actionr_runs and num_closed_action_runs should be updated
|
||||||
err = UpdateRepoRunsNumbers(t.Context(), repo)
|
UpdateRepoRunsNumbers(t.Context(), repo.ID)
|
||||||
assert.NoError(t, err)
|
|
||||||
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||||
assert.Equal(t, 5, repo.NumActionRuns)
|
assert.Equal(t, 5, repo.NumActionRuns)
|
||||||
assert.Equal(t, 3, repo.NumClosedActionRuns)
|
assert.Equal(t, 3, repo.NumClosedActionRuns)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestActionRun_Duration_NonNegative(t *testing.T) {
|
||||||
|
run := &ActionRun{
|
||||||
|
Started: timeutil.TimeStamp(100),
|
||||||
|
Stopped: timeutil.TimeStamp(200),
|
||||||
|
Status: StatusSuccess,
|
||||||
|
PreviousDuration: -time.Hour,
|
||||||
|
}
|
||||||
|
assert.Equal(t, time.Duration(0), run.Duration())
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ type ActionRunner struct {
|
|||||||
AgentLabels []string `xorm:"TEXT"`
|
AgentLabels []string `xorm:"TEXT"`
|
||||||
// Store if this is a runner that only ever get one single job assigned
|
// Store if this is a runner that only ever get one single job assigned
|
||||||
Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
|
Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
|
||||||
|
// Store if this runner is disabled and should not pick up new jobs
|
||||||
|
IsDisabled bool `xorm:"is_disabled NOT NULL DEFAULT false"`
|
||||||
|
|
||||||
Created timeutil.TimeStamp `xorm:"created"`
|
Created timeutil.TimeStamp `xorm:"created"`
|
||||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||||
@@ -199,6 +201,7 @@ type FindRunnerOptions struct {
|
|||||||
Sort string
|
Sort string
|
||||||
Filter string
|
Filter string
|
||||||
IsOnline optional.Option[bool]
|
IsOnline optional.Option[bool]
|
||||||
|
IsDisabled optional.Option[bool]
|
||||||
WithAvailable bool // not only runners belong to, but also runners can be used
|
WithAvailable bool // not only runners belong to, but also runners can be used
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,6 +242,10 @@ func (opts FindRunnerOptions) ToConds() builder.Cond {
|
|||||||
cond = cond.And(builder.Lte{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})
|
cond = cond.And(builder.Lte{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.IsDisabled.Has() {
|
||||||
|
cond = cond.And(builder.Eq{"is_disabled": opts.IsDisabled.Value()})
|
||||||
|
}
|
||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,6 +304,20 @@ func UpdateRunner(ctx context.Context, r *ActionRunner, cols ...string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetRunnerDisabled(ctx context.Context, runner *ActionRunner, isDisabled bool) error {
|
||||||
|
if runner.IsDisabled == isDisabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
runner.IsDisabled = isDisabled
|
||||||
|
if err := UpdateRunner(ctx, runner, "is_disabled"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return IncreaseTaskVersion(ctx, runner.OwnerID, runner.RepoID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteRunner deletes a runner by given ID.
|
// DeleteRunner deletes a runner by given ID.
|
||||||
func DeleteRunner(ctx context.Context, id int64) error {
|
func DeleteRunner(ctx context.Context, id int64) error {
|
||||||
if _, err := GetRunnerByID(ctx, id); err != nil {
|
if _, err := GetRunnerByID(ctx, id); err != nil {
|
||||||
|
|||||||
+21
-4
@@ -8,11 +8,13 @@ import (
|
|||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
|
"code.gitea.io/gitea/modules/actions/jobparser"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
@@ -75,7 +77,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (task *ActionTask) Duration() time.Duration {
|
func (task *ActionTask) Duration() time.Duration {
|
||||||
return calculateDuration(task.Started, task.Stopped, task.Status)
|
return calculateDuration(task.Started, task.Stopped, task.Status, task.Updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (task *ActionTask) IsStopped() bool {
|
func (task *ActionTask) IsStopped() bool {
|
||||||
@@ -112,7 +114,7 @@ func (task *ActionTask) GetRepoLink() string {
|
|||||||
|
|
||||||
func (task *ActionTask) LoadJob(ctx context.Context) error {
|
func (task *ActionTask) LoadJob(ctx context.Context) error {
|
||||||
if task.Job == nil {
|
if task.Job == nil {
|
||||||
job, err := GetRunJobByID(ctx, task.JobID)
|
job, err := GetRunJobByRepoAndID(ctx, task.RepoID, task.JobID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -214,6 +216,20 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro
|
|||||||
return nil, errNotExist
|
return nil, errNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeTaskStepDisplayName(step *jobparser.Step, limit int) (name string) {
|
||||||
|
if step.Name != "" {
|
||||||
|
name = step.Name // the step has an explicit name
|
||||||
|
} else {
|
||||||
|
// for unnamed step, its "String()" method tries to get a display name by its "name", "uses",
|
||||||
|
// "run" or "id" (last fallback), we add the "Run " prefix for unnamed steps for better display
|
||||||
|
// for multi-line "run" scripts, only use the first line to match GitHub's behavior
|
||||||
|
// https://github.com/actions/runner/blob/66800900843747f37591b077091dd2c8cf2c1796/src/Runner.Worker/Handlers/ScriptHandler.cs#L45-L58
|
||||||
|
runStr, _, _ := strings.Cut(strings.TrimSpace(step.Run), "\n")
|
||||||
|
name = "Run " + util.IfZero(strings.TrimSpace(runStr), step.String())
|
||||||
|
}
|
||||||
|
return util.EllipsisDisplayString(name, limit) // database column has a length limit
|
||||||
|
}
|
||||||
|
|
||||||
func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) {
|
func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
ctx, committer, err := db.TxContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -293,9 +309,8 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
|
|||||||
if len(workflowJob.Steps) > 0 {
|
if len(workflowJob.Steps) > 0 {
|
||||||
steps := make([]*ActionTaskStep, len(workflowJob.Steps))
|
steps := make([]*ActionTaskStep, len(workflowJob.Steps))
|
||||||
for i, v := range workflowJob.Steps {
|
for i, v := range workflowJob.Steps {
|
||||||
name := util.EllipsisDisplayString(v.String(), 255)
|
|
||||||
steps[i] = &ActionTaskStep{
|
steps[i] = &ActionTaskStep{
|
||||||
Name: name,
|
Name: makeTaskStepDisplayName(v, 255),
|
||||||
TaskID: task.ID,
|
TaskID: task.ID,
|
||||||
Index: int64(i),
|
Index: int64(i),
|
||||||
RepoID: task.RepoID,
|
RepoID: task.RepoID,
|
||||||
@@ -373,6 +388,7 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task
|
|||||||
}
|
}
|
||||||
if _, err := UpdateRunJob(ctx, &ActionRunJob{
|
if _, err := UpdateRunJob(ctx, &ActionRunJob{
|
||||||
ID: task.JobID,
|
ID: task.JobID,
|
||||||
|
RepoID: task.RepoID,
|
||||||
Status: task.Status,
|
Status: task.Status,
|
||||||
Stopped: task.Stopped,
|
Stopped: task.Stopped,
|
||||||
}, nil); err != nil {
|
}, nil); err != nil {
|
||||||
@@ -434,6 +450,7 @@ func StopTask(ctx context.Context, taskID int64, status Status) error {
|
|||||||
task.Stopped = now
|
task.Stopped = now
|
||||||
if _, err := UpdateRunJob(ctx, &ActionRunJob{
|
if _, err := UpdateRunJob(ctx, &ActionRunJob{
|
||||||
ID: task.JobID,
|
ID: task.JobID,
|
||||||
|
RepoID: task.RepoID,
|
||||||
Status: task.Status,
|
Status: task.Status,
|
||||||
Stopped: task.Stopped,
|
Stopped: task.Stopped,
|
||||||
}, nil); err != nil {
|
}, nil); err != nil {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
// ActionTaskStep represents a step of ActionTask
|
// ActionTaskStep represents a step of ActionTask
|
||||||
type ActionTaskStep struct {
|
type ActionTaskStep struct {
|
||||||
ID int64
|
ID int64
|
||||||
Name string `xorm:"VARCHAR(255)"`
|
Name string `xorm:"VARCHAR(255)"` // the step name, for display purpose only, it will be truncated if it is too long
|
||||||
TaskID int64 `xorm:"index unique(task_index)"`
|
TaskID int64 `xorm:"index unique(task_index)"`
|
||||||
Index int64 `xorm:"index unique(task_index)"`
|
Index int64 `xorm:"index unique(task_index)"`
|
||||||
RepoID int64 `xorm:"index"`
|
RepoID int64 `xorm:"index"`
|
||||||
@@ -28,7 +28,7 @@ type ActionTaskStep struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (step *ActionTaskStep) Duration() time.Duration {
|
func (step *ActionTaskStep) Duration() time.Duration {
|
||||||
return calculateDuration(step.Started, step.Stopped, step.Status)
|
return calculateDuration(step.Started, step.Stopped, step.Status, step.Updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/actions/jobparser"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMakeTaskStepDisplayName(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
jobStep *jobparser.Step
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "explicit name",
|
||||||
|
jobStep: &jobparser.Step{
|
||||||
|
Name: "Test Step",
|
||||||
|
},
|
||||||
|
expected: "Test Step",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "uses step",
|
||||||
|
jobStep: &jobparser.Step{
|
||||||
|
Uses: "actions/checkout@v4",
|
||||||
|
},
|
||||||
|
expected: "Run actions/checkout@v4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single-line run",
|
||||||
|
jobStep: &jobparser.Step{
|
||||||
|
Run: "echo hello",
|
||||||
|
},
|
||||||
|
expected: "Run echo hello",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multi-line run block scalar",
|
||||||
|
jobStep: &jobparser.Step{
|
||||||
|
Run: "\n echo hello \r\n echo world \n ",
|
||||||
|
},
|
||||||
|
expected: "Run echo hello",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fallback to id",
|
||||||
|
jobStep: &jobparser.Step{
|
||||||
|
ID: "step-id",
|
||||||
|
},
|
||||||
|
expected: "Run step-id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "very long name truncated",
|
||||||
|
jobStep: &jobparser.Step{
|
||||||
|
Name: strings.Repeat("a", 300),
|
||||||
|
},
|
||||||
|
expected: strings.Repeat("a", 252) + "…",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "very long run truncated",
|
||||||
|
jobStep: &jobparser.Step{
|
||||||
|
Run: strings.Repeat("a", 300),
|
||||||
|
},
|
||||||
|
expected: "Run " + strings.Repeat("a", 248) + "…",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := makeTaskStepDisplayName(tt.jobStep, 255)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/models/unit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ComputeTaskTokenPermissions computes the effective permissions for a job token against the target repository.
|
||||||
|
// It uses the job's stored permissions (if any), then applies org/repo clamps and fork/cross-repo restrictions.
|
||||||
|
// Note: target repository access policy checks are enforced in GetActionsUserRepoPermission; this function only computes the job token's effective permission ceiling.
|
||||||
|
func ComputeTaskTokenPermissions(ctx context.Context, task *ActionTask, targetRepo *repo_model.Repository) (ret repo_model.ActionsTokenPermissions, err error) {
|
||||||
|
if err := task.LoadJob(ctx); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
if err := task.Job.LoadRepo(ctx); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
runRepo := task.Job.Repo
|
||||||
|
|
||||||
|
if err := runRepo.LoadOwner(ctx); err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
repoActionsCfg := runRepo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
|
||||||
|
ownerActionsCfg, err := GetOwnerActionsConfig(ctx, runRepo.OwnerID)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var jobDeclaredPerms repo_model.ActionsTokenPermissions
|
||||||
|
if task.Job.TokenPermissions != nil {
|
||||||
|
jobDeclaredPerms = *task.Job.TokenPermissions
|
||||||
|
} else if repoActionsCfg.OverrideOwnerConfig {
|
||||||
|
jobDeclaredPerms = repoActionsCfg.GetDefaultTokenPermissions()
|
||||||
|
} else {
|
||||||
|
jobDeclaredPerms = ownerActionsCfg.GetDefaultTokenPermissions()
|
||||||
|
}
|
||||||
|
|
||||||
|
var effectivePerms repo_model.ActionsTokenPermissions
|
||||||
|
if repoActionsCfg.OverrideOwnerConfig {
|
||||||
|
effectivePerms = repoActionsCfg.ClampPermissions(jobDeclaredPerms)
|
||||||
|
} else {
|
||||||
|
effectivePerms = ownerActionsCfg.ClampPermissions(jobDeclaredPerms)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cross-repository access and fork pull requests are strictly read-only for security.
|
||||||
|
// This ensures a "task repo" cannot gain write access to other repositories via CrossRepoAccess settings.
|
||||||
|
isSameRepo := task.Job.RepoID == targetRepo.ID
|
||||||
|
restrictCrossRepoAccess := task.IsForkPullRequest || !isSameRepo
|
||||||
|
if restrictCrossRepoAccess {
|
||||||
|
effectivePerms = repo_model.ClampActionsTokenPermissions(effectivePerms, repo_model.MakeRestrictedPermissions())
|
||||||
|
}
|
||||||
|
|
||||||
|
return effectivePerms, nil
|
||||||
|
}
|
||||||
+15
-2
@@ -13,6 +13,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
@@ -72,13 +73,25 @@ func (indexes *LogIndexes) ToDB() ([]byte, error) {
|
|||||||
|
|
||||||
var timeSince = time.Since
|
var timeSince = time.Since
|
||||||
|
|
||||||
func calculateDuration(started, stopped timeutil.TimeStamp, status Status) time.Duration {
|
// calculateDuration computes wall time for a run, job, task, or step. When status is terminal
|
||||||
|
// but stopped is missing or inconsistent with started, fallbackEnd (typically the row Updated
|
||||||
|
// time) is used so duration still reflects approximate elapsed time instead of 0 or a negative.
|
||||||
|
func calculateDuration(started, stopped timeutil.TimeStamp, status Status, fallbackEnd timeutil.TimeStamp) time.Duration {
|
||||||
if started == 0 {
|
if started == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
s := started.AsTime()
|
s := started.AsTime()
|
||||||
if status.IsDone() {
|
if status.IsDone() {
|
||||||
return stopped.AsTime().Sub(s)
|
end := stopped
|
||||||
|
if stopped.IsZero() || stopped < started {
|
||||||
|
if !fallbackEnd.IsZero() && fallbackEnd >= started {
|
||||||
|
end = fallbackEnd
|
||||||
|
} else {
|
||||||
|
log.Trace("actions: invalid duration timestamps (started=%d, stopped=%d, fallbackEnd=%d, status=%s)", started, stopped, fallbackEnd, status)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return end.AsTime().Sub(s)
|
||||||
}
|
}
|
||||||
return timeSince(s).Truncate(time.Second)
|
return timeSince(s).Truncate(time.Second)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,9 +45,10 @@ func Test_calculateDuration(t *testing.T) {
|
|||||||
return timeutil.TimeStamp(1000).AsTime().Sub(t)
|
return timeutil.TimeStamp(1000).AsTime().Sub(t)
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
started timeutil.TimeStamp
|
started timeutil.TimeStamp
|
||||||
stopped timeutil.TimeStamp
|
stopped timeutil.TimeStamp
|
||||||
status Status
|
status Status
|
||||||
|
fallbackEnd timeutil.TimeStamp
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -81,10 +82,48 @@ func Test_calculateDuration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: 100 * time.Second,
|
want: 100 * time.Second,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "done_stopped_zero_no_fallback",
|
||||||
|
args: args{
|
||||||
|
started: 500,
|
||||||
|
stopped: 0,
|
||||||
|
status: StatusSuccess,
|
||||||
|
},
|
||||||
|
want: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "done_stopped_zero_uses_fallback",
|
||||||
|
args: args{
|
||||||
|
started: 500,
|
||||||
|
stopped: 0,
|
||||||
|
status: StatusSuccess,
|
||||||
|
fallbackEnd: 600,
|
||||||
|
},
|
||||||
|
want: 100 * time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "done_stopped_before_started_no_fallback",
|
||||||
|
args: args{
|
||||||
|
started: 600,
|
||||||
|
stopped: 550,
|
||||||
|
status: StatusSuccess,
|
||||||
|
},
|
||||||
|
want: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "done_stopped_before_started_uses_fallback",
|
||||||
|
args: args{
|
||||||
|
started: 600,
|
||||||
|
stopped: 550,
|
||||||
|
status: StatusSuccess,
|
||||||
|
fallbackEnd: 650,
|
||||||
|
},
|
||||||
|
want: 50 * time.Second,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
assert.Equalf(t, tt.want, calculateDuration(tt.args.started, tt.args.stopped, tt.args.status), "calculateDuration(%v, %v, %v)", tt.args.started, tt.args.stopped, tt.args.status)
|
assert.Equalf(t, tt.want, calculateDuration(tt.args.started, tt.args.stopped, tt.args.status, tt.args.fallbackEnd), "calculateDuration(%v, %v, %v, %v)", tt.args.started, tt.args.stopped, tt.args.status, tt.args.fallbackEnd)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,15 +186,7 @@ func (a *Action) LoadActUser(ctx context.Context) {
|
|||||||
if a.ActUser != nil {
|
if a.ActUser != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var err error
|
a.ActUserID, a.ActUser, _ = user_model.GetPossibleUserByID(ctx, a.ActUserID)
|
||||||
a.ActUser, err = user_model.GetPossibleUserByID(ctx, a.ActUserID)
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
} else if user_model.IsErrUserNotExist(err) {
|
|
||||||
a.ActUser = user_model.NewGhostUser()
|
|
||||||
} else {
|
|
||||||
log.Error("GetUserByID(%d): %v", a.ActUserID, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Action) LoadRepo(ctx context.Context) error {
|
func (a *Action) LoadRepo(ctx context.Context) error {
|
||||||
@@ -444,6 +436,12 @@ type GetFeedsOptions struct {
|
|||||||
DontCount bool // do counting in GetFeeds
|
DontCount bool // do counting in GetFeeds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opts *GetFeedsOptions) ApplyPublicOnly(publicOnly bool) {
|
||||||
|
if publicOnly {
|
||||||
|
opts.IncludePrivate = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ActivityReadable return whether doer can read activities of user
|
// ActivityReadable return whether doer can read activities of user
|
||||||
func ActivityReadable(user, doer *user_model.User) bool {
|
func ActivityReadable(user, doer *user_model.User) bool {
|
||||||
return !user.KeepActivityPrivate ||
|
return !user.KeepActivityPrivate ||
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (actions ActionList) getUserIDs() []int64 {
|
|||||||
|
|
||||||
func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_model.User, error) {
|
func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_model.User, error) {
|
||||||
if len(actions) == 0 {
|
if len(actions) == 0 {
|
||||||
return nil, nil
|
return nil, nil //nolint:nilnil // returns nil when there are no actions
|
||||||
}
|
}
|
||||||
|
|
||||||
userIDs := actions.getUserIDs()
|
userIDs := actions.getUserIDs()
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
|
|||||||
}
|
}
|
||||||
toNotify.AddMultiple(issueParticipants...)
|
toNotify.AddMultiple(issueParticipants...)
|
||||||
|
|
||||||
// dont notify user who cause notification
|
// don't notify user who cause notification
|
||||||
delete(toNotify, notificationAuthorID)
|
delete(toNotify, notificationAuthorID)
|
||||||
// explicit unwatch on issue
|
// explicit unwatch on issue
|
||||||
issueUnWatches, err := issues_model.GetIssueWatchersIDs(ctx, issueID, false)
|
issueUnWatches, err := issues_model.GetIssueWatchersIDs(ctx, issueID, false)
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ type UserHeatmapData struct {
|
|||||||
Contributions int64 `json:"contributions"`
|
Contributions int64 `json:"contributions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserHeatmapDataByUser returns an array of UserHeatmapData
|
// GetUserHeatmapDataByUser returns an array of UserHeatmapData, it checks whether doer can access user's activity
|
||||||
func GetUserHeatmapDataByUser(ctx context.Context, user, doer *user_model.User) ([]*UserHeatmapData, error) {
|
func GetUserHeatmapDataByUser(ctx context.Context, user, doer *user_model.User) ([]*UserHeatmapData, error) {
|
||||||
return getUserHeatmapData(ctx, user, nil, doer)
|
return getUserHeatmapData(ctx, user, nil, doer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserHeatmapDataByUserTeam returns an array of UserHeatmapData
|
// GetUserHeatmapDataByOrgTeam returns an array of UserHeatmapData, it checks whether doer can access org's activity
|
||||||
func GetUserHeatmapDataByUserTeam(ctx context.Context, user *user_model.User, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
|
func GetUserHeatmapDataByOrgTeam(ctx context.Context, org *organization.Organization, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
|
||||||
return getUserHeatmapData(ctx, user, team, doer)
|
return getUserHeatmapData(ctx, org.AsUser(), team, doer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
|
func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
|
||||||
@@ -71,12 +71,3 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi
|
|||||||
OrderBy("timestamp").
|
OrderBy("timestamp").
|
||||||
Find(&hdata)
|
Find(&hdata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTotalContributionsInHeatmap returns the total number of contributions in a heatmap
|
|
||||||
func GetTotalContributionsInHeatmap(hdata []*UserHeatmapData) int64 {
|
|
||||||
var total int64
|
|
||||||
for _, v := range hdata {
|
|
||||||
total += v.Contributions
|
|
||||||
}
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -137,6 +137,11 @@ func (task *Task) MigrateConfig() (*migration.MigrateOptions, error) {
|
|||||||
log.Error("Unable to decrypt AuthToken, maybe SECRET_KEY is wrong: %v", err)
|
log.Error("Unable to decrypt AuthToken, maybe SECRET_KEY is wrong: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if opts.AWSSecretAccessKeyEncrypted != "" {
|
||||||
|
if opts.AWSSecretAccessKey, err = secret.DecryptSecret(setting.SecretKey, opts.AWSSecretAccessKeyEncrypted); err != nil {
|
||||||
|
log.Error("Unable to decrypt AWSSecretAccessKey, maybe SECRET_KEY is wrong: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &opts, nil
|
return &opts, nil
|
||||||
}
|
}
|
||||||
@@ -201,6 +206,8 @@ func FinishMigrateTask(ctx context.Context, task *Task) error {
|
|||||||
conf.AuthPasswordEncrypted = ""
|
conf.AuthPasswordEncrypted = ""
|
||||||
conf.AuthTokenEncrypted = ""
|
conf.AuthTokenEncrypted = ""
|
||||||
conf.CloneAddrEncrypted = ""
|
conf.CloneAddrEncrypted = ""
|
||||||
|
conf.AWSSecretAccessKey = ""
|
||||||
|
conf.AWSSecretAccessKeyEncrypted = ""
|
||||||
confBytes, err := json.Marshal(conf)
|
confBytes, err := json.Marshal(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func hashAndVerify(sig *packet.Signature, payload string, k *GPGKey) (*GPGKey, e
|
|||||||
// We will ignore errors in verification as they don't need to be propagated up
|
// We will ignore errors in verification as they don't need to be propagated up
|
||||||
err = verifySign(sig, hash, k)
|
err = verifySign(sig, hash, k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil
|
return nil, nil //nolint:nilnil // verification failed, not an error
|
||||||
}
|
}
|
||||||
return k, nil
|
return k, nil
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ func hashAndVerifyWithSubKeys(sig *packet.Signature, payload string, k *GPGKey)
|
|||||||
return verified, err
|
return verified, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil //nolint:nilnil // verification failed, not an error
|
||||||
}
|
}
|
||||||
|
|
||||||
func HashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *user_model.User, email string) *CommitVerification {
|
func HashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *user_model.User, email string) *CommitVerification {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
"github.com/ProtonMail/go-crypto/openpgp"
|
"github.com/ProtonMail/go-crypto/openpgp"
|
||||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||||
@@ -398,7 +397,7 @@ epiDVQ==
|
|||||||
func TestTryGetKeyIDFromSignature(t *testing.T) {
|
func TestTryGetKeyIDFromSignature(t *testing.T) {
|
||||||
assert.Empty(t, TryGetKeyIDFromSignature(&packet.Signature{}))
|
assert.Empty(t, TryGetKeyIDFromSignature(&packet.Signature{}))
|
||||||
assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{
|
assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{
|
||||||
IssuerKeyId: util.ToPointer(uint64(0x38D1A3EADDBEA9C)),
|
IssuerKeyId: new(uint64(0x38D1A3EADDBEA9C)),
|
||||||
}))
|
}))
|
||||||
assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{
|
assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{
|
||||||
IssuerFingerprint: []uint8{0xb, 0x23, 0x24, 0xc7, 0xe6, 0xfe, 0x4f, 0x3a, 0x6, 0x26, 0xc1, 0x21, 0x3, 0x8d, 0x1a, 0x3e, 0xad, 0xdb, 0xea, 0x9c},
|
IssuerFingerprint: []uint8{0xb, 0x23, 0x24, 0xc7, 0xe6, 0xfe, 0x4f, 0x3a, 0x6, 0x26, 0xc1, 0x21, 0x3, 0x8d, 0x1a, 0x3e, 0xad, 0xdb, 0xea, 0x9c},
|
||||||
@@ -419,7 +418,7 @@ func TestParseGPGKey(t *testing.T) {
|
|||||||
|
|
||||||
// then revoke the key
|
// then revoke the key
|
||||||
for _, id := range e.Identities {
|
for _, id := range e.Identities {
|
||||||
id.Revocations = append(id.Revocations, &packet.Signature{RevocationReason: util.ToPointer(packet.KeyCompromised)})
|
id.Revocations = append(id.Revocations, &packet.Signature{RevocationReason: new(packet.KeyCompromised)})
|
||||||
}
|
}
|
||||||
k, err = parseGPGKey(t.Context(), 1, e, true)
|
k, err = parseGPGKey(t.Context(), 1, e, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
@@ -64,7 +65,12 @@ func (key *PublicKey) AfterLoad() {
|
|||||||
|
|
||||||
// OmitEmail returns content of public key without email address.
|
// OmitEmail returns content of public key without email address.
|
||||||
func (key *PublicKey) OmitEmail() string {
|
func (key *PublicKey) OmitEmail() string {
|
||||||
return strings.Join(strings.Split(key.Content, " ")[:2], " ")
|
fields := strings.Split(key.Content, " ") // format: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... comment
|
||||||
|
if len(fields) < 2 {
|
||||||
|
setting.PanicInDevOrTesting("invalid public key %d content: %s", key.ID, key.Content)
|
||||||
|
return "" // not a valid public key, it shouldn't really happen, the value is managed internally
|
||||||
|
}
|
||||||
|
return strings.Join(fields[:2], " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func addKey(ctx context.Context, key *PublicKey) (err error) {
|
func addKey(ctx context.Context, key *PublicKey) (err error) {
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func CheckPrincipalKeyString(ctx context.Context, user *user_model.User, content
|
|||||||
if !email.IsActivated {
|
if !email.IsActivated {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if content == email.Email {
|
if strings.EqualFold(content, email.LowerEmail) {
|
||||||
return content, nil
|
return content, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+94
-44
@@ -5,15 +5,15 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/subtle"
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/container"
|
||||||
@@ -23,10 +23,19 @@ import (
|
|||||||
|
|
||||||
uuid "github.com/google/uuid"
|
uuid "github.com/google/uuid"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Authorization codes should expire within 10 minutes per https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2
|
||||||
|
const oauth2AuthorizationCodeValidity = 10 * time.Minute
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrOAuth2AuthorizationCodeInvalidated = errors.New("oauth2 authorization code already invalidated")
|
||||||
|
ErrOAuth2GrantStaleCounter = errors.New("oauth2 grant state changed during token refresh")
|
||||||
|
)
|
||||||
|
|
||||||
// OAuth2Application represents an OAuth2 client (RFC 6749)
|
// OAuth2Application represents an OAuth2 client (RFC 6749)
|
||||||
type OAuth2Application struct {
|
type OAuth2Application struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
@@ -145,30 +154,40 @@ func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
|
|||||||
// https://www.rfc-editor.org/rfc/rfc6819#section-5.2.3.3
|
// https://www.rfc-editor.org/rfc/rfc6819#section-5.2.3.3
|
||||||
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-12#section-3.1
|
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-12#section-3.1
|
||||||
contains := func(s string) bool {
|
redirectCandidates := []string{redirectURI}
|
||||||
s = strings.TrimSuffix(strings.ToLower(s), "/")
|
if !app.ConfidentialClient {
|
||||||
for _, u := range app.RedirectURIs {
|
loopbackRedirect, ok := normalizePublicClientRedirectURI(redirectURI)
|
||||||
if strings.TrimSuffix(strings.ToLower(u), "/") == s {
|
if ok {
|
||||||
|
redirectCandidates = append(redirectCandidates, loopbackRedirect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, candidate := range redirectCandidates {
|
||||||
|
normalizedCandidate := normalizeRedirectURIForComparison(candidate)
|
||||||
|
for _, registeredURI := range app.RedirectURIs {
|
||||||
|
if normalizeRedirectURIForComparison(registeredURI) == normalizedCandidate {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
if !app.ConfidentialClient {
|
|
||||||
uri, err := url.Parse(redirectURI)
|
return false
|
||||||
// ignore port for http loopback uris following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
|
}
|
||||||
if err == nil && uri.Scheme == "http" && uri.Port() != "" {
|
|
||||||
ip := net.ParseIP(uri.Hostname())
|
func normalizeRedirectURIForComparison(redirectURI string) string {
|
||||||
if ip != nil && ip.IsLoopback() {
|
return strings.TrimSuffix(util.ToLowerASCII(redirectURI), "/")
|
||||||
// strip port
|
}
|
||||||
uri.Host = uri.Hostname()
|
|
||||||
if contains(uri.String()) {
|
func normalizePublicClientRedirectURI(redirectURI string) (string, bool) {
|
||||||
return true
|
parsedURI, err := url.Parse(redirectURI)
|
||||||
}
|
if err != nil || parsedURI.Scheme != "http" || parsedURI.Port() == "" {
|
||||||
}
|
return "", false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return contains(redirectURI)
|
if ip := net.ParseIP(parsedURI.Hostname()); ip == nil || !ip.IsLoopback() {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
parsedURI.Host = parsedURI.Hostname()
|
||||||
|
return parsedURI.String(), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base32 characters, but lowercased.
|
// Base32 characters, but lowercased.
|
||||||
@@ -209,7 +228,7 @@ func (app *OAuth2Application) GetGrantByUserID(ctx context.Context, userID int64
|
|||||||
if has, err := db.GetEngine(ctx).Where("user_id = ? AND application_id = ?", userID, app.ID).Get(grant); err != nil {
|
if has, err := db.GetEngine(ctx).Where("user_id = ? AND application_id = ?", userID, app.ID).Get(grant); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return nil, nil
|
return nil, nil //nolint:nilnil // return nil to indicate that the object does not exist
|
||||||
}
|
}
|
||||||
return grant, nil
|
return grant, nil
|
||||||
}
|
}
|
||||||
@@ -386,6 +405,14 @@ func (code *OAuth2AuthorizationCode) TableName() string {
|
|||||||
return "oauth2_authorization_code"
|
return "oauth2_authorization_code"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsExpired reports whether the authorization code is expired.
|
||||||
|
func (code *OAuth2AuthorizationCode) IsExpired() bool {
|
||||||
|
if code.ValidUntil.IsZero() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return code.ValidUntil <= timeutil.TimeStampNow()
|
||||||
|
}
|
||||||
|
|
||||||
// GenerateRedirectURI generates a redirect URI for a successful authorization request. State will be used if not empty.
|
// GenerateRedirectURI generates a redirect URI for a successful authorization request. State will be used if not empty.
|
||||||
func (code *OAuth2AuthorizationCode) GenerateRedirectURI(state string) (*url.URL, error) {
|
func (code *OAuth2AuthorizationCode) GenerateRedirectURI(state string) (*url.URL, error) {
|
||||||
redirect, err := url.Parse(code.RedirectURI)
|
redirect, err := url.Parse(code.RedirectURI)
|
||||||
@@ -403,26 +430,44 @@ func (code *OAuth2AuthorizationCode) GenerateRedirectURI(state string) (*url.URL
|
|||||||
|
|
||||||
// Invalidate deletes the auth code from the database to invalidate this code
|
// Invalidate deletes the auth code from the database to invalidate this code
|
||||||
func (code *OAuth2AuthorizationCode) Invalidate(ctx context.Context) error {
|
func (code *OAuth2AuthorizationCode) Invalidate(ctx context.Context) error {
|
||||||
_, err := db.GetEngine(ctx).ID(code.ID).NoAutoCondition().Delete(code)
|
affected, err := db.GetEngine(ctx).ID(code.ID).NoAutoCondition().Delete(code)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if affected == 0 {
|
||||||
|
return ErrOAuth2AuthorizationCodeInvalidated
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (code *OAuth2AuthorizationCode) requiresCodeVerifier() bool {
|
||||||
|
return code.CodeChallengeMethod != "" || code.CodeChallenge != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func deriveCodeChallenge(method, verifier string) (string, bool) {
|
||||||
|
switch method {
|
||||||
|
case "S256":
|
||||||
|
return oauth2.S256ChallengeFromVerifier(verifier), true
|
||||||
|
case "plain":
|
||||||
|
return verifier, true
|
||||||
|
default:
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateCodeChallenge validates the given verifier against the saved code challenge. This is part of the PKCE implementation.
|
// ValidateCodeChallenge validates the given verifier against the saved code challenge. This is part of the PKCE implementation.
|
||||||
func (code *OAuth2AuthorizationCode) ValidateCodeChallenge(verifier string) bool {
|
func (code *OAuth2AuthorizationCode) ValidateCodeChallenge(verifier string) bool {
|
||||||
switch code.CodeChallengeMethod {
|
if !code.requiresCodeVerifier() {
|
||||||
case "S256":
|
|
||||||
// base64url(SHA256(verifier)) see https://tools.ietf.org/html/rfc7636#section-4.6
|
|
||||||
h := sha256.Sum256([]byte(verifier))
|
|
||||||
hashedVerifier := base64.RawURLEncoding.EncodeToString(h[:])
|
|
||||||
return hashedVerifier == code.CodeChallenge
|
|
||||||
case "plain":
|
|
||||||
return verifier == code.CodeChallenge
|
|
||||||
case "":
|
|
||||||
return true
|
return true
|
||||||
default:
|
}
|
||||||
// unsupported method -> return false
|
if verifier == "" || code.CodeChallengeMethod == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
expectedChallenge, ok := deriveCodeChallenge(code.CodeChallengeMethod, verifier)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return subtle.ConstantTimeCompare([]byte(expectedChallenge), []byte(code.CodeChallenge)) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOAuth2AuthorizationByCode returns an authorization by its code
|
// GetOAuth2AuthorizationByCode returns an authorization by its code
|
||||||
@@ -431,13 +476,13 @@ func GetOAuth2AuthorizationByCode(ctx context.Context, code string) (auth *OAuth
|
|||||||
if has, err := db.GetEngine(ctx).Where("code = ?", code).Get(auth); err != nil {
|
if has, err := db.GetEngine(ctx).Where("code = ?", code).Get(auth); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return nil, nil
|
return nil, nil //nolint:nilnil // return nil to indicate that the object does not exist
|
||||||
}
|
}
|
||||||
auth.Grant = new(OAuth2Grant)
|
auth.Grant = new(OAuth2Grant)
|
||||||
if has, err := db.GetEngine(ctx).ID(auth.GrantID).Get(auth.Grant); err != nil {
|
if has, err := db.GetEngine(ctx).ID(auth.GrantID).Get(auth.Grant); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return nil, nil
|
return nil, nil //nolint:nilnil // return nil to indicate that the object does not exist
|
||||||
}
|
}
|
||||||
return auth, nil
|
return auth, nil
|
||||||
}
|
}
|
||||||
@@ -472,6 +517,7 @@ func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redi
|
|||||||
// for code scanners to grab sensitive tokens.
|
// for code scanners to grab sensitive tokens.
|
||||||
codeSecret := "gta_" + base32Lower.EncodeToString(rBytes)
|
codeSecret := "gta_" + base32Lower.EncodeToString(rBytes)
|
||||||
|
|
||||||
|
validUntil := time.Now().Add(oauth2AuthorizationCodeValidity)
|
||||||
code = &OAuth2AuthorizationCode{
|
code = &OAuth2AuthorizationCode{
|
||||||
Grant: grant,
|
Grant: grant,
|
||||||
GrantID: grant.ID,
|
GrantID: grant.ID,
|
||||||
@@ -479,6 +525,7 @@ func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redi
|
|||||||
Code: codeSecret,
|
Code: codeSecret,
|
||||||
CodeChallenge: codeChallenge,
|
CodeChallenge: codeChallenge,
|
||||||
CodeChallengeMethod: codeChallengeMethod,
|
CodeChallengeMethod: codeChallengeMethod,
|
||||||
|
ValidUntil: timeutil.TimeStamp(validUntil.Unix()),
|
||||||
}
|
}
|
||||||
if err := db.Insert(ctx, code); err != nil {
|
if err := db.Insert(ctx, code); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -488,15 +535,18 @@ func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redi
|
|||||||
|
|
||||||
// IncreaseCounter increases the counter and updates the grant
|
// IncreaseCounter increases the counter and updates the grant
|
||||||
func (grant *OAuth2Grant) IncreaseCounter(ctx context.Context) error {
|
func (grant *OAuth2Grant) IncreaseCounter(ctx context.Context) error {
|
||||||
_, err := db.GetEngine(ctx).ID(grant.ID).Incr("counter").Update(new(OAuth2Grant))
|
affected, err := db.GetEngine(ctx).
|
||||||
|
Where("id = ?", grant.ID).
|
||||||
|
And("counter = ?", grant.Counter).
|
||||||
|
Incr("counter").
|
||||||
|
Update(new(OAuth2Grant))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
updatedGrant, err := GetOAuth2GrantByID(ctx, grant.ID)
|
if affected == 0 {
|
||||||
if err != nil {
|
return ErrOAuth2GrantStaleCounter
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
grant.Counter = updatedGrant.Counter
|
grant.Counter++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,7 +571,7 @@ func GetOAuth2GrantByID(ctx context.Context, id int64) (grant *OAuth2Grant, err
|
|||||||
if has, err := db.GetEngine(ctx).ID(id).Get(grant); err != nil {
|
if has, err := db.GetEngine(ctx).ID(id).Get(grant); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return nil, nil
|
return nil, nil //nolint:nilnil // return nil to indicate that the object does not exist
|
||||||
}
|
}
|
||||||
return grant, err
|
return grant, err
|
||||||
}
|
}
|
||||||
@@ -611,7 +661,7 @@ func GetActiveOAuth2SourceByAuthName(ctx context.Context, name string) (*Source,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !has {
|
if !has {
|
||||||
return nil, fmt.Errorf("oauth2 source not found, name: %q", name)
|
return nil, util.NewNotExistErrorf("oauth2 source not found, name: %q", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return authSource, nil
|
return authSource, nil
|
||||||
|
|||||||
+112
-25
@@ -5,13 +5,46 @@ package auth_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestOAuth2AuthorizationCodeValidity(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
t.Run("GenerateSetsValidUntil", func(t *testing.T) {
|
||||||
|
grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1})
|
||||||
|
expectedValidUntil := timeutil.TimeStamp(time.Now().Unix() + 600)
|
||||||
|
code, err := grant.GenerateNewAuthorizationCode(t.Context(), "http://127.0.0.1/", "", "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedValidUntil, code.ValidUntil)
|
||||||
|
assert.False(t, code.IsExpired())
|
||||||
|
assert.NoError(t, code.Invalidate(t.Context()))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Expired", func(t *testing.T) {
|
||||||
|
defer timeutil.MockSet(time.Unix(2, 0).UTC())()
|
||||||
|
|
||||||
|
code := &auth_model.OAuth2AuthorizationCode{ValidUntil: timeutil.TimeStamp(1)}
|
||||||
|
assert.True(t, code.IsExpired())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("InvalidateTwice", func(t *testing.T) {
|
||||||
|
code, err := auth_model.GetOAuth2AuthorizationByCode(t.Context(), "authcode")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, code) {
|
||||||
|
assert.NoError(t, code.Invalidate(t.Context()))
|
||||||
|
assert.ErrorIs(t, code.Invalidate(t.Context()), auth_model.ErrOAuth2AuthorizationCodeInvalidated)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
|
func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||||
@@ -72,6 +105,47 @@ func TestOAuth2Application_ContainsRedirect_Slash(t *testing.T) {
|
|||||||
assert.False(t, app.ContainsRedirectURI("http://127.0.0.1/other"))
|
assert.False(t, app.ContainsRedirectURI("http://127.0.0.1/other"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOAuth2Application_ContainsRedirectURI_ASCIIOnlyNormalization(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
registered []string
|
||||||
|
redirectURI string
|
||||||
|
allowed bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exact-match",
|
||||||
|
registered: []string{"https://signin.example.test/callback"},
|
||||||
|
redirectURI: "https://signin.example.test/callback",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ascii-case-insensitive",
|
||||||
|
registered: []string{"https://signin.example.test/callback"},
|
||||||
|
redirectURI: "https://signIN.example.test/callback",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-ascii-not-folded",
|
||||||
|
registered: []string{"https://signin.example.test/callback"},
|
||||||
|
redirectURI: "https://signİn.example.test/callback",
|
||||||
|
allowed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "loopback-strips-port",
|
||||||
|
registered: []string{"http://127.0.0.1/callback"},
|
||||||
|
redirectURI: "http://127.0.0.1:12345/callback",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
app := &auth_model.OAuth2Application{RedirectURIs: tc.registered}
|
||||||
|
assert.Equal(t, tc.allowed, app.ContainsRedirectURI(tc.redirectURI))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
|
func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||||
@@ -149,6 +223,16 @@ func TestOAuth2Grant_IncreaseCounter(t *testing.T) {
|
|||||||
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 2})
|
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 2})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOAuth2Grant_IncreaseCounterRejectsStaleCounter(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 1})
|
||||||
|
stale := *grant
|
||||||
|
|
||||||
|
assert.NoError(t, grant.IncreaseCounter(t.Context()))
|
||||||
|
err := stale.IncreaseCounter(t.Context())
|
||||||
|
assert.ErrorIs(t, err, auth_model.ErrOAuth2GrantStaleCounter)
|
||||||
|
}
|
||||||
|
|
||||||
func TestOAuth2Grant_ScopeContains(t *testing.T) {
|
func TestOAuth2Grant_ScopeContains(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Scope: "openid profile"})
|
grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Scope: "openid profile"})
|
||||||
@@ -206,35 +290,38 @@ func TestGetOAuth2AuthorizationByCode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
|
func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
|
||||||
// test plain
|
s256Verifier := "s256-verifier"
|
||||||
code := &auth_model.OAuth2AuthorizationCode{
|
s256Challenge := oauth2.S256ChallengeFromVerifier(s256Verifier)
|
||||||
CodeChallengeMethod: "plain",
|
missingVerifierChallenge := oauth2.S256ChallengeFromVerifier("verifier-not-supplied")
|
||||||
CodeChallenge: "test123",
|
|
||||||
}
|
|
||||||
assert.True(t, code.ValidateCodeChallenge("test123"))
|
|
||||||
assert.False(t, code.ValidateCodeChallenge("ierwgjoergjio"))
|
|
||||||
|
|
||||||
// test S256
|
testCases := []struct {
|
||||||
code = &auth_model.OAuth2AuthorizationCode{
|
name string
|
||||||
CodeChallengeMethod: "S256",
|
method string
|
||||||
CodeChallenge: "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg",
|
challenge string
|
||||||
|
verifier string
|
||||||
|
valid bool
|
||||||
|
}{
|
||||||
|
{"plain-success", "plain", "plain-secret", "plain-secret", true},
|
||||||
|
{"plain-failure", "plain", "plain-secret", "ierwgjoergjio", false},
|
||||||
|
{"s256-success", "S256", s256Challenge, s256Verifier, true},
|
||||||
|
{"s256-failure", "S256", s256Challenge, "wiogjerogorewngoenrgoiuenorg", false},
|
||||||
|
{"unsupported-method", "monkey", "foiwgjioriogeiogjerger", "foiwgjioriogeiogjerger", false},
|
||||||
|
{"no-pkce-configured", "", "", "", true},
|
||||||
|
{"s256-missing-verifier", "S256", missingVerifierChallenge, "", false},
|
||||||
|
{"plain-missing-verifier", "plain", "plain-secret", "", false},
|
||||||
|
{"missing-method-with-challenge", "", "foierjiogerogerg", "", false},
|
||||||
|
{"missing-method-rejects-even-matching-verifier", "", "foierjiogerogerg", "foierjiogerogerg", false},
|
||||||
}
|
}
|
||||||
assert.True(t, code.ValidateCodeChallenge("N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt"))
|
|
||||||
assert.False(t, code.ValidateCodeChallenge("wiogjerogorewngoenrgoiuenorg"))
|
|
||||||
|
|
||||||
// test unknown
|
for _, tc := range testCases {
|
||||||
code = &auth_model.OAuth2AuthorizationCode{
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
CodeChallengeMethod: "monkey",
|
code := &auth_model.OAuth2AuthorizationCode{
|
||||||
CodeChallenge: "foiwgjioriogeiogjerger",
|
CodeChallengeMethod: tc.method,
|
||||||
|
CodeChallenge: tc.challenge,
|
||||||
|
}
|
||||||
|
assert.Equal(t, tc.valid, code.ValidateCodeChallenge(tc.verifier))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
assert.False(t, code.ValidateCodeChallenge("foiwgjioriogeiogjerger"))
|
|
||||||
|
|
||||||
// test no code challenge
|
|
||||||
code = &auth_model.OAuth2AuthorizationCode{
|
|
||||||
CodeChallengeMethod: "",
|
|
||||||
CodeChallenge: "foierjiogerogerg",
|
|
||||||
}
|
|
||||||
assert.True(t, code.ValidateCodeChallenge(""))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOAuth2AuthorizationCode_GenerateRedirectURI(t *testing.T) {
|
func TestOAuth2AuthorizationCode_GenerateRedirectURI(t *testing.T) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
@@ -117,7 +118,7 @@ func RegisterTypeConfig(typ Type, exemplar Config) {
|
|||||||
type Source struct {
|
type Source struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
Type Type
|
Type Type
|
||||||
Name string `xorm:"UNIQUE"`
|
Name string `xorm:"UNIQUE"` // it can be the OIDC's provider name, see services/auth/source/oauth2/source_register.go: RegisterSource
|
||||||
IsActive bool `xorm:"INDEX NOT NULL DEFAULT false"`
|
IsActive bool `xorm:"INDEX NOT NULL DEFAULT false"`
|
||||||
IsSyncEnabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
|
IsSyncEnabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
|
||||||
TwoFactorPolicy string `xorm:"two_factor_policy NOT NULL DEFAULT ''"`
|
TwoFactorPolicy string `xorm:"two_factor_policy NOT NULL DEFAULT ''"`
|
||||||
@@ -139,7 +140,10 @@ func init() {
|
|||||||
// BeforeSet is invoked from XORM before setting the value of a field of this object.
|
// BeforeSet is invoked from XORM before setting the value of a field of this object.
|
||||||
func (source *Source) BeforeSet(colName string, val xorm.Cell) {
|
func (source *Source) BeforeSet(colName string, val xorm.Cell) {
|
||||||
if colName == "type" {
|
if colName == "type" {
|
||||||
typ := Type(db.Cell2Int64(val))
|
typ, _, err := db.CellToInt(val, NoType)
|
||||||
|
if err != nil {
|
||||||
|
setting.PanicInDevOrTesting("Unable to convert login source (id=%d) type: %v", source.ID, err)
|
||||||
|
}
|
||||||
constructor, ok := registeredConfigs[typ]
|
constructor, ok := registeredConfigs[typ]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
|||||||
+16
-24
@@ -17,13 +17,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TestSource struct {
|
type TestSource struct {
|
||||||
auth_model.ConfigBase
|
auth_model.ConfigBase `json:"-"`
|
||||||
|
|
||||||
Provider string
|
TestField string
|
||||||
ClientID string
|
|
||||||
ClientSecret string
|
|
||||||
OpenIDConnectAutoDiscoveryURL string
|
|
||||||
IconURL string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromDB fills up a LDAPConfig from serialized format.
|
// FromDB fills up a LDAPConfig from serialized format.
|
||||||
@@ -37,27 +33,23 @@ func (source *TestSource) ToDB() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDumpAuthSource(t *testing.T) {
|
func TestDumpAuthSource(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
require.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
authSourceSchema, err := unittest.GetXORMEngine().TableInfo(new(auth_model.Source))
|
authSourceSchema, err := unittest.GetXORMEngine().TableInfo(new(auth_model.Source))
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
auth_model.RegisterTypeConfig(auth_model.OAuth2, new(TestSource))
|
auth_model.RegisterTypeConfig(auth_model.OAuth2, new(TestSource))
|
||||||
|
source := &auth_model.Source{
|
||||||
|
Type: auth_model.OAuth2,
|
||||||
|
Name: "TestSource",
|
||||||
|
Cfg: &TestSource{TestField: "TestValue"},
|
||||||
|
}
|
||||||
|
require.NoError(t, auth_model.CreateSource(t.Context(), source))
|
||||||
|
|
||||||
auth_model.CreateSource(t.Context(), &auth_model.Source{
|
// intentionally test the "dump" to make sure the dumped JSON is correct: https://github.com/go-gitea/gitea/pull/16847
|
||||||
Type: auth_model.OAuth2,
|
sb := &strings.Builder{}
|
||||||
Name: "TestSource",
|
require.NoError(t, unittest.GetXORMEngine().DumpTables([]*schemas.Table{authSourceSchema}, sb))
|
||||||
IsActive: false,
|
// the dumped SQL is something like:
|
||||||
Cfg: &TestSource{
|
// INSERT INTO `login_source` (`id`, `type`, `name`, `is_active`, `is_sync_enabled`, `two_factor_policy`, `cfg`, `created_unix`, `updated_unix`) VALUES (1,6,'TestSource',0,0,'','{"TestField":"TestValue"}',1774179784,1774179784);
|
||||||
Provider: "ConvertibleSourceName",
|
assert.Contains(t, sb.String(), `'{"TestField":"TestValue"}'`)
|
||||||
ClientID: "42",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
sb := new(strings.Builder)
|
|
||||||
|
|
||||||
// TODO: this test is quite hacky, it should use a low-level "select" (without model processors) but not a database dump
|
|
||||||
engine := unittest.GetXORMEngine()
|
|
||||||
require.NoError(t, engine.DumpTables([]*schemas.Table{authSourceSchema}, sb))
|
|
||||||
assert.Contains(t, sb.String(), `"Provider":"ConvertibleSourceName"`)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pquerna/otp/totp"
|
"github.com/pquerna/otp/totp"
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -107,20 +108,43 @@ func (t *TwoFactor) SetSecret(secretString string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateTOTP validates the provided passcode.
|
// validateTOTP validates the provided passcode. It does not consume the passcode; all login
|
||||||
func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
|
// surfaces must go through ValidateAndConsumeTOTP so that a passcode cannot be redeemed twice.
|
||||||
|
func (t *TwoFactor) validateTOTP(passcode string) (bool, error) {
|
||||||
decodedStoredSecret, err := base64.StdEncoding.DecodeString(t.Secret)
|
decodedStoredSecret, err := base64.StdEncoding.DecodeString(t.Secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("ValidateTOTP invalid base64: %w", err)
|
return false, fmt.Errorf("validateTOTP invalid base64: %w", err)
|
||||||
}
|
}
|
||||||
secretBytes, err := secret.AesDecrypt(t.getEncryptionKey(), decodedStoredSecret)
|
secretBytes, err := secret.AesDecrypt(t.getEncryptionKey(), decodedStoredSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("ValidateTOTP unable to decrypt (maybe SECRET_KEY is wrong): %w", err)
|
return false, fmt.Errorf("validateTOTP unable to decrypt (maybe SECRET_KEY is wrong): %w", err)
|
||||||
}
|
}
|
||||||
secretStr := string(secretBytes)
|
secretStr := string(secretBytes)
|
||||||
return totp.Validate(passcode, secretStr), nil
|
return totp.Validate(passcode, secretStr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateAndConsumeTOTP validates the passcode and atomically records it as used so that the
|
||||||
|
// same passcode cannot be redeemed more than once (RFC 6238 §5.2). It returns false for an
|
||||||
|
// invalid passcode as well as for a replay, including the case where a concurrent request with
|
||||||
|
// the same passcode won the race first. All TOTP login surfaces must go through this helper.
|
||||||
|
func (t *TwoFactor) ValidateAndConsumeTOTP(ctx context.Context, passcode string) (bool, error) {
|
||||||
|
ok, err := t.validateTOTP(passcode)
|
||||||
|
if err != nil || !ok {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// Conditional update: only a row whose stored passcode differs from this one is updated, so a
|
||||||
|
// replay (or a concurrent duplicate) matches zero rows and is rejected. The row lock taken by
|
||||||
|
// the UPDATE serializes racing requests, closing the read-validate-write TOCTOU window.
|
||||||
|
t.LastUsedPasscode = passcode
|
||||||
|
n, err := db.GetEngine(ctx).ID(t.ID).
|
||||||
|
Where(builder.Or(builder.IsNull{"last_used_passcode"}, builder.Neq{"last_used_passcode": passcode})).
|
||||||
|
Cols("last_used_passcode").Update(t)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return n == 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewTwoFactor creates a new two-factor authentication token.
|
// NewTwoFactor creates a new two-factor authentication token.
|
||||||
func NewTwoFactor(ctx context.Context, t *TwoFactor) error {
|
func NewTwoFactor(ctx context.Context, t *TwoFactor) error {
|
||||||
_, err := db.GetEngine(ctx).Insert(t)
|
_, err := db.GetEngine(ctx).Insert(t)
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package auth_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
|
"github.com/pquerna/otp/totp"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTwoFactorValidateAndConsumeTOTP(t *testing.T) {
|
||||||
|
require.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
key, err := totp.Generate(totp.GenerateOpts{SecretSize: 40, Issuer: "gitea-test", AccountName: "consume"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tfa := &auth_model.TwoFactor{UID: 1}
|
||||||
|
require.NoError(t, tfa.SetSecret(key.Secret()))
|
||||||
|
require.NoError(t, auth_model.NewTwoFactor(t.Context(), tfa))
|
||||||
|
|
||||||
|
passcode, err := totp.GenerateCode(key.Secret(), time.Now())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// first use of a valid passcode succeeds
|
||||||
|
ok, err := tfa.ValidateAndConsumeTOTP(t.Context(), passcode)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
// replaying the same passcode is refused, even when still inside the TOTP validity window
|
||||||
|
reloaded, err := auth_model.GetTwoFactorByUID(t.Context(), tfa.UID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
ok, err = reloaded.ValidateAndConsumeTOTP(t.Context(), passcode)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.False(t, ok)
|
||||||
|
|
||||||
|
// an invalid passcode is rejected without consuming anything
|
||||||
|
ok, err = reloaded.ValidateAndConsumeTOTP(t.Context(), "000000")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.False(t, ok)
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user