mirror of
https://github.com/go-gitea/gitea.git
synced 2026-11-10 09:53:22 -06:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ebd5e319b | ||
|
|
d6945fb0b5 | ||
|
|
69a2bfcfbd | ||
|
|
15a0ea83ca | ||
|
|
30cc41ff97 | ||
|
|
7f672160de | ||
|
|
e27f69bc88 | ||
|
|
a67171af37 | ||
|
|
c7bd37e901 | ||
|
|
0a4078558e | ||
|
|
e66e773a6f | ||
|
|
08c123299a | ||
|
|
219b0e7c17 | ||
|
|
02fd18970b | ||
|
|
efee6d7ce4 | ||
|
|
0ca36eeb3c | ||
|
|
3ee1924069 | ||
|
|
99ac787400 | ||
|
|
1856fef7a8 | ||
|
|
7440f1e452 | ||
|
|
638c75a4d3 | ||
|
|
575754cbeb | ||
|
|
f24f3e1eda | ||
|
|
1f615a406f | ||
|
|
9c77a87908 | ||
|
|
4c3df3ab3c | ||
|
|
565957503c | ||
|
|
e48591ba64 | ||
|
|
f2a08e0261 | ||
|
|
532fb8f4f4 | ||
|
|
25416e9be7 | ||
|
|
1c4fff096f | ||
|
|
d16daed041 | ||
|
|
2d58c8c3df | ||
|
|
1e28bb1bd7 | ||
|
|
f0e8c3c3d0 | ||
|
|
7c58b73243 | ||
|
|
6546382f4e | ||
|
|
0930bd71fe | ||
|
|
15b8a5805a |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Gitea DevContainer",
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1.27-trixie",
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1.26-trixie",
|
||||
"containerEnv": {
|
||||
// override "local" from packaged version
|
||||
"GOTOOLCHAIN": "auto"
|
||||
|
||||
@@ -63,6 +63,7 @@ cpu.out
|
||||
/public/assets/fonts
|
||||
/public/assets/img/avatar
|
||||
/vendor
|
||||
/VERSION
|
||||
/.air
|
||||
/.go-licenses
|
||||
/Dockerfile
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -6,8 +6,6 @@
|
||||
/public/assets/img/svg/*.svg linguist-generated
|
||||
/templates/swagger/*.generated.json linguist-generated
|
||||
/options/fileicon/** linguist-generated
|
||||
/services/migrations/_mock_data/** linguist-generated
|
||||
/tests/integration/_mock_data/** linguist-generated
|
||||
/vendor/** -text -eol linguist-vendored
|
||||
Dockerfile.* linguist-language=Dockerfile
|
||||
Makefile.* linguist-language=Makefile
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/release.yaml
vendored
2
.github/ISSUE_TEMPLATE/release.yaml
vendored
@@ -6,7 +6,7 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
Follow the [release management guide](https://github.com/go-gitea/gitea/blob/main/docs/release-management.md).
|
||||
Replace the examples below and mark inapplicable tasks as such.
|
||||
Set the issue title and milestone to the version being released. Replace the examples below and mark inapplicable tasks as such.
|
||||
CI signs the tag, generates release notes, and publishes binaries and containers. Track verification here; no manual changelog PR or release upload is needed.
|
||||
- type: input
|
||||
id: version
|
||||
|
||||
31
.github/actions/docker-dryrun/action.yml
vendored
Normal file
31
.github/actions/docker-dryrun/action.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: docker-dryrun
|
||||
description: Composite action that performs the container build steps for a single platform.
|
||||
|
||||
inputs:
|
||||
platform:
|
||||
description: "The target platform: linux/amd64, linux/arm64, linux/riscv64."
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
|
||||
with:
|
||||
cache-image: false
|
||||
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
- name: Build regular image
|
||||
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ inputs.platform }}
|
||||
push: false
|
||||
file: Dockerfile
|
||||
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
|
||||
- name: Build rootless image
|
||||
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ inputs.platform }}
|
||||
push: false
|
||||
file: Dockerfile.rootless
|
||||
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless
|
||||
4
.github/actions/node-setup/action.yml
vendored
4
.github/actions/node-setup/action.yml
vendored
@@ -9,8 +9,8 @@ inputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: pnpm/action-setup@73e46992e05b19abd31f2f3c20e0e7370d762115 # v6.1.1
|
||||
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 26
|
||||
- if: ${{ inputs.cache == 'true' }}
|
||||
|
||||
2
.github/actions/pgsql-shard/action.yml
vendored
2
.github/actions/pgsql-shard/action.yml
vendored
@@ -34,7 +34,7 @@ runs:
|
||||
env:
|
||||
# pgsql is chosen to be the unlucky one to run with the slow "race detector", it is about 60% slower.
|
||||
GOTEST_FLAGS: -race -timeout=40m
|
||||
TAGS: bindata
|
||||
TAGS: bindata gogit
|
||||
TEST_LDAP: 1
|
||||
TEST_SHARD: ${{ inputs.shard }}
|
||||
TEST_TOTAL_SHARDS: ${{ inputs.total-shards }}
|
||||
|
||||
4
.github/actions/python-setup/action.yml
vendored
4
.github/actions/python-setup/action.yml
vendored
@@ -4,7 +4,7 @@ description: Set up uv and python and restore the uv cache
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
||||
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
||||
with:
|
||||
python-version: 3.15 # renovate: datasource=python-version depName=python versioning=python
|
||||
python-version: 3.14
|
||||
save-cache: ${{ github.workflow == 'cache-seeder' }}
|
||||
|
||||
9
.github/workflows/cache-seeder.yml
vendored
9
.github/workflows/cache-seeder.yml
vendored
@@ -37,15 +37,22 @@ jobs:
|
||||
- uses: ./.github/actions/go-setup
|
||||
- run: make deps-backend deps-tools
|
||||
- run: TAGS="bindata" make backend
|
||||
- run: TAGS="bindata gogit" make backend
|
||||
- name: warm test compile cache (bindata)
|
||||
env:
|
||||
TAGS: bindata
|
||||
GOTEST_FLAGS: -race -list=^$$ -count=1
|
||||
run: make test-backend
|
||||
- name: warm test compile cache (bindata gogit)
|
||||
env:
|
||||
TAGS: bindata gogit
|
||||
GOTEST_FLAGS: -race -list=^$$ -count=1
|
||||
run: make test-backend
|
||||
- name: warm integration compile cache
|
||||
run: |
|
||||
TAGS="bindata" make test-integration-compile
|
||||
TAGS="bindata" GOTEST_FLAGS="-race" make test-integration-compile
|
||||
TAGS="bindata gogit" make test-integration-compile
|
||||
TAGS="bindata gogit" GOTEST_FLAGS="-race" make test-integration-compile
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
5
.github/workflows/cron-renovate.yml
vendored
5
.github/workflows/cron-renovate.yml
vendored
@@ -9,7 +9,7 @@ concurrency:
|
||||
group: cron-renovate
|
||||
|
||||
env:
|
||||
RENOVATE_VERSION: 44.210.0 # renovate: datasource=npm depName=renovate
|
||||
RENOVATE_VERSION: 44.109.1 # renovate: datasource=npm depName=renovate
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- uses: renovatebot/github-action@1c0cf989c02653fba0efe7ca5772338860ca71dc # v46.3.12
|
||||
- uses: renovatebot/github-action@9fea9f0fbf80401026d11d03911d62b1f70fef1f # v46.3.3
|
||||
with:
|
||||
renovate-version: ${{ env.RENOVATE_VERSION }}
|
||||
configurationFile: renovate.json5
|
||||
@@ -30,4 +30,3 @@ jobs:
|
||||
RENOVATE_BINARY_SOURCE: install # auto-install go/node toolchains needed by post-upgrade tasks.
|
||||
RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^make (tidy|svg|generate-codemirror-languages)$"]'
|
||||
RENOVATE_REPOSITORIES: '["go-gitea/gitea"]'
|
||||
RENOVATE_DOCKER_MAX_PAGES: 10 # https://github.com/renovatebot/renovate/issues/46925
|
||||
|
||||
2
.github/workflows/cron-translations.yml
vendored
2
.github/workflows/cron-translations.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- uses: crowdin/github-action@5b9790df740c85af5412a3a3a91b24988d5d324f # v3.5.1
|
||||
- uses: crowdin/github-action@df474cdfb9f41d6ae777118749477c2cdf7cacc8 # v3.2.0
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
|
||||
8
.github/workflows/files-changed.yml
vendored
8
.github/workflows/files-changed.yml
vendored
@@ -15,6 +15,8 @@ on:
|
||||
value: ${{ jobs.detect.outputs.templates }}
|
||||
docker:
|
||||
value: ${{ jobs.detect.outputs.docker }}
|
||||
dockerfile:
|
||||
value: ${{ jobs.detect.outputs.dockerfile }}
|
||||
swagger:
|
||||
value: ${{ jobs.detect.outputs.swagger }}
|
||||
yaml:
|
||||
@@ -40,6 +42,7 @@ jobs:
|
||||
actions: ${{ steps.changes.outputs.actions }}
|
||||
templates: ${{ steps.changes.outputs.templates }}
|
||||
docker: ${{ steps.changes.outputs.docker }}
|
||||
dockerfile: ${{ steps.changes.outputs.dockerfile }}
|
||||
swagger: ${{ steps.changes.outputs.swagger }}
|
||||
yaml: ${{ steps.changes.outputs.yaml }}
|
||||
json: ${{ steps.changes.outputs.json }}
|
||||
@@ -100,11 +103,16 @@ jobs:
|
||||
|
||||
docker:
|
||||
- ".github/workflows/pull-docker-dryrun.yml"
|
||||
- ".github/actions/docker-dryrun/**"
|
||||
- "Dockerfile"
|
||||
- "Dockerfile.rootless"
|
||||
- "docker/**"
|
||||
- "Makefile"
|
||||
|
||||
dockerfile:
|
||||
- "Dockerfile"
|
||||
- "Dockerfile.rootless"
|
||||
|
||||
swagger:
|
||||
- "templates/swagger/*.json"
|
||||
- "Makefile"
|
||||
|
||||
7
.github/workflows/giteabot-backport.yml
vendored
7
.github/workflows/giteabot-backport.yml
vendored
@@ -4,14 +4,13 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/v*
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
@@ -20,8 +19,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: go-gitea/giteabot@15b18fdedd98a80d0968d0bd9db3a9428814b808 # v1.5.1
|
||||
- uses: go-gitea/giteabot@f48c6a15e0d384f037aea19cc05ff5e9551096b9 # v1.1.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITEABOT_TOKEN }}
|
||||
gitea_fork: giteabot/gitea
|
||||
checks: backport,backport_conflict_free
|
||||
checks: backport
|
||||
|
||||
18
.github/workflows/giteabot.yml
vendored
18
.github/workflows/giteabot.yml
vendored
@@ -1,11 +1,16 @@
|
||||
name: giteabot
|
||||
|
||||
on:
|
||||
# When main advances, rerun merge queue maintenance so the oldest
|
||||
# reviewed/wait-merge PR can be updated against the new base promptly.
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
# pull_request_target gives this workflow access to GITEABOT_TOKEN on PRs from
|
||||
# forks, which the bot needs to write labels, statuses and comments. Safe here
|
||||
# because the job only runs a pinned action and never checks out PR HEAD.
|
||||
# These PR lifecycle events drive label maintenance, lgtm, and explicit bot
|
||||
# actions triggered by relevant label changes.
|
||||
# These PR lifecycle events drive label maintenance, queue maintenance, and
|
||||
# explicit bot actions triggered by relevant label changes.
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers]
|
||||
types:
|
||||
- opened
|
||||
@@ -21,7 +26,8 @@ on:
|
||||
- giteabot-review
|
||||
types:
|
||||
- requested
|
||||
# Daily housekeeping like locks, stale feedback issues, last calls and milestones
|
||||
# Periodic maintenance is still useful as a backstop for queue cleanup and
|
||||
# other housekeeping, even though main pushes now trigger it promptly.
|
||||
schedule:
|
||||
- cron: "15 3 * * *"
|
||||
# Allow maintainers to rerun selected checks manually when debugging bot
|
||||
@@ -31,7 +37,7 @@ on:
|
||||
checks:
|
||||
description: Comma-separated list of non-backport checks to run
|
||||
required: false
|
||||
default: backport_branches,backport_conflict_free,labels,lock,feedback,last_call,milestones,lgtm,translation_comment,pr_actions
|
||||
default: labels,merge_queue,lock,feedback,last_call,milestones,lgtm,translation_comment,pr_actions
|
||||
|
||||
permissions: {}
|
||||
|
||||
@@ -50,7 +56,7 @@ jobs:
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
steps:
|
||||
- uses: go-gitea/giteabot@15b18fdedd98a80d0968d0bd9db3a9428814b808 # v1.5.1
|
||||
- uses: go-gitea/giteabot@f48c6a15e0d384f037aea19cc05ff5e9551096b9 # v1.1.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITEABOT_TOKEN }}
|
||||
checks: ${{ github.event.inputs.checks || 'backport_branches,backport_conflict_free,labels,lock,feedback,last_call,milestones,lgtm,translation_comment,pr_actions' }}
|
||||
checks: ${{ github.event.inputs.checks || 'labels,merge_queue,lock,feedback,last_call,milestones,lgtm,translation_comment,pr_actions' }}
|
||||
|
||||
16
.github/workflows/merge-group.yml
vendored
16
.github/workflows/merge-group.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: merge-group
|
||||
|
||||
# Reports the required giteabot/lgtm check on merge queue commits. giteabot only sets it
|
||||
# on PR heads, which already passed it before entering the queue.
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
lgtm:
|
||||
name: giteabot/lgtm
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: "true"
|
||||
8
.github/workflows/pull-compliance.yml
vendored
8
.github/workflows/pull-compliance.yml
vendored
@@ -2,7 +2,6 @@ name: compliance
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
@@ -28,7 +27,6 @@ jobs:
|
||||
- run: TAGS="bindata" make generate-go lint-backend # lint-go can lint with "bindata" tags
|
||||
|
||||
lint-on-demand:
|
||||
if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true' || needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.swagger == 'true' || needs.files-changed.outputs.json == 'true' || needs.files-changed.outputs.shell == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -40,6 +38,8 @@ jobs:
|
||||
with:
|
||||
cache: "false"
|
||||
|
||||
- run: make lint-spell
|
||||
|
||||
- if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
uses: ./.github/actions/python-setup
|
||||
- if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true'
|
||||
@@ -93,13 +93,13 @@ jobs:
|
||||
env:
|
||||
GOOS: linux
|
||||
GOARCH: arm64
|
||||
TAGS: bindata
|
||||
TAGS: bindata gogit
|
||||
- name: build-backend-windows
|
||||
run: go build -ldflags '-s -w' -o gitea-windows
|
||||
env:
|
||||
GOOS: windows
|
||||
GOARCH: amd64
|
||||
TAGS: bindata
|
||||
TAGS: bindata gogit
|
||||
- name: build-backend-386
|
||||
run: go build -ldflags '-s -w' -o gitea-linux-386
|
||||
env:
|
||||
|
||||
27
.github/workflows/pull-db-tests.yml
vendored
27
.github/workflows/pull-db-tests.yml
vendored
@@ -2,7 +2,6 @@ name: db-tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
@@ -26,8 +25,6 @@ jobs:
|
||||
env:
|
||||
POSTGRES_DB: test
|
||||
POSTGRES_PASSWORD: postgres
|
||||
options: >-
|
||||
--mount type=tmpfs,destination=/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
ldap:
|
||||
@@ -64,8 +61,6 @@ jobs:
|
||||
env:
|
||||
POSTGRES_DB: test
|
||||
POSTGRES_PASSWORD: postgres
|
||||
options: >-
|
||||
--mount type=tmpfs,destination=/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
ldap:
|
||||
@@ -100,17 +95,17 @@ jobs:
|
||||
- run: make deps-backend
|
||||
- run: make backend
|
||||
env:
|
||||
TAGS: bindata
|
||||
TAGS: bindata gogit
|
||||
- run: GITEA_TEST_DATABASE=sqlite make test-migration
|
||||
env:
|
||||
TAGS: bindata
|
||||
TAGS: bindata gogit
|
||||
- name: run tests
|
||||
run: GITEA_TEST_DATABASE=sqlite make test-integration
|
||||
timeout-minutes: 50
|
||||
env:
|
||||
# sqlite driver can contain large amount of Golang code, so don't use race detector for it, otherwise, extremely slow
|
||||
GOTEST_FLAGS: -timeout=40m
|
||||
TAGS: bindata
|
||||
TAGS: bindata gogit
|
||||
|
||||
test-unit:
|
||||
if: needs.files-changed.outputs.backend == 'true'
|
||||
@@ -118,7 +113,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
elasticsearch:
|
||||
image: elasticsearch:8.19.23@sha256:ef5cafd961d7e273f13fed8def82e63c5ae0491106b5eecb5a69aa12d64f089b
|
||||
image: elasticsearch:8.19.21@sha256:ae1f398ec8734a63eb7f719cecc2ea33e2bb46e23bdf24641dec86fd256bf594
|
||||
env:
|
||||
discovery.type: single-node
|
||||
xpack.security.enabled: false
|
||||
@@ -128,7 +123,7 @@ jobs:
|
||||
ports:
|
||||
- "9200:9200"
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1@sha256:88f2a08eb9532cc54a60d8fd014a23bf5cfda33d0c01ecbbaca9b4b463e17466
|
||||
image: getmeili/meilisearch:v1@sha256:c94e58ca09662dd6e65e8f1b0fd145767be3da7d5422a863a27b8d2b68e090c9
|
||||
env:
|
||||
MEILI_ENV: development # disable auth
|
||||
ports:
|
||||
@@ -168,6 +163,13 @@ jobs:
|
||||
GOTEST_FLAGS: -race -timeout=20m
|
||||
TAGS: bindata
|
||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
||||
- name: unit-tests-gogit
|
||||
run: make test-backend
|
||||
env:
|
||||
GOTEST_FLAGS: -race -timeout=20m
|
||||
TAGS: bindata gogit
|
||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
||||
GITEA_TEST_CI_SKIP_EXTERNAL: true
|
||||
- run: make test-check
|
||||
|
||||
test-mysql:
|
||||
@@ -186,7 +188,7 @@ jobs:
|
||||
options: >-
|
||||
--mount type=tmpfs,destination=/bitnami/mysql/data
|
||||
elasticsearch:
|
||||
image: elasticsearch:8.19.23@sha256:ef5cafd961d7e273f13fed8def82e63c5ae0491106b5eecb5a69aa12d64f089b
|
||||
image: elasticsearch:8.19.21@sha256:ae1f398ec8734a63eb7f719cecc2ea33e2bb46e23bdf24641dec86fd256bf594
|
||||
env:
|
||||
discovery.type: single-node
|
||||
xpack.security.enabled: false
|
||||
@@ -216,6 +218,7 @@ jobs:
|
||||
run: GITEA_TEST_DATABASE=mysql make test-integration
|
||||
env:
|
||||
TAGS: bindata
|
||||
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
|
||||
|
||||
test-mssql:
|
||||
if: needs.files-changed.outputs.backend == 'true'
|
||||
@@ -223,7 +226,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mssql:
|
||||
image: mcr.microsoft.com/mssql/server:2019-latest@sha256:07d7dedecdbfcf6776035dc5eefb5bfd9aa1804b2f6b99f7dedb397d8a599069
|
||||
image: mcr.microsoft.com/mssql/server:2019-latest@sha256:ef0b8db33970ecd01bed49c3a84a1d083c435a9891718df619298b67b352e74a
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
MSSQL_PID: Standard
|
||||
|
||||
38
.github/workflows/pull-docker-dryrun.yml
vendored
38
.github/workflows/pull-docker-dryrun.yml
vendored
@@ -14,26 +14,34 @@ jobs:
|
||||
files-changed:
|
||||
uses: ./.github/workflows/files-changed.yml
|
||||
|
||||
container:
|
||||
# QEMU-based build is slow (40-50 minutes), so run arm64 and riscv64 when dockerfile changes.
|
||||
# Run amd64 when any docker-related files change, which is fast (4 minutes).
|
||||
container-amd64:
|
||||
if: needs.files-changed.outputs.docker == 'true'
|
||||
needs: [files-changed]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
|
||||
- uses: ./.github/actions/docker-dryrun
|
||||
with:
|
||||
cache-image: false
|
||||
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
- name: Build regular image
|
||||
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
|
||||
platform: linux/amd64
|
||||
|
||||
container-arm64:
|
||||
if: needs.files-changed.outputs.dockerfile == 'true'
|
||||
needs: [files-changed]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- uses: ./.github/actions/docker-dryrun
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
|
||||
- name: Build rootless image
|
||||
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
|
||||
platform: linux/arm64
|
||||
|
||||
container-riscv64:
|
||||
if: needs.files-changed.outputs.dockerfile == 'true'
|
||||
needs: [files-changed]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- uses: ./.github/actions/docker-dryrun
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
file: Dockerfile.rootless
|
||||
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless
|
||||
platform: linux/riscv64
|
||||
|
||||
1
.github/workflows/pull-e2e-tests.yml
vendored
1
.github/workflows/pull-e2e-tests.yml
vendored
@@ -2,7 +2,6 @@ name: e2e-tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
|
||||
2
.github/workflows/pull-labeler.yml
vendored
2
.github/workflows/pull-labeler.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.sha }}
|
||||
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 26
|
||||
# Labels are only synced after the title lints, so an invalid title never reaches the label diff.
|
||||
|
||||
4
.github/workflows/release-create-tag.yml
vendored
4
.github/workflows/release-create-tag.yml
vendored
@@ -13,9 +13,7 @@ permissions: {}
|
||||
jobs:
|
||||
tag:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release-signing
|
||||
deployment: false
|
||||
environment: release-signing
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
|
||||
48
.github/workflows/release-nightly-snapcraft.yml
vendored
Normal file
48
.github/workflows/release-nightly-snapcraft.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: release-nightly-snapcraft
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runner: [ubuntu-24.04, ubuntu-24.04-arm]
|
||||
runs-on: ${{ matrix.runner }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build snap
|
||||
id: build
|
||||
env:
|
||||
SNAPCRAFT_IMAGE_INFO: '{"build_url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}'
|
||||
run: |
|
||||
# snapcraft installs build-snaps and apt packages system-wide, which needs root
|
||||
sudo snap install --classic snapcraft
|
||||
sudo SNAPCRAFT_BUILD_INFO=1 SNAPCRAFT_IMAGE_INFO="$SNAPCRAFT_IMAGE_INFO" snapcraft pack --destructive-mode
|
||||
echo "snap=$(ls ./*.snap)" >> "$GITHUB_OUTPUT"
|
||||
# retry snapcraft uploads which can be flaky
|
||||
- name: Publish snap
|
||||
run: |
|
||||
for attempt in 1 2 3 4 5; do
|
||||
snapcraft upload "$SNAP" --release latest/edge && exit 0
|
||||
echo "::warning::snap upload attempt $attempt failed, retrying in 15s"
|
||||
sleep 15
|
||||
done
|
||||
exit 1
|
||||
env:
|
||||
SNAP: ${{ steps.build.outputs.snap }}
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||
99
.github/workflows/release-nightly.yml
vendored
99
.github/workflows/release-nightly.yml
vendored
@@ -1,9 +1,8 @@
|
||||
name: release-nightly
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "17 */12 * * *"
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main, release/v*]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -12,42 +11,13 @@ concurrency:
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
branches:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
outputs:
|
||||
branches: ${{ steps.changed.outputs.branches }}
|
||||
steps:
|
||||
- id: changed
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
run: |
|
||||
since=$(gh run list --workflow release-nightly.yml --status success --limit 1 --json createdAt -q '.[0].createdAt')
|
||||
branches=$(gh api --paginate "repos/$GH_REPO/branches" -q '.[].name | select(. == "main" or startswith("release/v"))' |
|
||||
while read -r branch; do
|
||||
if [ -z "$since" ] || [ -n "$(gh api "repos/$GH_REPO/commits?sha=$branch&since=$since&per_page=1" -q '.[].sha')" ]; then
|
||||
echo "$branch"
|
||||
fi
|
||||
done | jq -Rnc '[inputs]')
|
||||
echo "branches=$branches" >> "$GITHUB_OUTPUT"
|
||||
|
||||
nightly-binary:
|
||||
needs: branches
|
||||
if: needs.branches.outputs.branches != '[]'
|
||||
strategy:
|
||||
matrix:
|
||||
branch: ${{ fromJSON(needs.branches.outputs.branches) }}
|
||||
runs-on: namespace-profile-gitea-release-binary
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ matrix.branch }}
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
@@ -58,9 +28,7 @@ jobs:
|
||||
cache: false
|
||||
- uses: ./.github/actions/node-setup
|
||||
- run: make deps-frontend deps-backend
|
||||
- run: make release GITHUB_REF_NAME="$BRANCH"
|
||||
env:
|
||||
BRANCH: ${{ matrix.branch }}
|
||||
- run: make release
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
- name: import gpg key
|
||||
@@ -82,7 +50,7 @@ jobs:
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
env:
|
||||
REF: ${{ matrix.branch }}
|
||||
REF: ${{ github.ref }}
|
||||
run: |
|
||||
REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
@@ -99,19 +67,12 @@ jobs:
|
||||
aws s3 sync dist/release "s3://$CLOUDFLARE_R2_BUCKET/gitea/$BRANCH" --endpoint-url "https://$CLOUDFLARE_R2_ACCOUNT_ID.r2.cloudflarestorage.com" --no-progress
|
||||
|
||||
nightly-container:
|
||||
needs: branches
|
||||
if: needs.branches.outputs.branches != '[]'
|
||||
strategy:
|
||||
matrix:
|
||||
branch: ${{ fromJSON(needs.branches.outputs.branches) }}
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write # to publish to ghcr.io
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ matrix.branch }}
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
@@ -122,14 +83,13 @@ jobs:
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
env:
|
||||
REF: ${{ matrix.branch }}
|
||||
REF: ${{ github.ref }}
|
||||
run: |
|
||||
REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
id: meta
|
||||
with:
|
||||
context: git
|
||||
images: |-
|
||||
gitea/gitea
|
||||
ghcr.io/go-gitea/gitea
|
||||
@@ -140,7 +100,6 @@ jobs:
|
||||
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
id: meta_rootless
|
||||
with:
|
||||
context: git
|
||||
images: |-
|
||||
gitea/gitea
|
||||
ghcr.io/go-gitea/gitea
|
||||
@@ -152,12 +111,12 @@ jobs:
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Login to GHCR using PAT
|
||||
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -183,47 +142,3 @@ jobs:
|
||||
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
|
||||
|
||||
nightly-snap:
|
||||
needs: branches
|
||||
if: contains(fromJSON(needs.branches.outputs.branches), 'main')
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runner: [ubuntu-24.04, ubuntu-24.04-arm]
|
||||
runs-on: ${{ matrix.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
- name: Build snap
|
||||
id: build
|
||||
env:
|
||||
SNAPCRAFT_IMAGE_INFO: '{"build_url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}'
|
||||
run: |
|
||||
# snapcraft installs build-snaps and apt packages system-wide, which needs root
|
||||
sudo snap install --classic snapcraft
|
||||
sudo SNAPCRAFT_BUILD_INFO=1 SNAPCRAFT_IMAGE_INFO="$SNAPCRAFT_IMAGE_INFO" snapcraft pack --destructive-mode
|
||||
echo "snap=$(ls ./*.snap)" >> "$GITHUB_OUTPUT"
|
||||
# retry snapcraft uploads which can be flaky
|
||||
- name: Publish snap
|
||||
run: |
|
||||
# part-gitea-pull.sh decides what is built, see the comments there:
|
||||
# * grade devel (main or a prerelease tag) goes to latest/edge
|
||||
# * grade stable (a new release tag) goes to latest/stable, and to latest/candidate which marks it as released
|
||||
channel=latest/edge
|
||||
if unsquashfs -cat "$SNAP" meta/snap.yaml | grep -qx 'grade: stable'; then
|
||||
channel=latest/stable,latest/candidate
|
||||
fi
|
||||
for attempt in 1 2 3 4 5; do
|
||||
snapcraft upload "$SNAP" --release "$channel" && exit 0
|
||||
echo "::warning::snap upload attempt $attempt failed, retrying in 15s"
|
||||
sleep 15
|
||||
done
|
||||
exit 1
|
||||
env:
|
||||
SNAP: ${{ steps.build.outputs.snap }}
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||
|
||||
16
.github/workflows/release-tag-version.yml
vendored
16
.github/workflows/release-tag-version.yml
vendored
@@ -68,25 +68,23 @@ jobs:
|
||||
BRANCH: ${{ steps.clean_name.outputs.branch }}
|
||||
run: |
|
||||
aws s3 sync dist/release "s3://$CLOUDFLARE_R2_BUCKET/gitea/$BRANCH" --endpoint-url "https://$CLOUDFLARE_R2_ACCOUNT_ID.r2.cloudflarestorage.com" --no-progress
|
||||
- name: Install GH CLI
|
||||
uses: dev-hanz-ops/install-gh-cli-action@6089bdde54118ad7ca3d22053eb2d69387fd2779 # v0.3.0
|
||||
with:
|
||||
gh-cli-version: 2.39.1
|
||||
- id: range
|
||||
run: |
|
||||
previous=$(git tag --list --sort=-v:refname | grep -xE 'v[0-9]+\.[0-9]+\.[0-9]+' | grep -A1 -xF "$GITHUB_REF_NAME" | tail -1) # highest stable version below this one
|
||||
echo "range=$previous..$GITHUB_SHA" >> "$GITHUB_OUTPUT"
|
||||
- name: skip reverts and reverted commits in changelog
|
||||
env:
|
||||
RANGE: ${{ steps.range.outputs.range }}
|
||||
run: ./tools/generate-cliffignore.sh "$RANGE" | sort -u > .cliffignore
|
||||
- uses: orhun/git-cliff-action@a9a95522b26fe6403f7bb24031f21fb573d0f5ff # v4.9.1
|
||||
with:
|
||||
args: --tag ${{ github.ref_name }} ${{ steps.range.outputs.range }}
|
||||
- name: create github release
|
||||
env:
|
||||
GH_CLI_VERSION: 2.103.3 # renovate: datasource=github-releases depName=cli/cli extractVersion=^v(?<version>.+)$
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
run: |
|
||||
curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_CLI_VERSION}/gh_${GH_CLI_VERSION}_linux_amd64.tar.gz" | tar xz --strip-components=2 "gh_${GH_CLI_VERSION}_linux_amd64/bin/gh"
|
||||
./gh release create "$TAG" --title "$TAG" --notes-file git-cliff/CHANGELOG.md dist/release/*
|
||||
gh release create "$TAG" --title "$TAG" --notes-file git-cliff/CHANGELOG.md dist/release/*
|
||||
|
||||
container:
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
@@ -140,12 +138,12 @@ jobs:
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Login to GHCR using PAT
|
||||
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
|
||||
11
AGENTS.md
11
AGENTS.md
@@ -1,14 +1,10 @@
|
||||
- Never assume, verify before claiming
|
||||
- List development targets with `make help`
|
||||
- Before working on code, read the `docs` guides for every area touched: `guidelines-backend.md` for Go, `guidelines-frontend.md` for TS, CSS and Vue, both for templates, `testing.md` for tests, `development.md` for config options and migrations, `guidelines-refactoring.md` for refactors
|
||||
- Before designing a feature or behavior, check prior art in GitHub and GitLab
|
||||
- Don't add code for theoretical edge cases, only for ones that can realistically occur. Ask humans to make decisions if the edge case matters, comment the edge case for why it can be ignored if it doesn't matter
|
||||
- Write docs concisely and user-facing, skip implementation details
|
||||
- PR descriptions: minimal, only what and why, no task or file listings, no headings. Aim for less than 1000 characters. References go at the end of the description like `Fixes: <url>`.
|
||||
- PR descriptions and update comments: Include screenshots for UI changes, before and after side by side. Take them at 4x scale in dark theme, cropped to the changes with some padding, and embed them at half CSS size
|
||||
- Read relevant developer documentation in the `docs` folder
|
||||
- PR descriptions: minimal, only what and why, no task or file listings. Include screenshots for UI changes, before and after when modifying existing UI. Aim for less than 1000 characters
|
||||
- Reference issues and PRs by full URL, not by number
|
||||
- Use Conventional Commits for commit messages and PR titles, plus Gitea's `enhance` type for user-facing enhancements
|
||||
- Add an `Assisted-by: AGENT_NAME:MODEL_VERSION` trailer to commit messages and PR descriptions, never `Co-Authored-By` or `Signed-off-by`
|
||||
- Add an `Assisted-by: AGENT_NAME:MODEL_VERSION` trailer to commit messages, never `Co-Authored-By` or `Signed-off-by`
|
||||
- Attribute agent authorship on one trailing line in issue and PR comments, never as a PR description section
|
||||
- Never rewrite git history unless asked, update PRs with new commits and normal push
|
||||
- Comments: write almost none, short and preferably same-line, explaining why for a future reader. Never narrate code, the change or the prompt. Preserve existing ones that still apply. If you need to write a paragraph-long comment, rethink your implementation, it is likely too complicated
|
||||
@@ -16,7 +12,6 @@
|
||||
- In `options/locale`, only edit `locale_en-US.json`, other locales are synced automatically
|
||||
- In TS, use `!` instead of `?.`/`??` when a value always exists
|
||||
- In Go, prefer to use modern language features wherever possible
|
||||
- In Go, function-name prefixes in errors like `fmt.Errorf("Foo: %w", err)` must always name the function they are in
|
||||
- Write sizes as multiplications like `64 * 1024`, not bit shifts like `64 << 10`
|
||||
- Prefer `tw-*` utilities over inline `style` and `flex-*` helpers over per-child `tw-ml-*`/`tw-mr-*` margins, falling back to `tw-*` where specificity requires `!important`
|
||||
- Run `make fmt` after `.go` edits, `make tidy` after `go.mod` edits, `make generate-swagger` after API changes, and lint what changed with `make lint-go`, `lint-js`, `lint-css` or `lint-templates`
|
||||
|
||||
@@ -58,8 +58,7 @@ Contributions made with the assistance of AI tools are welcome, but contributors
|
||||
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 rely solely on AI to generate replies or engage in discussions.
|
||||
Ensure your responses are concise, accurate, and thoughtful, avoiding generic or low-quality AI-generated content.
|
||||
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.
|
||||
@@ -176,7 +175,7 @@ PR titles must follow the [Conventional Commits](https://www.conventionalcommits
|
||||
type(scope)!: subject
|
||||
```
|
||||
|
||||
The scope in parentheses is optional. A `!` immediately before the colon marks a [breaking change](https://www.conventionalcommits.org/en/v1.0.0/#summary): either `type!:` or `type(scope)!:` (not `type!(scope):`). Do not mark `build`, `chore`, `ci`, `docs`, `style`, or `test` as breaking.
|
||||
The scope in parentheses is optional. A `!` immediately before the colon marks a [breaking change](https://www.conventionalcommits.org/en/v1.0.0/#summary): either `type!:` or `type(scope)!:` (not `type!(scope):`).
|
||||
|
||||
Use one of these types:
|
||||
|
||||
|
||||
12
Dockerfile
12
Dockerfile
@@ -8,8 +8,8 @@ RUN --mount=type=cache,target=/root/.local/share/pnpm/store pnpm install --froze
|
||||
COPY --exclude=.git/ . .
|
||||
RUN make frontend
|
||||
|
||||
# Cross-compile backend on the native platform to avoid slow QEMU emulation
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27-alpine3.24 AS build-env
|
||||
# Build backend for each target platform
|
||||
FROM docker.io/library/golang:1.27-alpine3.24 AS build-env
|
||||
|
||||
ARG GITEA_VERSION
|
||||
ARG TAGS=""
|
||||
@@ -28,15 +28,11 @@ RUN go mod download
|
||||
# ".git" directory is mounted separately later only for version data extraction.
|
||||
COPY --exclude=.git/ . .
|
||||
COPY --from=frontend-build /src/public/assets public/assets
|
||||
RUN --mount=type=cache,target="/root/.cache/go-build" make generate-backend
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
# Build gitea, .git mount is required for version data
|
||||
RUN --mount=type=cache,target="/root/.cache/go-build" \
|
||||
--mount=type=bind,source=".git/",target=".git/" \
|
||||
GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT#v} make gitea
|
||||
make backend
|
||||
|
||||
COPY docker/root /tmp/local
|
||||
|
||||
@@ -52,7 +48,7 @@ FROM docker.io/library/alpine:3.24 AS gitea
|
||||
|
||||
EXPOSE 22 3000
|
||||
|
||||
RUN apk --no-cache upgrade && apk --no-cache add \
|
||||
RUN apk --no-cache add \
|
||||
bash \
|
||||
ca-certificates \
|
||||
curl \
|
||||
|
||||
@@ -8,8 +8,8 @@ RUN --mount=type=cache,target=/root/.local/share/pnpm/store pnpm install --froze
|
||||
COPY --exclude=.git/ . .
|
||||
RUN make frontend
|
||||
|
||||
# Cross-compile backend on the native platform
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27-alpine3.24 AS build-env
|
||||
# Build backend for each target platform
|
||||
FROM docker.io/library/golang:1.27-alpine3.24 AS build-env
|
||||
|
||||
ARG GITEA_VERSION
|
||||
ARG TAGS=""
|
||||
@@ -27,15 +27,11 @@ RUN go mod download
|
||||
# See the comments in Dockerfile
|
||||
COPY --exclude=.git/ . .
|
||||
COPY --from=frontend-build /src/public/assets public/assets
|
||||
RUN --mount=type=cache,target="/root/.cache/go-build" make generate-backend
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
# Build gitea, .git mount is required for version data
|
||||
RUN --mount=type=cache,target="/root/.cache/go-build" \
|
||||
--mount=type=bind,source=".git/",target=".git/" \
|
||||
GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT#v} make gitea
|
||||
make backend
|
||||
|
||||
COPY docker/rootless /tmp/local
|
||||
|
||||
@@ -47,7 +43,7 @@ FROM docker.io/library/alpine:3.24 AS gitea-rootless
|
||||
|
||||
EXPOSE 2222 3000
|
||||
|
||||
RUN apk --no-cache upgrade && apk --no-cache add \
|
||||
RUN apk --no-cache add \
|
||||
bash \
|
||||
ca-certificates \
|
||||
dumb-init \
|
||||
|
||||
@@ -64,6 +64,4 @@ Christopher Homberger <christopher.homberger@web.de> (@ChristopherHX)
|
||||
Tobias Balle-Petersen <tobiasbp@gmail.com> (@tobiasbp)
|
||||
TheFox <thefox0x7@gmail.com> (@TheFox0x7)
|
||||
Nicolas <bircni@icloud.com> (@bircni)
|
||||
Minjie Fang <wingsallen@gmail.com> (@wingsallen)
|
||||
Shudhanshu Singh <sudhanshuwriterblc@gmail.com> (@SudhanshuMatrix)
|
||||
Shivansh Garg <shivanshgarg587@gmail.com> (@SHIVANSHGARG07)
|
||||
Minjie Fang <wingsallen@gmail.com> (@wingsallen)
|
||||
68
Makefile
68
Makefile
@@ -1,21 +1,22 @@
|
||||
DIST := dist
|
||||
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
||||
HASH := \#
|
||||
|
||||
GO ?= go
|
||||
SHASUM ?= shasum -a 256
|
||||
|
||||
AIR_PACKAGE ?= github.com/air-verse/air@v1.67.4 # renovate: datasource=go
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v4/cmd/editorconfig-checker@v4.0.2 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.15.0 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.2 # renovate: datasource=go
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.17 # renovate: datasource=go
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.36.7 # renovate: datasource=go
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.9.0 # renovate: datasource=go
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.8.0 # renovate: datasource=go
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.36.6 # renovate: datasource=go
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.8.0 # renovate: datasource=go
|
||||
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 # renovate: datasource=go
|
||||
SHELLCHECK_IMAGE ?= docker.io/koalaman/shellcheck:v0.11.0@sha256:61862eba1fcf09a484ebcc6feea46f1782532571a34ed51fedf90dd25f925a8d # renovate: datasource=docker
|
||||
|
||||
CONTAINER_RUNTIME ?= $(shell hash docker >/dev/null 2>&1 && echo docker || echo podman)
|
||||
|
||||
PLAYWRIGHT_BROWSERS ?= chromium firefox webkit
|
||||
PLAYWRIGHT_BROWSERS ?= chromium firefox
|
||||
PLAYWRIGHT_FLAGS ?=
|
||||
|
||||
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
|
||||
@@ -68,15 +69,14 @@ endif
|
||||
|
||||
# GOTEST_FLAGS is for unit test and integration test
|
||||
GOTEST_FLAGS ?= -timeout 40m
|
||||
# disable race detection for the packages that slow down the tests
|
||||
GOTEST_FLAGS := -gcflags=modernc.org/sqlite/lib=-race=false $(GOTEST_FLAGS)
|
||||
|
||||
STORED_VERSION_FILE := VERSION
|
||||
|
||||
GITHUB_REF_TYPE ?= branch
|
||||
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
# VERSION: the name for the build and filenames, e.g.: "1.2.3" for tag "v1.2.3", "main-nightly" for branch "main"
|
||||
# VERSION: the branch name for the build and filenames, e.g.: "feature/foo-bar", "main"
|
||||
# branch name "release/v1.27.2" is stripped to "1.27.2".
|
||||
# GITEA_VERSION: the Gitea's internal version for display, e.g. "1.28.0+dev-356-ge47d0b66ea"
|
||||
ifeq ($(GITHUB_REF_TYPE),tag)
|
||||
# convert tag "v1.2.3" to "1.2.3"
|
||||
@@ -101,6 +101,11 @@ else
|
||||
$(error unsupported ref type $(GITHUB_REF_TYPE))
|
||||
endif
|
||||
|
||||
# if version == "main" then add "-nightly" to the version for nightly builds: "main-nightly"
|
||||
ifeq ($(VERSION),main)
|
||||
VERSION := main-nightly
|
||||
endif
|
||||
|
||||
LDFLAGS := $(LDFLAGS) -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
||||
RELEASE_ENV = GO="$(GO)" TAGS="$(TAGS)" LDFLAGS="$(LDFLAGS)" DIST="$(DIST)" VERSION="$(VERSION)"
|
||||
|
||||
@@ -127,9 +132,11 @@ GO_LICENSE_FILE := assets/go-licenses.json
|
||||
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR)
|
||||
|
||||
GO_DIRS := build cmd modelmigration models modules routers services tests tools
|
||||
WEB_DIRS := web_src/js web_src/css
|
||||
|
||||
ESLINT_FILES := web_src/js tools *.ts tests/e2e
|
||||
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
|
||||
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.json .github $(wildcard *.go *.md *.yml *.yaml *.toml)
|
||||
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.json
|
||||
|
||||
GO_SOURCES := $(wildcard *.go)
|
||||
@@ -266,10 +273,10 @@ checks-frontend: lockfile-check svg-check ## check frontend files
|
||||
checks-backend: tidy-check swagger-check openapi3-check fmt-check swagger-validate security-check ## check backend files
|
||||
|
||||
.PHONY: lint
|
||||
lint: lint-frontend lint-backend lint-templates lint-swagger lint-md lint-actions lint-json lint-yaml lint-shell ## lint everything
|
||||
lint: lint-frontend lint-backend lint-templates lint-swagger lint-spell lint-md lint-actions lint-json lint-yaml lint-shell ## lint everything
|
||||
|
||||
.PHONY: lint-fix
|
||||
lint-fix: lint-frontend-fix lint-backend-fix ## lint everything and fix issues
|
||||
lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix ## lint everything and fix issues
|
||||
|
||||
.PHONY: lint-frontend
|
||||
lint-frontend: lint-js lint-css ## lint frontend files
|
||||
@@ -313,6 +320,14 @@ lint-md: node_modules ## lint markdown files
|
||||
lint-md-fix: node_modules ## lint markdown files and fix issues
|
||||
pnpm exec markdownlint --fix *.md
|
||||
|
||||
.PHONY: lint-spell
|
||||
lint-spell: ## lint spelling
|
||||
@git ls-files $(SPELLCHECK_FILES) | xargs go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -error
|
||||
|
||||
.PHONY: lint-spell-fix
|
||||
lint-spell-fix: ## lint spelling and fix issues
|
||||
@git ls-files $(SPELLCHECK_FILES) | xargs go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -w
|
||||
|
||||
.PHONY: lint-go
|
||||
lint-go: ## lint go files
|
||||
GO=$(GO) GOLANGCI_LINT_PACKAGE=$(GOLANGCI_LINT_PACKAGE) $(GO) run ./tools/lint-go-all.go
|
||||
@@ -385,11 +400,10 @@ test-check:
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
TEST_BACKEND_GOALS := $(filter test-backend$(HASH)%,$(MAKECMDGOALS))
|
||||
.PHONY: $(TEST_BACKEND_GOALS)
|
||||
$(TEST_BACKEND_GOALS): test-backend$(HASH)%:
|
||||
.PHONY: test-backend\#%
|
||||
test-backend\#%:
|
||||
@echo "Running go test with -tags '$(TAGS)'..."
|
||||
@$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' -run '$*' $(GO_TEST_PACKAGES)
|
||||
@$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' -run '$(subst .,/,$*)' $(GO_TEST_PACKAGES)
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
@@ -444,10 +458,9 @@ test-integration: $(EXECUTABLE)
|
||||
test-integration-compile:
|
||||
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -c -o /dev/null gitea.dev/tests/integration
|
||||
|
||||
TEST_INTEGRATION_GOALS := $(filter test-integration$(HASH)%,$(MAKECMDGOALS))
|
||||
.PHONY: $(TEST_INTEGRATION_GOALS)
|
||||
$(TEST_INTEGRATION_GOALS): test-integration$(HASH)%: $(EXECUTABLE)
|
||||
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -run '$*' gitea.dev/tests/integration
|
||||
.PHONY: test-integration\#%
|
||||
test-integration\#%: $(EXECUTABLE)
|
||||
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -run '$(subst .,/,$*)' gitea.dev/tests/integration
|
||||
|
||||
.PHONY: test-migration
|
||||
test-migration: migrations.integration.test migrations.individual.test
|
||||
@@ -543,7 +556,7 @@ release-compress: | $(DIST_DIRS)
|
||||
|
||||
.PHONY: release-sources
|
||||
release-sources: | $(DIST_DIRS)
|
||||
echo $(GITEA_VERSION) > $(STORED_VERSION_FILE)
|
||||
echo $(VERSION) > $(STORED_VERSION_FILE)
|
||||
# bsdtar needs a ^ to prevent matching subdirectories
|
||||
$(eval EXCL := --exclude=$(shell tar --help | grep -q bsdtar && echo "^")./)
|
||||
# use transform to a add a release-folder prefix; in bsdtar the transform parameter equivalent is -s
|
||||
@@ -565,14 +578,15 @@ deps-backend: ## install backend dependencies
|
||||
$(GO) mod download
|
||||
|
||||
.PHONY: deps-tools
|
||||
deps-tools: ## fetch and compile tool dependencies
|
||||
$(GO) run -exec true $(AIR_PACKAGE) & \
|
||||
$(GO) run -exec true $(EDITORCONFIG_CHECKER_PACKAGE) & \
|
||||
$(GO) run -exec true $(GOLANGCI_LINT_PACKAGE) & \
|
||||
$(GO) run -exec true $(GXZ_PACKAGE) & \
|
||||
$(GO) run -exec true $(SWAGGER_PACKAGE) & \
|
||||
$(GO) run -exec true $(GOVULNCHECK_PACKAGE) & \
|
||||
$(GO) run -exec true $(ACTIONLINT_PACKAGE) & \
|
||||
deps-tools: ## install tool dependencies
|
||||
$(GO) install $(AIR_PACKAGE) & \
|
||||
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE) & \
|
||||
$(GO) install $(GOLANGCI_LINT_PACKAGE) & \
|
||||
$(GO) install $(GXZ_PACKAGE) & \
|
||||
$(GO) install $(MISSPELL_PACKAGE) & \
|
||||
$(GO) install $(SWAGGER_PACKAGE) & \
|
||||
$(GO) install $(GOVULNCHECK_PACKAGE) & \
|
||||
$(GO) install $(ACTIONLINT_PACKAGE) & \
|
||||
wait
|
||||
|
||||
node_modules: pnpm-lock.yaml
|
||||
|
||||
@@ -68,7 +68,7 @@ Get more information from [the translation section of our contributing guide](CO
|
||||
|
||||
## Official and Third-Party Projects
|
||||
|
||||
We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [gt](https://gitea.com/gitea/cli) and an [action runner](https://gitea.com/gitea/runner) for Gitea Action.
|
||||
We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [tea](https://gitea.com/gitea/tea) and an [action runner](https://gitea.com/gitea/runner) for Gitea Action.
|
||||
|
||||
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea), where you can discover more third-party projects, including SDKs, plugins, themes, and more.
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
## 官方和第三方项目
|
||||
|
||||
我们提供了一个官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一个名为 [gt](https://gitea.com/gitea/cli) 的 CLI 工具和一个 Gitea Action 的 [action runner](https://gitea.com/gitea/runner)。
|
||||
我们提供了一个官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一个名为 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一个 Gitea Action 的 [action runner](https://gitea.com/gitea/runner)。
|
||||
|
||||
我们在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 维护了一个 Gitea 相关项目的列表,您可以在那里发现更多的第三方项目,包括 SDK、插件、主题等。
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
## 官方和第三方項目
|
||||
|
||||
我們提供了一個官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一個名為 [gt](https://gitea.com/gitea/cli) 的 CLI 工具和一個 Gitea Action 的 [action runner](https://gitea.com/gitea/runner)。
|
||||
我們提供了一個官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一個名為 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一個 Gitea Action 的 [action runner](https://gitea.com/gitea/runner)。
|
||||
|
||||
我們在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 維護了一個 Gitea 相關項目的列表,您可以在那裡發現更多的第三方項目,包括 SDK、插件、主題等。
|
||||
|
||||
|
||||
18
SECURITY.md
18
SECURITY.md
@@ -14,28 +14,12 @@ Reports must be filed against a supported version. Before reporting, verify that
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please **DO NOT** file a public issue. Instead, report the vulnerability privately by opening a [GitHub security advisory](https://github.com/go-gitea/gitea/security/advisories/new), or optionally by sending your report to `security@gitea.io`. Former is preferred if given a choice.
|
||||
Please **DO NOT** file a public issue. Instead, report the vulnerability privately by opening a [GitHub security advisory](https://github.com/go-gitea/gitea/security/advisories/new), or optionally by sending your report to `security@gitea.io`.
|
||||
If reporting via GitHub, open an advisory per found issue. You can group the found issues if they relate to a single area (same feature, same fault present in multiple routes) but do not group unrelated reports into one as it makes it much harder to assess and track for duplicates.
|
||||
Do not use LLM to write the reports. It's meant for people to read so take your time and use your own words to write it. If you have used LLM to find the bug, be transparent about it and verify that it is not hallucination before submitting the report.
|
||||
|
||||
If your report turns out to be a duplicate, you will be credited in the original report.
|
||||
|
||||
### Report requirements
|
||||
|
||||
When filing the report please document it. Reports must include:
|
||||
|
||||
- Version or a range of versions it was successfully tested against. If reproducible against the `main` branch, a commit hash is also acceptable. Ambiguous versions like `latest` are not acceptable.
|
||||
- Settings used, especially when they diverge from the defaults
|
||||
- Reproduction steps from freshly installed instance. If the fault is only reproducible through upgrade from older version, reproduction should state that.
|
||||
|
||||
Take your time to format it properly as it'll be much easier to read. Prefer a concise writing style.
|
||||
When submitting an advisory, it should have the `gitea.dev` go package as package `name.cd`
|
||||
|
||||
### Triage process
|
||||
|
||||
Github does not have any search or tagging in GHSA so in most cases your report will leave triage state when we have a fix for it in works for next release.
|
||||
This helps us keep track of items fixed and pending release and separate them from things that we either haven't looked at yet or have not fixed.
|
||||
|
||||
## Protecting Security Information
|
||||
|
||||
Due to the sensitive nature of security information, you can use the below GPG public key to encrypt your mail body.
|
||||
|
||||
93
assets/go-licenses.json
generated
93
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
21
assets/misspellings.csv
Normal file
21
assets/misspellings.csv
Normal file
@@ -0,0 +1,21 @@
|
||||
acounts,accounts
|
||||
canidate,candidate
|
||||
comfirm,confirm
|
||||
converage,coverage
|
||||
currrently,currently
|
||||
delimeter,delimiter
|
||||
differrent,different
|
||||
exclusing,excluding
|
||||
finshed,finished
|
||||
formated,formatted
|
||||
inderect,indirect
|
||||
insuficient,insufficient
|
||||
likly,likely
|
||||
mergable,mergeable
|
||||
overrided,overridden
|
||||
priortized,prioritized
|
||||
registeration,registration
|
||||
reuqest,request
|
||||
reviwer,reviewer
|
||||
superceded,superseded
|
||||
underlaying,underlying
|
||||
|
@@ -16,7 +16,6 @@ import (
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/json"
|
||||
)
|
||||
@@ -32,9 +31,7 @@ type emoji struct {
|
||||
description string
|
||||
}
|
||||
|
||||
var slugRe = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`[^a-z0-9]+`)
|
||||
})
|
||||
var slugRe = regexp.MustCompile(`[^a-z0-9]+`)
|
||||
|
||||
func main() {
|
||||
if err := generate(); err != nil {
|
||||
@@ -111,7 +108,7 @@ func fetchEmojis(existingAliases map[string][]string) ([]*emoji, error) {
|
||||
for _, e := range emojis {
|
||||
e.Aliases = existingAliases[e.Emoji]
|
||||
if e.Aliases == nil {
|
||||
e.Aliases = []string{strings.Trim(slugRe().ReplaceAllString(strings.ReplaceAll(strings.ToLower(e.description), "’", ""), "_"), "_")}
|
||||
e.Aliases = []string{strings.Trim(slugRe.ReplaceAllString(strings.ReplaceAll(strings.ToLower(e.description), "’", ""), "_"), "_")}
|
||||
}
|
||||
delete(existingAliases, e.Emoji)
|
||||
for _, alias := range e.Aliases {
|
||||
|
||||
@@ -15,24 +15,17 @@ import (
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
licenseRe *regexp.Regexp
|
||||
primaryLicenseRe *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// regexp is based on go-license, excluding README and NOTICE
|
||||
// https://github.com/google/go-licenses/blob/master/licenses/find.go
|
||||
// also defined in vite.config.ts
|
||||
ret.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.
|
||||
ret.primaryLicenseRe = regexp.MustCompile(`^(?i)(LICEN[SC]E|COPYING)$`)
|
||||
return ret
|
||||
})
|
||||
// regexp is based on go-license, excluding README and NOTICE
|
||||
// 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).*$`)
|
||||
|
||||
// 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{
|
||||
@@ -151,7 +144,7 @@ func scanDirForLicenses(dir, modulePath, moduleRoot string) []LicenseEntry {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
if !globalVars().licenseRe.MatchString(name) {
|
||||
if !licenseRe.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
if excludedExt[strings.ToLower(filepath.Ext(name))] {
|
||||
@@ -188,7 +181,7 @@ func scanDirForLicenses(dir, modulePath, moduleRoot string) []LicenseEntry {
|
||||
var primary []LicenseEntry
|
||||
for _, e := range entries {
|
||||
fileName := e.Path[strings.LastIndex(e.Path, "/")+1:]
|
||||
if globalVars().primaryLicenseRe.MatchString(fileName) {
|
||||
if primaryLicenseRe.MatchString(fileName) {
|
||||
primary = append(primary, e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/json"
|
||||
|
||||
@@ -17,23 +16,15 @@ import (
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
)
|
||||
|
||||
var globalVars = sync.OnceValue(func() (ret struct {
|
||||
rxDeprecated *regexp.Regexp
|
||||
rxSwaggerEnum *regexp.Regexp
|
||||
},
|
||||
) {
|
||||
// rxDeprecated matches "deprecated" as a word at the start of a description
|
||||
// or preceded by whitespace/punctuation that indicates a leading marker (e.g.
|
||||
// "Deprecated: true", "deprecated (use X instead)"). Rejects negated phrases
|
||||
// like "not deprecated" or "previously deprecated, now supported".
|
||||
ret.rxDeprecated = regexp.MustCompile(`(?i)(?:^|[\n.;])\s*deprecated\b`)
|
||||
ret.rxSwaggerEnum = regexp.MustCompile(`swagger:enum\s+(\w+)`)
|
||||
return ret
|
||||
})
|
||||
// rxDeprecated matches "deprecated" as a word at the start of a description
|
||||
// or preceded by whitespace/punctuation that indicates a leading marker (e.g.
|
||||
// "Deprecated: true", "deprecated (use X instead)"). Rejects negated phrases
|
||||
// like "not deprecated" or "previously deprecated, now supported".
|
||||
var rxDeprecated = regexp.MustCompile(`(?i)(?:^|[\n.;])\s*deprecated\b`)
|
||||
|
||||
// Convert parses a Swagger 2.0 spec and returns an OAS3 spec, applying
|
||||
// Gitea-specific post-processing: file-schema fixups, URI formats,
|
||||
// deprecated flags, and shared-enum extraction.
|
||||
// Gitea-specific post-processing: server URL, file-schema fixups, URI
|
||||
// formats, deprecated flags, and shared-enum extraction.
|
||||
//
|
||||
// astEnumMap is a value-set-key → Go-type-name(s) map (built by
|
||||
// ScanSwaggerEnumTypes). When a value set is shared by multiple Go types,
|
||||
@@ -51,6 +42,7 @@ func Convert(swaggerJSON []byte, astEnumMap map[string][]string) (*openapi3.T, e
|
||||
return nil, fmt.Errorf("converting to openapi 3.0: %w", err)
|
||||
}
|
||||
|
||||
oas3.Servers = openapi3.Servers{{URL: swagger2.BasePath}}
|
||||
fixFileSchemas(oas3)
|
||||
addURIFormats(oas3)
|
||||
addDeprecatedFlags(oas3)
|
||||
@@ -167,7 +159,7 @@ func addDeprecatedFlags(doc *openapi3.T) {
|
||||
if propRef == nil || propRef.Value == nil || propRef.Ref != "" {
|
||||
continue
|
||||
}
|
||||
if globalVars().rxDeprecated.MatchString(propRef.Value.Description) {
|
||||
if rxDeprecated.MatchString(propRef.Value.Description) {
|
||||
propRef.Value.Deprecated = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,19 +42,27 @@ func TestDeriveEnumName_miss(t *testing.T) {
|
||||
func TestExtractSharedEnums_usesASTMap(t *testing.T) {
|
||||
doc := &openapi3.T{
|
||||
Components: &openapi3.Components{
|
||||
Schemas: openapi3.Schemas{},
|
||||
},
|
||||
}
|
||||
for _, name := range []string{"A", "B"} {
|
||||
doc.Components.Schemas[name] = &openapi3.SchemaRef{Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"object"},
|
||||
Properties: openapi3.Schemas{
|
||||
"color": {Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"string"},
|
||||
Enum: []any{"red", "green", "blue"},
|
||||
Schemas: openapi3.Schemas{
|
||||
"A": {Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"object"},
|
||||
Properties: openapi3.Schemas{
|
||||
"color": {Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"string"},
|
||||
Enum: []any{"red", "green", "blue"},
|
||||
}},
|
||||
},
|
||||
}},
|
||||
"B": {Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"object"},
|
||||
Properties: openapi3.Schemas{
|
||||
"color": {Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"string"},
|
||||
Enum: []any{"red", "green", "blue"},
|
||||
}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}}
|
||||
},
|
||||
}
|
||||
astMap := map[string][]string{EnumKey([]any{"red", "green", "blue"}): {"Color"}}
|
||||
if err := extractSharedEnums(doc, astMap); err != nil {
|
||||
@@ -182,19 +190,27 @@ func TestExtractEnumTypeName_rejectsIncidentalPrefix(t *testing.T) {
|
||||
func TestExtractSharedEnums_missReturnsError(t *testing.T) {
|
||||
doc := &openapi3.T{
|
||||
Components: &openapi3.Components{
|
||||
Schemas: openapi3.Schemas{},
|
||||
},
|
||||
}
|
||||
for _, name := range []string{"A", "B"} {
|
||||
doc.Components.Schemas[name] = &openapi3.SchemaRef{Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"object"},
|
||||
Properties: openapi3.Schemas{
|
||||
"color": {Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"string"},
|
||||
Enum: []any{"red", "green"},
|
||||
Schemas: openapi3.Schemas{
|
||||
"A": {Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"object"},
|
||||
Properties: openapi3.Schemas{
|
||||
"color": {Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"string"},
|
||||
Enum: []any{"red", "green"},
|
||||
}},
|
||||
},
|
||||
}},
|
||||
"B": {Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"object"},
|
||||
Properties: openapi3.Schemas{
|
||||
"color": {Value: &openapi3.Schema{
|
||||
Type: &openapi3.Types{"string"},
|
||||
Enum: []any{"red", "green"},
|
||||
}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}}
|
||||
},
|
||||
}
|
||||
if err := extractSharedEnums(doc, map[string][]string{}); err == nil {
|
||||
t.Fatal("expected miss error")
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -30,6 +31,8 @@ func EnumKey(values []any) string {
|
||||
return strings.Join(strs, "|")
|
||||
}
|
||||
|
||||
var rxSwaggerEnum = regexp.MustCompile(`swagger:enum\s+(\w+)`)
|
||||
|
||||
// ScanSwaggerEnumTypes walks .go files under each dir and returns a map from
|
||||
// a canonical value-set key (see EnumKey) to the Go type names declared with
|
||||
// // swagger:enum TypeName. Multiple type names per key are allowed (e.g.
|
||||
@@ -143,7 +146,7 @@ func registerEnumAnnotation(doc *ast.CommentGroup, specs []ast.Spec, enumTypes m
|
||||
if doc == nil {
|
||||
return nil
|
||||
}
|
||||
matches := globalVars().rxSwaggerEnum.FindStringSubmatch(doc.Text())
|
||||
matches := rxSwaggerEnum.FindStringSubmatch(doc.Text())
|
||||
if len(matches) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -33,17 +33,9 @@ func TestEnumKey_handlesNonStringValues(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanSwaggerEnumTypes(t *testing.T) {
|
||||
scan := func(t *testing.T, filename, src string) (map[string][]string, error) {
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, filename), []byte(src), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return ScanSwaggerEnumTypes([]string{dir})
|
||||
}
|
||||
|
||||
t.Run("basic", func(t *testing.T) {
|
||||
got, err := scan(t, "color.go", `package fixture
|
||||
func TestScanSwaggerEnumTypes_basic(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := `package fixture
|
||||
|
||||
// Color is a primary color.
|
||||
// swagger:enum Color
|
||||
@@ -54,18 +46,24 @@ const (
|
||||
ColorGreen Color = "green"
|
||||
ColorBlue Color = "blue"
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
|
||||
}
|
||||
wantKey := EnumKey([]any{"red", "green", "blue"})
|
||||
if single(got, wantKey) != "Color" {
|
||||
t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Color")
|
||||
}
|
||||
})
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(dir, "color.go"), []byte(src), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Run("orphanAnnotation", func(t *testing.T) {
|
||||
_, err := scan(t, "typo.go", `package fixture
|
||||
got, err := ScanSwaggerEnumTypes([]string{dir})
|
||||
if err != nil {
|
||||
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
|
||||
}
|
||||
wantKey := EnumKey([]any{"red", "green", "blue"})
|
||||
if single(got, wantKey) != "Color" {
|
||||
t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Color")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanSwaggerEnumTypes_orphanAnnotation(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := `package fixture
|
||||
|
||||
// swagger:enum Sttype
|
||||
type StateType string
|
||||
@@ -73,17 +71,23 @@ type StateType string
|
||||
const (
|
||||
StateOpen StateType = "open"
|
||||
)
|
||||
`)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for annotation referencing a non-matching type name")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "Sttype") {
|
||||
t.Fatalf("error %q should mention the typo'd name Sttype", err.Error())
|
||||
}
|
||||
})
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(dir, "typo.go"), []byte(src), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Run("collision", func(t *testing.T) {
|
||||
got, err := scan(t, "dup.go", `package fixture
|
||||
_, err := ScanSwaggerEnumTypes([]string{dir})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for annotation referencing a non-matching type name")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "Sttype") {
|
||||
t.Fatalf("error %q should mention the typo'd name Sttype", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanSwaggerEnumTypes_collision(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := `package fixture
|
||||
|
||||
// swagger:enum Alpha
|
||||
type Alpha string
|
||||
@@ -98,90 +102,52 @@ const (
|
||||
BetaX Beta = "x"
|
||||
BetaY Beta = "y"
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
|
||||
}
|
||||
key := EnumKey([]any{"x", "y"})
|
||||
names := got[key]
|
||||
if !slices.Equal(names, []string{"Alpha", "Beta"}) {
|
||||
t.Fatalf("map[%q] = %v, want [Alpha Beta]", key, names)
|
||||
}
|
||||
})
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(dir, "dup.go"), []byte(src), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Run("parseFailure", func(t *testing.T) {
|
||||
_, err := scan(t, "bad.go", "package fixture\nfunc Foo() {")
|
||||
if err == nil {
|
||||
t.Fatal("expected parse error, got nil")
|
||||
}
|
||||
})
|
||||
got, err := ScanSwaggerEnumTypes([]string{dir})
|
||||
if err != nil {
|
||||
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
|
||||
}
|
||||
key := EnumKey([]any{"x", "y"})
|
||||
names := got[key]
|
||||
if !slices.Equal(names, []string{"Alpha", "Beta"}) {
|
||||
t.Fatalf("map[%q] = %v, want [Alpha Beta]", key, names)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("annotationWithoutConsts", func(t *testing.T) {
|
||||
_, err := scan(t, "lonely.go", `package fixture
|
||||
func TestScanSwaggerEnumTypes_parseFailure(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, "bad.go"), []byte("package fixture\nfunc Foo() {"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err := ScanSwaggerEnumTypes([]string{dir})
|
||||
if err == nil {
|
||||
t.Fatal("expected parse error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanSwaggerEnumTypes_annotationWithoutConsts(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := `package fixture
|
||||
|
||||
// swagger:enum Lonely
|
||||
type Lonely string
|
||||
`)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for annotation without consts")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "Lonely") {
|
||||
t.Fatalf("error %q should mention Lonely", err.Error())
|
||||
}
|
||||
})
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(dir, "lonely.go"), []byte(src), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Run("constsBeforeType", func(t *testing.T) {
|
||||
got, err := scan(t, "shade.go", `package fixture
|
||||
|
||||
const (
|
||||
ShadeDark Shade = "dark"
|
||||
ShadeLight Shade = "light"
|
||||
)
|
||||
|
||||
// swagger:enum Shade
|
||||
type Shade string
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
|
||||
}
|
||||
wantKey := EnumKey([]any{"dark", "light"})
|
||||
if single(got, wantKey) != "Shade" {
|
||||
t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Shade")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("groupedTypeDecl", func(t *testing.T) {
|
||||
got, err := scan(t, "grouped.go", `package fixture
|
||||
|
||||
type (
|
||||
// swagger:enum Color
|
||||
Color string
|
||||
// swagger:enum Shade
|
||||
Shade string
|
||||
)
|
||||
|
||||
const (
|
||||
ColorRed Color = "red"
|
||||
ColorBlue Color = "blue"
|
||||
)
|
||||
|
||||
const (
|
||||
ShadeDark Shade = "dark"
|
||||
ShadeLight Shade = "light"
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
|
||||
}
|
||||
colorKey := EnumKey([]any{"red", "blue"})
|
||||
shadeKey := EnumKey([]any{"dark", "light"})
|
||||
if single(got, colorKey) != "Color" {
|
||||
t.Fatalf("Color: map[%q] = %q, want %q", colorKey, got[colorKey], "Color")
|
||||
}
|
||||
if single(got, shadeKey) != "Shade" {
|
||||
t.Fatalf("Shade: map[%q] = %q, want %q", shadeKey, got[shadeKey], "Shade")
|
||||
}
|
||||
})
|
||||
_, err := ScanSwaggerEnumTypes([]string{dir})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for annotation without consts")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "Lonely") {
|
||||
t.Fatalf("error %q should mention Lonely", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanSwaggerEnumTypes_constsAndTypeInDifferentFiles(t *testing.T) {
|
||||
@@ -216,3 +182,68 @@ type Hue string
|
||||
t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Hue")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanSwaggerEnumTypes_constsBeforeType(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := `package fixture
|
||||
|
||||
const (
|
||||
ShadeDark Shade = "dark"
|
||||
ShadeLight Shade = "light"
|
||||
)
|
||||
|
||||
// swagger:enum Shade
|
||||
type Shade string
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(dir, "shade.go"), []byte(src), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, err := ScanSwaggerEnumTypes([]string{dir})
|
||||
if err != nil {
|
||||
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
|
||||
}
|
||||
wantKey := EnumKey([]any{"dark", "light"})
|
||||
if single(got, wantKey) != "Shade" {
|
||||
t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Shade")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanSwaggerEnumTypes_groupedTypeDecl(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
src := `package fixture
|
||||
|
||||
type (
|
||||
// swagger:enum Color
|
||||
Color string
|
||||
// swagger:enum Shade
|
||||
Shade string
|
||||
)
|
||||
|
||||
const (
|
||||
ColorRed Color = "red"
|
||||
ColorBlue Color = "blue"
|
||||
)
|
||||
|
||||
const (
|
||||
ShadeDark Shade = "dark"
|
||||
ShadeLight Shade = "light"
|
||||
)
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(dir, "grouped.go"), []byte(src), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, err := ScanSwaggerEnumTypes([]string{dir})
|
||||
if err != nil {
|
||||
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
|
||||
}
|
||||
colorKey := EnumKey([]any{"red", "blue"})
|
||||
shadeKey := EnumKey([]any{"dark", "light"})
|
||||
if single(got, colorKey) != "Color" {
|
||||
t.Fatalf("Color: map[%q] = %q, want %q", colorKey, got[colorKey], "Color")
|
||||
}
|
||||
if single(got, shadeKey) != "Shade" {
|
||||
t.Fatalf("Shade: map[%q] = %q, want %q", shadeKey, got[shadeKey], "Shade")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,11 +49,11 @@ func commonLdapCLIFlags() []cli.Flag {
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "host",
|
||||
Usage: "Comma-separated LDAP hosts or ldap://, ldaps:// or ldap+starttls:// URLs, tried in order.",
|
||||
Usage: "The address where the LDAP server can be reached.",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "port",
|
||||
Usage: "The port to use when connecting to hosts given without a URL.",
|
||||
Usage: "The port to use when connecting to the LDAP server.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "user-search-base",
|
||||
|
||||
@@ -489,20 +489,13 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
||||
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
|
||||
|
||||
// Test cases
|
||||
type testCase struct {
|
||||
cases := []struct {
|
||||
args []string
|
||||
id int64
|
||||
existingAuthSource *auth.Source
|
||||
authSource *auth.Source
|
||||
errMsg string
|
||||
}
|
||||
withConfig := func(config *ldap.Source, args ...string) testCase {
|
||||
return testCase{
|
||||
args: append([]string{"ldap-test", "--id", "1"}, args...),
|
||||
authSource: &auth.Source{Type: auth.LDAP, Cfg: config},
|
||||
}
|
||||
}
|
||||
cases := []testCase{
|
||||
}{
|
||||
// case 0
|
||||
{
|
||||
args: []string{
|
||||
@@ -581,7 +574,16 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
||||
},
|
||||
},
|
||||
// case 1
|
||||
withConfig(&ldap.Source{}),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{},
|
||||
},
|
||||
},
|
||||
// case 2
|
||||
{
|
||||
args: []string{
|
||||
@@ -616,44 +618,215 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
||||
},
|
||||
},
|
||||
// case 4
|
||||
withConfig(&ldap.Source{SecurityProtocol: ldap.SecurityProtocol(1)}, "--security-protocol", "LDAPS"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--security-protocol", "LDAPS",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
SecurityProtocol: ldap.SecurityProtocol(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 5
|
||||
withConfig(&ldap.Source{SkipVerify: true}, "--skip-tls-verify"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--skip-tls-verify",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
SkipVerify: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 6
|
||||
withConfig(&ldap.Source{Host: "ldap-server"}, "--host", "ldap-server"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--host", "ldap-server",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
Host: "ldap-server",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 7
|
||||
withConfig(&ldap.Source{Port: 389}, "--port", "389"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--port", "389",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
Port: 389,
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 8
|
||||
withConfig(
|
||||
&ldap.Source{UserBase: "ou=Users,dc=domain,dc=org"},
|
||||
"--user-search-base", "ou=Users,dc=domain,dc=org",
|
||||
),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--user-search-base", "ou=Users,dc=domain,dc=org",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
UserBase: "ou=Users,dc=domain,dc=org",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 9
|
||||
withConfig(
|
||||
&ldap.Source{Filter: "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)"},
|
||||
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
|
||||
),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
Filter: "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 10
|
||||
withConfig(
|
||||
&ldap.Source{AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)"},
|
||||
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
|
||||
),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 11
|
||||
withConfig(&ldap.Source{AttributeUsername: "uid"}, "--username-attribute", "uid"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--username-attribute", "uid",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributeUsername: "uid",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 12
|
||||
withConfig(&ldap.Source{AttributeName: "givenName"}, "--firstname-attribute", "givenName"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--firstname-attribute", "givenName",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributeName: "givenName",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 13
|
||||
withConfig(&ldap.Source{AttributeSurname: "sn"}, "--surname-attribute", "sn"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--surname-attribute", "sn",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributeSurname: "sn",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 14
|
||||
withConfig(&ldap.Source{AttributeMail: "mail"}, "--email-attribute", "mail"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--email-attribute", "mail",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributeMail: "mail",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 15
|
||||
withConfig(&ldap.Source{AttributesInBind: true}, "--attributes-in-bind"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--attributes-in-bind",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributesInBind: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 16
|
||||
withConfig(&ldap.Source{AttributeSSHPublicKey: "publickey"}, "--public-ssh-key-attribute", "publickey"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--public-ssh-key-attribute", "publickey",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributeSSHPublicKey: "publickey",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 17
|
||||
withConfig(&ldap.Source{BindDN: "cn=readonly,dc=domain,dc=org"}, "--bind-dn", "cn=readonly,dc=domain,dc=org"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--bind-dn", "cn=readonly,dc=domain,dc=org",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
BindDN: "cn=readonly,dc=domain,dc=org",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 18
|
||||
withConfig(&ldap.Source{BindPassword: "secret"}, "--bind-password", "secret"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--bind-password", "secret",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
BindPassword: "secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 19
|
||||
{
|
||||
args: []string{
|
||||
@@ -668,7 +841,19 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
||||
},
|
||||
},
|
||||
// case 20
|
||||
withConfig(&ldap.Source{SearchPageSize: 12}, "--page-size", "12"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--page-size", "12",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
Cfg: &ldap.Source{
|
||||
SearchPageSize: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 21
|
||||
{
|
||||
args: []string{
|
||||
@@ -779,20 +964,13 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
|
||||
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
|
||||
|
||||
// Test cases
|
||||
type testCase struct {
|
||||
cases := []struct {
|
||||
args []string
|
||||
id int64
|
||||
existingAuthSource *auth.Source
|
||||
authSource *auth.Source
|
||||
errMsg string
|
||||
}
|
||||
withConfig := func(config *ldap.Source, args ...string) testCase {
|
||||
return testCase{
|
||||
args: append([]string{"ldap-test", "--id", "1"}, args...),
|
||||
authSource: &auth.Source{Type: auth.DLDAP, Cfg: config},
|
||||
}
|
||||
}
|
||||
cases := []testCase{
|
||||
}{
|
||||
// case 0
|
||||
{
|
||||
args: []string{
|
||||
@@ -842,7 +1020,16 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
|
||||
},
|
||||
},
|
||||
// case 1
|
||||
withConfig(&ldap.Source{}),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{},
|
||||
},
|
||||
},
|
||||
// case 2
|
||||
{
|
||||
args: []string{
|
||||
@@ -877,43 +1064,187 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
|
||||
},
|
||||
},
|
||||
// case 4
|
||||
withConfig(&ldap.Source{SecurityProtocol: ldap.SecurityProtocol(2)}, "--security-protocol", "starttls"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--security-protocol", "starttls",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
SecurityProtocol: ldap.SecurityProtocol(2),
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 5
|
||||
withConfig(&ldap.Source{SkipVerify: true}, "--skip-tls-verify"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--skip-tls-verify",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
SkipVerify: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 6
|
||||
withConfig(&ldap.Source{Host: "ldap-server"}, "--host", "ldap-server"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--host", "ldap-server",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
Host: "ldap-server",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 7
|
||||
withConfig(&ldap.Source{Port: 987}, "--port", "987"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--port", "987",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
Port: 987,
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 8
|
||||
withConfig(
|
||||
&ldap.Source{UserBase: "ou=Users,dc=domain,dc=org"},
|
||||
"--user-search-base", "ou=Users,dc=domain,dc=org",
|
||||
),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--user-search-base", "ou=Users,dc=domain,dc=org",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
UserBase: "ou=Users,dc=domain,dc=org",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 9
|
||||
withConfig(
|
||||
&ldap.Source{Filter: "(&(objectClass=posixAccount)(cn=%s))"},
|
||||
"--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
|
||||
),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
Filter: "(&(objectClass=posixAccount)(cn=%s))",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 10
|
||||
withConfig(
|
||||
&ldap.Source{AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)"},
|
||||
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
|
||||
),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 11
|
||||
withConfig(&ldap.Source{AttributeUsername: "uid"}, "--username-attribute", "uid"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--username-attribute", "uid",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributeUsername: "uid",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 12
|
||||
withConfig(&ldap.Source{AttributeName: "givenName"}, "--firstname-attribute", "givenName"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--firstname-attribute", "givenName",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributeName: "givenName",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 13
|
||||
withConfig(&ldap.Source{AttributeSurname: "sn"}, "--surname-attribute", "sn"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--surname-attribute", "sn",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributeSurname: "sn",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 14
|
||||
withConfig(&ldap.Source{AttributeMail: "mail"}, "--email-attribute", "mail"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--email-attribute", "mail",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributeMail: "mail",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 15
|
||||
withConfig(&ldap.Source{AttributeSSHPublicKey: "publickey"}, "--public-ssh-key-attribute", "publickey"),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--public-ssh-key-attribute", "publickey",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
AttributeSSHPublicKey: "publickey",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 16
|
||||
withConfig(
|
||||
&ldap.Source{UserDN: "cn=%s,ou=Users,dc=domain,dc=org"},
|
||||
"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
|
||||
),
|
||||
{
|
||||
args: []string{
|
||||
"ldap-test",
|
||||
"--id", "1",
|
||||
"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
|
||||
},
|
||||
authSource: &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
Cfg: &ldap.Source{
|
||||
UserDN: "cn=%s,ou=Users,dc=domain,dc=org",
|
||||
},
|
||||
},
|
||||
},
|
||||
// case 17
|
||||
{
|
||||
args: []string{
|
||||
|
||||
@@ -16,11 +16,10 @@ import (
|
||||
|
||||
func TestAddOauth(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
config *oauth2.Source
|
||||
twoFactorPolicy string
|
||||
errMsg string
|
||||
name string
|
||||
args []string
|
||||
source *auth_model.Source
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid config",
|
||||
@@ -30,11 +29,17 @@ func TestAddOauth(t *testing.T) {
|
||||
"--key", "some_key",
|
||||
"--secret", "some_secret",
|
||||
},
|
||||
config: &oauth2.Source{
|
||||
Scopes: []string{},
|
||||
Provider: "github",
|
||||
ClientID: "some_key",
|
||||
ClientSecret: "some_secret",
|
||||
source: &auth_model.Source{
|
||||
Type: auth_model.OAuth2,
|
||||
Name: "test",
|
||||
IsActive: true,
|
||||
Cfg: &oauth2.Source{
|
||||
Scopes: []string{},
|
||||
Provider: "github",
|
||||
ClientID: "some_key",
|
||||
ClientSecret: "some_secret",
|
||||
},
|
||||
TwoFactorPolicy: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -46,12 +51,18 @@ func TestAddOauth(t *testing.T) {
|
||||
"--secret", "some_secret",
|
||||
"--auto-discover-url", "https://example.com",
|
||||
},
|
||||
config: &oauth2.Source{
|
||||
Scopes: []string{},
|
||||
Provider: "openidConnect",
|
||||
ClientID: "some_key",
|
||||
ClientSecret: "some_secret",
|
||||
OpenIDConnectAutoDiscoveryURL: "https://example.com",
|
||||
source: &auth_model.Source{
|
||||
Type: auth_model.OAuth2,
|
||||
Name: "test",
|
||||
IsActive: true,
|
||||
Cfg: &oauth2.Source{
|
||||
Scopes: []string{},
|
||||
Provider: "openidConnect",
|
||||
ClientID: "some_key",
|
||||
ClientSecret: "some_secret",
|
||||
OpenIDConnectAutoDiscoveryURL: "https://example.com",
|
||||
},
|
||||
TwoFactorPolicy: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -80,30 +91,35 @@ func TestAddOauth(t *testing.T) {
|
||||
"--ssh-public-key-claim-name", "attr_ssh_pub_key",
|
||||
"--full-name-claim-name", "attr_full_name",
|
||||
},
|
||||
config: &oauth2.Source{
|
||||
Provider: "gitlab",
|
||||
ClientID: "some_key",
|
||||
ClientSecret: "some_secret",
|
||||
CustomURLMapping: &oauth2.CustomURLMapping{
|
||||
TokenURL: "https://example.com/token",
|
||||
AuthURL: "https://example.com/auth",
|
||||
ProfileURL: "https://example.com/profile",
|
||||
EmailURL: "https://example.com/email",
|
||||
Tenant: "some_tenant",
|
||||
source: &auth_model.Source{
|
||||
Type: auth_model.OAuth2,
|
||||
Name: "test",
|
||||
IsActive: true,
|
||||
Cfg: &oauth2.Source{
|
||||
Provider: "gitlab",
|
||||
ClientID: "some_key",
|
||||
ClientSecret: "some_secret",
|
||||
CustomURLMapping: &oauth2.CustomURLMapping{
|
||||
TokenURL: "https://example.com/token",
|
||||
AuthURL: "https://example.com/auth",
|
||||
ProfileURL: "https://example.com/profile",
|
||||
EmailURL: "https://example.com/email",
|
||||
Tenant: "some_tenant",
|
||||
},
|
||||
IconURL: "https://example.com/icon",
|
||||
Scopes: []string{"scope1", "scope2"},
|
||||
RequiredClaimName: "claim_name",
|
||||
RequiredClaimValue: "claim_value",
|
||||
GroupClaimName: "group_name",
|
||||
AdminGroup: "admin",
|
||||
RestrictedGroup: "restricted",
|
||||
GroupTeamMap: `{"group1": [1,2]}`,
|
||||
GroupTeamMapRemoval: true,
|
||||
SSHPublicKeyClaimName: "attr_ssh_pub_key",
|
||||
FullNameClaimName: "attr_full_name",
|
||||
},
|
||||
IconURL: "https://example.com/icon",
|
||||
Scopes: []string{"scope1", "scope2"},
|
||||
RequiredClaimName: "claim_name",
|
||||
RequiredClaimValue: "claim_value",
|
||||
GroupClaimName: "group_name",
|
||||
AdminGroup: "admin",
|
||||
RestrictedGroup: "restricted",
|
||||
GroupTeamMap: `{"group1": [1,2]}`,
|
||||
GroupTeamMapRemoval: true,
|
||||
SSHPublicKeyClaimName: "attr_ssh_pub_key",
|
||||
FullNameClaimName: "attr_full_name",
|
||||
TwoFactorPolicy: "skip",
|
||||
},
|
||||
twoFactorPolicy: "skip",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -134,13 +150,7 @@ func TestAddOauth(t *testing.T) {
|
||||
assert.EqualError(t, err, tc.errMsg)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &auth_model.Source{
|
||||
Type: auth_model.OAuth2,
|
||||
Name: "test",
|
||||
IsActive: true,
|
||||
Cfg: tc.config,
|
||||
TwoFactorPolicy: tc.twoFactorPolicy,
|
||||
}, createdSource)
|
||||
assert.Equal(t, tc.source, createdSource)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -148,10 +158,12 @@ func TestAddOauth(t *testing.T) {
|
||||
|
||||
func TestUpdateOauth(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
authSource *auth_model.Source
|
||||
errMsg string
|
||||
name string
|
||||
args []string
|
||||
id int64
|
||||
existingAuthSource *auth_model.Source
|
||||
authSource *auth_model.Source
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "missing id",
|
||||
@@ -162,6 +174,19 @@ func TestUpdateOauth(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid config",
|
||||
id: 1,
|
||||
existingAuthSource: &auth_model.Source{
|
||||
ID: 1,
|
||||
Type: auth_model.OAuth2,
|
||||
Name: "old name",
|
||||
IsActive: true,
|
||||
Cfg: &oauth2.Source{
|
||||
Provider: "github",
|
||||
ClientID: "old_key",
|
||||
ClientSecret: "old_secret",
|
||||
},
|
||||
TwoFactorPolicy: "",
|
||||
},
|
||||
args: []string{
|
||||
"--id", "1",
|
||||
"--name", "test",
|
||||
@@ -185,6 +210,37 @@ func TestUpdateOauth(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid config with options",
|
||||
id: 1,
|
||||
existingAuthSource: &auth_model.Source{
|
||||
ID: 1,
|
||||
Type: auth_model.OAuth2,
|
||||
Name: "old name",
|
||||
IsActive: true,
|
||||
Cfg: &oauth2.Source{
|
||||
Provider: "gitlab",
|
||||
ClientID: "old_key",
|
||||
ClientSecret: "old_secret",
|
||||
CustomURLMapping: &oauth2.CustomURLMapping{
|
||||
TokenURL: "https://old.example.com/token",
|
||||
AuthURL: "https://old.example.com/auth",
|
||||
ProfileURL: "https://old.example.com/profile",
|
||||
EmailURL: "https://old.example.com/email",
|
||||
Tenant: "old_tenant",
|
||||
},
|
||||
IconURL: "https://old.example.com/icon",
|
||||
Scopes: []string{"old_scope1", "old_scope2"},
|
||||
RequiredClaimName: "old_claim_name",
|
||||
RequiredClaimValue: "old_claim_value",
|
||||
GroupClaimName: "old_group_name",
|
||||
AdminGroup: "old_admin",
|
||||
RestrictedGroup: "old_restricted",
|
||||
GroupTeamMap: `{"old_group1": [1,2]}`,
|
||||
GroupTeamMapRemoval: true,
|
||||
SSHPublicKeyClaimName: "old_ssh_pub_key",
|
||||
FullNameClaimName: "old_full_name",
|
||||
},
|
||||
TwoFactorPolicy: "",
|
||||
},
|
||||
args: []string{
|
||||
"--id", "1",
|
||||
"--name", "test",
|
||||
|
||||
@@ -132,10 +132,11 @@ func TestAddSMTP(t *testing.T) {
|
||||
|
||||
func TestUpdateSMTP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
authSource *auth_model.Source
|
||||
errMsg string
|
||||
name string
|
||||
args []string
|
||||
existingAuthSource *auth_model.Source
|
||||
authSource *auth_model.Source
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "missing id",
|
||||
@@ -148,6 +149,17 @@ func TestUpdateSMTP(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid config",
|
||||
existingAuthSource: &auth_model.Source{
|
||||
ID: 1,
|
||||
Type: auth_model.SMTP,
|
||||
Name: "old name",
|
||||
IsActive: true,
|
||||
Cfg: &smtp.Source{
|
||||
Auth: "PLAIN",
|
||||
Host: "old host",
|
||||
Port: 26,
|
||||
},
|
||||
},
|
||||
args: []string{
|
||||
"--id", "1",
|
||||
"--name", "test",
|
||||
@@ -168,6 +180,20 @@ func TestUpdateSMTP(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid config with options",
|
||||
existingAuthSource: &auth_model.Source{
|
||||
ID: 1,
|
||||
Type: auth_model.SMTP,
|
||||
Name: "old name",
|
||||
IsActive: true,
|
||||
Cfg: &smtp.Source{
|
||||
Auth: "PLAIN",
|
||||
Host: "old host",
|
||||
Port: 26,
|
||||
HeloHostname: "old.example.com",
|
||||
AllowedDomains: "old.example.com",
|
||||
},
|
||||
TwoFactorPolicy: "",
|
||||
},
|
||||
args: []string{
|
||||
"--id", "1",
|
||||
"--name", "test",
|
||||
|
||||
@@ -24,29 +24,42 @@ func TestAdminUserDelete(t *testing.T) {
|
||||
require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{}))
|
||||
}()
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
args []string
|
||||
}{
|
||||
{"delete user by id", []string{"delete-test", "--id", ""}},
|
||||
{"delete user by username", []string{"delete-test", "--username", "testuser"}},
|
||||
{"delete user by email", []string{"delete-test", "--email", "testuser@gitea.local"}},
|
||||
{"delete user by all 3 attributes", []string{"delete", "--id", "", "--username", "testuser", "--email", "testuser@gitea.local"}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
||||
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
|
||||
require.NoError(t, err)
|
||||
|
||||
if tc.args[1] == "--id" {
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
||||
tc.args[2] = strconv.FormatInt(user.ID, 10)
|
||||
}
|
||||
err = microcmdUserDelete().Run(ctx, tc.args)
|
||||
require.NoError(t, err)
|
||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
||||
})
|
||||
setupTestUser := func(t *testing.T) {
|
||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
||||
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
t.Run("delete user by id", func(t *testing.T) {
|
||||
setupTestUser(t)
|
||||
|
||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
||||
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--id", strconv.FormatInt(u.ID, 10)})
|
||||
require.NoError(t, err)
|
||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
||||
})
|
||||
t.Run("delete user by username", func(t *testing.T) {
|
||||
setupTestUser(t)
|
||||
|
||||
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--username", "testuser"})
|
||||
require.NoError(t, err)
|
||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
||||
})
|
||||
t.Run("delete user by email", func(t *testing.T) {
|
||||
setupTestUser(t)
|
||||
|
||||
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--email", "testuser@gitea.local"})
|
||||
require.NoError(t, err)
|
||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
||||
})
|
||||
t.Run("delete user by all 3 attributes", func(t *testing.T) {
|
||||
setupTestUser(t)
|
||||
|
||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
||||
err := microcmdUserDelete().Run(ctx, []string{"delete", "--id", strconv.FormatInt(u.ID, 10), "--username", "testuser", "--email", "testuser@gitea.local"})
|
||||
require.NoError(t, err)
|
||||
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestAdminUserDeleteFailure(t *testing.T) {
|
||||
|
||||
@@ -33,7 +33,7 @@ func runListUsers(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
users, err := user_model.GetAllUsersForAdminListing(ctx)
|
||||
users, err := user_model.GetAllUsers(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -41,17 +41,17 @@ func runListUsers(ctx context.Context, c *cli.Command) error {
|
||||
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
|
||||
|
||||
if c.IsSet("admin") {
|
||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tType\n")
|
||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n")
|
||||
for _, u := range users {
|
||||
if u.IsAdmin {
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%s\n", u.ID, u.Name, u.Email, u.IsActive, u.Type.DisplayName())
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
twofa := user_model.UserList(users).GetTwoFaStatus(ctx)
|
||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\tType\n")
|
||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n")
|
||||
for _, u := range users {
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\t%s\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID], u.Type.DisplayName())
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,28 +18,61 @@ func TestMustChangePassword(t *testing.T) {
|
||||
defer func() {
|
||||
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
|
||||
}()
|
||||
for _, name := range []string{"testuser", "testuserexclude"} {
|
||||
require.NoError(t, microcmdUserCreate().Run(t.Context(), []string{"create", "--username", name, "--email", name + "@gitea.local", "--random-password"}))
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
args []string
|
||||
assertUser, assertExcludedUser assert.BoolAssertionFunc
|
||||
}{
|
||||
// Reset password change flag
|
||||
{[]string{"change-test", "--all", "--unset"}, assert.False, assert.False},
|
||||
// Make all users change password
|
||||
{[]string{"change-test", "--all"}, assert.True, assert.True},
|
||||
// Reset password change flag but exclude all tested users
|
||||
{[]string{"change-test", "--all", "--unset", "--exclude", "testuser,testuserexclude"}, assert.True, assert.True},
|
||||
// Reset password change flag by listing multiple users
|
||||
{[]string{"change-test", "--unset", "testuser", "testuserexclude"}, assert.False, assert.False},
|
||||
// Exclude a user from all user
|
||||
{[]string{"change-test", "--all", "--exclude", "testuserexclude"}, assert.True, assert.False},
|
||||
// Unset a flag for single user
|
||||
{[]string{"change-test", "--unset", "testuser"}, assert.False, assert.False},
|
||||
} {
|
||||
require.NoError(t, microcmdUserMustChangePassword().Run(t.Context(), tc.args))
|
||||
tc.assertUser(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}).MustChangePassword)
|
||||
tc.assertExcludedUser(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}).MustChangePassword)
|
||||
}
|
||||
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
|
||||
require.NoError(t, err)
|
||||
err = microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuserexclude", "--email", "testuserexclude@gitea.local", "--random-password"})
|
||||
require.NoError(t, err)
|
||||
// Reset password change flag
|
||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset"})
|
||||
require.NoError(t, err)
|
||||
|
||||
testUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
||||
assert.False(t, testUser.MustChangePassword)
|
||||
testUserExclude := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
||||
assert.False(t, testUserExclude.MustChangePassword)
|
||||
|
||||
// Make all users change password
|
||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all"})
|
||||
require.NoError(t, err)
|
||||
|
||||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
||||
assert.True(t, testUser.MustChangePassword)
|
||||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
||||
assert.True(t, testUserExclude.MustChangePassword)
|
||||
|
||||
// Reset password change flag but exclude all tested users
|
||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset", "--exclude", "testuser,testuserexclude"})
|
||||
require.NoError(t, err)
|
||||
|
||||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
||||
assert.True(t, testUser.MustChangePassword)
|
||||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
||||
assert.True(t, testUserExclude.MustChangePassword)
|
||||
|
||||
// Reset password change flag by listing multiple users
|
||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser", "testuserexclude"})
|
||||
require.NoError(t, err)
|
||||
|
||||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
||||
assert.False(t, testUser.MustChangePassword)
|
||||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
||||
assert.False(t, testUserExclude.MustChangePassword)
|
||||
|
||||
// Exclude a user from all user
|
||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--exclude", "testuserexclude"})
|
||||
require.NoError(t, err)
|
||||
|
||||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
||||
assert.True(t, testUser.MustChangePassword)
|
||||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
||||
assert.False(t, testUserExclude.MustChangePassword)
|
||||
|
||||
// Unset a flag for single user
|
||||
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser"})
|
||||
require.NoError(t, err)
|
||||
|
||||
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
|
||||
assert.False(t, testUser.MustChangePassword)
|
||||
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
|
||||
assert.False(t, testUserExclude.MustChangePassword)
|
||||
}
|
||||
|
||||
@@ -76,82 +76,110 @@ func TestCliCmd(t *testing.T) {
|
||||
cli.SubcommandHelpTemplate = "(subcommand help template)"
|
||||
|
||||
cases := []struct {
|
||||
env map[string]string
|
||||
cmds []string
|
||||
exp string
|
||||
env map[string]string
|
||||
cmd string
|
||||
exp string
|
||||
}{
|
||||
// help commands
|
||||
{
|
||||
cmds: []string{"./gitea -h", "./gitea help"},
|
||||
exp: "DEFAULT CONFIGURATION:",
|
||||
cmd: "./gitea -h",
|
||||
exp: "DEFAULT CONFIGURATION:",
|
||||
},
|
||||
{
|
||||
cmds: []string{
|
||||
"./gitea -c /dev/null -h",
|
||||
"./gitea -c /dev/null help",
|
||||
"./gitea help -c /dev/null",
|
||||
"./gitea -c /dev/null test-cmd -h",
|
||||
"./gitea test-cmd -c /dev/null -h",
|
||||
"./gitea test-cmd -h -c /dev/null",
|
||||
"./gitea -c /dev/null test-cmd help",
|
||||
"./gitea test-cmd -c /dev/null help",
|
||||
"./gitea test-cmd help -c /dev/null",
|
||||
},
|
||||
cmd: "./gitea help",
|
||||
exp: "DEFAULT CONFIGURATION:",
|
||||
},
|
||||
|
||||
{
|
||||
cmd: "./gitea -c /dev/null -h",
|
||||
exp: "ConfigFile: /dev/null",
|
||||
},
|
||||
|
||||
{
|
||||
cmd: "./gitea -c /dev/null help",
|
||||
exp: "ConfigFile: /dev/null",
|
||||
},
|
||||
{
|
||||
cmd: "./gitea help -c /dev/null",
|
||||
exp: "ConfigFile: /dev/null",
|
||||
},
|
||||
|
||||
{
|
||||
cmd: "./gitea -c /dev/null test-cmd -h",
|
||||
exp: "ConfigFile: /dev/null",
|
||||
},
|
||||
{
|
||||
cmd: "./gitea test-cmd -c /dev/null -h",
|
||||
exp: "ConfigFile: /dev/null",
|
||||
},
|
||||
{
|
||||
cmd: "./gitea test-cmd -h -c /dev/null",
|
||||
exp: "ConfigFile: /dev/null",
|
||||
},
|
||||
|
||||
{
|
||||
cmd: "./gitea -c /dev/null test-cmd help",
|
||||
exp: "ConfigFile: /dev/null",
|
||||
},
|
||||
{
|
||||
cmd: "./gitea test-cmd -c /dev/null help",
|
||||
exp: "ConfigFile: /dev/null",
|
||||
},
|
||||
{
|
||||
cmd: "./gitea test-cmd help -c /dev/null",
|
||||
exp: "ConfigFile: /dev/null",
|
||||
},
|
||||
|
||||
// parse paths
|
||||
{
|
||||
cmds: []string{"./gitea test-cmd"},
|
||||
exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf),
|
||||
cmd: "./gitea test-cmd",
|
||||
exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf),
|
||||
},
|
||||
{
|
||||
cmds: []string{"./gitea -c /tmp/app.ini test-cmd"},
|
||||
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
|
||||
cmd: "./gitea -c /tmp/app.ini test-cmd",
|
||||
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
|
||||
},
|
||||
{
|
||||
cmds: []string{"./gitea test-cmd -c /tmp/app.ini"},
|
||||
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
|
||||
cmd: "./gitea test-cmd -c /tmp/app.ini",
|
||||
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
|
||||
},
|
||||
{
|
||||
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
|
||||
cmds: []string{"./gitea test-cmd"},
|
||||
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"),
|
||||
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
|
||||
cmd: "./gitea test-cmd",
|
||||
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"),
|
||||
},
|
||||
{
|
||||
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
|
||||
cmds: []string{"./gitea test-cmd --work-path /tmp/other"},
|
||||
exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"),
|
||||
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
|
||||
cmd: "./gitea test-cmd --work-path /tmp/other",
|
||||
exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"),
|
||||
},
|
||||
{
|
||||
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
|
||||
cmds: []string{"./gitea test-cmd --config /tmp/app-other.ini"},
|
||||
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"),
|
||||
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
|
||||
cmd: "./gitea test-cmd --config /tmp/app-other.ini",
|
||||
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
for _, command := range c.cmds {
|
||||
t.Run(command, func(t *testing.T) {
|
||||
app := newTestApp(cli.Command{
|
||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
||||
return nil
|
||||
},
|
||||
})
|
||||
for k, v := range c.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
args := strings.Split(command, " ") // for test only, "split" is good enough
|
||||
r, err := runTestApp(app, args...)
|
||||
assert.NoError(t, err, command)
|
||||
assert.NotEmpty(t, c.exp, command)
|
||||
if !assert.Contains(t, r.Stdout, c.exp, command) {
|
||||
t.Log("Full output:\n" + r.Stdout)
|
||||
t.Log("Expected:\n" + c.exp)
|
||||
}
|
||||
t.Run(c.cmd, func(t *testing.T) {
|
||||
app := newTestApp(cli.Command{
|
||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
for k, v := range c.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
|
||||
r, err := runTestApp(app, args...)
|
||||
assert.NoError(t, err, c.cmd)
|
||||
assert.NotEmpty(t, c.exp, c.cmd)
|
||||
if !assert.Contains(t, r.Stdout, c.exp, c.cmd) {
|
||||
t.Log("Full output:\n" + r.Stdout)
|
||||
t.Log("Expected:\n" + c.exp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
cmd/dump.go
12
cmd/dump.go
@@ -13,11 +13,13 @@ import (
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/dump"
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/storage"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"gitea.com/go-chi/session"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
@@ -90,7 +92,7 @@ func newDumpCommand() *cli.Command {
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "type",
|
||||
Usage: `Dump output format, default to the --file extension if supported, else "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "),
|
||||
Usage: `Dump output format, default to "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -247,8 +249,12 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
||||
log.Info("Packing data directory...%s", setting.AppDataPath)
|
||||
|
||||
var excludes []string
|
||||
if setting.SessionConfig.Provider == "file" {
|
||||
excludes = append(excludes, setting.SessionConfig.ProviderConfig)
|
||||
if setting.SessionConfig.OriginalProvider == "file" {
|
||||
var opts session.Options
|
||||
if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil {
|
||||
return err
|
||||
}
|
||||
excludes = append(excludes, opts.ProviderConfig)
|
||||
}
|
||||
|
||||
if cmd.IsSet("skip-index") && cmd.Bool("skip-index") {
|
||||
|
||||
@@ -33,7 +33,7 @@ func newDumpRepositoryCommand() *cli.Command {
|
||||
&cli.StringFlag{
|
||||
Name: "git_service",
|
||||
Value: "",
|
||||
Usage: "Git service, git, github, gitea, forgejo, 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{
|
||||
Name: "repo_dir",
|
||||
@@ -117,8 +117,6 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
|
||||
serviceStr = "gitlab"
|
||||
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitea.com/") {
|
||||
serviceStr = "gitea"
|
||||
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://codeberg.org/") {
|
||||
serviceStr = "forgejo"
|
||||
}
|
||||
if serviceStr == "" {
|
||||
return errors.New("git_service missed or clone_addr cannot be recognized")
|
||||
|
||||
@@ -261,7 +261,13 @@ func extractAsset(c *cli.Command, d string, a assetFile, overwrite, rename bool)
|
||||
perms = fi.Mode()
|
||||
}
|
||||
|
||||
if err := os.WriteFile(dest, data, perms); err != nil {
|
||||
file, err := os.OpenFile(dest, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, perms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", dest, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if _, err = file.Write(data); err != nil {
|
||||
return fmt.Errorf("%s: %w", dest, err)
|
||||
}
|
||||
|
||||
|
||||
64
cmd/hook.go
64
cmd/hook.go
@@ -169,6 +169,10 @@ func parseGitHookCommitRefLine(line string) (oldCommitID, newCommitID string, re
|
||||
}
|
||||
|
||||
func runHookPreReceive(ctx context.Context, c *cli.Command) error {
|
||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
||||
return nil
|
||||
}
|
||||
|
||||
setup(ctx, c.Bool("debug"))
|
||||
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
@@ -209,6 +213,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
refFullNames := make([]git.RefName, hookBatchSize)
|
||||
count := 0
|
||||
total := 0
|
||||
lastline := 0
|
||||
|
||||
out := io.Discard
|
||||
if setting.Git.VerbosePush {
|
||||
@@ -221,6 +226,8 @@ Gitea or set your environment appropriately.`, "")
|
||||
}
|
||||
}
|
||||
|
||||
supportProcReceive := git.DefaultFeatures().SupportProcReceive
|
||||
|
||||
for scanner.Scan() {
|
||||
// TODO: support news feeds for wiki
|
||||
if isWiki {
|
||||
@@ -233,23 +240,37 @@ Gitea or set your environment appropriately.`, "")
|
||||
}
|
||||
|
||||
total++
|
||||
oldCommitIDs[count] = oldCommitID
|
||||
newCommitIDs[count] = newCommitID
|
||||
refFullNames[count] = refFullName
|
||||
count++
|
||||
fmt.Fprintf(out, "*")
|
||||
lastline++
|
||||
|
||||
if count >= hookBatchSize {
|
||||
fmt.Fprintf(out, " Checking %d references\n", count)
|
||||
// If the ref is a branch or tag, check if it's protected
|
||||
// if supportProcReceive all ref should be checked because
|
||||
// permission check was delayed
|
||||
if supportProcReceive || refFullName.IsBranch() || refFullName.IsTag() {
|
||||
oldCommitIDs[count] = oldCommitID
|
||||
newCommitIDs[count] = newCommitID
|
||||
refFullNames[count] = refFullName
|
||||
count++
|
||||
fmt.Fprintf(out, "*")
|
||||
|
||||
hookOptions.OldCommitIDs = oldCommitIDs
|
||||
hookOptions.NewCommitIDs = newCommitIDs
|
||||
hookOptions.RefFullNames = refFullNames
|
||||
extra := private.HookPreReceive(ctx, ownerName, repoName, hookOptions)
|
||||
if extra.HasError() {
|
||||
return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error)
|
||||
if count >= hookBatchSize {
|
||||
fmt.Fprintf(out, " Checking %d references\n", count)
|
||||
|
||||
hookOptions.OldCommitIDs = oldCommitIDs
|
||||
hookOptions.NewCommitIDs = newCommitIDs
|
||||
hookOptions.RefFullNames = refFullNames
|
||||
extra := private.HookPreReceive(ctx, ownerName, repoName, hookOptions)
|
||||
if extra.HasError() {
|
||||
return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error)
|
||||
}
|
||||
count = 0
|
||||
lastline = 0
|
||||
}
|
||||
count = 0
|
||||
} else {
|
||||
fmt.Fprintf(out, ".")
|
||||
}
|
||||
if lastline >= hookBatchSize {
|
||||
fmt.Fprintf(out, "\n")
|
||||
lastline = 0
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
@@ -267,6 +288,8 @@ Gitea or set your environment appropriately.`, "")
|
||||
if extra.HasError() {
|
||||
return fail(ctx, extra.UserMsg, "HookPreReceive(last) failed: %v", extra.Error)
|
||||
}
|
||||
} else if lastline > 0 {
|
||||
fmt.Fprintf(out, "\n")
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "Checked %d references in total\n", total)
|
||||
@@ -276,6 +299,10 @@ Gitea or set your environment appropriately.`, "")
|
||||
// runHookUpdate avoid to do heavy operations on update hook because it will be
|
||||
// invoked for every ref update which does not like pre-receive and post-receive
|
||||
func runHookUpdate(_ context.Context, c *cli.Command) error {
|
||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update is empty and is kept only for backwards compatibility
|
||||
if len(os.Args) < 3 {
|
||||
return nil
|
||||
@@ -296,6 +323,11 @@ func runHookPostReceive(ctx context.Context, c *cli.Command) error {
|
||||
return fmt.Errorf("failed to call 'git update-server-info': %w", err)
|
||||
}
|
||||
|
||||
// Now if we're an internal don't do anything else
|
||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||
return fail(ctx, `Rejecting changes as Gitea environment not set.
|
||||
@@ -443,6 +475,10 @@ Gitea or set your environment appropriately.`, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
if !git.DefaultFeatures().SupportProcReceive {
|
||||
return fail(ctx, "No proc-receive support", "current git version doesn't support proc-receive.")
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
repoUser := os.Getenv(repo_module.EnvRepoUsername)
|
||||
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
|
||||
|
||||
10
cmd/serv.go
10
cmd/serv.go
@@ -195,10 +195,12 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
|
||||
if len(sshCmdArgs) < 2 {
|
||||
// for AGit Flow
|
||||
if cmd == "ssh_info" {
|
||||
cprintf(c, "%s", agit.SshInfoJson)
|
||||
return nil
|
||||
if git.DefaultFeatures().SupportProcReceive {
|
||||
// for AGit Flow
|
||||
if cmd == "ssh_info" {
|
||||
cprintf(c, "%s", agit.SshInfoJson)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
;; Username to use for the builtin SSH server. If blank, then it is the value of RUN_USER.
|
||||
;BUILTIN_SSH_SERVER_USER =
|
||||
;;
|
||||
;; Domain name to be exposed in clone URL, defaults to the domain part of ROOT_URL
|
||||
;; Domain name to be exposed in clone URL, defaults to DOMAIN or the domain part of ROOT_URL
|
||||
;SSH_DOMAIN =
|
||||
;;
|
||||
;; Port number to be exposed in clone URL.
|
||||
@@ -198,7 +198,7 @@
|
||||
;; For the built-in SSH server, choose the keypair to offer as the host key
|
||||
;; The private key should be at SSH_SERVER_HOST_KEY and the public SSH_SERVER_HOST_KEY.pub
|
||||
;; relative paths are made absolute relative to the APP_DATA_PATH
|
||||
;SSH_SERVER_HOST_KEYS=ssh/gitea.rsa, ssh/gitea.ed25519, ssh/gitea.ecdsa, ssh/gogs.rsa
|
||||
;SSH_SERVER_HOST_KEYS=ssh/gitea.rsa, ssh/gogs.rsa
|
||||
;;
|
||||
;; Enable SSH Authorized Key Backup when rewriting all keys, default is false
|
||||
;SSH_AUTHORIZED_KEYS_BACKUP = false
|
||||
@@ -237,7 +237,7 @@
|
||||
;SSH_PER_WRITE_PER_KB_TIMEOUT = 30s
|
||||
;;
|
||||
;; Indicate whether to check minimum key size with corresponding type
|
||||
;MINIMUM_KEY_SIZE_CHECK = true
|
||||
;MINIMUM_KEY_SIZE_CHECK = false
|
||||
;;
|
||||
;; TLS Settings: Either ACME or manual
|
||||
;; (Other common TLS configuration are found before)
|
||||
@@ -459,7 +459,7 @@ INTERNAL_TOKEN =
|
||||
;LOGIN_REMEMBER_DAYS = 31
|
||||
;;
|
||||
;; Name of cookie used to store authentication information.
|
||||
;COOKIE_REMEMBER_NAME = gitea_remember
|
||||
;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.
|
||||
@@ -471,8 +471,7 @@ INTERNAL_TOKEN =
|
||||
;REVERSE_PROXY_AUTHENTICATION_EMAIL = X-WEBAUTH-EMAIL
|
||||
;REVERSE_PROXY_AUTHENTICATION_FULL_NAME = X-WEBAUTH-FULLNAME
|
||||
;;
|
||||
;; Interpret the X-Real-IP, X-Forwarded-For or Forwarded header, in that order, and set this as the remote IP for the request.
|
||||
;; The client port is taken from the header value or, with X-Real-IP, from X-Real-Port.
|
||||
;; Interpret X-Forwarded-For header or the X-Real-IP header and set this as the remote IP for the request
|
||||
;REVERSE_PROXY_LIMIT = 1
|
||||
;;
|
||||
;; List of IP addresses and networks separated by comma of trusted proxy servers. Use `*` to trust all.
|
||||
@@ -637,10 +636,9 @@ ENABLED = true
|
||||
;; Pre-register OAuth2 applications for some universally useful services
|
||||
;; * https://github.com/hickford/git-credential-oauth
|
||||
;; * https://github.com/git-ecosystem/git-credential-manager
|
||||
;; * https://gitea.com/gitea/cli
|
||||
;; * https://gitea.com/gitea/tea
|
||||
;; * Gitea App (the official Gitea mobile app)
|
||||
;; * https://gitea.com/gitea/gitea-vscode
|
||||
;DEFAULT_APPLICATIONS = git-credential-oauth, git-credential-manager, gitea-cli, gitea-app, gitea-vscode
|
||||
;DEFAULT_APPLICATIONS = git-credential-oauth, git-credential-manager, tea, gitea-app
|
||||
;;
|
||||
;; By default, OAuth2 applications can only use "http" and "https" as their redirect URI schemes.
|
||||
;; If you need to use other schemes (e.g. for desktop applications), you can specify them here as a comma-separated list.
|
||||
@@ -784,6 +782,8 @@ LEVEL = Info
|
||||
;; Respond to pushes to a non-default branch with a URL for creating a Pull Request (if the repository has them enabled)
|
||||
;PULL_REQUEST_PUSH_MESSAGE = true
|
||||
;;
|
||||
;; (Go-Git only) Don't cache objects greater than this in memory. (Set to 0 to disable.)
|
||||
;LARGE_OBJECT_THRESHOLD = 1048576
|
||||
;; Set to true to forcibly set core.protectNTFS=false
|
||||
;DISABLE_CORE_PROTECT_NTFS=false
|
||||
;; Disable the usage of using partial clones for git.
|
||||
@@ -810,7 +810,7 @@ LEVEL = Info
|
||||
;[git.config]
|
||||
;diff.algorithm = histogram
|
||||
;core.logAllRefUpdates = true
|
||||
;maintenance.strategy = gc
|
||||
;gc.reflogExpire = 90
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -838,7 +838,7 @@ LEVEL = Info
|
||||
;EMAIL_DOMAIN_BLOCKLIST =
|
||||
;;
|
||||
;; Disallow registration, only allow admins to create accounts.
|
||||
;DISABLE_REGISTRATION = true
|
||||
;DISABLE_REGISTRATION = false
|
||||
;;
|
||||
;; Allow registration only using gitea itself, it works only when DISABLE_REGISTRATION is false
|
||||
;ALLOW_ONLY_INTERNAL_REGISTRATION = false
|
||||
@@ -867,10 +867,9 @@ LEVEL = Info
|
||||
;; Allow users to sign-in with a passkey
|
||||
;ENABLE_PASSKEY_AUTHENTICATION = true
|
||||
;;
|
||||
;; Reverse proxy authentication for the web interface, Git over HTTP, Git LFS and the package registry.
|
||||
;; Gitea must only be reachable through the proxy, which must set or remove the authentication headers on every request.
|
||||
;; More detail: https://github.com/gogits/gogs/issues/165
|
||||
;ENABLE_REVERSE_PROXY_AUTHENTICATION = false
|
||||
;; Reverse proxy authentication for the REST API under /api/v1.
|
||||
; Enable this to allow reverse proxy authentication for API requests, the reverse proxy is responsible for ensuring that no CSRF is possible.
|
||||
;ENABLE_REVERSE_PROXY_AUTHENTICATION_API = false
|
||||
;ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
|
||||
;ENABLE_REVERSE_PROXY_EMAIL = false
|
||||
@@ -971,11 +970,12 @@ LEVEL = Info
|
||||
;; Value for the domain part of the user's email address in the git log if user
|
||||
;; has set KeepEmailPrivate to true. The user's email will be replaced with a
|
||||
;; concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS. Default
|
||||
;; value is "noreply." + the domain part of ROOT_URL
|
||||
;NO_REPLY_ADDRESS =
|
||||
;; value is "noreply." + DOMAIN, where DOMAIN resolves to the value from server.DOMAIN
|
||||
;; Note: do not use the <DOMAIN> notation below
|
||||
;NO_REPLY_ADDRESS = ; noreply.<DOMAIN>
|
||||
;;
|
||||
;; Show Registration button, defaults to true only if both DISABLE_REGISTRATION and ALLOW_ONLY_EXTERNAL_REGISTRATION are false
|
||||
;SHOW_REGISTRATION_BUTTON = false
|
||||
;; Show Registration button
|
||||
;SHOW_REGISTRATION_BUTTON = true
|
||||
;;
|
||||
;; Show milestones dashboard page - a view of all the user's milestones
|
||||
;SHOW_MILESTONES_DASHBOARD_PAGE = true
|
||||
@@ -1098,9 +1098,8 @@ LEVEL = Info
|
||||
;; Force ssh:// clone url instead of scp-style uri when default SSH port is used
|
||||
;USE_COMPAT_SSH_URI = false
|
||||
;;
|
||||
;; Scheme of the returned URL for the "go get" response.
|
||||
;; Default is "https" if DISABLE_HTTP_GIT=false or SSH is disabled, otherwise "ssh".
|
||||
;GO_GET_CLONE_URL_PROTOCOL =
|
||||
;; Value for the "go get" request returns the repository url as https or ssh, default is https
|
||||
;GO_GET_CLONE_URL_PROTOCOL = https
|
||||
;;
|
||||
;; Close issues as long as a commit on any branch marks it as fixed
|
||||
;DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false
|
||||
@@ -1145,6 +1144,11 @@ LEVEL = Info
|
||||
;; The default Git object format of new repositories. Available values: sha1, sha256.
|
||||
;DEFAULT_OBJECT_FORMAT = sha1
|
||||
;;
|
||||
;; Allow adoption of unadopted repositories
|
||||
;ALLOW_ADOPTION_OF_UNADOPTED_REPOSITORIES = false
|
||||
;;
|
||||
;; Allow deletion of unadopted repositories
|
||||
;ALLOW_DELETION_OF_UNADOPTED_REPOSITORIES = false
|
||||
|
||||
;; Don't allow download source archive files from UI
|
||||
;DISABLE_DOWNLOAD_SOURCE_ARCHIVES = false
|
||||
@@ -1216,9 +1220,8 @@ LEVEL = Info
|
||||
;; In default merge messages only include approvers who are official
|
||||
;DEFAULT_MERGE_MESSAGE_OFFICIAL_APPROVERS_ONLY = true
|
||||
;;
|
||||
;; Set default squash merge commit message body, valid options: PR_BODY (pull request description), COMMIT_MESSAGES (commit messages), BLANK (none)
|
||||
;; Repositories store the current value when created or when their settings are next saved, and can change it there.
|
||||
;DEFAULT_SQUASH_MERGE_MESSAGE = PR_BODY
|
||||
;; In default squash-merge messages include the commit message of all commits comprising the pull request.
|
||||
;POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES = false
|
||||
;;
|
||||
;; Add co-authored-by and co-committed-by trailers if committer does not match author
|
||||
;ADD_CO_COMMITTER_TRAILERS = true
|
||||
@@ -1237,7 +1240,7 @@ LEVEL = Info
|
||||
;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 = true
|
||||
;DEFAULT_DELETE_BRANCH_AFTER_MERGE = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -1432,14 +1435,11 @@ LEVEL = Info
|
||||
;; The icon theme for folders (basic/material)
|
||||
;FOLDER_ICON_THEME = basic
|
||||
;;
|
||||
;; All available reactions users can choose on issues, pull requests and comments.
|
||||
;; All available reactions users can choose on issues/prs and comments.
|
||||
;; Values can be emoji alias (:smile:) or a unicode emoji.
|
||||
;; For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png
|
||||
;REACTIONS = +1, -1, laugh, hooray, confused, heart, rocket, eyes
|
||||
;;
|
||||
;; Reactions available for releases
|
||||
;;RELEASE_REACTIONS = +1, laugh, hooray, heart, rocket, eyes
|
||||
;;
|
||||
;; Change the number of users that are displayed in reactions tooltip (triggered by mouse hover).
|
||||
;REACTION_MAX_USER_NUM = 10
|
||||
;;
|
||||
@@ -1672,13 +1672,13 @@ LEVEL = Info
|
||||
;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; General queue type, currently support: level, channel, redis, dummy
|
||||
;; default to level
|
||||
;TYPE = level
|
||||
;; General queue queue type, currently support: persistable-channel, channel, level, redis, dummy
|
||||
;; default to persistable-channel
|
||||
;TYPE = persistable-channel
|
||||
;;
|
||||
;; data-dir for storing level queues, individual queues will default to `queues/common` meaning the queue is shared.
|
||||
;; data-dir for storing persistable queues and level queues, individual queues will default to `queues/common` meaning the queue is shared.
|
||||
;; Relative paths will be made absolute against "APP_DATA_PATH"
|
||||
;DATADIR = queues/common
|
||||
;DATADIR = queues/
|
||||
;;
|
||||
;; Default queue length before a channel queue will block
|
||||
;LENGTH = 100000
|
||||
@@ -1686,7 +1686,7 @@ LEVEL = Info
|
||||
;; Batch size to send for batched queues
|
||||
;BATCH_LENGTH = 20
|
||||
;;
|
||||
;; When `TYPE` is `level`, this provides a directory for the underlying leveldb
|
||||
;; When `TYPE` is `persistable-channel`, this provides a directory for the underlying leveldb
|
||||
;; or additional options of the form `leveldb://path/to/db?option=value&....`, and will override `DATADIR`.
|
||||
;; When `TYPE` is `redis` and this is left empty, it falls back to the shared [redis] CONN_STR.
|
||||
;CONN_STR =
|
||||
@@ -1716,18 +1716,59 @@ LEVEL = Info
|
||||
;; - manage_ssh_keys: a user cannot configure ssh keys
|
||||
;; - manage_gpg_keys: a user cannot configure gpg keys
|
||||
;; - manage_mfa: a user cannot configure mfa devices
|
||||
;; - manage_credentials: a user cannot configure emails or passwords
|
||||
;; - manage_credentials: a user cannot configure emails, passwords, or openid
|
||||
;USER_DISABLED_FEATURES =
|
||||
;; Comma separated list of disabled features ONLY if the user has an external login type (eg. LDAP, Oauth, etc.), could be "deletion", "manage_ssh_keys", "manage_gpg_keys", "manage_mfa", "manage_credentials". This setting is independent from `USER_DISABLED_FEATURES` and supplements its behavior.
|
||||
;; - deletion: a user cannot delete their own account
|
||||
;; - manage_ssh_keys: a user cannot configure ssh keys
|
||||
;; - manage_gpg_keys: a user cannot configure gpg keys
|
||||
;; - manage_mfa: a user cannot configure mfa devices
|
||||
;; - manage_credentials: a user cannot configure emails or passwords
|
||||
;; - manage_credentials: a user cannot configure emails, passwords, or openid
|
||||
;; - change_username: a user cannot change their username
|
||||
;; - change_full_name: a user cannot change their full name
|
||||
;;EXTERNAL_USER_DISABLE_FEATURES =
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[openid]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; OpenID is an open, standard and decentralized authentication protocol.
|
||||
;; Your identity is the address of a webpage you provide, which describes
|
||||
;; how to prove you are in control of that page.
|
||||
;;
|
||||
;; For more info: https://en.wikipedia.org/wiki/OpenID
|
||||
;;
|
||||
;; Current implementation supports OpenID-2.0
|
||||
;;
|
||||
;; Tested to work providers at the time of writing:
|
||||
;; - Any GNUSocial node (your.hostname.tld/username)
|
||||
;; - Any SimpleID provider (http://simpleid.koinic.net)
|
||||
;; - http://openid.org.cn/
|
||||
;; - openid.stackexchange.com
|
||||
;; - login.launchpad.net
|
||||
;; - <username>.livejournal.com
|
||||
;;
|
||||
;; Whether to allow sign-in via OpenID
|
||||
;ENABLE_OPENID_SIGNIN = false
|
||||
;;
|
||||
;; Whether to allow registering via OpenID
|
||||
;; Do not include to rely on rhw DISABLE_REGISTRATION setting
|
||||
;;ENABLE_OPENID_SIGNUP = false
|
||||
;;
|
||||
;; Allowed URI patterns (POSIX regexp).
|
||||
;; Space separated.
|
||||
;; Only these would be allowed if non-blank.
|
||||
;; Example value: trusted.domain.org trusted.domain.net
|
||||
;WHITELISTED_URIS =
|
||||
;;
|
||||
;; Forbidden URI patterns (POSIX regexp).
|
||||
;; Space separated.
|
||||
;; Only used if WHITELISTED_URIS is blank.
|
||||
;; Example value: loadaverage.org/badguy stackexchange.com/.*spammer
|
||||
;BLACKLISTED_URIS =
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[oauth2_client]
|
||||
@@ -1944,7 +1985,7 @@ LEVEL = Info
|
||||
;; Either "memory", "redis", "memcache", or "twoqueue". default is "memory"
|
||||
;ADAPTER = memory
|
||||
;;
|
||||
;; For "memory" and "twoqueue", GC interval in seconds, default is 60
|
||||
;; For "memory" only, GC interval in seconds, default is 60
|
||||
;INTERVAL = 60
|
||||
;;
|
||||
;; For "redis" and "memcache", connection host address
|
||||
@@ -1975,17 +2016,19 @@ LEVEL = Info
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; Either "memory", "file", "redis" or "db", default is "file". "db" will reuse the configuration in [database]
|
||||
;PROVIDER = file
|
||||
;; Either "memory", "file", "redis", "db", "mysql", "couchbase", "memcache" or "postgres"
|
||||
;; Default is "memory". "db" will reuse the configuration in [database]
|
||||
;PROVIDER = memory
|
||||
;;
|
||||
;; Provider config options
|
||||
;; memory: doesn't have any config yet
|
||||
;; file: session file path, e.g. `data/sessions`, relative paths will be made absolute against _`AppWorkPath`_.
|
||||
;; redis: default to [redis] CONN_STR
|
||||
;; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table`
|
||||
;PROVIDER_CONFIG =
|
||||
;;
|
||||
;; Session cookie name
|
||||
;COOKIE_NAME = gitea_session
|
||||
;COOKIE_NAME = i_like_gitea
|
||||
;;
|
||||
;; If you use session in https only: true or false. If not set, it defaults to `true` if the ROOT_URL is an HTTPS URL.
|
||||
;COOKIE_SECURE =
|
||||
@@ -2011,8 +2054,7 @@ LEVEL = Info
|
||||
;; How Gitea deals with missing repository avatars
|
||||
;; none = no avatar will be displayed; random = random avatar will be displayed; image = default image will be used
|
||||
;REPOSITORY_AVATAR_FALLBACK = none
|
||||
;; Image URL for the "image" fallback, used as-is, defaults to Gitea's builtin repository avatar
|
||||
;REPOSITORY_AVATAR_FALLBACK_IMAGE =
|
||||
;REPOSITORY_AVATAR_FALLBACK_IMAGE = /img/repo_default.png
|
||||
;;
|
||||
;; Max Width and Height of uploaded avatars.
|
||||
;; This is to limit the amount of RAM used when resizing the image.
|
||||
@@ -2053,7 +2095,7 @@ LEVEL = Info
|
||||
;ENABLED = true
|
||||
;;
|
||||
;; 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,.jxl,.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 issue/pull-request attachment file. Defaults to 100MB
|
||||
;MAX_SIZE = 100
|
||||
@@ -2880,8 +2922,6 @@ LEVEL = Info
|
||||
;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
|
||||
;; Maximum size of a Terraform module archive upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||
;LIMIT_SIZE_TERRAFORM_MODULE = -1
|
||||
;; 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
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -3042,7 +3082,7 @@ LEVEL = Info
|
||||
;LOG_RETENTION_DAYS = 365
|
||||
;; Days to keep artifacts. Old artifacts will be deleted after this period. 0 means keep forever.
|
||||
;; Changes only apply to newly uploaded artifacts, existing ones keep the expiry stored when they were uploaded.
|
||||
;; Workflows can request a shorter retention period, down to 1 day.
|
||||
;; Artifacts could have their own retention periods by setting the `retention-days` option in `actions/upload-artifact` step.
|
||||
;ARTIFACT_RETENTION_DAYS = 90
|
||||
;; Max artifact size in bytes that can be browsed or previewed in the web UI. Set to 0 to disable artifact previews, or -1 for no limit.
|
||||
;ARTIFACT_PREVIEW_MAX_SIZE = 10485760
|
||||
|
||||
@@ -25,6 +25,7 @@ Depending on requirements, the following build tags can be included.
|
||||
- `bindata`: Build a single monolithic binary, with all assets included. Required for distribution and production build.
|
||||
- `pam`: Enable support for PAM (Linux Pluggable Authentication Modules).
|
||||
Can be used to authenticate local users or extend authentication to methods available to PAM.
|
||||
- `gogit`: (EXPERIMENTAL) Use go-git variants of Git commands.
|
||||
|
||||
To include all assets, use the `bindata` tag:
|
||||
|
||||
@@ -32,6 +33,13 @@ To include all assets, use the `bindata` tag:
|
||||
TAGS="bindata" make build
|
||||
```
|
||||
|
||||
Tag `gogit` is used to try to resolve some Windows-specific performance problems, POSIX systems don't need it.
|
||||
You can build a Windows binary by:
|
||||
|
||||
```bash
|
||||
GOOS=windows TAGS="bindata gogit" make build
|
||||
```
|
||||
|
||||
## Changing default paths
|
||||
|
||||
Gitea will search for a number of things from the _`CustomPath`_.
|
||||
|
||||
@@ -74,13 +74,15 @@ Changes to Gitea must be reviewed before they are accepted, including changes fr
|
||||
|
||||
We require two maintainer approvals for every PR. When that is satisfied, your PR gets the `lgtm/done` label. After that, you mainly fix merge conflicts and respond to or implement maintainer requests; maintainers drive getting the PR merged.
|
||||
|
||||
If a PR has `lgtm/done`, no open discussions, and no merge conflicts, a merger rewrites its title and description into the final [commit message](#commit-messages) and clicks **Merge when ready**. That adds the PR to the [merge queue](https://github.com/go-gitea/gitea/queue/main), which tests it on top of the latest `main` and the PRs ahead of it, and merges it once CI passes. Edits to the title and description no longer reach the commit once the queue starts building the PR, so make them before queueing.
|
||||
If a PR has `lgtm/done`, no open discussions, and no merge conflicts, any maintainer may add `reviewed/wait-merge`. That puts the PR in the merge queue. PRs are merged from the queue in the order of this list:
|
||||
|
||||
Gitea uses its own tool, <https://github.com/go-gitea/gitea/blob/main/.github/workflows/giteabot.yml>, to automate parts of the review process. It:
|
||||
<https://github.com/go-gitea/gitea/pulls?q=is%3Apr+label%3Areviewed%2Fwait-merge+sort%3Acreated-asc+is%3Aopen>
|
||||
|
||||
Gitea uses its own tool, <https://github.com/go-gitea/gitea/blob/main/.github/workflows/giteabot.yml>, to automate parts of the review process. The backporter:
|
||||
|
||||
- Counts maintainer approvals and sets the `lgtm/*` labels and the `giteabot/lgtm` status.
|
||||
- Takes a PR out of the merge queue when it loses its approval.
|
||||
- Creates a backport PR when needed after the initial PR merges.
|
||||
- Removes the PR from the merge queue after it merges.
|
||||
- Keeps the oldest branch in the merge queue up to date with merges.
|
||||
|
||||
### Final call
|
||||
|
||||
@@ -162,7 +164,7 @@ Any account with write access (including bots and TOC members) **must** use [2FA
|
||||
|
||||
Mergers are the maintainers who carry out the final merge of approved PRs. Their responsibilities, described throughout this guide, are:
|
||||
|
||||
- Adding PRs to the [merge queue](#getting-prs-merged) once a PR has `lgtm/done`, no open discussions, and no merge conflicts.
|
||||
- Merging PRs from the [merge queue](#getting-prs-merged) in order, once a PR has `lgtm/done`, no open discussions, and no merge conflicts.
|
||||
- Rewriting the PR title and description prior to the merge, making the [commit message](#commit-messages) clear.\
|
||||
In particular, mergers should edit the PR description.\
|
||||
Mergers should **not** edit the actual commit message except to remove unnecessary information. Because of that, even if users are looking at the PR, they can understand what changed.
|
||||
|
||||
@@ -105,13 +105,10 @@ When adding configuration options it is not enough to add them to the
|
||||
[`custom/conf/app.example.ini`](../custom/conf/app.example.ini), and document them in
|
||||
the [configuration cheat sheet](https://docs.gitea.com/administration/config-cheat-sheet),
|
||||
which lives in the [documentation repository](https://gitea.com/gitea/docs).
|
||||
Every `app.example.ini` change needs a companion PR there that copies the ini text
|
||||
verbatim into every affected version's cheat sheet, translated where feasible, with a
|
||||
`WIP:` title prefix until the Gitea PR merges.
|
||||
|
||||
## Database migrations
|
||||
|
||||
Avoid migrations unless the alternative is a workaround. If you make breaking changes to a database-persisted struct under `models/`, add a
|
||||
If you make breaking changes to a database-persisted struct under `models/`, add a
|
||||
new migration in `modelmigration/`. See [testing.md](testing.md#migration-tests)
|
||||
for running the migration tests.
|
||||
|
||||
|
||||
@@ -74,26 +74,6 @@ conditional `UPDATE` or a lock.
|
||||
plain `Sync`.
|
||||
- When inserting rows with preset IDs, MSSQL requires `SET IDENTITY_INSERT` to be
|
||||
enabled and PostgreSQL requires the sequence to be updated afterwards.
|
||||
- Struct conditions skip zero-valued fields, so `Get(&Reaction{IssueID: 1})` also
|
||||
matches comment reactions. Use `Where` or `builder` when a zero value matters.
|
||||
- Struct updates skip zero-valued fields unless selected by `Cols` or `AllCols()`.
|
||||
`Cols` silently ignores unknown names. `updated` columns are always written unless
|
||||
`NoAutoTime()` is used.
|
||||
- `xorm:"deleted"` columns count both `0` and `NULL` as not deleted, so hand-written
|
||||
conditions must accept both.
|
||||
- On every start, `SyncAllTables` drops indexes absent from the model, so declare
|
||||
migration-created indexes there, including in backports. It only warns about
|
||||
nullability and default changes, so apply them through migrations.
|
||||
- `ForUpdate` only works on MySQL and PostgreSQL. MSSQL rejects TEXT columns in
|
||||
struct conditions and inserts of more than 1000 rows.
|
||||
- Paginated queries need a unique tiebreaker like `id` for a stable order.
|
||||
|
||||
### Git
|
||||
|
||||
- Pass already opened repositories down or use `git.RepositoryFromContextOrOpen`
|
||||
rather than reopening them or spawning extra `git` processes.
|
||||
- If git command stderr output is not handled by the caller, then it needs to use RunWithStderr
|
||||
to make sure the git error message is included in the returned error and displayed in the logs.
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -147,8 +127,8 @@ breaking v1.
|
||||
|
||||
### Adding and maintaining API routes
|
||||
|
||||
- Document success responses in the swagger comments on the route. Replace explicit
|
||||
error responses with a `default` response referencing `#/responses/error`.
|
||||
- All possible results (errors, success, and failure messages) must be documented in
|
||||
the swagger comments on the route.
|
||||
- Every JSON request body must be defined as a struct in `modules/structs/` and
|
||||
registered in [`routers/api/v1/swagger/options.go`](../routers/api/v1/swagger/options.go).
|
||||
- Every JSON response must be defined as a struct in `modules/structs/` and
|
||||
|
||||
@@ -24,8 +24,6 @@ Now, that's not a requirement anymore as our [backport bot](https://github.com/G
|
||||
|
||||
The `backport/manual` label signifies either that you want to backport the change yourself, or that there were conflicts when backporting, thus you **must** do it yourself.
|
||||
|
||||
The bot labels open PRs targeting `main` that have a `backport/<version>` label with `backport/conflict-free` when they would cherry-pick onto the latest release branch without conflicts.
|
||||
|
||||
### Format of backport PRs
|
||||
|
||||
The title of backport PRs should be
|
||||
|
||||
@@ -48,10 +48,6 @@ Run a single integration test with the `#` selector:
|
||||
make test-integration#TestName
|
||||
```
|
||||
|
||||
Each run gets its own work directory under `tests/integration/` and random ports, so SQLite runs
|
||||
can execute in parallel. Runs against other databases still share the database and object storage.
|
||||
The work directory is removed after a passing run and kept after a failure.
|
||||
|
||||
If you hit errors such as a mismatched database version or SSH push failures, try a
|
||||
clean rebuild first:
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import unescapedHtmlLiteral from './tools/eslint-rules/unescaped-html-literal.ts
|
||||
|
||||
const jsExts = ['js', 'mjs', 'cjs'] as const;
|
||||
const tsExts = ['ts', 'mts', 'cts'] as const;
|
||||
const vueExts = ['vue'] as const;
|
||||
|
||||
const restrictedGlobals = [
|
||||
{name: 'localStorage', message: 'Use `modules/user-settings.ts` instead.'},
|
||||
@@ -37,7 +36,7 @@ export default defineConfig([
|
||||
'public/assets/js',
|
||||
]),
|
||||
{
|
||||
files: [`**/*.{${[...jsExts, ...tsExts, ...vueExts].join(',')}}`],
|
||||
files: [`**/*.{${[...jsExts, ...tsExts].join(',')}}`],
|
||||
ignores: ['dist/*'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
@@ -49,9 +48,7 @@ export default defineConfig([
|
||||
ecmaFeatures: {
|
||||
impliedStrict: true,
|
||||
},
|
||||
parser: typescriptParser,
|
||||
project: true,
|
||||
extraFileExtensions: vueExts.map((ext) => `.${ext}`),
|
||||
},
|
||||
},
|
||||
linterOptions: {
|
||||
@@ -69,11 +66,8 @@ export default defineConfig([
|
||||
wc,
|
||||
},
|
||||
settings: {
|
||||
'import-x/extensions': [...jsExts, ...tsExts, ...vueExts].map((ext) => `.${ext}`),
|
||||
'import-x/parsers': {
|
||||
'@typescript-eslint/parser': [...jsExts, ...tsExts].map((ext) => `.${ext}`),
|
||||
'vue-eslint-parser': vueExts.map((ext) => `.${ext}`),
|
||||
},
|
||||
'import-x/extensions': [...jsExts, ...tsExts].map((ext) => `.${ext}`),
|
||||
'import-x/parsers': {'@typescript-eslint/parser': [...jsExts, ...tsExts].map((ext) => `.${ext}`)},
|
||||
'import-x/resolver': {'typescript': true},
|
||||
},
|
||||
rules: {
|
||||
@@ -211,7 +205,7 @@ export default defineConfig([
|
||||
'@typescript-eslint/no-magic-numbers': [0],
|
||||
'@typescript-eslint/no-meaningless-void-operator': [0],
|
||||
'@typescript-eslint/no-misused-new': [2],
|
||||
'@typescript-eslint/no-misused-promises': [2, {checksVoidReturn: {attributes: false, arguments: false, properties: false}}],
|
||||
'@typescript-eslint/no-misused-promises': [2, {checksVoidReturn: {attributes: false, arguments: false}}],
|
||||
'@typescript-eslint/no-misused-spread': [2],
|
||||
'@typescript-eslint/no-mixed-enums': [0],
|
||||
'@typescript-eslint/no-namespace': [2],
|
||||
@@ -239,7 +233,6 @@ export default defineConfig([
|
||||
'@typescript-eslint/no-unsafe-assignment': [0],
|
||||
'@typescript-eslint/no-unsafe-call': [0],
|
||||
'@typescript-eslint/no-unsafe-declaration-merging': [2],
|
||||
'@typescript-eslint/no-unsafe-enum-assignment': [0],
|
||||
'@typescript-eslint/no-unsafe-enum-comparison': [0],
|
||||
'@typescript-eslint/no-unsafe-function-type': [2],
|
||||
'@typescript-eslint/no-unsafe-member-access': [0],
|
||||
@@ -311,7 +304,6 @@ export default defineConfig([
|
||||
'func-names': [0],
|
||||
'func-style': [0],
|
||||
'getter-return': [2],
|
||||
'gitea/unescaped-html-literal': [2],
|
||||
'grouped-accessor-pairs': [2],
|
||||
'guard-for-in': [0],
|
||||
'id-denylist': [0],
|
||||
@@ -712,7 +704,6 @@ export default defineConfig([
|
||||
'unicorn/better-dom-traversing': [2],
|
||||
'unicorn/catch-error-name': [0],
|
||||
'unicorn/class-reference-in-static-methods': [2],
|
||||
'unicorn/comma-spacing': [0], // only applies to json language
|
||||
'unicorn/comment-content': [0],
|
||||
'unicorn/consistent-arrow-return-style': [0],
|
||||
'unicorn/consistent-assert': [0],
|
||||
@@ -743,10 +734,8 @@ export default defineConfig([
|
||||
'unicorn/filename-case': [0],
|
||||
'unicorn/id-match': [2],
|
||||
'unicorn/import-style': [0],
|
||||
'unicorn/indent': [0], // only applies to json and css languages
|
||||
'unicorn/isolated-functions': [2, {functions: []}],
|
||||
'unicorn/iteration-fallback-style': [2, 'fallback'],
|
||||
'unicorn/key-name-casing': [0], // only applies to json, yaml and toml languages
|
||||
'unicorn/logical-assignment-operators': [0],
|
||||
'unicorn/max-nested-calls': [0],
|
||||
'unicorn/name-replacements': [0],
|
||||
@@ -779,20 +768,21 @@ export default defineConfig([
|
||||
'unicorn/no-chained-comparison': [2],
|
||||
'unicorn/no-collection-bracket-access': [2],
|
||||
'unicorn/no-computed-property-existence-check': [0],
|
||||
'unicorn/no-conflicting-constraints': [2],
|
||||
'unicorn/no-confusing-array-splice': [2],
|
||||
'unicorn/no-confusing-array-with': [2],
|
||||
'unicorn/no-console-spaces': [0],
|
||||
'unicorn/no-constant-zero-expression': [2],
|
||||
'unicorn/no-declarations-before-early-exit': [0],
|
||||
'unicorn/no-deprecated-css-features': [0],
|
||||
'unicorn/no-document-cookie': [2],
|
||||
'unicorn/no-double-comparison': [2],
|
||||
'unicorn/no-duplicate-css-selectors': [0],
|
||||
'unicorn/no-duplicate-font-family-names': [0],
|
||||
'unicorn/no-duplicate-if-branches': [2],
|
||||
'unicorn/no-duplicate-logical-operands': [2],
|
||||
'unicorn/no-duplicate-loops': [0],
|
||||
'unicorn/no-duplicate-set-values': [2],
|
||||
'unicorn/no-empty-file': [2],
|
||||
'unicorn/no-empty-link-text': [0], // only applies to markdown language
|
||||
'unicorn/no-error-property-assignment': [2],
|
||||
'unicorn/no-exports-in-scripts': [2],
|
||||
'unicorn/no-for-each': [2],
|
||||
@@ -800,34 +790,21 @@ export default defineConfig([
|
||||
'unicorn/no-global-object-property-assignment': [0],
|
||||
'unicorn/no-immediate-mutation': [0],
|
||||
'unicorn/no-impossible-length-comparison': [2],
|
||||
'unicorn/no-incomplete-accessor-override': [2],
|
||||
'unicorn/no-incorrect-query-selector': [2],
|
||||
'unicorn/no-incorrect-template-string-interpolation': [0],
|
||||
'unicorn/no-ineffective-csp-directives': [2],
|
||||
'unicorn/no-instanceof-builtins': [2],
|
||||
'unicorn/no-invalid-argument-count': [2],
|
||||
'unicorn/no-invalid-boolean-attribute-value': [2],
|
||||
'unicorn/no-invalid-character-comparison': [2],
|
||||
'unicorn/no-invalid-dom-token': [2],
|
||||
'unicorn/no-invalid-fetch-options': [2],
|
||||
'unicorn/no-invalid-file-input-accept': [2],
|
||||
'unicorn/no-invalid-integrity': [2],
|
||||
'unicorn/no-invalid-intl-options': [2],
|
||||
'unicorn/no-invalid-property-descriptor': [2],
|
||||
'unicorn/no-invalid-media-features': [0],
|
||||
'unicorn/no-invalid-remove-event-listener': [2],
|
||||
'unicorn/no-invalid-response-options': [2],
|
||||
'unicorn/no-invalid-style-set-property': [2],
|
||||
'unicorn/no-invalid-temporal-arithmetic': [2],
|
||||
'unicorn/no-invalid-url-protocol-comparison': [2],
|
||||
'unicorn/no-invalid-well-known-symbol-methods': [2],
|
||||
'unicorn/no-javascript-url': [0], // only applies to markdown language
|
||||
'unicorn/no-keyword-prefix': [0],
|
||||
'unicorn/no-late-current-target-access': [2],
|
||||
'unicorn/no-late-event-control': [2],
|
||||
'unicorn/no-leading-empty-lines': [0], // handled by @stylistic/no-multiple-empty-lines
|
||||
'unicorn/no-lonely-if': [2],
|
||||
'unicorn/no-loop-iterable-mutation': [2],
|
||||
'unicorn/no-loss-of-precision': [0], // only applies to json, toml and css languages
|
||||
'unicorn/no-magic-array-flat-depth': [0],
|
||||
'unicorn/no-manually-wrapped-comments': [0], // too opinionated
|
||||
'unicorn/no-mismatched-map-key': [2],
|
||||
@@ -840,6 +817,7 @@ export default defineConfig([
|
||||
'unicorn/no-negated-condition': [0],
|
||||
'unicorn/no-negation-in-equality-check': [2],
|
||||
'unicorn/no-nested-ternary': [0],
|
||||
'unicorn/no-nesting-with-mixed-specificity': [0],
|
||||
'unicorn/no-new-array': [0],
|
||||
'unicorn/no-new-buffer': [2],
|
||||
'unicorn/no-non-function-verb-prefix': [0],
|
||||
@@ -848,10 +826,9 @@ export default defineConfig([
|
||||
'unicorn/no-object-as-default-parameter': [0],
|
||||
'unicorn/no-object-methods-with-collections': [2],
|
||||
'unicorn/no-optional-chaining-on-undeclared-variable': [2],
|
||||
'unicorn/no-prevent-default-in-passive-listener': [2],
|
||||
'unicorn/no-process-exit': [0],
|
||||
'unicorn/no-redundant-comparison': [2],
|
||||
'unicorn/no-repeated-call-wrappers': [0],
|
||||
'unicorn/no-redundant-nested-style-rules': [0],
|
||||
'unicorn/no-return-array-push': [2],
|
||||
'unicorn/no-selector-as-dom-name': [2],
|
||||
'unicorn/no-shorthand-property-overrides': [0], // only applies to css language
|
||||
@@ -867,16 +844,16 @@ export default defineConfig([
|
||||
'unicorn/no-typeof-undefined': [2],
|
||||
'unicorn/no-uncalled-method': [2],
|
||||
'unicorn/no-undeclared-class-members': [2],
|
||||
'unicorn/no-unknown-css-annotations': [0],
|
||||
'unicorn/no-unknown-pseudo-selectors': [0],
|
||||
'unicorn/no-unnecessary-array-flat-depth': [2],
|
||||
'unicorn/no-unnecessary-array-flat-map': [2],
|
||||
'unicorn/no-unnecessary-array-splice-count': [2],
|
||||
'unicorn/no-unnecessary-await': [2],
|
||||
'unicorn/no-unnecessary-boolean-comparison': [2],
|
||||
'unicorn/no-unnecessary-default-arguments': [0],
|
||||
'unicorn/no-unnecessary-fetch-options': [0],
|
||||
'unicorn/no-unnecessary-global-this': [0],
|
||||
'unicorn/no-unnecessary-nested-ternary': [2],
|
||||
'unicorn/no-unnecessary-parameters': [0],
|
||||
'unicorn/no-unnecessary-polyfills': [2],
|
||||
'unicorn/no-unnecessary-slice-end': [2],
|
||||
'unicorn/no-unnecessary-splice': [2],
|
||||
@@ -888,15 +865,14 @@ export default defineConfig([
|
||||
'unicorn/no-unreadable-object-destructuring': [0],
|
||||
'unicorn/no-unsafe-buffer-conversion': [2],
|
||||
'unicorn/no-unsafe-dom-html': [0],
|
||||
'unicorn/no-unsafe-json-serialization': [2],
|
||||
'unicorn/no-unsafe-promise-all-settled-values': [2],
|
||||
'unicorn/no-unsafe-property-key': [0],
|
||||
'unicorn/no-unsafe-sqlite-interpolation': [2],
|
||||
'unicorn/no-unsafe-string-replacement': [2],
|
||||
'unicorn/no-unscoped-css-nesting-selector': [0],
|
||||
'unicorn/no-unused-builtin-method-return': [2],
|
||||
'unicorn/no-unused-iterator-helper': [2],
|
||||
'unicorn/no-unused-properties': [2],
|
||||
'unicorn/no-url-in-search-params': [2],
|
||||
'unicorn/no-useless-boolean-cast': [2],
|
||||
'unicorn/no-useless-coercion': [2],
|
||||
'unicorn/no-useless-collection-argument': [2],
|
||||
@@ -933,12 +909,12 @@ export default defineConfig([
|
||||
'unicorn/prefer-array-find': [0], // handled by @typescript-eslint/prefer-find
|
||||
'unicorn/prefer-array-flat': [2],
|
||||
'unicorn/prefer-array-flat-map': [2],
|
||||
'unicorn/prefer-array-from-async': [0], // Array.fromAsync requires ES2026
|
||||
'unicorn/prefer-array-from-async': [2],
|
||||
'unicorn/prefer-array-from-map': [2],
|
||||
'unicorn/prefer-array-from-range': [2],
|
||||
'unicorn/prefer-array-index-of': [2],
|
||||
'unicorn/prefer-array-iterable-methods': [2],
|
||||
'unicorn/prefer-array-last-methods': [0], // Array#findLast/findLastIndex requires ES2023
|
||||
'unicorn/prefer-array-last-methods': [2],
|
||||
'unicorn/prefer-array-slice': [2],
|
||||
'unicorn/prefer-array-some': [2],
|
||||
'unicorn/prefer-at': [0],
|
||||
@@ -955,7 +931,7 @@ export default defineConfig([
|
||||
'unicorn/prefer-date-now': [2],
|
||||
'unicorn/prefer-default-parameters': [0],
|
||||
'unicorn/prefer-direct-iteration': [2],
|
||||
'unicorn/prefer-dispose': [0], // `using` requires ES2027
|
||||
'unicorn/prefer-dispose': [2],
|
||||
'unicorn/prefer-dom-node-append': [2],
|
||||
'unicorn/prefer-dom-node-html-methods': [0],
|
||||
'unicorn/prefer-dom-node-remove': [2],
|
||||
@@ -964,14 +940,14 @@ export default defineConfig([
|
||||
'unicorn/prefer-early-return': [0],
|
||||
'unicorn/prefer-else-if': [2],
|
||||
'unicorn/prefer-error-is-error': [0],
|
||||
'unicorn/prefer-escaped-irregular-whitespace': [2],
|
||||
'unicorn/prefer-event-target': [2],
|
||||
'unicorn/prefer-explicit-viewport-units': [0], // only applies to css language
|
||||
'unicorn/prefer-export-from': [0],
|
||||
'unicorn/prefer-flat-math-min-max': [2],
|
||||
'unicorn/prefer-get-or-insert-computed': [0], // Map#getOrInsertComputed requires ES2026
|
||||
'unicorn/prefer-get-or-insert-computed': [2],
|
||||
'unicorn/prefer-global-number-constants': [2],
|
||||
'unicorn/prefer-global-this': [0],
|
||||
'unicorn/prefer-group-by': [0], // Object.groupBy/Map.groupBy requires ES2024
|
||||
'unicorn/prefer-group-by': [2],
|
||||
'unicorn/prefer-has-check': [2],
|
||||
'unicorn/prefer-hoisting-branch-code': [2],
|
||||
'unicorn/prefer-https': [0], // false-positives on namespace and schema URIs
|
||||
@@ -982,12 +958,11 @@ export default defineConfig([
|
||||
'unicorn/prefer-iterable-in-constructor': [2],
|
||||
'unicorn/prefer-iterator-concat': [0], // too opinionated
|
||||
'unicorn/prefer-iterator-helpers': [0],
|
||||
'unicorn/prefer-iterator-to-array': [0], // Iterator#toArray requires ES2025
|
||||
'unicorn/prefer-iterator-to-array': [2],
|
||||
'unicorn/prefer-iterator-to-array-at-end': [2],
|
||||
'unicorn/prefer-iterator-zip': [0],
|
||||
'unicorn/prefer-json-import': [0],
|
||||
'unicorn/prefer-keyboard-event-key': [2],
|
||||
'unicorn/prefer-literal-ascii': [2],
|
||||
'unicorn/prefer-location-assign': [2],
|
||||
'unicorn/prefer-logical-operator-over-ternary': [0],
|
||||
'unicorn/prefer-map-from-entries': [0],
|
||||
@@ -995,6 +970,7 @@ export default defineConfig([
|
||||
'unicorn/prefer-math-constants': [2],
|
||||
'unicorn/prefer-math-min-max': [2],
|
||||
'unicorn/prefer-math-trunc': [2],
|
||||
'unicorn/prefer-media-feature-range-syntax': [0],
|
||||
'unicorn/prefer-minimal-ternary': [0],
|
||||
'unicorn/prefer-modern-dom-apis': [0],
|
||||
'unicorn/prefer-modern-math-apis': [2],
|
||||
@@ -1013,9 +989,8 @@ export default defineConfig([
|
||||
'unicorn/prefer-optional-catch-binding': [2],
|
||||
'unicorn/prefer-path2d': [2],
|
||||
'unicorn/prefer-private-class-fields': [0],
|
||||
'unicorn/prefer-promise-static-methods': [2],
|
||||
'unicorn/prefer-promise-try': [0], // Promise.try requires ES2025
|
||||
'unicorn/prefer-promise-with-resolvers': [0], // Promise.withResolvers requires ES2024
|
||||
'unicorn/prefer-promise-try': [2],
|
||||
'unicorn/prefer-promise-with-resolvers': [2],
|
||||
'unicorn/prefer-prototype-methods': [2],
|
||||
'unicorn/prefer-query-selector': [2],
|
||||
'unicorn/prefer-queue-microtask': [2],
|
||||
@@ -1028,7 +1003,6 @@ export default defineConfig([
|
||||
'unicorn/prefer-set-methods': [0],
|
||||
'unicorn/prefer-set-size': [2],
|
||||
'unicorn/prefer-short-arrow-method': [2],
|
||||
'unicorn/prefer-short-escape-sequences': [2],
|
||||
'unicorn/prefer-simple-condition-first': [0],
|
||||
'unicorn/prefer-simple-sort-comparator': [2],
|
||||
'unicorn/prefer-simplified-conditions': [2],
|
||||
@@ -1036,7 +1010,7 @@ export default defineConfig([
|
||||
'unicorn/prefer-single-call': [2],
|
||||
'unicorn/prefer-single-object-destructuring': [2],
|
||||
'unicorn/prefer-single-replace': [2],
|
||||
'unicorn/prefer-smaller-scope': [0],
|
||||
'unicorn/prefer-smaller-scope': [2],
|
||||
'unicorn/prefer-split-limit': [0], // too opinionated
|
||||
'unicorn/prefer-spread': [0],
|
||||
'unicorn/prefer-string-match-all': [2],
|
||||
@@ -1050,7 +1024,7 @@ export default defineConfig([
|
||||
'unicorn/prefer-structured-clone': [2],
|
||||
'unicorn/prefer-switch': [0],
|
||||
'unicorn/prefer-temporal': [0],
|
||||
'unicorn/prefer-temporal-conversion': [0], // Temporal requires ES2027
|
||||
'unicorn/prefer-temporal-conversion': [2],
|
||||
'unicorn/prefer-ternary': [0],
|
||||
'unicorn/prefer-then-catch': [2],
|
||||
'unicorn/prefer-toggle-attribute': [2],
|
||||
@@ -1076,7 +1050,6 @@ export default defineConfig([
|
||||
'unicorn/require-passive-events': [2],
|
||||
'unicorn/require-post-message-target-origin': [0],
|
||||
'unicorn/require-proxy-trap-boolean-return': [2],
|
||||
'unicorn/require-text-decoder-streaming': [2],
|
||||
'unicorn/single-line-block-comment-style': [0],
|
||||
'unicorn/string-content': [0],
|
||||
'unicorn/switch-case-braces': [0],
|
||||
@@ -1123,13 +1096,17 @@ export default defineConfig([
|
||||
},
|
||||
},
|
||||
{
|
||||
files: vueExts.map((ext) => `**/*.${ext}`),
|
||||
files: ['**/*.vue'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
},
|
||||
},
|
||||
extends: [
|
||||
vue.configs['flat/recommended'],
|
||||
vueScopedCss.configs.recommended,
|
||||
],
|
||||
rules: {
|
||||
'@typescript-eslint/no-redundant-type-constituents': [0], // types imported from .vue files resolve to any via typescript-eslint's *.vue shim
|
||||
'vue/attributes-order': [0],
|
||||
'vue/html-closing-bracket-spacing': [2, {startTag: 'never', endTag: 'never', selfClosingTag: 'never'}],
|
||||
'vue/max-attributes-per-line': [0],
|
||||
@@ -1242,9 +1219,6 @@ export default defineConfig([
|
||||
{
|
||||
files: ['*', 'tools/**/*', 'tests/**/*'],
|
||||
languageOptions: {globals: globals.nodeBuiltin},
|
||||
rules: {
|
||||
'gitea/unescaped-html-literal': [0],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['web_src/**/*'],
|
||||
|
||||
143
go.mod
143
go.mod
@@ -2,60 +2,67 @@ module gitea.dev
|
||||
|
||||
go 1.27
|
||||
|
||||
toolchain go1.27.3
|
||||
toolchain go1.27.1
|
||||
|
||||
require (
|
||||
connectrpc.com/connect/v2 v2.0.2
|
||||
connectrpc.com/connect v1.21.0
|
||||
gitea.com/go-chi/binding v0.0.0-20260819122636-082915a69981
|
||||
gitea.com/go-chi/cache v0.2.1
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
|
||||
gitea.com/go-chi/session v0.0.0-20260708011333-ebced8a7a2d6
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
gitea.com/lunny/levelqueue v0.5.1
|
||||
gitea.dev/actionslib v1.5.2
|
||||
gitea.dev/sdk v1.5.1
|
||||
github.com/42wim/httpsig v1.3.0
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||
gitea.dev/actionslib v1.2.1
|
||||
gitea.dev/sdk v1.2.0
|
||||
github.com/42wim/httpsig v1.2.4
|
||||
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432
|
||||
github.com/Azure/go-ntlmssp v0.1.1
|
||||
github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347
|
||||
github.com/ProtonMail/go-crypto v1.5.2
|
||||
github.com/ProtonMail/go-crypto v1.5.0
|
||||
github.com/PuerkitoBio/goquery v1.13.0
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0
|
||||
github.com/alecthomas/chroma/v2 v2.27.0
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/blevesearch/bleve/v2 v2.6.2
|
||||
github.com/blevesearch/bleve/v2 v2.6.1
|
||||
github.com/bohde/codel v0.2.0
|
||||
github.com/bradfitz/gomemcache v0.0.0-20260422231931-4d751bb6e37c
|
||||
github.com/buildkite/terminal-to-html/v3 v3.17.3
|
||||
github.com/caddyserver/certmagic v0.25.6
|
||||
github.com/buildkite/terminal-to-html/v3 v3.17.1
|
||||
github.com/caddyserver/certmagic v0.25.4
|
||||
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20260812203852-971c0284dc33
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
github.com/coder/websocket v1.8.15
|
||||
github.com/dlclark/regexp2/v2 v2.12.0
|
||||
github.com/dlclark/regexp2/v2 v2.8.0
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
|
||||
github.com/dustin/go-humanize v1.1.0
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.5
|
||||
github.com/emersion/go-imap v1.2.1
|
||||
github.com/emirpasic/gods v1.18.1
|
||||
github.com/felixge/fgprof v0.9.5
|
||||
github.com/fsnotify/fsnotify v1.10.1
|
||||
github.com/getkin/kin-openapi v0.152.0
|
||||
github.com/go-chi/chi/v5 v5.5.0
|
||||
github.com/getkin/kin-openapi v0.149.0
|
||||
github.com/go-chi/chi/v5 v5.3.2
|
||||
github.com/go-chi/cors v1.2.2
|
||||
github.com/go-co-op/gocron/v2 v2.23.0
|
||||
github.com/go-co-op/gocron/v2 v2.22.0
|
||||
github.com/go-enry/go-enry/v2 v2.9.6
|
||||
github.com/go-ldap/ldap/v3 v3.4.15
|
||||
github.com/go-redsync/redsync/v4 v4.18.1
|
||||
github.com/go-git/go-billy/v5 v5.9.1
|
||||
github.com/go-git/go-git/v5 v5.19.2
|
||||
github.com/go-ldap/ldap/v3 v3.4.14
|
||||
github.com/go-redsync/redsync/v4 v4.18.0
|
||||
github.com/go-sql-driver/mysql v1.10.1
|
||||
github.com/go-webauthn/webauthn v0.18.2
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/google/go-github/v93 v93.0.0
|
||||
github.com/google/go-github/v92 v92.0.0
|
||||
github.com/google/licenseclassifier/v2 v2.0.0
|
||||
github.com/google/pprof v0.0.0-20261026192309-dbd0ed30aa88
|
||||
github.com/google/pprof v0.0.0-20260906184651-6331bc6350fe
|
||||
github.com/gorilla/feeds v1.2.0
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/hashicorp/go-version v1.9.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/huandu/xstrings v1.6.3
|
||||
github.com/huandu/xstrings v1.6.1
|
||||
github.com/jhillyerd/enmime/v2 v2.5.0
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/compress v1.20.3
|
||||
github.com/klauspost/compress v1.20.0
|
||||
github.com/lib/pq v1.12.3
|
||||
github.com/markbates/goth v1.82.0
|
||||
github.com/mattn/go-isatty v0.0.24
|
||||
@@ -64,39 +71,42 @@ require (
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/microsoft/go-mssqldb v1.11.2
|
||||
github.com/minio/minio-go/v7 v7.4.1
|
||||
github.com/minio/minio-go/v7 v7.3.0
|
||||
github.com/msteinert/pam/v2 v2.1.0
|
||||
github.com/niklasfasching/go-org v1.9.1
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/prometheus/client_golang v1.25.0
|
||||
github.com/prometheus/client_golang v1.24.1
|
||||
github.com/quasoft/websspi v1.1.2
|
||||
github.com/redis/go-redis/v9 v9.23.0
|
||||
github.com/redis/go-redis/v9 v9.22.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3
|
||||
github.com/sassoftware/go-rpmutils v0.4.0
|
||||
github.com/sergi/go-diff v1.4.0
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.17
|
||||
github.com/urfave/cli-docs/v3 v3.2.0
|
||||
github.com/urfave/cli/v3 v3.14.0
|
||||
github.com/urfave/cli-docs/v3 v3.1.0
|
||||
github.com/urfave/cli/v3 v3.13.0
|
||||
github.com/wneessen/go-mail v0.8.1
|
||||
github.com/yuin/goldmark v1.8.7
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.43.0
|
||||
go.yaml.in/yaml/v4 v4.0.0
|
||||
golang.org/x/crypto v0.58.0
|
||||
golang.org/x/image v0.47.0
|
||||
golang.org/x/mod v0.42.0
|
||||
golang.org/x/net v0.62.0
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.8.6
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.13.0
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6
|
||||
golang.org/x/crypto v0.57.0
|
||||
golang.org/x/image v0.46.0
|
||||
golang.org/x/mod v0.41.0
|
||||
golang.org/x/net v0.59.0
|
||||
golang.org/x/oauth2 v0.37.0
|
||||
golang.org/x/sync v0.24.0
|
||||
golang.org/x/sys v0.49.0
|
||||
golang.org/x/text v0.43.0
|
||||
google.golang.org/grpc v1.85.0
|
||||
golang.org/x/sync v0.23.0
|
||||
golang.org/x/sys v0.48.0
|
||||
golang.org/x/text v0.42.0
|
||||
google.golang.org/grpc v1.84.0
|
||||
google.golang.org/protobuf v1.36.12
|
||||
gopkg.in/ini.v1 v1.67.3
|
||||
modernc.org/sqlite v1.61.1
|
||||
modernc.org/sqlite v1.59.0
|
||||
mvdan.cc/xurls/v2 v2.6.0
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.4.3
|
||||
@@ -104,16 +114,19 @@ require (
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
github.com/DataDog/zstd v1.5.7 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/RoaringBitmap/roaring/v2 v2.18.2 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/andybalholm/brotli v1.2.7 // indirect
|
||||
github.com/andybalholm/brotli v1.2.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.4 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.5 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.4.1 // indirect
|
||||
github.com/blevesearch/geo v0.2.7-0.20260831141219-9c3e3a4d84bc // indirect
|
||||
github.com/blevesearch/geo v0.2.6 // indirect
|
||||
github.com/blevesearch/go-faiss v1.1.5 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
@@ -122,26 +135,31 @@ require (
|
||||
github.com/blevesearch/segment v0.9.1 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
||||
github.com/blevesearch/vellum v1.2.1 // indirect
|
||||
github.com/blevesearch/vellum v1.2.0 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.4.3 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.4.3 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.4.3 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.4.3 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.4.3 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.3.4 // indirect
|
||||
github.com/blevesearch/zapx/v17 v17.2.4 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.4 // indirect
|
||||
github.com/blevesearch/zapx/v17 v17.2.3 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.4 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.6 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20260422231931-4d751bb6e37c // indirect
|
||||
github.com/caddyserver/zerossl v0.1.5 // indirect
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // 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/gomemcached v0.3.4 // indirect
|
||||
github.com/couchbase/goutils v0.3.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
|
||||
@@ -150,6 +168,7 @@ require (
|
||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.23.1 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.26.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
@@ -157,7 +176,9 @@ require (
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/flatbuffers v25.12.19+incompatible // indirect
|
||||
github.com/google/go-querystring v1.2.0 // indirect
|
||||
github.com/google/go-tpm v0.9.8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
@@ -167,8 +188,10 @@ require (
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // 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/jonboulle/clockwork v0.5.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
|
||||
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
@@ -176,8 +199,8 @@ require (
|
||||
github.com/markbates/going v1.0.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.15 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.24 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.7 // indirect
|
||||
github.com/miekg/dns v1.1.73 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.6 // indirect
|
||||
github.com/miekg/dns v1.1.72 // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
@@ -187,7 +210,7 @@ require (
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/ncruces/go-strftime v1.1.0 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.3 // indirect
|
||||
github.com/oasdiff/yaml v0.1.1 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.14 // indirect
|
||||
@@ -196,37 +219,43 @@ require (
|
||||
github.com/olekukonko/ll v0.1.8 // indirect
|
||||
github.com/olekukonko/tablewriter v1.1.4 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.34.1 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.27 // indirect
|
||||
github.com/pjbgf/sha1cd v0.6.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.3 // indirect
|
||||
github.com/prometheus/common v0.72.0 // indirect
|
||||
github.com/prometheus/procfs v0.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.70.1 // indirect
|
||||
github.com/prometheus/procfs v0.21.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.10.2 // indirect
|
||||
github.com/skeema/knownhosts v1.3.2 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/stangelandcl/ppmd v0.1.1 // indirect
|
||||
github.com/tinylib/msgp v1.6.4 // indirect
|
||||
github.com/unknwon/com v1.0.1 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
github.com/zeebo/xxh3 v1.1.0 // indirect
|
||||
go.etcd.io/bbolt v1.4.3 // indirect
|
||||
go.uber.org/atomic v1.12.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.28.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.6 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
|
||||
golang.org/x/time v0.16.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 // indirect
|
||||
modernc.org/libc v1.79.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.49.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
modernc.org/libc v1.75.7 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.12.1 // indirect
|
||||
)
|
||||
|
||||
401
go.sum
401
go.sum
@@ -2,22 +2,32 @@ cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdB
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE=
|
||||
code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM=
|
||||
connectrpc.com/connect/v2 v2.0.2 h1:PoPDmOlhc46elsuCdKCKTxjNBb3HJlKF+s/sP2oucPw=
|
||||
connectrpc.com/connect/v2 v2.0.2/go.mod h1:N32OLrstcPUzkykFynnefFro3+xzSdOrZlK2jHmS/xo=
|
||||
connectrpc.com/connect v1.21.0 h1:LhqSJt7jHf5NJBo9Jq/t/9FjcYAideif0mg+qe2jCUs=
|
||||
connectrpc.com/connect v1.21.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
gitea.com/go-chi/binding v0.0.0-20260819122636-082915a69981 h1:LmdlwGbzgZFZA3bK3R1q8QrNabaSz3KpnOJBLmzhN6E=
|
||||
gitea.com/go-chi/binding v0.0.0-20260819122636-082915a69981/go.mod h1:q1SSPpkC9A0gfNnoqqZ3My6kEHIpKN6QsTI+Zx73B/o=
|
||||
gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
|
||||
gitea.com/go-chi/cache v0.2.1/go.mod h1:Qic0HZ8hOHW62ETGbonpwz8WYypj9NieU9659wFUJ8Q=
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo=
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098/go.mod h1:LjzIOHlRemuUyO7WR12fmm18VZIlCAaOt9L3yKw40pk=
|
||||
gitea.com/go-chi/session v0.0.0-20260708011333-ebced8a7a2d6 h1:YWzVGeC/8SZThrJS48ZmQYLkzssdeABxHPhbdnxPDIU=
|
||||
gitea.com/go-chi/session v0.0.0-20260708011333-ebced8a7a2d6/go.mod h1:KDvcfMUoXfATPHs2mbMoXFTXT45/FAFAS39waz9tPk0=
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:VyMQP6ue6MKHM8UsOXfNfuMKD0oSAWZdXVcpHIN2yaY=
|
||||
gitea.com/lunny/levelqueue v0.5.1 h1:15q1oBWaO2Aw9t3rDthQSTX+vBNgjYNkCgRJgES9gA8=
|
||||
gitea.com/lunny/levelqueue v0.5.1/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHqdhS7keYWioqfmxdnfblFDTGoOwcZ+o=
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||
gitea.dev/actionslib v1.5.2 h1:PSSUd+4jIjI+ycqo62+BRgAWkBnzjgeGQAcI3ZqH7LY=
|
||||
gitea.dev/actionslib v1.5.2/go.mod h1:i8wUQMnvGwT13f2re2ZTMCFL/TyauQLeOb1L4ngjtPk=
|
||||
gitea.dev/sdk v1.5.1 h1:bMBU3rPoU87a8OOgtfu5w4sZn2x/ydwNAkqFLfIN8c8=
|
||||
gitea.dev/sdk v1.5.1/go.mod h1:cM7S3Y9TY3LraJoRHHDLlY3YnVTu4bTc9TEBGuFa2Jw=
|
||||
github.com/42wim/httpsig v1.3.0 h1:HK2bieeZ99X9IvOoc1ST6yrhLek0f4MqlEGES2JCP3g=
|
||||
github.com/42wim/httpsig v1.3.0/go.mod h1:lgC3Yrvp4lxi1odEF1ge3kQBu/VM+SRcZQu5YwiR3dU=
|
||||
gitea.dev/actionslib v1.2.1 h1:GL//K/0zIZV6h1OOksv1awvFVg0rg7fug2xWcG7mkG0=
|
||||
gitea.dev/actionslib v1.2.1/go.mod h1:1+gqOKGSEPn2IFHgY8S3GC5Ld+Hn8jF3OxiFHQ/fpx4=
|
||||
gitea.dev/sdk v1.2.0 h1:avRtJl/nKCGispgSalo9czoZM9Rto1awnE0caNAoXGo=
|
||||
gitea.dev/sdk v1.2.0/go.mod h1:rfh5oNdIK24cbCREwIn1tqWKQW+IICXFGWJyebuOAOE=
|
||||
github.com/42wim/httpsig v1.2.4 h1:mI5bH0nm4xn7K18fo1K3okNDRq8CCJ0KbBYWyA6r8lU=
|
||||
github.com/42wim/httpsig v1.2.4/go.mod h1:yKsYfSyTBEohkPik224QPFylmzEBtda/kjyIAJjh3ps=
|
||||
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432 h1:3Fcz1QzlS7Jv4FT2KI3cHNSZL+KPN3dXxurn9f3YL/Y=
|
||||
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432/go.mod h1:BLWe6Nol65Xxncvaw07yYMxiyk02We1lBrbRYsMYsjE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.1 h1:zvXfGJCWvywnCA814d8ZiVyt+fm9nnTE8xSb99zRyfo=
|
||||
@@ -34,10 +44,15 @@ github.com/Azure/go-ntlmssp v0.1.1 h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDP
|
||||
github.com/Azure/go-ntlmssp v0.1.1/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.8.0 h1:Nljr4q1GRA/5vCrMONS+g4u4LRHNgOXVSh3O43J2CnI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.8.0/go.mod h1:Y33QHnf0FfdVewFFISOGe20mkZbxX4H839o955/PoeI=
|
||||
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
|
||||
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347 h1:3JhDl+JysaO8nhNU1XMaw35VSGjV4IEQAefaG4Lyok4=
|
||||
github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347/go.mod h1:2ErI0aycD43Ufr6CFK5lT/NrHGmoZuVbn1nlPThw69o=
|
||||
github.com/ProtonMail/go-crypto v1.5.2 h1:cucYnvqcY7UOXVD//mSyjeaPY0SSN3v5cDkYPxumINk=
|
||||
github.com/ProtonMail/go-crypto v1.5.2/go.mod h1:/RaSu30DaKO4RY+XdV/ACcCcZkGr7AhUIduq5sjzzCo=
|
||||
github.com/ProtonMail/go-crypto v1.5.0 h1:sKmuvjOgsnrtpMvZ+84MCnTJCpjxZ1qCFn076lz1yT0=
|
||||
github.com/ProtonMail/go-crypto v1.5.0/go.mod h1:/RaSu30DaKO4RY+XdV/ACcCcZkGr7AhUIduq5sjzzCo=
|
||||
github.com/PuerkitoBio/goquery v1.13.0 h1:mqHbjD7Jmnul4DTR24LKTjo1uUmHUh072kteGV+xpFM=
|
||||
github.com/PuerkitoBio/goquery v1.13.0/go.mod h1:Hip5mdBL8K2wEGKJdr27sRaNwIdDajmCwB/ExUPwW+g=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.18.2 h1:oPq3Cgx//iDuJQVp6xSInAKW34J9CEwE5GmLI2z+Eic=
|
||||
@@ -48,16 +63,22 @@ github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0 h1:LvK7+C6qgz8BPnmn7x
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0/go.mod h1:I28hc9eaiqKCoOB+9Wh/P1IOScfm0xCgyWC6DAo0lmo=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/chroma/v2 v2.27.0 h1:FodwmyOBgJULFYmDqibcp9pvfDLWdtPRh9v/r5BXYZs=
|
||||
github.com/alecthomas/chroma/v2 v2.27.0/go.mod h1:NjJ3ciIgrqBNeIkWZ4e46nseoLDslxU1LmfCoL+wcY8=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
|
||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/andybalholm/brotli v1.2.7 h1:UgafRPGMam5ySHfRkhthMqOFA6IFoeF3D7mWIDLjG5A=
|
||||
github.com/andybalholm/brotli v1.2.7/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro=
|
||||
github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/andybalholm/cascadia v1.3.4 h1:vM2lgh0Vru9Vwyfm4cQqWP2HHMW0u0+2PAW7Q38Qufg=
|
||||
github.com/andybalholm/cascadia v1.3.4/go.mod h1:BLRmbRjpEtNKieZOCCvYj4RqN+KRA41GBe/5O+G93kM=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -66,12 +87,12 @@ github.com/bits-and-blooms/bitset v1.24.5 h1:654xBVHc23gJMAgOTkPNoCVfiRxuIOAUnAZ
|
||||
github.com/bits-and-blooms/bitset v1.24.5/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/blevesearch/bleve/v2 v2.6.2 h1:CaPS1fjYgp+ejW5Nd7OTmVTCkuFzzqyjrADi/XCizdU=
|
||||
github.com/blevesearch/bleve/v2 v2.6.2/go.mod h1:w86Ae7GPU0r5IW2S6OSrdUojZY3z7WmOb2pgAnfg8fQ=
|
||||
github.com/blevesearch/bleve/v2 v2.6.1 h1:47vLskRTqxvQEtxVPYHjf5KpOgzD2msslXFjvUQCgWQ=
|
||||
github.com/blevesearch/bleve/v2 v2.6.1/go.mod h1:Dvvx6ZoEBTOj6RSzfk0lEz0wce/qhe2yOUubXeuzd2c=
|
||||
github.com/blevesearch/bleve_index_api v1.4.1 h1:CYIyecFlI+/RYjzUm+NmDjYbSvk870Bb7f+Vl4b12q8=
|
||||
github.com/blevesearch/bleve_index_api v1.4.1/go.mod h1:xvd48t5XMeeioWQ5/jZvgLrV98flT2rdvEJ3l/ki4Ko=
|
||||
github.com/blevesearch/geo v0.2.7-0.20260831141219-9c3e3a4d84bc h1:2nCheDGYfGpD+w6d1czqyzuLXUgQpuk8tTi66si470c=
|
||||
github.com/blevesearch/geo v0.2.7-0.20260831141219-9c3e3a4d84bc/go.mod h1:6qzVUiB4BK47QkSZcRqiXEP2W3EeXuzM5XFTF8AdZ8A=
|
||||
github.com/blevesearch/geo v0.2.6 h1:7K1oyQKYlauC+mJuo2AfNPyjN/4mihEoJMfyClVH1Mo=
|
||||
github.com/blevesearch/geo v0.2.6/go.mod h1:6qzVUiB4BK47QkSZcRqiXEP2W3EeXuzM5XFTF8AdZ8A=
|
||||
github.com/blevesearch/go-faiss v1.1.5 h1:/IU5lkOahH9Ghfk9n3F6N0XD7PYVXZJWmNDc9TtXuco=
|
||||
github.com/blevesearch/go-faiss v1.1.5/go.mod h1:w3W9AiWsFRGVaMG+/cmJi7iHEAuGyC6blsgO1EzCK/M=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
|
||||
@@ -88,8 +109,8 @@ github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD
|
||||
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A=
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ=
|
||||
github.com/blevesearch/vellum v1.2.1 h1:PYryJDqBHjLM6n3YindZSQcrseaMs8QpcsrrAOpwwj8=
|
||||
github.com/blevesearch/vellum v1.2.1/go.mod h1:uEcfBJz7mAOf0Kvq6qoEKQQkLODBF46SINYNkZNae4k=
|
||||
github.com/blevesearch/vellum v1.2.0 h1:xkDiOEsHc2t3Cp0NsNZZ36pvc130sCzcGKOPMzXe+e0=
|
||||
github.com/blevesearch/vellum v1.2.0/go.mod h1:uEcfBJz7mAOf0Kvq6qoEKQQkLODBF46SINYNkZNae4k=
|
||||
github.com/blevesearch/zapx/v11 v11.4.3 h1:PTZOO5loKpHC/x/GzmPZNa9cw7GZIQxd5qRjwij9tHY=
|
||||
github.com/blevesearch/zapx/v11 v11.4.3/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc=
|
||||
github.com/blevesearch/zapx/v12 v12.4.3 h1:eElXvAaAX4m04t//CGBQAtHNPA+Q6A1hHZVrN3LSFYo=
|
||||
@@ -102,10 +123,10 @@ github.com/blevesearch/zapx/v15 v15.4.3 h1:iJiMJOHrz216jyO6lS0m9RTCEkprUnzvqAI2l
|
||||
github.com/blevesearch/zapx/v15 v15.4.3/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
|
||||
github.com/blevesearch/zapx/v16 v16.3.4 h1:hDAqA8qusZTNbPEL7//w5P65UZ2de6yhSeUaTbp0Po0=
|
||||
github.com/blevesearch/zapx/v16 v16.3.4/go.mod h1:zqkPPqs9GS9FzVWzCO3Wf1X044yWAV17+4zb+FTiEHg=
|
||||
github.com/blevesearch/zapx/v17 v17.2.4 h1:6BekwKXVubfivq87E91dqJgRDhmguA0Hvm1JTyqNzWM=
|
||||
github.com/blevesearch/zapx/v17 v17.2.4/go.mod h1:8gLqU0Hr+MuFCBbL/lDS3O9MiTFjyoTcLXkiVCjwqMs=
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.4 h1:oTKkRmRuOsKMqFe1AQazd9gGBFNEpOmgKp/xzB97dnA=
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.4/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/blevesearch/zapx/v17 v17.2.3 h1:UYYJPAt5b2tVxldx5h0jmv23RMsg8/UZKFVya7v92po=
|
||||
github.com/blevesearch/zapx/v17 v17.2.3/go.mod h1:r7mb4QWbDQSkbAnOjCb9iCfkcrzajB4yBdJpuBIo/fE=
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=
|
||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
||||
@@ -124,18 +145,20 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buildkite/terminal-to-html/v3 v3.17.3 h1:Q4l5cgoDsP5gTu91wy+Yku1h21qRYyK8NoQMudNwNHM=
|
||||
github.com/buildkite/terminal-to-html/v3 v3.17.3/go.mod h1:VpG+CDQqu4/bzOWR6gwYF5tB3P+Ts4hVgdxXpqLLY4s=
|
||||
github.com/caddyserver/certmagic v0.25.6 h1:vHMtFSLKTa6kuZz6w0SnRMpQ388tSkww8ye355BgAM4=
|
||||
github.com/caddyserver/certmagic v0.25.6/go.mod h1:xq6cRNimqW+Tv91XNc5lNHs2XYTsLhQhYiH01H4AySs=
|
||||
github.com/caddyserver/zerossl v0.1.6 h1:1yrhTx5DWi43wOJPQ+Y4mVl++EPmSqqT/4REW//lsas=
|
||||
github.com/caddyserver/zerossl v0.1.6/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/buildkite/terminal-to-html/v3 v3.17.1 h1:gs9WhKUwxK27YHf3hijj4tG4wuDLfCANYbRPd5ltMdI=
|
||||
github.com/buildkite/terminal-to-html/v3 v3.17.1/go.mod h1:0pv+Z75FC+NvIju2o+Cw+YHIDkueeudIoeyfVm8i8GQ=
|
||||
github.com/caddyserver/certmagic v0.25.4 h1:8eIXh0HC3MsGnNo8One+BCxMGTbe5zb/oz+2KsxBFQg=
|
||||
github.com/caddyserver/certmagic v0.25.4/go.mod h1:YVs43D5+H/Dckt4bTga1KSO/xYfFBfVZainGDywYPAA=
|
||||
github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE=
|
||||
github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20260812203852-971c0284dc33 h1:YG5mXlv9SxS4rdtSxK3RuH146x1HIQzsbVJqSp/Rvfc=
|
||||
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20260812203852-971c0284dc33/go.mod h1:R+SetERD4+IL7QH0WHp9MLifvITvh9gL3g8vX1j2Fcs=
|
||||
github.com/chi-middleware/proxy v1.1.1 h1:4HaXUp8o2+bhHr1OhVy+VjN0+L7/07JDcn6v7YrTjrQ=
|
||||
github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdikvbVJVHv/M+0=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
@@ -150,8 +173,17 @@ github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA=
|
||||
github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
||||
github.com/couchbase/go-couchbase v0.1.1 h1:ClFXELcKj/ojyoTYbsY34QUrrYCBi/1G749sXSCkdhk=
|
||||
github.com/couchbase/go-couchbase v0.1.1/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=
|
||||
github.com/couchbase/gomemcached v0.3.4 h1:VGdrZUJbt5lLyI/MXnyVCZKHKYXg/vaud08lJIAeZps=
|
||||
github.com/couchbase/gomemcached v0.3.4/go.mod h1:pISAjweI42vljCumsJIo7CVhqIMIIP9g3Wfhl1JJw68=
|
||||
github.com/couchbase/goutils v0.1.2/go.mod h1:h89Ek/tiOxxqjz30nPPlwZdQbdB8BwgnuBxeoUe/ViE=
|
||||
github.com/couchbase/goutils v0.3.0 h1:rsv72B6BDjW9jmwlfiDUrdu3EpNvPuo5WLULHzQ0DLE=
|
||||
github.com/couchbase/goutils v0.3.0/go.mod h1:7Gm+D3vXfV4HS+hQWvKfy6e6ILCptGXNqBKvQXhplhk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -160,10 +192,12 @@ github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454Wv
|
||||
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2/v2 v2.12.0 h1:GsCgDZZeHluUDHASr2yqQonZk4Tma5LFmLH9CSsFFEE=
|
||||
github.com/dlclark/regexp2/v2 v2.12.0/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8=
|
||||
github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2/v2 v2.8.0 h1:CekDhPLGfm+GAJmPVFIG+5dqMIQPkyHJll7BbdneDfw=
|
||||
github.com/dlclark/regexp2/v2 v2.8.0/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
@@ -171,6 +205,8 @@ github.com/dustin/go-humanize v1.1.0 h1:dbKTrvD0klcbBV/h4AWJdMuZogJACoMlvWIWZ5b2
|
||||
github.com/dustin/go-humanize v1.1.0/go.mod h1:hc1CvRkJMsgxqjmjMQF3QNRAZBwY8AXBAzKYoSX9sFI=
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.5 h1:MTcuJQkIFRLfNn9FfAvdO0p3FhNyaCf+IGTwy0TxX6E=
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.5/go.mod h1:SizrS3EM1vFF0v/JZlJ5LfK6tXhwln2823YZwNh/vBE=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
|
||||
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
||||
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
|
||||
@@ -178,6 +214,8 @@ github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTe
|
||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
|
||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
|
||||
@@ -188,26 +226,37 @@ github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx5
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/fxamacker/cbor/v2 v2.9.4 h1:xwjVlxEMR3S605oUlgBjKLTTeGFciYPGYCtF/35LKGo=
|
||||
github.com/fxamacker/cbor/v2 v2.9.4/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/getkin/kin-openapi v0.152.0 h1:XRVo77lMRyOGMHf1paMu9lxX3wzuQDZeprssZJnz/x8=
|
||||
github.com/getkin/kin-openapi v0.152.0/go.mod h1:1+BHDzstro+P5CKtPy1X4PfofnFgmRe6uvMy9+r9fKY=
|
||||
github.com/getkin/kin-openapi v0.149.0 h1:ZbhmVJ4yq5RZDUsyP8lcBcGMsjsaTqXEFt6isdtMDfA=
|
||||
github.com/getkin/kin-openapi v0.149.0/go.mod h1:1+BHDzstro+P5CKtPy1X4PfofnFgmRe6uvMy9+r9fKY=
|
||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
|
||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-chi/chi/v5 v5.5.0 h1:xdKQ1dJmVJsmgahrXSKhUx8NyVPzpwdlyrrQFpMPJgg=
|
||||
github.com/go-chi/chi/v5 v5.5.0/go.mod h1:d1IG7GtKPSjPyBP365PH8sAGVHk3qfB/IhjFNRH1wWM=
|
||||
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.3.2 h1:5YQkICvTCSZ25hoRsyJazN0scjzKGiu4VAUc7H1o1nY=
|
||||
github.com/go-chi/chi/v5 v5.3.2/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
|
||||
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
||||
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-co-op/gocron/v2 v2.23.0 h1:Qkhe0ZlUf3sRMCEuoXRi5VU/t8fi5lyD97eO+PsGJ4g=
|
||||
github.com/go-co-op/gocron/v2 v2.23.0/go.mod h1:QwAfxPmGC9tPDL5R9R3inaJYPDvYEXs/zkYrXD3L9jk=
|
||||
github.com/go-co-op/gocron/v2 v2.22.0 h1:uEuH2F7k7VoESb1BYSaffuuV+T0kkpzsC0aXk7/z79I=
|
||||
github.com/go-co-op/gocron/v2 v2.22.0/go.mod h1:hiH/U9RMhTi1BBZJmef9s3KC9QwhpBF6PFrvUKaXY9M=
|
||||
github.com/go-enry/go-enry/v2 v2.9.6 h1:np63eOtMV56zfYDHnFVgpEVOk8fr2kmylcMnAZUDbSs=
|
||||
github.com/go-enry/go-enry/v2 v2.9.6/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
|
||||
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
|
||||
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.9.1 h1:8U73XiOTfINdItHVa6z4Gv7ToObcZ6grkqQbLryLCdA=
|
||||
github.com/go-git/go-billy/v5 v5.9.1/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.19.2 h1:wkfn7vOlUBu8ivAWKBWisTiwJK4jYHzTF8Ndv1LyGqY=
|
||||
github.com/go-git/go-git/v5 v5.19.2/go.mod h1:QqCBE1EFN5ddFmrliLQ3/ntRCUjZU3EJuwuB/jWEHjk=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-ldap/ldap/v3 v3.4.15 h1:eRB4f4Ecn1POL1mO86+uUDlecmyWsdiJwh60bAUS4UY=
|
||||
github.com/go-ldap/ldap/v3 v3.4.15/go.mod h1:jeD0c98EdZQCKKKKe4kTWs5aCf6y+j8KuYsYLznZSQk=
|
||||
github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs=
|
||||
github.com/go-ldap/ldap/v3 v3.4.14/go.mod h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA=
|
||||
github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4=
|
||||
github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY=
|
||||
github.com/go-openapi/swag/jsonname v0.26.1 h1:VReupaV6WxlAsCn0e4DUfgV6bPmINnPpyJDLqSfNPcE=
|
||||
@@ -220,8 +269,8 @@ github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOr
|
||||
github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-redsync/redsync/v4 v4.18.1 h1:vESflzCCKyQKScRkdxQFwtXglNJyz3ZGCLaEj+IUM7c=
|
||||
github.com/go-redsync/redsync/v4 v4.18.1/go.mod h1:SjQtx+rI0To8ycKKS6CFqexYxXRZvFuL72gSP+upOco=
|
||||
github.com/go-redsync/redsync/v4 v4.18.0 h1:DDM09PEhmNTf/vziolf2x6391y1hHxF5vQ4ytHl9xQc=
|
||||
github.com/go-redsync/redsync/v4 v4.18.0/go.mod h1:yuoqcQ55FS1VypSeVTMqFsJqTdWpEH5eA4MQGKJ9c4M=
|
||||
github.com/go-sql-driver/mysql v1.10.1 h1:arlSnNLq6a5yxGxV7qg9lF4j0C+KwD6NbQyKr9QL6ME=
|
||||
github.com/go-sql-driver/mysql v1.10.1/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
@@ -248,6 +297,8 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
@@ -263,6 +314,9 @@ github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v1.9.3 h1:dNPSXeXv6HCq2jdyWfjgmhBdqnR6PRO3m/G05nvpPC8=
|
||||
github.com/gomodule/redigo v1.9.3/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
|
||||
github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
@@ -271,8 +325,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-github/v93 v93.0.0 h1:qElXPLv+XbSz1qrLdWyb0t3gSYGsWBTzr9T4/PZTCfk=
|
||||
github.com/google/go-github/v93 v93.0.0/go.mod h1:l8I3rt4/mdjANKpg4Y84LEy9HJQCetUkZ8WTl2bPU8M=
|
||||
github.com/google/go-github/v92 v92.0.0 h1:4vW4RVffwIvoEfIA4RX09mRSB47qQX557/2+HgeWRRg=
|
||||
github.com/google/go-github/v92 v92.0.0/go.mod h1:w3CH62ZcmRfvW1cdXpyTztSOVMtdjxtKpgo0GouLmjY=
|
||||
github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0=
|
||||
github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
@@ -285,10 +339,12 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||
github.com/google/licenseclassifier/v2 v2.0.0 h1:1Y57HHILNf4m0ABuMVb6xk4vAJYEUO0gDxNpog0pyeA=
|
||||
github.com/google/licenseclassifier/v2 v2.0.0/go.mod h1:cOjbdH0kyC9R22sdQbYsFkto4NGCAc+ZSwbeThazEtM=
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/pprof v0.0.0-20261026192309-dbd0ed30aa88 h1:BnzMe/RUfFezpFwXsOWHmSMNO8qt7755OL25C+AezGQ=
|
||||
github.com/google/pprof v0.0.0-20261026192309-dbd0ed30aa88/go.mod h1:yUo1AMMpsPIDFGuwDK8nLcf8+vvAd5l2O6JDZ6hpwrM=
|
||||
github.com/google/pprof v0.0.0-20260906184651-6331bc6350fe h1:QAinXoAFJdGQYztXn3VpFey7KCwpedbZ/EkzbplQ0cY=
|
||||
github.com/google/pprof v0.0.0-20260906184651-6331bc6350fe/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||
@@ -320,11 +376,13 @@ github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyf
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.6.3 h1:7997PcI0mYC9uPNBJ7udg/KrdJh1GhJid8f2tzKFHYQ=
|
||||
github.com/huandu/xstrings v1.6.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.6.1 h1:yYdKSd6Sjv3fNZt9BDjl1FDsPNfQEaYi19L5LzK2JKs=
|
||||
github.com/huandu/xstrings v1.6.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
||||
github.com/inbucket/html2text v1.0.0 h1:N5kza++4uBBDJ2Z3KUnTRyPNoBcW+YfOgNiNmNB+sgs=
|
||||
github.com/inbucket/html2text v1.0.0/go.mod h1:5TrhXQKGU+LXurODaSm55Y9eXoPBRnYiOz4x2XfUoJU=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||
@@ -344,11 +402,16 @@ github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7X
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
|
||||
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.20.3 h1:zWNoFqq/lSuYQRim3dUXlXeA0IqwuZGicdG5ZTfY5j4=
|
||||
github.com/klauspost/compress v1.20.3/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||
@@ -369,8 +432,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/letsencrypt/challtestsrv v1.4.2 h1:0ON3ldMhZyWlfVNYYpFuWRTmZNnyfiL9Hh5YzC3JVwU=
|
||||
github.com/letsencrypt/challtestsrv v1.4.2/go.mod h1:GhqMqcSoeGpYd5zX5TgwA6er/1MbWzx/o7yuuVya+Wk=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.1 h1:oKHx3lgN4e5Nno2LKTMrVx+b+NkDptkO9aDireiBDGE=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.1/go.mod h1:KtYhQ4YTjT5MtoCZ6RTCXlbrrz6cKyXROCuTpIUDJFY=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.0 h1:Wq6gYXlsY6ubqI3hhxsTzdyotvfdjFBxuwYqCLCnj/U=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.0/go.mod h1:Sk8cmUIPcIdv2nINo+9PB4L+ZBhzY+F9A1a/h/xmWiQ=
|
||||
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
||||
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U=
|
||||
@@ -390,24 +453,24 @@ github.com/mattn/go-sqlite3 v1.14.52 h1:wVbm2Qnf4OXkqhBTSPuCRZDRnxfbVrrmiCEroVdo
|
||||
github.com/mattn/go-sqlite3 v1.14.52/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
|
||||
github.com/meilisearch/meilisearch-go v0.36.3 h1:Yx1aTY5jDgtbStPVkhJTDoLnZTy5sejQSPyjfNMy6e4=
|
||||
github.com/meilisearch/meilisearch-go v0.36.3/go.mod h1:hWcR0MuWLSzHfbz9GGzIr3s9rnXLm1jqkmHkJPbUSvM=
|
||||
github.com/mholt/acmez/v3 v3.1.7 h1:XTqpuUcRdRoIBuDoI+M4mCnRnPTPN4rqH3Mb723Rg4U=
|
||||
github.com/mholt/acmez/v3 v3.1.7/go.mod h1:Lwv6P/czh/AOq+c99tAzP68/VP92s+8y+hDs4FCgxvo=
|
||||
github.com/mholt/acmez/v3 v3.1.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk=
|
||||
github.com/mholt/acmez/v3 v3.1.6/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY=
|
||||
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
|
||||
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/microsoft/go-mssqldb v1.11.2 h1:FCgeBIK8um2+X4tbun6Q71N1KsfyCDPKY41e1yGVjSE=
|
||||
github.com/microsoft/go-mssqldb v1.11.2/go.mod h1:CYgwG5AMXFojbjTg+GNP5G/y6uz1BhTyZaPqQWzkGnQ=
|
||||
github.com/miekg/dns v1.1.73 h1:uhT8nJxmTrPJYClxVxTCX+CVn6qnzSiybRk72Z6DgrE=
|
||||
github.com/miekg/dns v1.1.73/go.mod h1:RW2Obtfd5NZHvOFe3zYG0W8koWOQtAzyHaLo8vASBuQ=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
|
||||
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
|
||||
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
|
||||
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.4.1 h1:PtU/zIOfQRWZ+QydXAZysc+xZYHdG+BBR4Z6TUvy73w=
|
||||
github.com/minio/minio-go/v7 v7.4.1/go.mod h1:nQnQi7zTfXR4lREtYXZQWqo5aw0Rqv+ZqCGrTORygRk=
|
||||
github.com/minio/minio-go/v7 v7.3.0 h1:HM4pFCSQq/TK+j0/zmorSh5ddh81iDgRgU0BG0Vz/YU=
|
||||
github.com/minio/minio-go/v7 v7.3.0/go.mod h1:KUPWdecEO1LWyUz+sTGXAuf2jZHrPh5fCsRH86QbPfk=
|
||||
github.com/minio/minlz v1.1.1 h1:OGmft1V6AnI/Wme332U6bhG54nxEan+VFgkD7lat4KM=
|
||||
github.com/minio/minlz v1.1.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -423,8 +486,8 @@ github.com/msteinert/pam/v2 v2.1.0 h1:er5F9TKV5nGFuTt12ubtqPHEUdeBwReP7vd3wovidG
|
||||
github.com/msteinert/pam/v2 v2.1.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/ncruces/go-strftime v1.1.0 h1:vtPmPWMkb539RGV9WpWT9EwJGvrOCKvMNI4obWL47Hk=
|
||||
github.com/ncruces/go-strftime v1.1.0/go.mod h1:yVaq7iwCN09vHRrKBrrflXT0M9tTMG2KmZ9Fj7xeG/g=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0=
|
||||
github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48=
|
||||
github.com/nwaples/rardecode/v2 v2.2.3 h1:qaVuy3ChZDbAQZshPLjHeNJKF3Cru8uo9jmgveKIy2A=
|
||||
@@ -463,6 +526,8 @@ github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk=
|
||||
github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
|
||||
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -472,40 +537,52 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
||||
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v1.25.0 h1:cDtW1TcFsMi1RJc8khwCKB4VhzwV04aKqbYtNOXuiK0=
|
||||
github.com/prometheus/client_golang v1.25.0/go.mod h1:j2X/4/kaQHLPnmyEU8zjmkndo1T317VpCIQEml8LTKk=
|
||||
github.com/prometheus/client_model v0.6.3 h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo=
|
||||
github.com/prometheus/client_model v0.6.3/go.mod h1:gpN5P9S7Rr6Yr92PiQ+Ixvhf6JZEkF1dnxsYL2aPBEM=
|
||||
github.com/prometheus/common v0.72.0 h1:tAYsE+sPJxIncDAobm4H5aQjmox9ZxEIIqPbiffa8G4=
|
||||
github.com/prometheus/common v0.72.0/go.mod h1:77NWqAQ2tXT7BIK40qjJdw5Acrsrg1TlHAnsQi3i6mk=
|
||||
github.com/prometheus/procfs v0.22.0 h1:6q9+/JL9IKAPbCmBrv9n5O5Ty3NKnciV5X7YGw0oics=
|
||||
github.com/prometheus/procfs v0.22.0/go.mod h1:CvmFr/GVhIjIvWJZW3tgkODBQMRIf0EyWMQLHCHab58=
|
||||
github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU=
|
||||
github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY=
|
||||
github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc=
|
||||
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
|
||||
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
|
||||
github.com/quasoft/websspi v1.1.2 h1:/mA4w0LxWlE3novvsoEL6BBA1WnjJATbjkh1kFrTidw=
|
||||
github.com/quasoft/websspi v1.1.2/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
|
||||
github.com/redis/go-redis/v9 v9.23.0 h1:/TTVdJa8BRNsybwwGGnmiYktHNlUXtbOQxlWpHBPL3s=
|
||||
github.com/redis/go-redis/v9 v9.23.0/go.mod h1:EWP2UUk+XDaI1s5nQB5mablZN6vccXlmBQTyfnF87D0=
|
||||
github.com/redis/rueidis v1.0.79 h1:2faCYvkq0G8ozDIAn7+3d5k5bBYCc0DBG2THK5WYXF0=
|
||||
github.com/redis/rueidis v1.0.79/go.mod h1:L8mnCQJJaSNL6I4pIR6Rz732HTGS9vmuXm0yT9dRvjo=
|
||||
github.com/redis/rueidis/rueidiscompat v1.0.79 h1:j6kLhuyLMetbakGuu+5sjuZ3WE01h1LVjoFrc2kmELQ=
|
||||
github.com/redis/rueidis/rueidiscompat v1.0.79/go.mod h1:SHSig7K+0XeGOeeeDMmEoJVkd94IBKvBIUbKkRZaxfc=
|
||||
github.com/redis/go-redis/v9 v9.22.0 h1:laDvpYXTJtZLloinw1fA5Kqd6HAEH2XKxOkG/PDq2F0=
|
||||
github.com/redis/go-redis/v9 v9.22.0/go.mod h1:y2g0Wj8rQvuK0ELM+oxSudcLtC09JScs98I/X9gRWY4=
|
||||
github.com/redis/rueidis v1.0.77 h1:ZR41bgJcm7oRFb3aSDPrRhC0eonDSrPzjvvZvHIlNjM=
|
||||
github.com/redis/rueidis v1.0.77/go.mod h1:L8mnCQJJaSNL6I4pIR6Rz732HTGS9vmuXm0yT9dRvjo=
|
||||
github.com/redis/rueidis/rueidiscompat v1.0.77 h1:S1xiLQsFv8XQ46e0LelC8ihGqbDzVRocXXZLzVAeh64=
|
||||
github.com/redis/rueidis/rueidiscompat v1.0.77/go.mod h1:aXCvRVUTvTHZY0owHuz9JjveO1hyuzyDWrpJh8/E3Ds=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g=
|
||||
github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
|
||||
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
|
||||
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo=
|
||||
github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q=
|
||||
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
|
||||
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
@@ -524,10 +601,12 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||
@@ -540,19 +619,31 @@ github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77ro
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.17 h1:flR0y/x1hgM8EGV1AW3Xll6T413G0glV8UfBwR617V4=
|
||||
github.com/ulikunitz/xz v0.5.17/go.mod h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw=
|
||||
github.com/urfave/cli-docs/v3 v3.2.0 h1:1J2pmtDuMRConzYb/QHyPbeFB6HvMtYm3xre9uhs81c=
|
||||
github.com/urfave/cli-docs/v3 v3.2.0/go.mod h1:cjSVza4yCaqQet06zO6QhYqXQYjGRqbUj8zok6mHDRU=
|
||||
github.com/urfave/cli/v3 v3.14.0 h1:a8414NQlHJs0c/iBsulKLzlES0n/lEAskbL2LKpU4/s=
|
||||
github.com/urfave/cli/v3 v3.14.0/go.mod h1:vXn6HxPNccJSzQr2QvwVncOKrgYGIHU0HY5h8B2nQj4=
|
||||
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
github.com/urfave/cli-docs/v3 v3.1.0 h1:Sa5xm19IpE5gpm6tZzXdfjdFxn67PnEsE4dpXF7vsKw=
|
||||
github.com/urfave/cli-docs/v3 v3.1.0/go.mod h1:59d+5Hz1h6GSGJ10cvcEkbIe3j233t4XDqI72UIx7to=
|
||||
github.com/urfave/cli/v3 v3.13.0 h1:Dr6jqMfIyyFsRVn7Nz5mqLsMY+ZMpfh3a0aMs+umPVY=
|
||||
github.com/urfave/cli/v3 v3.13.0/go.mod h1:vXn6HxPNccJSzQr2QvwVncOKrgYGIHU0HY5h8B2nQj4=
|
||||
github.com/wneessen/go-mail v0.8.1 h1:tVcncj02/QySVFw3zr/kXOzZcuFQqBNT6K+Rbgm/pcM=
|
||||
github.com/wneessen/go-mail v0.8.1/go.mod h1:dWZ61zadzCIyvB4y1/YzC5O7MrbbzBfPkARmbosdf8w=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js=
|
||||
github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.8.7 h1:XT/tNqCIbabh0ln675w4x3dv04kQBKi8NwYqi9IJaxI=
|
||||
github.com/yuin/goldmark v1.8.7/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.8.6 h1:d0VcaP1sx9GkFVkoW+KtggpGi2KZ965i14b0+bDQST4=
|
||||
github.com/yuin/goldmark v1.8.6/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
@@ -561,12 +652,12 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.43.0 h1:X7qHPPt6If/5f0bgBpeT8GYWx2zDnuUzRsfnzJa9/cc=
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.43.0/go.mod h1:8cuzgeUJzkVvaRvw57iZ8foGmL5q8GZXi4lHTiF5edY=
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.13.0 h1:CTUXvcL6OrGjYNafbws5C1lJwlXrxGJHWQGMF+hCdz0=
|
||||
gitlab.com/gitlab-org/api/client-go/v3 v3.13.0/go.mod h1:k7uYxRoIeuSqyWZSJfAPZlf0L32OH0lUBImAl4gWUFc=
|
||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||
go.uber.org/atomic v1.12.0 h1:BvcXdFKuviU4fTL/f+SxdQ5qJX/Jix8pAkgdUcb3XOE=
|
||||
go.uber.org/atomic v1.12.0/go.mod h1:I6c4cg+6HCxRjfjSsYtApoFILnpc0CGUdGkXVqbYVNk=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
@@ -579,80 +670,136 @@ go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
go.yaml.in/yaml/v3 v3.0.6 h1:zcDfvjXEqTQmpjniUBLc91xYJMB2c4QOFxEF/LCEckM=
|
||||
go.yaml.in/yaml/v3 v3.0.6/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||
go.yaml.in/yaml/v4 v4.0.0 h1:/LG1pNO84Z4l603j/xMn69CcS5OYhMQ/f7l2LoQUGuw=
|
||||
go.yaml.in/yaml/v4 v4.0.0/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6 h1:1h7H1ohdUh93/FyE4YaDa1Zh64K6VVbjF4K6WUxMtH4=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw=
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54=
|
||||
golang.org/x/crypto v0.58.0/go.mod h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
|
||||
golang.org/x/image v0.47.0 h1:za/8gVz++PPZqmZJBNSISICN5Rrj5zixP/8K++fgmqo=
|
||||
golang.org/x/image v0.47.0/go.mod h1:IkN5o4A7vq55vXN8FWjjnhg9Rs2GOtffqsSi9mWAKYE=
|
||||
golang.org/x/image v0.46.0 h1:b1+oYj0Jbp6K5MDT4i4/eZpYlk3V8SJhhDKh6LBHAyQ=
|
||||
golang.org/x/image v0.46.0/go.mod h1:3B3W05VGVQyuXucLINLjXKrqISASfi4Xj+iCVkLMwew=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg=
|
||||
golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
|
||||
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.62.0 h1:18ELalfM5+XNRwzM2nYqXsZQ9ByS1uhj2O/U7+5jqRY=
|
||||
golang.org/x/net v0.62.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues=
|
||||
golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
|
||||
golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98=
|
||||
golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
||||
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
|
||||
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM=
|
||||
golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE=
|
||||
golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
|
||||
golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
|
||||
golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
|
||||
golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.52.0 h1:ryxg4U63oaUYk7uOLTF37n43f4qgtGvqV4Ioit1BaEs=
|
||||
golang.org/x/tools v0.52.0/go.mod h1:ItcP5UNaVGG+Yc4eIYmaFLNJ4soEopkx5Y2d4lcEurs=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
|
||||
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 h1:utmqiUzZAEINbLmRyAt1QjVkMyLQ+lvIwCpLaB1rZfw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.85.0 h1:BPBCfuVRQ1qvBhKDOq9uvQllMvO59rKsYhXpp7Dd8vw=
|
||||
google.golang.org/grpc v1.85.0/go.mod h1:0rQrOD58yjal/0EaVbGBANindixsCZNQPDV3VKtaKuQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 h1:qEHAMpSaUhtD0p3NbEEI83HwNGFxEwaSJ1G9PLnCBZE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0=
|
||||
google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -670,6 +817,8 @@ gopkg.in/ini.v1 v1.67.3 h1:iM9Lhz5MRSGhHVGGwCuzG9KO8PoirCXj/m/qTmOJJQw=
|
||||
gopkg.in/ini.v1 v1.67.3/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -679,20 +828,20 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/cc/v4 v4.30.3 h1:aDq8gIX1X7+VFgO/JYYji6pLWL2H4tv3g8WatT3KCys=
|
||||
modernc.org/cc/v4 v4.30.3/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.37.0 h1:BAuJCUtIHIxFIYaFhqXIw5YaoMBlNCUlFovejHfMip4=
|
||||
modernc.org/ccgo/v4 v4.37.0/go.mod h1:/8627VhwWtAbqUXmPORxFQU5SgLrAx2jl9seLw9GBhY=
|
||||
modernc.org/cc/v4 v4.29.2 h1:h6+9ciCnPKutf4I03CvheAvDLX7+IHlqR6Iy6J+cgd8=
|
||||
modernc.org/cc/v4 v4.29.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.35.0 h1:F+TUsmw09QxLzmi3aeYYGxjAXarmZaKgj3mKQHNaA8w=
|
||||
modernc.org/ccgo/v4 v4.35.0/go.mod h1:qrVGs9S3Sr2Ztcg9ve+kTAYMp5a3YvWjo+SoN06kJ5I=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.2.0 h1:/Y3LGDMQaoYf55PebP1qsZ5FDGgAZyG1RYKEHeQzh1o=
|
||||
modernc.org/gc/v3 v3.2.0/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/gc/v3 v3.1.5 h1:21ldfPfRYE31Tb7B3mwAK8gy1AxP4+dKjrOQPfqakoc=
|
||||
modernc.org/gc/v3 v3.1.5/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.79.0 h1:9Dvvv++bAMSkSuBzuOFAatbTV5OIoq6Y6FohmD3e7q8=
|
||||
modernc.org/libc v1.79.0/go.mod h1:uT3kR61NsbeKJSLqq83GJwSnZMVLlSjyAz4DfTaBy1M=
|
||||
modernc.org/libc v1.75.7 h1:o3DTP9/0p9pKmY2WCKQaySW6wIiZhNM7wc2lUoyhfew=
|
||||
modernc.org/libc v1.75.7/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
|
||||
@@ -701,8 +850,8 @@ modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.61.1 h1:TBEE49wz7VlxS+qXRXMQiPUZYOXHJ4ku83daYv1fe04=
|
||||
modernc.org/sqlite v1.61.1/go.mod h1:f4gr5tbpx2umklaT/QVtbi/50hNdM+4ng+2zMo8KXJ0=
|
||||
modernc.org/sqlite v1.59.0 h1:X1es1GpqBlS/5T+vbM4HLUdaa8OtQx468DF2vrx+38A=
|
||||
modernc.org/sqlite v1.59.0/go.mod h1:+paeT2A3iPRHkQDwG7oA6Tk0zQd5woMEI8q7orfry8k=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -518,20 +518,3 @@ func ModifyColumn(ctx context.Context, x EngineMigration, tableName string, col
|
||||
func Iterate[Bean any](ctx context.Context, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error {
|
||||
return db.Iterate(ctx, cond, f)
|
||||
}
|
||||
|
||||
func EngineTransaction(x EngineMigration, f func(Session) error) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
err := f(sess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func IterateTableByColumn[Bean any](ctx context.Context, x EngineMigration, sess Session, colName string, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error {
|
||||
return db.IterateTableByColumn(ctx, x, sess, colName, cond, f)
|
||||
}
|
||||
|
||||
@@ -45,15 +45,38 @@ func Test_DropTableColumns(t *testing.T) {
|
||||
x.SetMapper(names.GonicMapper{})
|
||||
|
||||
for i := range columns {
|
||||
for j := i; j < len(columns); j++ {
|
||||
if err := x.Sync(new(DropTest)); err != nil {
|
||||
t.Errorf("unable to create DropTest table: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
if err := sess.Begin(); err != nil {
|
||||
sess.Close()
|
||||
t.Errorf("unable to begin transaction: %v", err)
|
||||
return
|
||||
}
|
||||
if err := base.DropTableColumns(sess, "drop_test", columns[i:]...); err != nil {
|
||||
sess.Close()
|
||||
t.Errorf("Unable to drop columns[%d:]: %s from drop_test: %v", i, columns[i:], err)
|
||||
return
|
||||
}
|
||||
if err := sess.Commit(); err != nil {
|
||||
sess.Close()
|
||||
t.Errorf("unable to commit transaction: %v", err)
|
||||
return
|
||||
}
|
||||
sess.Close()
|
||||
if err := x.DropTables(new(DropTest)); err != nil {
|
||||
t.Errorf("unable to drop table: %v", err)
|
||||
return
|
||||
}
|
||||
for j := range columns[i+1:] {
|
||||
if err := x.Sync(new(DropTest)); err != nil {
|
||||
t.Errorf("unable to create DropTest table: %v", err)
|
||||
return
|
||||
}
|
||||
dropcols := columns[i:]
|
||||
if j > i {
|
||||
dropcols = append([]string{columns[i]}, columns[j:]...)
|
||||
}
|
||||
dropcols := append([]string{columns[i]}, columns[j+i+1:]...)
|
||||
sess := x.NewSession()
|
||||
if err := sess.Begin(); err != nil {
|
||||
sess.Close()
|
||||
@@ -62,11 +85,7 @@ func Test_DropTableColumns(t *testing.T) {
|
||||
}
|
||||
if err := base.DropTableColumns(sess, "drop_test", dropcols...); err != nil {
|
||||
sess.Close()
|
||||
if j == i {
|
||||
t.Errorf("Unable to drop columns[%d:]: %s from drop_test: %v", i, columns[i:], err)
|
||||
} else {
|
||||
t.Errorf("Unable to drop columns: %s from drop_test: %v", dropcols, err)
|
||||
}
|
||||
t.Errorf("Unable to drop columns: %s from drop_test: %v", dropcols, err)
|
||||
return
|
||||
}
|
||||
if err := sess.Commit(); err != nil {
|
||||
|
||||
@@ -431,20 +431,9 @@ func prepareMigrationTasks() []*migration {
|
||||
newMigration(354, "Add Actions job queue indexes", v28.AddActionQueueIndexes),
|
||||
newMigration(355, "Add AutoMerge merged_commit_id column", v28.AddAutoMergeMergedCommitID),
|
||||
// Gitea 28.0.0 ends at migration ID number 355 (database version 356)
|
||||
|
||||
// HERE: 2 migrations from 29 are added since they don't change database structure
|
||||
newMigration(356, "Add index on action_run commit_sha", v29.AddActionRunCommitSHAIndex),
|
||||
newMigration(357, "Normalize legacy team authorize values", v29.NormalizeLegacyTeamAuthorize),
|
||||
newMigration(358, "Track previous remember-me token hashes", v29.AddPreviousAuthTokenHash),
|
||||
newMigration(359, "Add stage and number columns to action_task_step", v29.AddStageAndNumberToActionTaskStep),
|
||||
newMigration(360, "Move branch protection settings to JSON", v29.MoveBranchProtectionConfigToJSON),
|
||||
newMigration(361, "Drop legacy commit_id column from comment", v29.DropCommentCommitID),
|
||||
newMigration(362, "Add reusable_workflow_ref column to action_run_job", v29.AddReusableWorkflowRefToActionRunJob),
|
||||
newMigration(363, "Add release_id to reaction table", v29.AddReleaseIDToReaction),
|
||||
newMigration(364, "Add new table project_workflow", v29.AddProjectWorkflow),
|
||||
newMigration(365, "Normalize legacy repository unit and login source JSON", v29.NormalizeLegacyConfigJSON),
|
||||
newMigration(366, "Drop user_open_id table", v29.DropUserOpenIDTable),
|
||||
newMigration(367, "Add latest_release_id to repository table", v29.AddLatestReleaseIDToRepository),
|
||||
newMigration(368, "Add unique index for team name", v29.AddUniqueTeamNameIndex),
|
||||
}
|
||||
return preparedMigrations
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ func LoadTableSchemasMap(t *testing.T, x base.EngineMigration) map[string]*schem
|
||||
|
||||
func mainTest(m *testing.M) int {
|
||||
testlogger.Init()
|
||||
workPath, err := setting.PrepareIntegrationTestConfig()
|
||||
err := setting.PrepareIntegrationTestConfig()
|
||||
if err != nil {
|
||||
return testlogger.MainErrorf("Unable to prepare integration test config: %v", err)
|
||||
}
|
||||
@@ -117,11 +117,7 @@ func mainTest(m *testing.M) int {
|
||||
setting.Database.SlowQueryThreshold = 0
|
||||
setting.LoadDBSetting()
|
||||
setting.InitLoggersForTest()
|
||||
exitCode := m.Run()
|
||||
if exitCode == 0 {
|
||||
_ = os.RemoveAll(workPath)
|
||||
}
|
||||
return exitCode
|
||||
return m.Run()
|
||||
}
|
||||
|
||||
func MainTest(m *testing.M) {
|
||||
|
||||
@@ -78,17 +78,12 @@ func Test_ConvertScopedAccessTokens(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(0), count)
|
||||
|
||||
session := x.NewSession()
|
||||
defer session.Close()
|
||||
assert.NoError(t, session.Begin())
|
||||
|
||||
for _, tc := range tests {
|
||||
_, err = session.Insert(&AccessToken{
|
||||
_, err = x.Insert(&AccessToken{
|
||||
Scope: string(tc.Old),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.NoError(t, session.Commit())
|
||||
|
||||
// migrate the scopes
|
||||
err = ConvertScopedAccessTokens(t.Context(), x)
|
||||
|
||||
@@ -43,11 +43,13 @@ func Test_AddUniqueIndexForUserBadge(t *testing.T) {
|
||||
}
|
||||
|
||||
// check that we have duplicates
|
||||
for _, userID := range []int{1, 3} {
|
||||
count, err := x.Where("user_id = ? AND badge_id = ?", userID, userID).Count(&UserBadgeBefore{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(2), count)
|
||||
}
|
||||
count, err := x.Where("user_id = ? AND badge_id = ?", 1, 1).Count(&UserBadgeBefore{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(2), count)
|
||||
|
||||
count, err = x.Where("user_id = ? AND badge_id = ?", 3, 3).Count(&UserBadgeBefore{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(2), count)
|
||||
|
||||
totalCount, err := x.Count(&UserBadgeBefore{})
|
||||
assert.NoError(t, err)
|
||||
@@ -60,11 +62,13 @@ func Test_AddUniqueIndexForUserBadge(t *testing.T) {
|
||||
}
|
||||
|
||||
// verify the duplicates were removed
|
||||
for _, userID := range []int{1, 3} {
|
||||
count, err := x.Where("user_id = ? AND badge_id = ?", userID, userID).Count(&UserBadgeBefore{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), count)
|
||||
}
|
||||
count, err = x.Where("user_id = ? AND badge_id = ?", 1, 1).Count(&UserBadgeBefore{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
count, err = x.Where("user_id = ? AND badge_id = ?", 3, 3).Count(&UserBadgeBefore{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
// check total count
|
||||
totalCount, err = x.Count(&UserBadgeBefore{})
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
// NormalizeLegacyTeamAuthorize sets leftover read/write authorize values to none.
|
||||
// https://github.com/go-gitea/gitea/pull/34128 made non-admin teams use team_unit (authorize=none).
|
||||
// Any positive authorize now means blanket access on every unit; migrating legacy read/write
|
||||
// authorize>=write now means blanket access on every unit; migrating legacy read/write
|
||||
// to none preserves their existing team_unit-scoped access.
|
||||
func NormalizeLegacyTeamAuthorize(_ context.Context, x base.EngineMigration) error {
|
||||
// AccessModeNone=0, AccessModeRead=1, AccessModeWrite=2, AccessModeAdmin=3
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
"gitea.dev/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddPreviousAuthTokenHash(_ context.Context, x base.EngineMigration) error {
|
||||
type AuthToken struct {
|
||||
PreviousTokenHash string
|
||||
RotatedUnix timeutil.TimeStamp
|
||||
}
|
||||
_, err := x.SyncWithOptions(xorm.SyncOptions{
|
||||
IgnoreDropIndices: true,
|
||||
IgnoreConstrains: true,
|
||||
}, new(AuthToken))
|
||||
return err
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddStageAndNumberToActionTaskStep(_ context.Context, x base.EngineMigration) error {
|
||||
type ActionTaskStep struct {
|
||||
Stage int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
Number int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
_, err := x.SyncWithOptions(xorm.SyncOptions{
|
||||
IgnoreConstrains: true,
|
||||
IgnoreDropIndices: true,
|
||||
}, new(ActionTaskStep))
|
||||
return err
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type protectedBranchConfigV358 struct {
|
||||
CanPush bool `xorm:"NOT NULL DEFAULT false" json:"can_push,omitzero"`
|
||||
EnableWhitelist bool `json:"enable_whitelist,omitzero"`
|
||||
WhitelistUserIDs []int64 `xorm:"JSON TEXT" json:"whitelist_user_ids,omitzero"`
|
||||
WhitelistTeamIDs []int64 `xorm:"JSON TEXT" json:"whitelist_team_ids,omitzero"`
|
||||
EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false" json:"enable_merge_whitelist,omitzero"`
|
||||
WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false" json:"-"`
|
||||
MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT" json:"merge_whitelist_user_ids,omitzero"`
|
||||
MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT" json:"merge_whitelist_team_ids,omitzero"`
|
||||
EnableBypassAllowlist bool `xorm:"NOT NULL DEFAULT false" json:"enable_bypass_allowlist,omitzero"`
|
||||
BypassAllowlistUserIDs []int64 `xorm:"JSON TEXT" json:"bypass_allowlist_user_ids,omitzero"`
|
||||
BypassAllowlistTeamIDs []int64 `xorm:"JSON TEXT" json:"bypass_allowlist_team_ids,omitzero"`
|
||||
CanForcePush bool `xorm:"NOT NULL DEFAULT false" json:"can_force_push,omitzero"`
|
||||
EnableForcePushAllowlist bool `xorm:"NOT NULL DEFAULT false" json:"enable_force_push_allowlist,omitzero"`
|
||||
ForcePushAllowlistUserIDs []int64 `xorm:"JSON TEXT" json:"force_push_allowlist_user_ids,omitzero"`
|
||||
ForcePushAllowlistTeamIDs []int64 `xorm:"JSON TEXT" json:"force_push_allowlist_team_ids,omitzero"`
|
||||
ForcePushAllowlistDeployKeys bool `xorm:"NOT NULL DEFAULT false" json:"-"`
|
||||
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false" json:"enable_status_check,omitzero"`
|
||||
StatusCheckContexts []string `xorm:"JSON TEXT" json:"status_check_contexts,omitzero"`
|
||||
EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false" json:"enable_approvals_whitelist,omitzero"`
|
||||
ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT" json:"approvals_whitelist_user_ids,omitzero"`
|
||||
ApprovalsWhitelistTeamIDs []int64 `xorm:"JSON TEXT" json:"approvals_whitelist_team_ids,omitzero"`
|
||||
RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0" json:"required_approvals,omitzero"`
|
||||
BlockOnRejectedReviews bool `xorm:"NOT NULL DEFAULT false" json:"block_on_rejected_reviews,omitzero"`
|
||||
BlockOnOfficialReviewRequests bool `xorm:"NOT NULL DEFAULT false" json:"block_on_official_review_requests,omitzero"`
|
||||
BlockOnCodeownerReviews bool `xorm:"NOT NULL DEFAULT false" json:"block_on_codeowner_reviews,omitzero"`
|
||||
BlockOnOutdatedBranch bool `xorm:"NOT NULL DEFAULT false" json:"block_on_outdated_branch,omitzero"`
|
||||
DismissStaleApprovals bool `xorm:"NOT NULL DEFAULT false" json:"dismiss_stale_approvals,omitzero"`
|
||||
IgnoreStaleApprovals bool `xorm:"NOT NULL DEFAULT false" json:"ignore_stale_approvals,omitzero"`
|
||||
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false" json:"require_signed_commits,omitzero"`
|
||||
ProtectedFilePatterns string `xorm:"TEXT" json:"protected_file_patterns,omitzero"`
|
||||
UnprotectedFilePatterns string `xorm:"TEXT" json:"unprotected_file_patterns,omitzero"`
|
||||
BlockAdminMergeOverride bool `xorm:"NOT NULL DEFAULT false" json:"block_admin_merge_override,omitzero"`
|
||||
}
|
||||
|
||||
type protectedBranchV358 struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||
BranchName string `xorm:"UNIQUE(s)"`
|
||||
Priority int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
Config string `xorm:"LONGTEXT"`
|
||||
CreatedUnix timeutil.TimeStamp
|
||||
UpdatedUnix timeutil.TimeStamp
|
||||
}
|
||||
|
||||
func (protectedBranchV358) TableName() string { return "protected_branch" }
|
||||
|
||||
type intermediateProtectedBranch struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
NewConfig string `xorm:"LONGTEXT config"`
|
||||
OldConfig protectedBranchConfigV358 `xorm:"extends"`
|
||||
}
|
||||
|
||||
func (intermediateProtectedBranch) TableName() string { return "protected_branch" }
|
||||
|
||||
func MoveBranchProtectionConfigToJSON(ctx context.Context, x base.EngineMigration) error {
|
||||
if _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(protectedBranchV358)); err != nil {
|
||||
return err
|
||||
}
|
||||
exists, err := x.Dialect().IsColumnExist(x.DB(), ctx, "protected_branch", "can_push")
|
||||
if err != nil || !exists {
|
||||
return err
|
||||
}
|
||||
|
||||
err = base.EngineTransaction(x, func(sess base.Session) error {
|
||||
err := base.IterateTableByColumn(ctx, x, sess, "id", nil, func(ctx context.Context, pb *intermediateProtectedBranch) error {
|
||||
if pb.NewConfig != "" {
|
||||
return nil // skip the already migrated fields
|
||||
}
|
||||
const deployKeyUserID int64 = -3 // frozen user.DeployKeyUserID
|
||||
if pb.OldConfig.WhitelistDeployKeys {
|
||||
pb.OldConfig.WhitelistUserIDs = append(pb.OldConfig.WhitelistUserIDs, deployKeyUserID)
|
||||
}
|
||||
if pb.OldConfig.ForcePushAllowlistDeployKeys {
|
||||
pb.OldConfig.ForcePushAllowlistUserIDs = append(pb.OldConfig.ForcePushAllowlistUserIDs, deployKeyUserID)
|
||||
}
|
||||
configJson, err := json.Marshal(pb.OldConfig)
|
||||
util.MustNoError(err)
|
||||
_, err = sess.ID(pb.ID).Cols("config").Update(&protectedBranchV358{Config: string(configJson)})
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
legacyTableInfo, err := x.TableInfo(new(protectedBranchConfigV358))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return base.DropTableColumns(sess, "protected_branch", legacyTableInfo.ColumnsSeq()...)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// SQLite rebuilds the table when dropping columns, so restore its unique index.
|
||||
return x.Sync(new(protectedBranchV358))
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modelmigration/migrationtest"
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/timeutil"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type protectedBranchBeforeV358 struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||
BranchName string `xorm:"UNIQUE(s)"`
|
||||
Priority int64
|
||||
CreatedUnix timeutil.TimeStamp
|
||||
UpdatedUnix timeutil.TimeStamp
|
||||
Config protectedBranchConfigV358 `xorm:"extends"`
|
||||
}
|
||||
|
||||
func (protectedBranchBeforeV358) TableName() string { return "protected_branch" }
|
||||
|
||||
func TestMoveBranchProtectionConfigToJSON(t *testing.T) {
|
||||
x, deferrable := migrationtest.PrepareTestEnv(t, 0, new(protectedBranchBeforeV358))
|
||||
defer deferrable()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
}
|
||||
|
||||
const configJSON = `{
|
||||
"can_push":true,
|
||||
"enable_whitelist":true,
|
||||
"whitelist_user_ids":[2,4,-3],
|
||||
"whitelist_team_ids":[1,2],
|
||||
"enable_merge_whitelist":true,
|
||||
"merge_whitelist_user_ids":[3,5],
|
||||
"merge_whitelist_team_ids":[3,4],
|
||||
"enable_bypass_allowlist":true,
|
||||
"bypass_allowlist_user_ids":[6,7],
|
||||
"bypass_allowlist_team_ids":[5,6],
|
||||
"can_force_push":true,
|
||||
"enable_force_push_allowlist":true,
|
||||
"force_push_allowlist_user_ids":[8,9,-3],
|
||||
"force_push_allowlist_team_ids":[7,8],
|
||||
"enable_status_check":true,
|
||||
"status_check_contexts":["ci/*","lint"],
|
||||
"enable_approvals_whitelist":true,
|
||||
"approvals_whitelist_user_ids":[10,11],
|
||||
"approvals_whitelist_team_ids":[9,10],
|
||||
"required_approvals":2,
|
||||
"block_on_rejected_reviews":true,
|
||||
"block_on_official_review_requests":true,
|
||||
"block_on_codeowner_reviews":true,
|
||||
"block_on_outdated_branch":true,
|
||||
"dismiss_stale_approvals":true,
|
||||
"ignore_stale_approvals":true,
|
||||
"require_signed_commits":true,
|
||||
"protected_file_patterns":"*.go;*.md",
|
||||
"unprotected_file_patterns":"docs/**",
|
||||
"block_admin_merge_override":true
|
||||
}`
|
||||
var config protectedBranchConfigV358
|
||||
require.NoError(t, json.Unmarshal([]byte(configJSON), &config))
|
||||
config.WhitelistUserIDs = []int64{2, 4}
|
||||
config.ForcePushAllowlistUserIDs = []int64{8, 9}
|
||||
config.WhitelistDeployKeys = true
|
||||
config.ForcePushAllowlistDeployKeys = true
|
||||
for i := range 101 {
|
||||
branch := protectedBranchBeforeV358{RepoID: 1, BranchName: fmt.Sprintf("release-%d/*", i), Priority: int64(i), CreatedUnix: 123, UpdatedUnix: 456}
|
||||
if i%2 == 0 {
|
||||
branch.Config = config
|
||||
} else if i == 1 {
|
||||
branch.Config.StatusCheckContexts = []string{}
|
||||
}
|
||||
_, err := x.Insert(&branch)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, MoveBranchProtectionConfigToJSON(t.Context(), x))
|
||||
// A retry must preserve the migrated configuration and its indexes.
|
||||
require.NoError(t, MoveBranchProtectionConfigToJSON(t.Context(), x))
|
||||
|
||||
var branches []protectedBranchV358
|
||||
require.NoError(t, x.Asc("id").Find(&branches))
|
||||
require.Len(t, branches, 101)
|
||||
for i, branch := range branches {
|
||||
assert.EqualValues(t, i+1, branch.ID)
|
||||
assert.EqualValues(t, 1, branch.RepoID)
|
||||
assert.Equal(t, fmt.Sprintf("release-%d/*", i), branch.BranchName)
|
||||
assert.EqualValues(t, i, branch.Priority)
|
||||
assert.EqualValues(t, 123, branch.CreatedUnix)
|
||||
assert.EqualValues(t, 456, branch.UpdatedUnix)
|
||||
if i%2 == 0 {
|
||||
assert.JSONEq(t, configJSON, branch.Config)
|
||||
} else if i == 1 {
|
||||
assert.JSONEq(t, `{"status_check_contexts":[]}`, branch.Config)
|
||||
} else {
|
||||
assert.JSONEq(t, "{}", branch.Config)
|
||||
}
|
||||
}
|
||||
tables, err := x.DBMetas()
|
||||
require.NoError(t, err)
|
||||
for _, table := range tables {
|
||||
if table.Name == "protected_branch" {
|
||||
assert.ElementsMatch(t, []string{"id", "repo_id", "branch_name", "priority", "config", "created_unix", "updated_unix"}, table.ColumnsSeq())
|
||||
}
|
||||
}
|
||||
_, err = x.Insert(&protectedBranchV358{RepoID: 1, BranchName: branches[0].BranchName})
|
||||
require.Error(t, err, "the repository/rule unique constraint must survive the migration")
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
)
|
||||
|
||||
func DropCommentCommitID(ctx context.Context, x base.EngineMigration) error {
|
||||
exists, err := x.Dialect().IsColumnExist(x.DB(), ctx, "comment", "commit_id")
|
||||
if err != nil || !exists {
|
||||
return err
|
||||
}
|
||||
|
||||
return base.EngineTransaction(x, func(sess base.Session) error {
|
||||
return base.DropTableColumns(sess, "comment", "commit_id")
|
||||
})
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddReusableWorkflowRefToActionRunJob(_ context.Context, x base.EngineMigration) error {
|
||||
type ActionRunJob struct {
|
||||
ReusableWorkflowRef string `xorm:"VARCHAR(512) NOT NULL DEFAULT ''"`
|
||||
}
|
||||
_, err := x.SyncWithOptions(xorm.SyncOptions{
|
||||
IgnoreConstrains: true,
|
||||
IgnoreDropIndices: true,
|
||||
}, new(ActionRunJob))
|
||||
return err
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
"gitea.dev/modules/timeutil"
|
||||
)
|
||||
|
||||
func AddReleaseIDToReaction(_ctx context.Context, x base.EngineMigration) error {
|
||||
// rows older than v123 can hold NULL, which the unique index doesn't treat as a duplicate of ''
|
||||
if _, err := x.Exec("UPDATE reaction SET original_author = '' WHERE original_author IS NULL"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type Reaction struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
||||
UserID int64 `xorm:"UNIQUE(s) NOT NULL"` // first unique column implies INDEX
|
||||
IssueID int64 `xorm:"UNIQUE(s) INDEX(issue_comment) NOT NULL"`
|
||||
CommentID int64 `xorm:"UNIQUE(s) INDEX(issue_comment) INDEX NOT NULL"`
|
||||
ReleaseID int64 `xorm:"UNIQUE(s) INDEX NOT NULL DEFAULT(0)"`
|
||||
OriginalAuthorID int64 `xorm:"UNIQUE(s) NOT NULL DEFAULT(0)"`
|
||||
OriginalAuthor string `xorm:"UNIQUE(s) NOT NULL DEFAULT('')"`
|
||||
Type string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
}
|
||||
return x.Sync(new(Reaction))
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
"gitea.dev/modules/timeutil"
|
||||
)
|
||||
|
||||
func AddProjectWorkflow(_ context.Context, x base.EngineMigration) error {
|
||||
type ProjectWorkflow struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
ProjectID int64 `xorm:"INDEX"`
|
||||
WorkflowEvent string
|
||||
WorkflowFilters string `xorm:"TEXT JSON"`
|
||||
WorkflowActions string `xorm:"TEXT JSON"`
|
||||
Enabled bool `xorm:"DEFAULT true NOT NULL"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
||||
return x.Sync(&ProjectWorkflow{})
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
// unmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
|
||||
// possible that a Blob may be double encoded or gain an unwanted prefix of 0xff 0xfe.
|
||||
func unmarshalHandleDoubleEncode(bs []byte, v any) error {
|
||||
if len(bs) == 0 {
|
||||
// json.Unmarshal will report errors if input is empty (nil or zero-length)
|
||||
// It seems that XORM ignores the nil but still passes zero-length string into this function
|
||||
// To be consistent, we should treat all empty inputs as success
|
||||
return nil
|
||||
}
|
||||
err := json.Unmarshal(bs, v)
|
||||
if err != nil {
|
||||
ok := true
|
||||
rs := []byte{}
|
||||
temp := make([]byte, 2)
|
||||
for _, rn := range string(bs) {
|
||||
if rn > 0xffff {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
binary.LittleEndian.PutUint16(temp, uint16(rn))
|
||||
rs = append(rs, temp...)
|
||||
}
|
||||
if ok {
|
||||
if len(rs) > 1 && rs[0] == 0xff && rs[1] == 0xfe {
|
||||
rs = rs[2:]
|
||||
}
|
||||
err = json.Unmarshal(rs, v)
|
||||
}
|
||||
}
|
||||
if err != nil && len(bs) > 2 && bs[0] == 0xff && bs[1] == 0xfe {
|
||||
err = json.Unmarshal(bs[2:], v)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func normalizeLegacyConfigJSON(config string) string {
|
||||
if config == "" || json.Valid(util.UnsafeStringToBytes(config)) {
|
||||
return config
|
||||
}
|
||||
|
||||
m := map[any]any{}
|
||||
err := unmarshalHandleDoubleEncode(util.UnsafeStringToBytes(config), &m)
|
||||
if err != nil {
|
||||
return config
|
||||
}
|
||||
if bs, err := json.Marshal(m); err == nil {
|
||||
return string(bs)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func NormalizeLegacyConfigJSON(ctx context.Context, x base.EngineMigration) error {
|
||||
type RepoUnit struct {
|
||||
ID int64
|
||||
Config string
|
||||
}
|
||||
type LoginSource struct {
|
||||
ID int64
|
||||
Cfg string
|
||||
}
|
||||
return base.EngineTransaction(x, func(sess base.Session) error {
|
||||
if err := base.IterateTableByColumn(ctx, x, sess, "id", nil, func(_ context.Context, unit *RepoUnit) error {
|
||||
config := normalizeLegacyConfigJSON(unit.Config)
|
||||
if config == unit.Config {
|
||||
return nil
|
||||
}
|
||||
_, err := sess.ID(unit.ID).Cols("config").Update(&RepoUnit{Config: config})
|
||||
return err
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return base.IterateTableByColumn(ctx, x, sess, "id", nil, func(_ context.Context, source *LoginSource) error {
|
||||
config := normalizeLegacyConfigJSON(source.Cfg)
|
||||
if config == source.Cfg {
|
||||
return nil
|
||||
}
|
||||
_, err := sess.ID(source.ID).Cols("cfg").Update(&LoginSource{Cfg: config})
|
||||
return err
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modelmigration/migrationtest"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNormalizeLegacyConfigJSONValue(t *testing.T) {
|
||||
const (
|
||||
config = `{"Host":"smtp.example.com"}`
|
||||
packed = "≻潈瑳㨢猢瑭硥浡汰潣≭⁽"
|
||||
)
|
||||
for _, tc := range []struct {
|
||||
name, input, want string
|
||||
}{
|
||||
{"empty", "", ""},
|
||||
{"valid", config + " ", config + " "},
|
||||
{"unicode and large number", ` {"name":"例😀","id":9007199254740993} `, ` {"name":"例😀","id":9007199254740993} `},
|
||||
{"double encoded", packed, config},
|
||||
{"double encoded with BOM", "\ufeff" + packed, config},
|
||||
{"BOM", "\xff\xfe" + config, config},
|
||||
{"BOM with non-BMP rune", "\xff\xfe" + `{"name":"😀"}`, `{"name":"😀"}`},
|
||||
{"corrupt", "invalid", "invalid"},
|
||||
{"corrupt non-BMP rune", "invalid😀", "invalid😀"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := normalizeLegacyConfigJSON(tc.input)
|
||||
assert.Equal(t, tc.want, got)
|
||||
assert.Equal(t, got, normalizeLegacyConfigJSON(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeLegacyConfigJSON(t *testing.T) {
|
||||
type RepoUnit struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64
|
||||
Config string `xorm:"TEXT"`
|
||||
}
|
||||
type LoginSource struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Name string
|
||||
Cfg string `xorm:"TEXT"`
|
||||
}
|
||||
x, cleanup := migrationtest.PrepareTestEnv(t, 0, new(RepoUnit), new(LoginSource))
|
||||
defer cleanup()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
}
|
||||
defer test.MockVariableValue(&setting.Database.IterateBufferSize, 2)()
|
||||
|
||||
inputs := []string{
|
||||
` {"id":9007199254740993,"name":"例😀"} `,
|
||||
"≻潈瑳㨢猢瑭硥浡汰潣≭⁽",
|
||||
"\ufeff≻潈瑳㨢猢瑭硥浡汰潣≭⁽",
|
||||
"",
|
||||
"invalid",
|
||||
}
|
||||
want := []string{inputs[0], `{"Host":"smtp.example.com"}`, `{"Host":"smtp.example.com"}`, "", "invalid", ""}
|
||||
for _, input := range inputs {
|
||||
_, err := x.Insert(&RepoUnit{RepoID: 42, Config: input})
|
||||
require.NoError(t, err)
|
||||
_, err = x.Insert(&LoginSource{Name: "test", Cfg: input})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
_, err := x.Exec("INSERT INTO `repo_unit` (`repo_id`, `config`) VALUES (?, NULL)", 42)
|
||||
require.NoError(t, err)
|
||||
_, err = x.Exec("INSERT INTO `login_source` (`name`, `cfg`) VALUES (?, NULL)", "test")
|
||||
require.NoError(t, err)
|
||||
|
||||
for range 2 {
|
||||
require.NoError(t, NormalizeLegacyConfigJSON(t.Context(), x))
|
||||
var units []RepoUnit
|
||||
require.NoError(t, x.Asc("id").Find(&units))
|
||||
require.Len(t, units, len(want))
|
||||
var sources []LoginSource
|
||||
require.NoError(t, x.Asc("id").Find(&sources))
|
||||
require.Len(t, sources, len(want))
|
||||
for i, config := range want {
|
||||
assert.Equal(t, config, units[i].Config)
|
||||
assert.EqualValues(t, 42, units[i].RepoID)
|
||||
assert.Equal(t, config, sources[i].Cfg)
|
||||
assert.Equal(t, "test", sources[i].Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
)
|
||||
|
||||
func DropUserOpenIDTable(_ctx context.Context, x base.EngineMigration) error {
|
||||
return x.DropTables("user_open_id")
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modelmigration/migrationtest"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDropUserOpenIDTable(t *testing.T) {
|
||||
type UserOpenID struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
}
|
||||
|
||||
x, cleanup := migrationtest.PrepareTestEnv(t, 0, new(UserOpenID))
|
||||
defer cleanup()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
}
|
||||
|
||||
exists, err := x.IsTableExist("user_open_id")
|
||||
require.NoError(t, err)
|
||||
require.True(t, exists)
|
||||
require.NoError(t, DropUserOpenIDTable(t.Context(), x))
|
||||
exists, err = x.IsTableExist("user_open_id")
|
||||
require.NoError(t, err)
|
||||
require.False(t, exists)
|
||||
require.NoError(t, DropUserOpenIDTable(t.Context(), x))
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddLatestReleaseIDToRepository(_ctx context.Context, engine base.EngineMigration) error {
|
||||
type Repository struct {
|
||||
LatestReleaseID int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
_, err := engine.SyncWithOptions(xorm.SyncOptions{
|
||||
IgnoreDropIndices: true,
|
||||
IgnoreConstrains: true,
|
||||
}, new(Repository))
|
||||
return err
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddUniqueTeamNameIndex(_ctx context.Context, x base.EngineMigration) error {
|
||||
type Team struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OrgID int64 `xorm:"INDEX UNIQUE(s)"`
|
||||
LowerName string `xorm:"UNIQUE(s)"`
|
||||
Name string
|
||||
}
|
||||
|
||||
var teams []Team
|
||||
if err := x.Where("id NOT IN (SELECT MIN(id) FROM team GROUP BY org_id, lower_name)").Asc("id").Find(&teams); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, team := range teams {
|
||||
for suffix := team.ID; ; suffix++ {
|
||||
ending := fmt.Sprintf("-%d", suffix)
|
||||
name := util.TruncateStringBytes(team.Name, 255-len(ending)) + ending
|
||||
exists, err := x.Where("org_id = ? AND lower_name = ?", team.OrgID, strings.ToLower(name)).Exist(new(Team))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
if _, err := x.ID(team.ID).Cols("name", "lower_name").Update(&Team{Name: name, LowerName: strings.ToLower(name)}); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Warn("renamed duplicate team id=%d in org id=%d from %q to %q", team.ID, team.OrgID, team.Name, name)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(Team))
|
||||
return err
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v29
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modelmigration/migrationtest"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddUniqueTeamNameIndex(t *testing.T) {
|
||||
type Team struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
LowerName, Name string
|
||||
}
|
||||
|
||||
x, cleanup := migrationtest.PrepareTestEnv(t, 0, new(Team))
|
||||
defer cleanup()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := x.Insert(
|
||||
&Team{OrgID: 1, Name: "Team", LowerName: "team"},
|
||||
&Team{OrgID: 1, Name: "Team", LowerName: "team"},
|
||||
&Team{OrgID: 1, Name: "Team-2", LowerName: "team-2"},
|
||||
&Team{OrgID: 2, Name: "Team", LowerName: "team"},
|
||||
&Team{OrgID: 1, Name: strings.Repeat("A", 255), LowerName: strings.Repeat("a", 255)},
|
||||
&Team{OrgID: 1, Name: strings.Repeat("A", 255), LowerName: strings.Repeat("a", 255)},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
for range 2 {
|
||||
require.NoError(t, AddUniqueTeamNameIndex(t.Context(), x))
|
||||
var teams []Team
|
||||
require.NoError(t, x.Asc("id").Find(&teams))
|
||||
require.Equal(t, []Team{
|
||||
{ID: 1, OrgID: 1, Name: "Team", LowerName: "team"},
|
||||
{ID: 2, OrgID: 1, Name: "Team-3", LowerName: "team-3"},
|
||||
{ID: 3, OrgID: 1, Name: "Team-2", LowerName: "team-2"},
|
||||
{ID: 4, OrgID: 2, Name: "Team", LowerName: "team"},
|
||||
{ID: 5, OrgID: 1, Name: strings.Repeat("A", 255), LowerName: strings.Repeat("a", 255)},
|
||||
{ID: 6, OrgID: 1, Name: strings.Repeat("A", 253) + "-6", LowerName: strings.Repeat("a", 253) + "-6"},
|
||||
}, teams)
|
||||
}
|
||||
_, err = x.Insert(&Team{OrgID: 1, Name: "TEAM", LowerName: "team"})
|
||||
require.Error(t, err)
|
||||
}
|
||||
@@ -93,12 +93,7 @@ const artifactKeepForever timeutil.TimeStamp = 0
|
||||
|
||||
func artifactExpiry(requested optional.Option[timeutil.TimeStamp]) timeutil.TimeStamp {
|
||||
if requested.Has() {
|
||||
now := timeutil.TimeStampNow()
|
||||
expiry := max(requested.Value(), now.Add(timeutil.Day)) // v4 clients use the runner's clock
|
||||
if setting.Actions.ArtifactRetentionDays > 0 {
|
||||
expiry = min(expiry, now.Add(timeutil.Day*setting.Actions.ArtifactRetentionDays))
|
||||
}
|
||||
return expiry
|
||||
return max(requested.Value(), artifactKeepForever+1)
|
||||
}
|
||||
if setting.Actions.ArtifactRetentionDays <= 0 {
|
||||
return artifactKeepForever
|
||||
|
||||
@@ -5,12 +5,6 @@ package actions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/test"
|
||||
"gitea.dev/modules/timeutil"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -31,21 +25,3 @@ func TestKeepLatestAttemptArtifacts(t *testing.T) {
|
||||
}
|
||||
assert.Equal(t, []int64{1, 4, 5}, ids)
|
||||
}
|
||||
|
||||
func TestArtifactExpiry(t *testing.T) {
|
||||
defer timeutil.MockSet(time.Unix(1_000_000_000, 0))()
|
||||
now := timeutil.TimeStampNow()
|
||||
days := func(n int64) timeutil.TimeStamp { return now.Add(n * timeutil.Day) }
|
||||
requested := optional.Some[timeutil.TimeStamp]
|
||||
|
||||
defer test.MockVariableValue(&setting.Actions.ArtifactRetentionDays, 90)()
|
||||
assert.Equal(t, days(90), artifactExpiry(optional.None[timeutil.TimeStamp]()))
|
||||
assert.Equal(t, days(5), artifactExpiry(requested(days(5))))
|
||||
assert.Equal(t, days(1), artifactExpiry(requested(days(-2))), "runner clock behind must not expire the artifact at once")
|
||||
assert.Equal(t, days(90), artifactExpiry(requested(days(1000))), "runner must not exceed the instance retention")
|
||||
|
||||
setting.Actions.ArtifactRetentionDays = 0
|
||||
assert.Equal(t, artifactKeepForever, artifactExpiry(optional.None[timeutil.TimeStamp]()))
|
||||
assert.Equal(t, days(1000), artifactExpiry(requested(days(1000))))
|
||||
assert.Equal(t, days(1), artifactExpiry(requested(now)))
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
// BelongingScope identifies the owner (user/org) or repository an Actions secret or variable belongs to.
|
||||
// Both zero values mean "global scope".
|
||||
// Always use the functions to create a BelongingScope, rather than creating one directly, to avoid accidentally creating an invalid scope.
|
||||
type BelongingScope struct {
|
||||
internalOwnerID int64
|
||||
internalRepoID int64
|
||||
}
|
||||
|
||||
func BelongingScopeOwner(ownerID int64) BelongingScope {
|
||||
return BelongingScope{internalOwnerID: ownerID}
|
||||
}
|
||||
|
||||
func BelongingScopeRepo(repoID int64) BelongingScope {
|
||||
return BelongingScope{internalRepoID: repoID}
|
||||
}
|
||||
|
||||
func BelongingScopeGlobal() BelongingScope {
|
||||
return BelongingScope{}
|
||||
}
|
||||
|
||||
func (scope BelongingScope) GetOwnerRepoIDs() (ownerID, repoID int64) {
|
||||
return scope.internalOwnerID, scope.internalRepoID
|
||||
}
|
||||
@@ -6,10 +6,10 @@ package actions
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/perm"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"xorm.io/xorm/convert"
|
||||
@@ -30,7 +30,7 @@ type OwnerActionsConfig struct {
|
||||
var _ convert.ConversionFrom = (*OwnerActionsConfig)(nil)
|
||||
|
||||
func (cfg *OwnerActionsConfig) FromDB(bytes []byte) error {
|
||||
db.UnmarshalFromDB(bytes, cfg)
|
||||
_ = json.Unmarshal(bytes, cfg)
|
||||
cfg.TokenPermissionMode, _ = util.EnumValue(cfg.TokenPermissionMode)
|
||||
return nil
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func (cfg *OwnerActionsConfig) GetMaxTokenPermissions() repo_model.ActionsTokenP
|
||||
return *cfg.MaxTokenPermissions
|
||||
}
|
||||
// Default max is write for everything
|
||||
return repo_model.MakeWriteAllActionsTokenPermissions()
|
||||
return repo_model.MakeActionsTokenPermissions(perm.AccessModeWrite)
|
||||
}
|
||||
|
||||
// ClampPermissions ensures that the given permissions don't exceed the maximum
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"gitea.dev/models/db"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/log"
|
||||
@@ -35,8 +34,8 @@ type ActionRun struct {
|
||||
RepoID int64 `xorm:"unique(repo_index) index(repo_status)"`
|
||||
Repo *repo_model.Repository `xorm:"-"`
|
||||
OwnerID int64 `xorm:"index"`
|
||||
WorkflowID string `xorm:"index"` // the name of workflow file
|
||||
Index int64 `xorm:"unique(repo_index)"` // a unique number for each run of a repository
|
||||
WorkflowID string `xorm:"index"` // the name of workflow file
|
||||
Index int64 `xorm:"index unique(repo_index)"` // a unique number for each run of a repository
|
||||
TriggerUserID int64 `xorm:"index"`
|
||||
TriggerUser *user_model.User `xorm:"-"`
|
||||
ScheduleID int64
|
||||
@@ -297,9 +296,7 @@ func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, er
|
||||
}
|
||||
|
||||
func GetRunsByRepoAndID(ctx context.Context, repoID int64, runIDs []int64) (runs []*ActionRun, err error) {
|
||||
if len(runIDs) > 0 {
|
||||
err = db.GetEngine(ctx).In("id", runIDs).Where("repo_id=?", repoID).OrderBy("id").Find(&runs)
|
||||
}
|
||||
err = db.GetEngine(ctx).In("id", runIDs).Where("repo_id=?", repoID).OrderBy("id").Find(&runs)
|
||||
return runs, err
|
||||
}
|
||||
|
||||
@@ -409,27 +406,26 @@ func CancelPreviousJobsByRunConcurrency(ctx context.Context, attempt *ActionRunA
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var jobsToCancel []*ActionRunJob
|
||||
|
||||
attempts, jobs, err := getConcurrencyEntriesToReplace(ctx, attempt.RepoID, attempt.ConcurrencyGroup, attempt.ConcurrencyCancel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find concurrent runs and jobs: %w", err)
|
||||
}
|
||||
jobs = slices.DeleteFunc(jobs, func(job *ActionRunJob) bool { return job.RunID == attempt.RunID })
|
||||
jobsToCancel = append(jobsToCancel, jobs...)
|
||||
|
||||
// cancel runs in the same concurrency group
|
||||
attempts = slices.DeleteFunc(attempts, func(concurrentAttempt *ActionRunAttempt) bool { return concurrentAttempt.RunID == attempt.RunID })
|
||||
attemptJobs, err := getRunJobsByAttempts(ctx, attempts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find concurrent attempt jobs: %w", err)
|
||||
}
|
||||
return CancelJobs(ctx, append(jobs, attemptJobs...), false)
|
||||
}
|
||||
|
||||
func getRunJobsByAttempts(ctx context.Context, attempts []*ActionRunAttempt) (jobs []*ActionRunJob, _ error) {
|
||||
for chunk := range slices.Chunk(attempts, db.DefaultMaxInSize) {
|
||||
attemptIDs := container.FilterSlice(chunk, func(attempt *ActionRunAttempt) (int64, bool) { return attempt.ID, true })
|
||||
if err := db.GetEngine(ctx).In("run_attempt_id", attemptIDs).Find(&jobs); err != nil {
|
||||
return nil, err
|
||||
for _, concurrentAttempt := range attempts {
|
||||
if concurrentAttempt.RunID == attempt.RunID {
|
||||
continue
|
||||
}
|
||||
jobs, err := GetRunJobsByRunAndAttemptID(ctx, concurrentAttempt.RunID, concurrentAttempt.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find run %d attempt %d jobs: %w", concurrentAttempt.RunID, concurrentAttempt.ID, err)
|
||||
}
|
||||
jobsToCancel = append(jobsToCancel, jobs...)
|
||||
}
|
||||
return jobs, nil
|
||||
|
||||
return CancelJobs(ctx, jobsToCancel, false)
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ func UpdateRunAttempt(ctx context.Context, attempt *ActionRunAttempt, cols ...st
|
||||
return err
|
||||
}
|
||||
if run.LatestAttemptID != attempt.ID {
|
||||
log.Warn("run %d cannot be updated by old attempt %d, latest attempt is %d", run.ID, attempt.ID, run.LatestAttemptID)
|
||||
log.Warn("run %d cannot be updated by an old attempt %d", run.LatestAttemptID, attempt.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
// 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
|
||||
@@ -99,7 +100,7 @@ type ActionRunJob struct {
|
||||
|
||||
// IsReusableCaller marks this job as a reusable workflow caller.
|
||||
// Caller jobs do not run on a runner; their status is derived from their child jobs.
|
||||
IsReusableCaller bool `xorm:"NOT NULL DEFAULT FALSE"`
|
||||
IsReusableCaller bool `xorm:"index NOT NULL DEFAULT FALSE"`
|
||||
// IsExpanded reports whether this job's lazy expansion (children-row insertion) is complete.
|
||||
// For a reusable workflow caller, true means children rows exist and CallPayload is populated.
|
||||
IsExpanded bool `xorm:"NOT NULL DEFAULT FALSE"`
|
||||
@@ -109,8 +110,6 @@ type ActionRunJob struct {
|
||||
// ReusableWorkflowContent is the content of the reusable workflow specified by "uses:".
|
||||
// Only set when IsReusableCaller is true.
|
||||
ReusableWorkflowContent []byte `xorm:"LONGBLOB"`
|
||||
// ReusableWorkflowRef is the fully qualified ref, or the commit, that "uses:" resolved to when the content was loaded.
|
||||
ReusableWorkflowRef string `xorm:"VARCHAR(512) NOT NULL DEFAULT ''"`
|
||||
// CallSecrets encodes the reusable workflow caller's "secrets:" section:
|
||||
// - "" : no "secrets:" section (children only see auto-generated tokens).
|
||||
// - "inherit" : the caller wrote "secrets: inherit".
|
||||
@@ -272,24 +271,6 @@ func GetLatestAttemptJobsByRun(ctx context.Context, run *ActionRun) (ActionJobLi
|
||||
return jobs, nil
|
||||
}
|
||||
|
||||
func GetLatestAttemptJobsByRuns(ctx context.Context, runs []*ActionRun) (map[int64]ActionJobList, error) {
|
||||
result := make(map[int64]ActionJobList, len(runs))
|
||||
if len(runs) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
attemptIDs := container.FilterSlice(runs, func(run *ActionRun) (int64, bool) { return run.LatestAttemptID, run.LatestAttemptID != 0 })
|
||||
legacyRunIDs := container.FilterSlice(runs, func(run *ActionRun) (int64, bool) { return run.ID, run.LatestAttemptID == 0 })
|
||||
cond := builder.In("run_attempt_id", attemptIDs).Or(builder.Eq{"run_attempt_id": 0}.And(builder.In("run_id", legacyRunIDs)))
|
||||
var jobs []*ActionRunJob
|
||||
if err := db.GetEngine(ctx).Where(cond).OrderBy("id").Find(&jobs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, job := range jobs {
|
||||
result[job.RunID] = append(result[job.RunID], job)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetAllRunJobsByRepoAndRunID returns all jobs for a run across all attempts.
|
||||
func GetAllRunJobsByRepoAndRunID(ctx context.Context, repoID, runID int64) (ActionJobList, error) {
|
||||
var jobs []*ActionRunJob
|
||||
@@ -459,6 +440,14 @@ func CollectAllDescendantJobs(parent *ActionRunJob, allJobs []*ActionRunJob) []*
|
||||
return out
|
||||
}
|
||||
|
||||
// hasWaitingJobsToPick reports whether any waiting, unclaimed, non-reusable job
|
||||
// remains in the repo, i.e. work that an idle runner could still pick up.
|
||||
func hasWaitingJobsToPick(ctx context.Context, repoID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).
|
||||
Where("repo_id = ? AND task_id = ? AND status = ? AND is_reusable_caller = ?", repoID, 0, StatusWaiting, false).
|
||||
Exist(&ActionRunJob{})
|
||||
}
|
||||
|
||||
func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, cols ...string) (int64, error) {
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
@@ -509,7 +498,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
|
||||
// Only bump when waiting work actually remains for this repo, otherwise
|
||||
// every job completion would needlessly bump the global version and wake
|
||||
// every idle runner instance-wide for nothing.
|
||||
hasWaiting, err := JobQueueOptions{RepoID: job.RepoID, Status: StatusWaiting}.session(ctx).Exist(new(ActionRunJob))
|
||||
hasWaiting, err := hasWaitingJobsToPick(ctx, job.RepoID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -696,18 +685,43 @@ func hasFailFastMatrixFailure(jobs []*ActionRunJob) bool {
|
||||
// It's useful when a new run is triggered, and all previous runs needn't be continued anymore.
|
||||
func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) ([]*ActionRunJob, error) {
|
||||
// Find all runs in the specified repository, reference, and workflow with non-final status
|
||||
runIDs := builder.Select("id").From("action_run").Where(FindRunOptions{
|
||||
runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{
|
||||
RepoID: repoID,
|
||||
Ref: ref,
|
||||
WorkflowID: workflowID,
|
||||
TriggerEvent: event,
|
||||
Status: []Status{StatusRunning, StatusWaiting, StatusBlocked, StatusCancelling},
|
||||
}.ToConds())
|
||||
var jobs []*ActionRunJob
|
||||
if err := db.GetEngine(ctx).Where(builder.In("run_id", runIDs)).Find(&jobs); err != nil {
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return CancelJobs(ctx, jobs, false)
|
||||
|
||||
// If there are no runs found, there's no need to proceed with cancellation, so return nil.
|
||||
if total == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cancelledJobs := make([]*ActionRunJob, 0, total)
|
||||
|
||||
// Iterate over each found run and cancel its associated jobs.
|
||||
for _, run := range runs {
|
||||
// Find all jobs associated with the current run.
|
||||
jobs, err := db.Find[ActionRunJob](ctx, FindRunJobOptions{
|
||||
RunID: run.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return cancelledJobs, err
|
||||
}
|
||||
|
||||
cjs, err := CancelJobs(ctx, jobs, false)
|
||||
if err != nil {
|
||||
return cancelledJobs, err
|
||||
}
|
||||
cancelledJobs = append(cancelledJobs, cjs...)
|
||||
}
|
||||
|
||||
// Return nil to indicate successful cancellation of all running and waiting jobs.
|
||||
return cancelledJobs, nil
|
||||
}
|
||||
|
||||
// GetAncestorCallerIDs returns the IDs of the reusable workflow callers the job is nested in.
|
||||
@@ -724,7 +738,7 @@ func GetAncestorCallerIDs(ctx context.Context, job *ActionRunJob) (container.Set
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func CancelPreviousJobsByJobConcurrency(ctx context.Context, job *ActionRunJob) ([]*ActionRunJob, error) {
|
||||
func CancelPreviousJobsByJobConcurrency(ctx context.Context, job *ActionRunJob) (jobsToCancel []*ActionRunJob, _ error) {
|
||||
if job.RawConcurrency == "" {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -744,14 +758,21 @@ func CancelPreviousJobsByJobConcurrency(ctx context.Context, job *ActionRunJob)
|
||||
return nil, err
|
||||
}
|
||||
jobs = slices.DeleteFunc(jobs, func(j *ActionRunJob) bool { return j.ID == job.ID || callerIDs.Contains(j.ID) })
|
||||
jobsToCancel = append(jobsToCancel, jobs...)
|
||||
|
||||
// cancel runs in the same concurrency group
|
||||
attempts = slices.DeleteFunc(attempts, func(attempt *ActionRunAttempt) bool { return attempt.ID == job.RunAttemptID })
|
||||
attemptJobs, err := getRunJobsByAttempts(ctx, attempts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find concurrent attempt jobs: %w", err)
|
||||
for _, attempt := range attempts {
|
||||
if attempt.ID == job.RunAttemptID {
|
||||
continue
|
||||
}
|
||||
jobs, err := GetRunJobsByRunAndAttemptID(ctx, attempt.RunID, attempt.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find run %d attempt %d jobs: %w", attempt.RunID, attempt.ID, err)
|
||||
}
|
||||
jobsToCancel = append(jobsToCancel, jobs...)
|
||||
}
|
||||
return CancelJobs(ctx, append(jobs, attemptJobs...), false)
|
||||
|
||||
return CancelJobs(ctx, jobsToCancel, false)
|
||||
}
|
||||
|
||||
// CancelJobs cancels every cancellable job it is given, force skipping the graceful cancelling
|
||||
@@ -832,34 +853,25 @@ func cancelReusableCaller(ctx context.Context, caller *ActionRunJob, force bool)
|
||||
}
|
||||
|
||||
// Cancel descendants deepest-first, then the caller: a caller's status is aggregated from its children,
|
||||
// so each child must be cancelled before its parent caller is re-read.
|
||||
// so each child must reach its final state before its parent caller is re-aggregated.
|
||||
// A child's ID always exceeds its parent's, so descending ID is a valid deepest-first order.
|
||||
descendants := CollectAllDescendantJobs(caller, attemptJobs)
|
||||
slices.SortFunc(descendants, func(a, b *ActionRunJob) int { return cmp.Compare(b.ID, a.ID) })
|
||||
callersWithChildren := make(container.Set[int64])
|
||||
for _, d := range descendants {
|
||||
callersWithChildren.Add(d.ParentJobID)
|
||||
}
|
||||
|
||||
for _, job := range append(descendants, caller) {
|
||||
if !callersWithChildren.Contains(job.ID) {
|
||||
cancelled, err := cancelOneJob(ctx, job, force)
|
||||
if err != nil {
|
||||
return cancelledJobs, err
|
||||
}
|
||||
if cancelled != nil {
|
||||
cancelledJobs = append(cancelledJobs, cancelled)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// the job is a caller and its children's cascade already re-aggregated it
|
||||
reloaded, err := GetRunJobByRunAndID(ctx, job.RunID, job.ID)
|
||||
for _, c := range descendants {
|
||||
cancelled, err := cancelOneJob(ctx, c, force)
|
||||
if err != nil {
|
||||
return cancelledJobs, err
|
||||
}
|
||||
if reloaded.Status != job.Status {
|
||||
cancelledJobs = append(cancelledJobs, reloaded)
|
||||
if cancelled != nil {
|
||||
cancelledJobs = append(cancelledJobs, cancelled)
|
||||
}
|
||||
}
|
||||
|
||||
if c, err := cancelOneJob(ctx, caller, force); err != nil {
|
||||
return cancelledJobs, err
|
||||
} else if c != nil {
|
||||
cancelledJobs = append(cancelledJobs, c)
|
||||
}
|
||||
return cancelledJobs, nil
|
||||
}
|
||||
|
||||
@@ -27,12 +27,13 @@ func (opts JobQueueOptions) session(ctx context.Context) *xorm.Session {
|
||||
sess = sess.And(builder.Eq{"`action_run_job`.repo_id": opts.RepoID})
|
||||
}
|
||||
if opts.OwnerID > 0 {
|
||||
sess = sess.And(builder.In("`action_run_job`.repo_id", builder.Select("id").From("repository").Where(builder.Eq{"owner_id": opts.OwnerID})))
|
||||
sess = sess.Join("INNER", "repository", "repository.id = `action_run_job`.repo_id AND repository.owner_id = ?", opts.OwnerID)
|
||||
}
|
||||
return sess.And(opts.statusCond())
|
||||
}
|
||||
|
||||
var (
|
||||
// keep in sync with CreateTaskForRunner
|
||||
queuedJobsCond = builder.Eq{"`action_run_job`.status": StatusWaiting, "`action_run_job`.task_id": 0}
|
||||
// a cancelling job still occupies its runner
|
||||
runningJobsCond = builder.In("`action_run_job`.status", StatusRunning, StatusCancelling)
|
||||
|
||||
@@ -10,6 +10,22 @@ import (
|
||||
)
|
||||
|
||||
func TestAggregateJobStatus(t *testing.T) {
|
||||
testStatuses := func(expected Status, statuses []Status) {
|
||||
t.Helper()
|
||||
var jobs []*ActionRunJob
|
||||
for _, v := range statuses {
|
||||
jobs = append(jobs, &ActionRunJob{Status: v})
|
||||
}
|
||||
actual := AggregateJobStatus(jobs)
|
||||
if !assert.Equal(t, expected, actual) {
|
||||
var statusStrings []string
|
||||
for _, s := range statuses {
|
||||
statusStrings = append(statusStrings, s.String())
|
||||
}
|
||||
t.Errorf("AggregateJobStatus(%v) = %v, want %v", statusStrings, statusNames[actual], statusNames[expected])
|
||||
}
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
statuses []Status
|
||||
expected Status
|
||||
@@ -76,17 +92,6 @@ func TestAggregateJobStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
var jobs []*ActionRunJob
|
||||
for _, status := range c.statuses {
|
||||
jobs = append(jobs, &ActionRunJob{Status: status})
|
||||
}
|
||||
actual := AggregateJobStatus(jobs)
|
||||
if !assert.Equal(t, c.expected, actual) {
|
||||
var statusStrings []string
|
||||
for _, status := range c.statuses {
|
||||
statusStrings = append(statusStrings, status.String())
|
||||
}
|
||||
t.Errorf("AggregateJobStatus(%v) = %v, want %v", statusStrings, statusNames[actual], statusNames[c.expected])
|
||||
}
|
||||
testStatuses(c.expected, c.statuses)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
runnerv1 "gitea.dev/actionslib/runner/v1"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/unittest"
|
||||
"gitea.dev/modules/timeutil"
|
||||
@@ -16,267 +15,189 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRunJobDatabase(t *testing.T) {
|
||||
func TestGetPriorAttemptChildrenByParent(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
ctx := t.Context()
|
||||
|
||||
t.Run("prior attempt children by parent", func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
// 3 attempts of one run:
|
||||
// 1: caller expanded with 3 matrix instances of "work" + non-matrix sibling "summary".
|
||||
// 2: caller skipped, no children rows.
|
||||
// 3: placeholder "current" attempt for the walkback subtest.
|
||||
|
||||
// 3 attempts of one run:
|
||||
// 1: caller expanded with 3 matrix instances of "work" + non-matrix sibling "summary".
|
||||
// 2: caller skipped, no children rows.
|
||||
// 3: placeholder "current" attempt for the walkback subtest.
|
||||
run := &ActionRun{
|
||||
Title: "prior-children-test",
|
||||
RepoID: 4,
|
||||
Index: 9501,
|
||||
OwnerID: 1,
|
||||
WorkflowID: "matrix.yaml",
|
||||
TriggerUserID: 1,
|
||||
Ref: "refs/heads/master",
|
||||
CommitSHA: "c2d72f548424103f01ee1dc02889c1e2bff816b0",
|
||||
Event: "push",
|
||||
TriggerEvent: "push",
|
||||
EventPayload: "{}",
|
||||
Status: StatusSuccess,
|
||||
}
|
||||
require.NoError(t, db.Insert(ctx, run))
|
||||
|
||||
run := &ActionRun{
|
||||
Title: "prior-children-test",
|
||||
RepoID: 4,
|
||||
Index: 9501,
|
||||
OwnerID: 1,
|
||||
WorkflowID: "matrix.yaml",
|
||||
const callerAttemptJobID int64 = 9001
|
||||
insertAttempt := func(t *testing.T, num int64, status Status) *ActionRunAttempt {
|
||||
t.Helper()
|
||||
a := &ActionRunAttempt{
|
||||
RepoID: run.RepoID,
|
||||
RunID: run.ID,
|
||||
Attempt: num,
|
||||
TriggerUserID: 1,
|
||||
Ref: "refs/heads/master",
|
||||
CommitSHA: "c2d72f548424103f01ee1dc02889c1e2bff816b0",
|
||||
Event: "push",
|
||||
TriggerEvent: "push",
|
||||
EventPayload: "{}",
|
||||
Status: StatusSuccess,
|
||||
Status: status,
|
||||
}
|
||||
require.NoError(t, db.Insert(ctx, run))
|
||||
|
||||
const callerAttemptJobID int64 = 9001
|
||||
insertAttempt := func(t *testing.T, num int64, status Status) *ActionRunAttempt {
|
||||
t.Helper()
|
||||
a := &ActionRunAttempt{
|
||||
RepoID: run.RepoID,
|
||||
RunID: run.ID,
|
||||
Attempt: num,
|
||||
TriggerUserID: 1,
|
||||
Status: status,
|
||||
}
|
||||
require.NoError(t, db.Insert(ctx, a))
|
||||
return a
|
||||
}
|
||||
insertCaller := func(t *testing.T, attemptID int64, status Status, expanded bool) *ActionRunJob {
|
||||
t.Helper()
|
||||
caller := &ActionRunJob{
|
||||
RunID: run.ID,
|
||||
RunAttemptID: attemptID,
|
||||
RepoID: run.RepoID,
|
||||
OwnerID: run.OwnerID,
|
||||
CommitSHA: run.CommitSHA,
|
||||
Name: "caller",
|
||||
JobID: "caller",
|
||||
Attempt: 1,
|
||||
Status: status,
|
||||
AttemptJobID: callerAttemptJobID,
|
||||
IsReusableCaller: true,
|
||||
IsExpanded: expanded,
|
||||
}
|
||||
require.NoError(t, db.Insert(ctx, caller))
|
||||
return caller
|
||||
}
|
||||
insertChild := func(t *testing.T, attemptID, parentID, attemptJobID int64, name, jobID string) {
|
||||
t.Helper()
|
||||
require.NoError(t, db.Insert(ctx, &ActionRunJob{
|
||||
RunID: run.ID,
|
||||
RunAttemptID: attemptID,
|
||||
RepoID: run.RepoID,
|
||||
OwnerID: run.OwnerID,
|
||||
CommitSHA: run.CommitSHA,
|
||||
Name: name,
|
||||
JobID: jobID,
|
||||
Attempt: 1,
|
||||
Status: StatusSuccess,
|
||||
AttemptJobID: attemptJobID,
|
||||
ParentJobID: parentID,
|
||||
}))
|
||||
require.NoError(t, db.Insert(ctx, a))
|
||||
return a
|
||||
}
|
||||
insertCaller := func(t *testing.T, attemptID int64, status Status, expanded bool) *ActionRunJob {
|
||||
t.Helper()
|
||||
caller := &ActionRunJob{
|
||||
RunID: run.ID,
|
||||
RunAttemptID: attemptID,
|
||||
RepoID: run.RepoID,
|
||||
OwnerID: run.OwnerID,
|
||||
CommitSHA: run.CommitSHA,
|
||||
Name: "caller",
|
||||
JobID: "caller",
|
||||
Attempt: 1,
|
||||
Status: status,
|
||||
AttemptJobID: callerAttemptJobID,
|
||||
IsReusableCaller: true,
|
||||
IsExpanded: expanded,
|
||||
}
|
||||
require.NoError(t, db.Insert(ctx, caller))
|
||||
return caller
|
||||
}
|
||||
insertChild := func(t *testing.T, attemptID, parentID, attemptJobID int64, name, jobID string) {
|
||||
t.Helper()
|
||||
require.NoError(t, db.Insert(ctx, &ActionRunJob{
|
||||
RunID: run.ID,
|
||||
RunAttemptID: attemptID,
|
||||
RepoID: run.RepoID,
|
||||
OwnerID: run.OwnerID,
|
||||
CommitSHA: run.CommitSHA,
|
||||
Name: name,
|
||||
JobID: jobID,
|
||||
Attempt: 1,
|
||||
Status: StatusSuccess,
|
||||
AttemptJobID: attemptJobID,
|
||||
ParentJobID: parentID,
|
||||
}))
|
||||
}
|
||||
|
||||
attempt1 := insertAttempt(t, 1, StatusSuccess)
|
||||
caller1 := insertCaller(t, attempt1.ID, StatusSuccess, true)
|
||||
insertChild(t, attempt1.ID, caller1.ID, 101, "work (alpha)", "work")
|
||||
insertChild(t, attempt1.ID, caller1.ID, 102, "work (beta)", "work")
|
||||
insertChild(t, attempt1.ID, caller1.ID, 103, "work (gamma)", "work")
|
||||
insertChild(t, attempt1.ID, caller1.ID, 104, "summary", "summary")
|
||||
attempt1 := insertAttempt(t, 1, StatusSuccess)
|
||||
caller1 := insertCaller(t, attempt1.ID, StatusSuccess, true)
|
||||
insertChild(t, attempt1.ID, caller1.ID, 101, "work (alpha)", "work")
|
||||
insertChild(t, attempt1.ID, caller1.ID, 102, "work (beta)", "work")
|
||||
insertChild(t, attempt1.ID, caller1.ID, 103, "work (gamma)", "work")
|
||||
insertChild(t, attempt1.ID, caller1.ID, 104, "summary", "summary")
|
||||
|
||||
attempt2 := insertAttempt(t, 2, StatusSkipped)
|
||||
insertCaller(t, attempt2.ID, StatusSkipped, false) // no children intentionally
|
||||
attempt2 := insertAttempt(t, 2, StatusSkipped)
|
||||
insertCaller(t, attempt2.ID, StatusSkipped, false) // no children intentionally
|
||||
|
||||
// both subtests expect attempt 1's expansion, differing only in the "current" attempt id
|
||||
assertAttempt1Children := func(t *testing.T, out map[string]map[string]*ActionRunJob) {
|
||||
t.Helper()
|
||||
// outer map keyed by JobID: "work" has 3 matrix instances, "summary" 1
|
||||
assert.Len(t, out, 2)
|
||||
assert.Len(t, out["work"], 3, "matrix instances must each get their own inner-map entry")
|
||||
assert.Len(t, out["summary"], 1)
|
||||
// both subtests expect attempt 1's expansion, differing only in the "current" attempt id
|
||||
assertAttempt1Children := func(t *testing.T, out map[string]map[string]*ActionRunJob) {
|
||||
t.Helper()
|
||||
// outer map keyed by JobID: "work" has 3 matrix instances, "summary" 1
|
||||
assert.Len(t, out, 2)
|
||||
assert.Len(t, out["work"], 3, "matrix instances must each get their own inner-map entry")
|
||||
assert.Len(t, out["summary"], 1)
|
||||
|
||||
require.NotNil(t, out["work"]["work (alpha)"])
|
||||
require.NotNil(t, out["work"]["work (beta)"])
|
||||
require.NotNil(t, out["work"]["work (gamma)"])
|
||||
require.NotNil(t, out["summary"]["summary"])
|
||||
require.NotNil(t, out["work"]["work (alpha)"])
|
||||
require.NotNil(t, out["work"]["work (beta)"])
|
||||
require.NotNil(t, out["work"]["work (gamma)"])
|
||||
require.NotNil(t, out["summary"]["summary"])
|
||||
|
||||
assert.Equal(t, int64(101), out["work"]["work (alpha)"].AttemptJobID)
|
||||
assert.Equal(t, int64(102), out["work"]["work (beta)"].AttemptJobID)
|
||||
assert.Equal(t, int64(103), out["work"]["work (gamma)"].AttemptJobID)
|
||||
assert.Equal(t, int64(104), out["summary"]["summary"].AttemptJobID)
|
||||
}
|
||||
assert.Equal(t, int64(101), out["work"]["work (alpha)"].AttemptJobID)
|
||||
assert.Equal(t, int64(102), out["work"]["work (beta)"].AttemptJobID)
|
||||
assert.Equal(t, int64(103), out["work"]["work (gamma)"].AttemptJobID)
|
||||
assert.Equal(t, int64(104), out["summary"]["summary"].AttemptJobID)
|
||||
}
|
||||
|
||||
t.Run("matrix instances and non-matrix sibling are indexed by (JobID, Name)", func(t *testing.T) {
|
||||
// "current" = attempt 2; prior = attempt 1, which is the immediately preceding attempt.
|
||||
out, err := GetPriorAttemptChildrenByParent(ctx, run.ID, attempt2.ID, callerAttemptJobID)
|
||||
require.NoError(t, err)
|
||||
assertAttempt1Children(t, out)
|
||||
})
|
||||
|
||||
t.Run("walkback past an attempt where the caller had no children", func(t *testing.T) {
|
||||
attempt3 := insertAttempt(t, 3, StatusRunning)
|
||||
// "current" = attempt 3; the immediately preceding attempt 2 has no children, so the lookup must walk further back to attempt 1.
|
||||
out, err := GetPriorAttemptChildrenByParent(ctx, run.ID, attempt3.ID, callerAttemptJobID)
|
||||
require.NoError(t, err)
|
||||
assertAttempt1Children(t, out)
|
||||
})
|
||||
t.Run("matrix instances and non-matrix sibling are indexed by (JobID, Name)", func(t *testing.T) {
|
||||
// "current" = attempt 2; prior = attempt 1, which is the immediately preceding attempt.
|
||||
out, err := GetPriorAttemptChildrenByParent(ctx, run.ID, attempt2.ID, callerAttemptJobID)
|
||||
require.NoError(t, err)
|
||||
assertAttempt1Children(t, out)
|
||||
})
|
||||
|
||||
t.Run("cancel reusable callers", func(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name, title, workflowID string
|
||||
index int64
|
||||
status Status
|
||||
}{
|
||||
{"NestedBlockedReusableCaller", "cancel-nested-caller", "caller.yaml", 9701, StatusBlocked},
|
||||
{"CallerWaitsForCancellingChild", "caller-cancelling-child", "test.yaml", 9811, StatusRunning},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
run := &ActionRun{
|
||||
Title: tc.title,
|
||||
RepoID: 4,
|
||||
Index: tc.index,
|
||||
OwnerID: 1,
|
||||
WorkflowID: tc.workflowID,
|
||||
TriggerUserID: 1,
|
||||
Ref: "refs/heads/master",
|
||||
CommitSHA: "c2d72f548424103f01ee1dc02889c1e2bff816b0",
|
||||
Event: "push",
|
||||
TriggerEvent: "push",
|
||||
EventPayload: "{}",
|
||||
Status: tc.status,
|
||||
}
|
||||
require.NoError(t, db.Insert(ctx, run))
|
||||
|
||||
attempt := &ActionRunAttempt{RepoID: run.RepoID, RunID: run.ID, Attempt: 1, TriggerUserID: 1, Status: tc.status}
|
||||
require.NoError(t, db.Insert(ctx, attempt))
|
||||
run.LatestAttemptID = attempt.ID
|
||||
require.NoError(t, UpdateRun(ctx, run, "latest_attempt_id"))
|
||||
|
||||
newJob := func(job *ActionRunJob) *ActionRunJob {
|
||||
job.RunID = run.ID
|
||||
job.RunAttemptID = attempt.ID
|
||||
job.RepoID = run.RepoID
|
||||
job.OwnerID = run.OwnerID
|
||||
job.CommitSHA = run.CommitSHA
|
||||
job.JobID = job.Name
|
||||
job.Attempt = 1
|
||||
job.Status = tc.status
|
||||
require.NoError(t, db.Insert(ctx, job))
|
||||
return job
|
||||
}
|
||||
|
||||
// A reusable caller subtree with a Blocked descendant (e.g. a nested caller stuck on an invalid `uses:`) must aggregate to Cancelled, when the run is cancelled.
|
||||
if tc.status == StatusBlocked {
|
||||
// outer: a valid top-level caller that expanded; inner: a nested caller stuck Blocked (invalid uses, never expands).
|
||||
outer := newJob(&ActionRunJob{Name: "outer", AttemptJobID: 1, IsReusableCaller: true, CallUses: "./.gitea/workflows/lib.yml"})
|
||||
inner := newJob(&ActionRunJob{Name: "inner", AttemptJobID: 2, ParentJobID: outer.ID, IsReusableCaller: true, CallUses: "https://other.example.com/o/r/.gitea/workflows/ci.yml@v1"})
|
||||
|
||||
// Cancel all jobs of the attempt, ordered by id (parent before child).
|
||||
jobs, err := GetRunJobsByRunAndAttemptID(ctx, run.ID, attempt.ID)
|
||||
require.NoError(t, err)
|
||||
_, err = CancelJobs(ctx, jobs, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, j := range []*ActionRunJob{outer, inner} {
|
||||
got := unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: j.ID})
|
||||
assert.Equal(t, StatusCancelled, got.Status, "job %q should be cancelled", j.JobID)
|
||||
}
|
||||
gotAttempt := unittest.AssertExistsAndLoadBean(t, &ActionRunAttempt{ID: attempt.ID})
|
||||
assert.Equal(t, StatusCancelled, gotAttempt.Status, "attempt must aggregate to Cancelled")
|
||||
gotRun := unittest.AssertExistsAndLoadBean(t, &ActionRun{ID: run.ID})
|
||||
assert.Equal(t, StatusCancelled, gotRun.Status, "run must aggregate to Cancelled, not stay Blocked")
|
||||
} else {
|
||||
outer := newJob(&ActionRunJob{Name: "outer", IsReusableCaller: true, IsExpanded: true})
|
||||
inner := newJob(&ActionRunJob{Name: "inner", ParentJobID: outer.ID, IsReusableCaller: true, IsExpanded: true})
|
||||
child := newJob(&ActionRunJob{Name: "child", ParentJobID: inner.ID})
|
||||
|
||||
runner := &ActionRunner{UUID: "caller-cancelling-child", Name: "caller-cancelling-child", HasCancellingSupport: true}
|
||||
require.NoError(t, db.Insert(ctx, runner))
|
||||
task := &ActionTask{JobID: child.ID, Attempt: 1, RunnerID: runner.ID, Status: StatusRunning, Started: timeutil.TimeStampNow(), RepoID: run.RepoID, OwnerID: run.OwnerID, CommitSHA: run.CommitSHA}
|
||||
require.NoError(t, db.Insert(ctx, task))
|
||||
child.TaskID = task.ID
|
||||
_, err := UpdateRunJob(ctx, child, nil, "task_id")
|
||||
require.NoError(t, err)
|
||||
|
||||
cancelled, err := CancelJobs(ctx, []*ActionRunJob{outer}, false)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, cancelled, 3)
|
||||
for _, job := range []*ActionRunJob{outer, inner, child} {
|
||||
assert.Equal(t, StatusCancelling, unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: job.ID}).Status, job.Name)
|
||||
}
|
||||
|
||||
_, err = UpdateTaskByState(ctx, runner.ID, &runnerv1.TaskState{Id: task.ID, Result: runnerv1.Result_RESULT_CANCELLED})
|
||||
require.NoError(t, err)
|
||||
for _, job := range []*ActionRunJob{outer, inner, child} {
|
||||
assert.Equal(t, StatusCancelled, unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: job.ID}).Status, job.Name)
|
||||
}
|
||||
assert.Equal(t, StatusCancelled, unittest.AssertExistsAndLoadBean(t, &ActionRun{ID: run.ID}).Status)
|
||||
}
|
||||
})
|
||||
}
|
||||
t.Run("walkback past an attempt where the caller had no children", func(t *testing.T) {
|
||||
attempt3 := insertAttempt(t, 3, StatusRunning)
|
||||
// "current" = attempt 3; the immediately preceding attempt 2 has no children, so the lookup must walk further back to attempt 1.
|
||||
out, err := GetPriorAttemptChildrenByParent(ctx, run.ID, attempt3.ID, callerAttemptJobID)
|
||||
require.NoError(t, err)
|
||||
assertAttempt1Children(t, out)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("batch load and cancel run jobs", func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
// A reusable caller subtree with a Blocked descendant (e.g. a nested caller stuck on an invalid `uses:`) must aggregate to Cancelled, when the run is cancelled.
|
||||
func TestCancelJobs_NestedBlockedReusableCaller(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
ctx := t.Context()
|
||||
|
||||
legacyRun := &ActionRun{RepoID: 4, OwnerID: 1, Index: 9600, TriggerUserID: 1, Ref: "refs/heads/batch", Status: StatusRunning}
|
||||
run := &ActionRun{RepoID: 4, OwnerID: 1, Index: 9601, TriggerUserID: 1, Ref: "refs/heads/batch", Status: StatusRunning}
|
||||
require.NoError(t, db.Insert(ctx, legacyRun, run))
|
||||
require.NoError(t, db.Insert(ctx, &ActionRunJob{RunID: legacyRun.ID, RepoID: legacyRun.RepoID, OwnerID: legacyRun.OwnerID}))
|
||||
oldAttempt := &ActionRunAttempt{RepoID: run.RepoID, RunID: run.ID, Attempt: 1, TriggerUserID: 1}
|
||||
latestAttempt := &ActionRunAttempt{RepoID: run.RepoID, RunID: run.ID, Attempt: 2, TriggerUserID: 2}
|
||||
require.NoError(t, db.Insert(ctx, oldAttempt, latestAttempt))
|
||||
for _, attemptID := range []int64{oldAttempt.ID, latestAttempt.ID, latestAttempt.ID} {
|
||||
require.NoError(t, db.Insert(ctx, &ActionRunJob{RunID: run.ID, RunAttemptID: attemptID, RepoID: run.RepoID, OwnerID: run.OwnerID}))
|
||||
run := &ActionRun{
|
||||
Title: "cancel-nested-caller",
|
||||
RepoID: 4,
|
||||
Index: 9701,
|
||||
OwnerID: 1,
|
||||
WorkflowID: "caller.yaml",
|
||||
TriggerUserID: 1,
|
||||
Ref: "refs/heads/master",
|
||||
CommitSHA: "c2d72f548424103f01ee1dc02889c1e2bff816b0",
|
||||
Event: "push",
|
||||
TriggerEvent: "push",
|
||||
EventPayload: "{}",
|
||||
Status: StatusBlocked,
|
||||
}
|
||||
require.NoError(t, db.Insert(ctx, run))
|
||||
|
||||
attempt := &ActionRunAttempt{RepoID: run.RepoID, RunID: run.ID, Attempt: 1, TriggerUserID: 1, Status: StatusBlocked}
|
||||
require.NoError(t, db.Insert(ctx, attempt))
|
||||
run.LatestAttemptID = attempt.ID
|
||||
require.NoError(t, UpdateRun(ctx, run, "latest_attempt_id"))
|
||||
|
||||
newJob := func(name string, attemptJobID, parentID int64, callUses string) *ActionRunJob {
|
||||
job := &ActionRunJob{
|
||||
RunID: run.ID,
|
||||
RunAttemptID: attempt.ID,
|
||||
RepoID: run.RepoID,
|
||||
OwnerID: run.OwnerID,
|
||||
CommitSHA: run.CommitSHA,
|
||||
Name: name,
|
||||
JobID: name,
|
||||
Attempt: 1,
|
||||
Status: StatusBlocked,
|
||||
AttemptJobID: attemptJobID,
|
||||
IsReusableCaller: true,
|
||||
CallUses: callUses,
|
||||
ParentJobID: parentID,
|
||||
}
|
||||
run.LatestAttemptID = latestAttempt.ID
|
||||
require.NoError(t, UpdateRun(ctx, run, "latest_attempt_id"))
|
||||
require.NoError(t, db.Insert(ctx, job))
|
||||
return job
|
||||
}
|
||||
|
||||
runs := RunList{legacyRun, run}
|
||||
jobsByRun, err := GetLatestAttemptJobsByRuns(ctx, runs)
|
||||
require.NoError(t, err)
|
||||
for _, listedRun := range runs {
|
||||
expected, err := GetLatestAttemptJobsByRun(ctx, listedRun)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, expected)
|
||||
assert.Equal(t, expected, jobsByRun[listedRun.ID])
|
||||
}
|
||||
// outer: a valid top-level caller that expanded; inner: a nested caller stuck Blocked (invalid uses, never expands).
|
||||
outer := newJob("outer", 1, 0, "./.gitea/workflows/lib.yml")
|
||||
inner := newJob("inner", 2, outer.ID, "https://other.example.com/o/r/.gitea/workflows/ci.yml@v1")
|
||||
|
||||
attempts, err := runs.GetLatestAttempts(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, attempts, 1)
|
||||
assert.EqualValues(t, 2, attempts[latestAttempt.ID].TriggerUser.ID)
|
||||
// Cancel all jobs of the attempt, ordered by id (parent before child).
|
||||
jobs, err := GetRunJobsByRunAndAttemptID(ctx, run.ID, attempt.ID)
|
||||
require.NoError(t, err)
|
||||
_, err = CancelJobs(ctx, jobs, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
chunkedAttempts := []*ActionRunAttempt{oldAttempt}
|
||||
for i := range db.DefaultMaxInSize {
|
||||
chunkedAttempts = append(chunkedAttempts, &ActionRunAttempt{ID: -int64(i + 1)})
|
||||
}
|
||||
attemptJobs, err := getRunJobsByAttempts(ctx, append(chunkedAttempts, latestAttempt))
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, attemptJobs, 3)
|
||||
|
||||
cancelled, err := CancelPreviousJobs(ctx, run.RepoID, run.Ref, "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, cancelled, 4)
|
||||
})
|
||||
for _, j := range []*ActionRunJob{outer, inner} {
|
||||
got := unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: j.ID})
|
||||
assert.Equal(t, StatusCancelled, got.Status, "job %q should be cancelled", j.JobID)
|
||||
}
|
||||
gotAttempt := unittest.AssertExistsAndLoadBean(t, &ActionRunAttempt{ID: attempt.ID})
|
||||
assert.Equal(t, StatusCancelled, gotAttempt.Status, "attempt must aggregate to Cancelled")
|
||||
gotRun := unittest.AssertExistsAndLoadBean(t, &ActionRun{ID: run.ID})
|
||||
assert.Equal(t, StatusCancelled, gotRun.Status, "run must aggregate to Cancelled, not stay Blocked")
|
||||
}
|
||||
|
||||
func TestSettleRunAfterCancel(t *testing.T) {
|
||||
|
||||
@@ -5,8 +5,6 @@ package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
@@ -44,21 +42,6 @@ func (runs RunList) LoadTriggerUser(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLatestAttempts returns the latest attempts of the runs keyed by attempt ID
|
||||
func (runs RunList) GetLatestAttempts(ctx context.Context) (map[int64]*ActionRunAttempt, error) {
|
||||
attemptIDs := container.FilterSlice(runs, func(run *ActionRun) (int64, bool) {
|
||||
return run.LatestAttemptID, run.LatestAttemptID != 0
|
||||
})
|
||||
attempts := make(map[int64]*ActionRunAttempt, len(attemptIDs))
|
||||
if len(attemptIDs) == 0 {
|
||||
return attempts, nil
|
||||
}
|
||||
if err := db.GetEngine(ctx).In("id", attemptIDs).Find(&attempts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return attempts, ActionRunAttemptList(slices.Collect(maps.Values(attempts))).LoadTriggerUser(ctx)
|
||||
}
|
||||
|
||||
func (runs RunList) LoadRepos(ctx context.Context) error {
|
||||
repoIDs := container.FilterSlice(runs, func(run *ActionRun) (int64, bool) {
|
||||
return run.RepoID, run.Repo == nil
|
||||
|
||||
@@ -200,21 +200,13 @@ func (r *ActionRunner) GenerateAndFillToken() {
|
||||
// CanMatchLabels checks whether the runner's labels can match a job's "runs-on"
|
||||
// See https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idruns-on
|
||||
func (r *ActionRunner) CanMatchLabels(jobRunsOn []string) bool {
|
||||
return len(jobRunsOn) > 0 && !slices.ContainsFunc(jobRunsOn, func(label string) bool { return !util.SliceContainsString(r.AgentLabels, label, true) })
|
||||
return !slices.ContainsFunc(jobRunsOn, func(label string) bool { return !util.SliceContainsString(r.AgentLabels, label, true) })
|
||||
}
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(&ActionRunner{})
|
||||
}
|
||||
|
||||
func GetRunnersMapByIDs(ctx context.Context, ids []int64) (map[int64]*ActionRunner, error) {
|
||||
runners := make(map[int64]*ActionRunner, len(ids))
|
||||
if len(ids) == 0 {
|
||||
return runners, nil
|
||||
}
|
||||
return runners, db.GetEngine(ctx).In("id", ids).Find(&runners)
|
||||
}
|
||||
|
||||
// FindRunnerOptions
|
||||
// ownerID == 0 and repoID == 0 means any runner including global runners
|
||||
// repoID != 0 and WithAvailable == false means any runner for the given repo
|
||||
|
||||
@@ -12,32 +12,72 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestShouldPersistRunnerTimestamps(t *testing.T) {
|
||||
for _, scenario := range []struct {
|
||||
name string
|
||||
persist func(timeutil.TimeStamp, time.Time) bool
|
||||
interval time.Duration
|
||||
freshAge time.Duration
|
||||
func TestShouldPersistLastOnline(t *testing.T) {
|
||||
now := time.Now()
|
||||
tests := []struct {
|
||||
name string
|
||||
last timeutil.TimeStamp
|
||||
want bool
|
||||
}{
|
||||
{"online", ShouldPersistLastOnline, RunnerHeartbeatInterval, 5 * time.Second},
|
||||
{"active", ShouldPersistLastActive, RunnerActiveInterval, time.Second},
|
||||
} {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
now := time.Now()
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
last timeutil.TimeStamp
|
||||
want bool
|
||||
}{
|
||||
{"fresh, skip write", timeutil.TimeStamp(now.Add(-scenario.freshAge).Unix()), false},
|
||||
{"exactly at interval, write", timeutil.TimeStamp(now.Add(-scenario.interval).Unix()), true},
|
||||
{"stale, write", timeutil.TimeStamp(now.Add(-2 * scenario.interval).Unix()), true},
|
||||
{"zero (never seen), write", 0, true},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, scenario.persist(tt.last, now))
|
||||
})
|
||||
}
|
||||
{
|
||||
name: "fresh, skip write",
|
||||
last: timeutil.TimeStamp(now.Add(-5 * time.Second).Unix()),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "exactly at interval, write",
|
||||
last: timeutil.TimeStamp(now.Add(-RunnerHeartbeatInterval).Unix()),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "stale, write",
|
||||
last: timeutil.TimeStamp(now.Add(-2 * RunnerHeartbeatInterval).Unix()),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "zero (never seen), write",
|
||||
last: 0,
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, ShouldPersistLastOnline(tt.last, now))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldPersistLastActive(t *testing.T) {
|
||||
now := time.Now()
|
||||
tests := []struct {
|
||||
name string
|
||||
last timeutil.TimeStamp
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "fresh, skip write",
|
||||
last: timeutil.TimeStamp(now.Add(-1 * time.Second).Unix()),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "exactly at interval, write",
|
||||
last: timeutil.TimeStamp(now.Add(-RunnerActiveInterval).Unix()),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "stale, write",
|
||||
last: timeutil.TimeStamp(now.Add(-2 * RunnerActiveInterval).Unix()),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "zero (never seen), write",
|
||||
last: 0,
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, ShouldPersistLastActive(tt.last, now))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -46,5 +86,4 @@ func TestCanMatchLabelsCaseInsensitive(t *testing.T) {
|
||||
runner := &ActionRunner{AgentLabels: []string{"self-hosted", "Linux", "X64"}}
|
||||
assert.True(t, runner.CanMatchLabels([]string{"SELF-HOSTED", "linux"}))
|
||||
assert.False(t, runner.CanMatchLabels([]string{"linux", "arm64"}))
|
||||
assert.False(t, runner.CanMatchLabels(nil))
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user