mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-29 00:59:39 +09:00
Compare commits
116
Commits
c186cc4b8d
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
453c38d7d6 | ||
|
|
062c8f2241 | ||
|
|
fbdeea0af7 | ||
|
|
b7cfa4e67e | ||
|
|
66c672d0b7 | ||
|
|
3b6ce586a7 | ||
|
|
2c6dacb015 | ||
|
|
646ea0f253 | ||
|
|
3c4d5a6a5c | ||
|
|
3005efa88d | ||
|
|
e21c37703e | ||
|
|
90c43e8e78 | ||
|
|
7668e7c00d | ||
|
|
38747d48fe | ||
|
|
1680ac24e6 | ||
|
|
c8660364d9 | ||
|
|
d17ccd4434 | ||
|
|
9eb4a9afad | ||
|
|
0b1067484f | ||
|
|
2a17cf7ded | ||
|
|
6b929ccb15 | ||
|
|
32728fc581 | ||
|
|
59a43c8733 | ||
|
|
3a806a58d0 | ||
|
|
4c915ae0c2 | ||
|
|
9914db8898 | ||
|
|
51b8da8b01 | ||
|
|
e50e4ed869 | ||
|
|
2bcf950b78 | ||
|
|
4852091e85 | ||
|
|
1c16f04bf5 | ||
|
|
0bed1232ee | ||
|
|
adc3db1f27 | ||
|
|
55a5f50961 | ||
|
|
204c0bafd3 | ||
|
|
bedd2afb47 | ||
|
|
d8f0e7e679 | ||
|
|
1fa6465efd | ||
|
|
e6af4c341c | ||
|
|
9251eeb66b | ||
|
|
5fc3cec87f | ||
|
|
51e42d4b11 | ||
|
|
66d6f74cb0 | ||
|
|
cce2360846 | ||
|
|
89c7019a3c | ||
|
|
84b67d50a6 | ||
|
|
fa0b39a42b | ||
|
|
6bb6ce678b | ||
|
|
f7072b0305 | ||
|
|
2d6fea5bdf | ||
|
|
1f349eb1eb | ||
|
|
d2bc0097bc | ||
|
|
5eba4f92ce | ||
|
|
aa96725ae7 | ||
|
|
fa4c0b1cf6 | ||
|
|
2b81cbbe70 | ||
|
|
ad05aaee80 | ||
|
|
a52e5f53c0 | ||
|
|
db24633e6d | ||
|
|
920b5f1e68 | ||
|
|
7306d5aff8 | ||
|
|
fcc23af280 | ||
|
|
fe567d26c1 | ||
|
|
ffd982c7ab | ||
|
|
475e51c7e0 | ||
|
|
943f026844 | ||
|
|
61be9fcdfa | ||
|
|
c778c6c920 | ||
|
|
fa5d876171 | ||
|
|
8f7eb9f161 | ||
|
|
ed4d7ea08d | ||
|
|
89b891b168 | ||
|
|
35786a6ca1 | ||
|
|
4e813a262f | ||
|
|
e355c39e91 | ||
|
|
f261adb53f | ||
|
|
c121f02a7e | ||
|
|
6904f6480c | ||
|
|
6c425fae6e | ||
|
|
5433c23dec | ||
|
|
83af7aa92e | ||
|
|
c95e3f3b00 | ||
|
|
c082b9a5ff | ||
|
|
e8e1973e16 | ||
|
|
19ee791fe7 | ||
|
|
3842e021e0 | ||
|
|
df71d5f5e2 | ||
|
|
55e7cafcb6 | ||
|
|
ed4a23e893 | ||
|
|
1cf904f101 | ||
|
|
e223c42ee6 | ||
|
|
551a6bb3a4 | ||
|
|
7857c5f843 | ||
|
|
346e6bab67 | ||
|
|
dea71bb8ba | ||
|
|
596b7f7a25 | ||
|
|
1b21c8a1e6 | ||
|
|
3cd6672e48 | ||
|
|
63f2918336 | ||
|
|
5e4d21acd5 | ||
|
|
56ad4689ad | ||
|
|
133a3b8567 | ||
|
|
2b8ea5476c | ||
|
|
43ace7cc8a | ||
|
|
a96a73c364 | ||
|
|
07843086c2 | ||
|
|
5b7b00477a | ||
|
|
dbe311197c | ||
|
|
b6368965fb | ||
|
|
befeacdf7d | ||
|
|
72a9debaff | ||
|
|
8b40df255b | ||
|
|
68feaba2ed | ||
|
|
d2be79a942 | ||
|
|
01e9febbea | ||
|
|
5287860efb |
@@ -10,7 +10,9 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
with:
|
||||
cache-image: false
|
||||
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
- name: Build regular image
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
|
||||
@@ -31,7 +31,7 @@ runs:
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
|
||||
restore-keys: gomod-${{ runner.os }}-${{ runner.arch }}
|
||||
restore-keys: gomod-${{ runner.os }}-${{ runner.arch }}-
|
||||
- if: ${{ github.workflow == 'cache-seeder' && inputs.lint-cache != 'true' }}
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
name: node-setup
|
||||
description: Set up pnpm and node and restore caches
|
||||
description: Set up pnpm and node and restore the pnpm store cache
|
||||
|
||||
inputs:
|
||||
cache:
|
||||
description: Cache pnpm downloads
|
||||
description: Restore the pnpm store cache
|
||||
default: "true"
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 26
|
||||
- if: ${{ inputs.cache == 'true' }}
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
id: store
|
||||
shell: bash
|
||||
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
- if: ${{ inputs.cache == 'true' && github.workflow == 'cache-seeder' }}
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
node-version: 26
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- if: ${{ inputs.cache != 'true' }}
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
path: ${{ steps.store.outputs.path }}
|
||||
key: pnpm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
- if: ${{ inputs.cache == 'true' && github.workflow != 'cache-seeder' }}
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
node-version: 26
|
||||
path: ${{ steps.store.outputs.path }}
|
||||
key: pnpm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: pnpm-${{ runner.os }}-${{ runner.arch }}-
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
name: python-setup
|
||||
description: Set up uv and python and restore the uv cache
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
||||
with:
|
||||
python-version: 3.14
|
||||
save-cache: ${{ github.workflow == 'cache-seeder' }}
|
||||
@@ -5,7 +5,7 @@ name: cache-prune
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "37 2 * * *" # every day at 02:37 UTC
|
||||
- cron: "37 */6 * * *" # every six hours at :37
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
|
||||
@@ -24,15 +24,25 @@ jobs:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
steps:
|
||||
# Deletes least recently used first, the order GitHub itself evicts in, which takes
|
||||
# superseded generations first as those stop being restored once a newer one exists.
|
||||
# Keep the newest generation per key, restores never reach the older ones.
|
||||
- name: delete superseded caches
|
||||
run: |
|
||||
gh cache list --limit 1000 --json id,key,ref,createdAt |
|
||||
jq -r 'group_by([.ref, (.key | sub("(-go[0-9.]+)?-[0-9a-f]{40,64}(-[0-9]+-[0-9]+)?$"; ""))])[]
|
||||
| sort_by(.createdAt)[:-1][] | "\(.id) \(.key)"' |
|
||||
while read -r id key; do
|
||||
echo "deleting $key"
|
||||
gh cache delete "$id" || true
|
||||
done
|
||||
|
||||
# Deletes least recently used first, the order GitHub itself evicts in.
|
||||
- name: delete caches over the size limit
|
||||
run: |
|
||||
caches=$(gh cache list --limit 1000 --sort last_accessed_at --order asc --json id,key,sizeInBytes)
|
||||
size=$(jq '[.[].sizeInBytes] | add // 0' <<< "$caches")
|
||||
echo "cache usage: $((size / 1000000)) MB"
|
||||
while [ "$size" -gt 6500000000 ] && read -r id bytes key; do
|
||||
while [ "$size" -gt 6000000000 ] && read -r id bytes key; do
|
||||
echo "deleting $key"
|
||||
gh cache delete "$id"
|
||||
gh cache delete "$id" || true
|
||||
size=$((size - bytes))
|
||||
done <<< "$(jq -r '.[] | "\(.id) \(.sizeInBytes) \(.key)"' <<< "$caches")"
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
# Populates main's cache scope so PR runs warm-start from it. Saves the go
|
||||
# module, go build (incl. test compile), and golangci-lint caches.
|
||||
#
|
||||
# Caches are ref-scoped: PR runs read their own scope then fall back to the
|
||||
# base branch. Per .github/actions/go-cache/action.yml, PRs are restore-only,
|
||||
# so push-to-main is the only opportunity to populate the fallback scope.
|
||||
# Populates main's cache scope so PR runs warm-start from it. Caches are ref-scoped:
|
||||
# PR runs read their own scope then fall back to the base branch, and only this
|
||||
# workflow saves, so push-to-main is the only chance to populate the fallback scope.
|
||||
|
||||
name: cache-seeder
|
||||
|
||||
@@ -16,8 +13,13 @@ on:
|
||||
- "go.mod" # a toolchain bump invalidates the build caches
|
||||
- "go.sum"
|
||||
- ".golangci.yml"
|
||||
- "pnpm-lock.yaml"
|
||||
- "pyproject.toml"
|
||||
- "uv.lock"
|
||||
- ".github/actions/go-cache/action.yml"
|
||||
- ".github/actions/go-setup/action.yml"
|
||||
- ".github/actions/node-setup/action.yml"
|
||||
- ".github/actions/python-setup/action.yml"
|
||||
- ".github/workflows/cache-seeder.yml"
|
||||
|
||||
concurrency:
|
||||
@@ -35,7 +37,7 @@ jobs:
|
||||
- uses: ./.github/actions/go-setup
|
||||
- run: make deps-backend deps-tools
|
||||
- run: TAGS="bindata" make backend
|
||||
- run: TAGS="bindata gogit" GOEXPERIMENT="" make backend
|
||||
- run: TAGS="bindata gogit" make backend
|
||||
- name: warm test compile cache (bindata)
|
||||
env:
|
||||
TAGS: bindata
|
||||
@@ -44,13 +46,12 @@ jobs:
|
||||
- name: warm test compile cache (bindata gogit)
|
||||
env:
|
||||
TAGS: bindata gogit
|
||||
GOEXPERIMENT:
|
||||
GOTEST_FLAGS: -race -list=^$$ -count=1
|
||||
run: make test-backend
|
||||
- name: warm integration compile cache
|
||||
run: |
|
||||
TAGS="bindata" make test-integration-compile
|
||||
TAGS="bindata gogit" GOEXPERIMENT="" make test-integration-compile
|
||||
TAGS="bindata gogit" make test-integration-compile
|
||||
TAGS="bindata gogit" GOTEST_FLAGS="-race" make test-integration-compile
|
||||
|
||||
lint:
|
||||
@@ -74,9 +75,23 @@ jobs:
|
||||
TAGS: ${{ matrix.tags }}
|
||||
TARGET: ${{ matrix.target }}
|
||||
|
||||
frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- uses: ./.github/actions/node-setup
|
||||
- run: make deps-frontend
|
||||
|
||||
python:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- uses: ./.github/actions/python-setup
|
||||
- run: make deps-py
|
||||
|
||||
# reclaims the caches this run superseded, so the next save still fits in the allowance
|
||||
prune:
|
||||
needs: [gobuild, lint]
|
||||
needs: [gobuild, lint, frontend, python]
|
||||
permissions:
|
||||
actions: write
|
||||
uses: ./.github/workflows/cache-prune.yml
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- uses: renovatebot/github-action@316d7cd859606d6039a2182b7d69199e9b036835 # v46.2.1
|
||||
- uses: renovatebot/github-action@e09d604f8f803bb527bd8321ed5be06c460b8682 # v46.2.2
|
||||
with:
|
||||
renovate-version: ${{ env.RENOVATE_VERSION }}
|
||||
configurationFile: renovate.json5
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
shell: ${{ steps.changes.outputs.shell }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
|
||||
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
@@ -73,8 +73,9 @@ jobs:
|
||||
frontend:
|
||||
- "*.ts"
|
||||
- "web_src/**"
|
||||
- "tools/generate-svg.ts"
|
||||
- "tools/generate-svg-vscode-extensions.json"
|
||||
- "tools/**/*.ts"
|
||||
- "tools/**/*.json"
|
||||
- "tools/playwright.sh"
|
||||
- "tsconfig.json"
|
||||
- "assets/emoji.json"
|
||||
- "package.json"
|
||||
@@ -134,6 +135,7 @@ jobs:
|
||||
e2e:
|
||||
- "tests/e2e/**"
|
||||
- "tools/test-e2e.sh"
|
||||
- "tools/playwright.sh"
|
||||
- "playwright.config.ts"
|
||||
|
||||
shell:
|
||||
|
||||
@@ -24,8 +24,7 @@ jobs:
|
||||
with:
|
||||
lint-cache: "true"
|
||||
- run: make deps-backend deps-tools
|
||||
- run: TAGS="bindata" make generate-go # lint-go also lints with "bindata" tags which requires "_bindata.go"
|
||||
- run: make lint-backend
|
||||
- run: TAGS="bindata" make generate-go lint-backend # lint-go can lint with "bindata" tags
|
||||
|
||||
lint-on-demand:
|
||||
needs: files-changed
|
||||
@@ -42,9 +41,7 @@ jobs:
|
||||
- run: make lint-spell
|
||||
|
||||
- if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
||||
with:
|
||||
python-version: 3.14
|
||||
uses: ./.github/actions/python-setup
|
||||
- if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true'
|
||||
run: make deps-py lint-templates lint-yaml
|
||||
|
||||
@@ -77,6 +74,7 @@ jobs:
|
||||
- run: make deps-frontend
|
||||
- run: make lint-frontend
|
||||
- run: make checks-frontend
|
||||
- run: make playwright
|
||||
- run: make test-frontend
|
||||
- run: make frontend
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
timeout-minutes: 50
|
||||
services:
|
||||
pgsql:
|
||||
image: postgres:14@sha256:2f439458ab6a57a925825ae14f9d06910e4fe4a41c8d4a0ae06397e65b707e1b
|
||||
image: postgres:14@sha256:2fdfb9b432d4a73bd3eea3d989752c1e669b68d502347e0bfd2cc6d709f3d6b4
|
||||
env:
|
||||
POSTGRES_DB: test
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
timeout-minutes: 50
|
||||
services:
|
||||
pgsql:
|
||||
image: postgres:14@sha256:2f439458ab6a57a925825ae14f9d06910e4fe4a41c8d4a0ae06397e65b707e1b
|
||||
image: postgres:14@sha256:2fdfb9b432d4a73bd3eea3d989752c1e669b68d502347e0bfd2cc6d709f3d6b4
|
||||
env:
|
||||
POSTGRES_DB: test
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -96,7 +96,6 @@ jobs:
|
||||
- run: make backend
|
||||
env:
|
||||
TAGS: bindata gogit
|
||||
GOEXPERIMENT:
|
||||
- run: GITEA_TEST_DATABASE=sqlite make test-migration
|
||||
env:
|
||||
TAGS: bindata gogit
|
||||
@@ -107,7 +106,6 @@ jobs:
|
||||
# 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 gogit
|
||||
GOEXPERIMENT:
|
||||
|
||||
test-unit:
|
||||
if: needs.files-changed.outputs.backend == 'true'
|
||||
@@ -125,13 +123,13 @@ jobs:
|
||||
ports:
|
||||
- "9200:9200"
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1@sha256:d36e713e8f89483af1ab0d72011bbd503f5ab100b68ccbfad51c39e3f0a0567d
|
||||
image: getmeili/meilisearch:v1@sha256:8d6643d86d71fad6ad3cba92cde7ccfce9e4d6c384bda67598eb553571c32431
|
||||
env:
|
||||
MEILI_ENV: development # disable auth
|
||||
ports:
|
||||
- "7700:7700"
|
||||
redis:
|
||||
image: redis:latest@sha256:52334768d4a6594d8969f51a1a6fee3ffa7545f6359a4877229cdc754d2def82
|
||||
image: redis:latest@sha256:1c4405ec7fb6ed58b6b83d26c7e3fc80625d2aca6cfa517ae03b6e963510f7ca
|
||||
options: >- # wait until redis has started
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 5s
|
||||
@@ -170,7 +168,6 @@ jobs:
|
||||
env:
|
||||
GOTEST_FLAGS: -race -timeout=20m
|
||||
TAGS: bindata gogit
|
||||
GOEXPERIMENT:
|
||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
||||
GITEA_TEST_CI_SKIP_EXTERNAL: true
|
||||
- run: make test-check
|
||||
|
||||
@@ -25,8 +25,15 @@ jobs:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1.3.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: |
|
||||
|
||||
@@ -28,10 +28,7 @@ jobs:
|
||||
cache: false
|
||||
- uses: ./.github/actions/node-setup
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
env:
|
||||
TAGS: bindata
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
- name: import gpg key
|
||||
@@ -49,7 +46,7 @@ jobs:
|
||||
cosign sign-blob "$f" --bundle "$f.sigstore.json" --yes
|
||||
echo "$GPG_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u "$GPG_FINGERPRINT" --output "$f.asc" "$f"
|
||||
done
|
||||
# clean branch name to get the folder name in S3
|
||||
# clean branch name to get the folder name in the object storage
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
env:
|
||||
@@ -58,25 +55,11 @@ jobs:
|
||||
REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
- name: upload binaries to s3
|
||||
env:
|
||||
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
|
||||
BRANCH: ${{ steps.clean_name.outputs.branch }}
|
||||
run: |
|
||||
aws s3 sync dist/release "s3://$AWS_S3_BUCKET/gitea/$BRANCH" --no-progress
|
||||
# configure-aws-credentials exports AWS_REGION job-wide and it wins over AWS_DEFAULT_REGION, so pin it here
|
||||
- name: upload binaries to cloudflare r2
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: auto
|
||||
AWS_REGION: auto
|
||||
CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }}
|
||||
CLOUDFLARE_R2_BUCKET: ${{ secrets.CLOUDFLARE_R2_BUCKET }}
|
||||
BRANCH: ${{ steps.clean_name.outputs.branch }}
|
||||
@@ -94,7 +77,9 @@ jobs:
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
with:
|
||||
cache-image: false
|
||||
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
env:
|
||||
|
||||
@@ -29,10 +29,7 @@ jobs:
|
||||
cache: false
|
||||
- uses: ./.github/actions/node-setup
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
env:
|
||||
TAGS: bindata
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
- name: import gpg key
|
||||
@@ -50,7 +47,7 @@ jobs:
|
||||
cosign sign-blob "$f" --bundle "$f.sigstore.json" --yes
|
||||
echo "$GPG_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u "$GPG_FINGERPRINT" --output "$f.asc" "$f"
|
||||
done
|
||||
# clean branch name to get the folder name in S3
|
||||
# clean branch name to get the folder name in the object storage
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
env:
|
||||
@@ -59,32 +56,18 @@ jobs:
|
||||
REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
- name: upload binaries to s3
|
||||
env:
|
||||
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
|
||||
BRANCH: ${{ steps.clean_name.outputs.branch }}
|
||||
run: |
|
||||
aws s3 sync dist/release "s3://$AWS_S3_BUCKET/gitea/$BRANCH" --no-progress
|
||||
# configure-aws-credentials exports AWS_REGION job-wide and it wins over AWS_DEFAULT_REGION, so pin it here
|
||||
- name: upload binaries to cloudflare r2
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: auto
|
||||
AWS_REGION: auto
|
||||
CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }}
|
||||
CLOUDFLARE_R2_BUCKET: ${{ secrets.CLOUDFLARE_R2_BUCKET }}
|
||||
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@af38ce09b1ec248aeb08eea2b16bbecea9e059f8 # v0.2.1
|
||||
uses: dev-hanz-ops/install-gh-cli-action@6089bdde54118ad7ca3d22053eb2d69387fd2779 # v0.3.0
|
||||
with:
|
||||
gh-cli-version: 2.39.1
|
||||
- name: create github release
|
||||
@@ -105,7 +88,9 @@ jobs:
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
with:
|
||||
cache-image: false
|
||||
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
id: meta
|
||||
with:
|
||||
|
||||
@@ -32,10 +32,7 @@ jobs:
|
||||
cache: false
|
||||
- uses: ./.github/actions/node-setup
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
env:
|
||||
TAGS: bindata
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
- name: import gpg key
|
||||
@@ -53,7 +50,7 @@ jobs:
|
||||
cosign sign-blob "$f" --bundle "$f.sigstore.json" --yes
|
||||
echo "$GPG_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u "$GPG_FINGERPRINT" --output "$f.asc" "$f"
|
||||
done
|
||||
# clean branch name to get the folder name in S3
|
||||
# clean branch name to get the folder name in the object storage
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
env:
|
||||
@@ -62,32 +59,18 @@ jobs:
|
||||
REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
- name: upload binaries to s3
|
||||
env:
|
||||
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
|
||||
BRANCH: ${{ steps.clean_name.outputs.branch }}
|
||||
run: |
|
||||
aws s3 sync dist/release "s3://$AWS_S3_BUCKET/gitea/$BRANCH" --no-progress
|
||||
# configure-aws-credentials exports AWS_REGION job-wide and it wins over AWS_DEFAULT_REGION, so pin it here
|
||||
- name: upload binaries to cloudflare r2
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: auto
|
||||
AWS_REGION: auto
|
||||
CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }}
|
||||
CLOUDFLARE_R2_BUCKET: ${{ secrets.CLOUDFLARE_R2_BUCKET }}
|
||||
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@af38ce09b1ec248aeb08eea2b16bbecea9e059f8 # v0.2.1
|
||||
uses: dev-hanz-ops/install-gh-cli-action@6089bdde54118ad7ca3d22053eb2d69387fd2779 # v0.3.0
|
||||
with:
|
||||
gh-cli-version: 2.39.1
|
||||
- name: create github release
|
||||
@@ -108,7 +91,9 @@ jobs:
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
with:
|
||||
cache-image: false
|
||||
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
id: meta
|
||||
with:
|
||||
|
||||
+15
-12
@@ -52,6 +52,8 @@ linters:
|
||||
desc: do not use the go-chi cache package, use gitea's cache system
|
||||
- pkg: github.com/pkg/errors
|
||||
desc: use builtin errors package instead
|
||||
- pkg: gitea.com/go-chi/binding
|
||||
desc: use our wrapper
|
||||
migrations:
|
||||
files:
|
||||
- '**/modelmigration/**/*.go'
|
||||
@@ -60,6 +62,9 @@ linters:
|
||||
desc: "migrations must not depend on the models package. HINT: MIGRATION-STRUCT-FROZEN"
|
||||
- pkg: gitea.dev/modules/structs
|
||||
desc: "migrations must not depend on modules/structs. HINT: MIGRATION-STRUCT-FROZEN"
|
||||
modernize:
|
||||
disable:
|
||||
- embedlit
|
||||
nolintlint:
|
||||
allow-unused: false
|
||||
require-explanation: true
|
||||
@@ -141,9 +146,6 @@ linters:
|
||||
- linters:
|
||||
- dupl
|
||||
path: modelmigration/v
|
||||
- linters:
|
||||
- forbidigo
|
||||
path: cmd
|
||||
- linters:
|
||||
- dupl
|
||||
text: (?i)webhook
|
||||
@@ -160,10 +162,10 @@ linters:
|
||||
- gocritic
|
||||
text: '(?i)exitAfterDefer:'
|
||||
paths:
|
||||
- node_modules
|
||||
- .venv
|
||||
- public
|
||||
- web_src
|
||||
- ^node_modules/
|
||||
- ^\.venv/
|
||||
- ^public/
|
||||
- ^web_src/
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
@@ -180,14 +182,15 @@ formatters:
|
||||
- blank
|
||||
- default
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
extra:
|
||||
group-params: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- node_modules
|
||||
- .venv
|
||||
- public
|
||||
- web_src
|
||||
- ^node_modules/
|
||||
- ^\.venv/
|
||||
- ^public/
|
||||
- ^web_src/
|
||||
|
||||
run:
|
||||
timeout: 10m
|
||||
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
disable=SC1091,SC2001,SC2002,SC2016,SC2028,SC2046,SC2124,SC2128,SC2129,SC2154,SC2155,SC2164,SC2181,SC2207
|
||||
# SC2153: false-alert "Possible misspelling: TAGS may not be assigned. Did you mean tags?". We already use strict mode.
|
||||
disable=SC1091,SC2001,SC2002,SC2016,SC2028,SC2046,SC2124,SC2128,SC2129,SC2153,SC2154,SC2155,SC2164,SC2181,SC2207
|
||||
|
||||
@@ -4,6 +4,44 @@ This changelog goes through the changes that have been made in each release
|
||||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
|
||||
## [1.27.2](https://github.com/go-gitea/gitea/releases/tag/v1.27.2) - 2026-08-14
|
||||
|
||||
* SECURITY
|
||||
* Fix: update collaborator access mode and httpsign (#38894, #38862) (#38895)
|
||||
* Refactor: external render (#38885) (#38898)
|
||||
* Fix(actions): resolve pull_request_target reusable workflows at the base commit (#38886) (#38897)
|
||||
* Refactor: markup render (#38864) (#38869)
|
||||
* Fix(deps): update dependency mermaid to v11.16.1 (#38816)
|
||||
* Fix(auth): set WebAuthn user verification per request (#38805) (#38810)
|
||||
* Fix: render highlight language (#38793) (#38795)
|
||||
|
||||
* ENHANCEMENTS
|
||||
* enhance: add missing npm package metadata properties (#38826) (#38831)
|
||||
|
||||
* BUGFIXES
|
||||
* fix(actions): keep github.event.inputs as strings for workflow_dispatch (#38899) (#38908)
|
||||
* fix(actions): let a rerun of selected jobs read the previous attempt's artifacts (#38857) (#38901)
|
||||
* fix(lfs): accept successful transfer responses (#38866) (#38875)
|
||||
* fix(packages): ignore nested Package.swift (#38788) (#38836)
|
||||
* fix: drop newline-bearing member names in arch ParsePackage (#38102) (#38830)
|
||||
* fix(storage): fix Azure Blob dump failing with file does not exist (#38814) (#38828)
|
||||
* fix(migration): migration deletion returned json redirection (#38796) (#38825)
|
||||
* fix(ui): change underlines to default browser style (#38819) (#38823)
|
||||
* fix(actions): allow cancelling runs without running jobs (#35842) (#38812)
|
||||
* fix(actions): evaluate each `${{ }}` part on its own (#38754) (#38797)
|
||||
* fix(actions): write an action task report in one transaction (#38792) (#38794)
|
||||
* fix: markup link (#38764) (#38765)
|
||||
* fix: set a minio part size when the content size is unknown (#38753) (#38755)
|
||||
* fix: bad path escape in subpath archive download (#38749) (#38750)
|
||||
* fix: remove the pull merge box from UI when the refreshed page doesn't contain it (#38742) (#38744)
|
||||
* fix(markdown): fix double strikethough on code (#38707) (#38729)
|
||||
* fix(lfs): failed upload deletes a concurrent upload's meta object (#38693) (#38722)
|
||||
* fix: correct full url when using sub-path (#38712) (#38716)
|
||||
* fix: avoid markup render panic (#38698) (#38703)
|
||||
* fix(ui): too many participants shown in commit avatar stacks (#38689) (#38700)
|
||||
* fix: support HEAD requests on Alpine registry APKINDEX.tar.gz (#38686) (#38688)
|
||||
* fix(migrations): use all configured GitHub tokens (#38841) (#38846)
|
||||
|
||||
## [1.27.1](https://github.com/go-gitea/gitea/releases/tag/v1.27.1) - 2026-07-27
|
||||
|
||||
* SECURITY
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# Build frontend on the native platform to avoid QEMU-related issues with nodejs ecosystem
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26-alpine3.24 AS frontend-build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27-alpine3.24 AS frontend-build
|
||||
RUN apk --no-cache add build-base git nodejs pnpm
|
||||
WORKDIR /src
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
@@ -9,7 +9,7 @@ COPY --exclude=.git/ . .
|
||||
RUN make frontend
|
||||
|
||||
# Build backend for each target platform
|
||||
FROM docker.io/library/golang:1.26-alpine3.24 AS build-env
|
||||
FROM docker.io/library/golang:1.27-alpine3.24 AS build-env
|
||||
|
||||
ARG GITEA_VERSION
|
||||
ARG TAGS=""
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# Build frontend on the native platform to avoid QEMU-related issues with nodejs ecosystem
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26-alpine3.24 AS frontend-build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27-alpine3.24 AS frontend-build
|
||||
RUN apk --no-cache add build-base git nodejs pnpm
|
||||
WORKDIR /src
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
@@ -9,7 +9,7 @@ COPY --exclude=.git/ . .
|
||||
RUN make frontend
|
||||
|
||||
# Build backend for each target platform
|
||||
FROM docker.io/library/golang:1.26-alpine3.24 AS build-env
|
||||
FROM docker.io/library/golang:1.27-alpine3.24 AS build-env
|
||||
|
||||
ARG GITEA_VERSION
|
||||
ARG TAGS=""
|
||||
|
||||
@@ -64,3 +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)
|
||||
@@ -1,29 +1,24 @@
|
||||
DIST := dist
|
||||
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
||||
|
||||
# By default use go's 1.25 experimental json v2 library when building
|
||||
# TODO: remove when no longer experimental
|
||||
export GOEXPERIMENT ?= jsonv2
|
||||
|
||||
GO ?= go
|
||||
SHASUM ?= shasum -a 256
|
||||
COMMA := ,
|
||||
|
||||
XGO_VERSION := go-1.26.x
|
||||
|
||||
AIR_PACKAGE ?= github.com/air-verse/air@v1.67.4 # renovate: datasource=go
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.9.0 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 # renovate: datasource=go
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.11.1 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.1 # renovate: datasource=go
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.16 # 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.1 # renovate: datasource=go
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@v1.9.0 # renovate: datasource=go
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.6.0 # renovate: datasource=go
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.36.4 # renovate: datasource=go
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.7.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
|
||||
PLAYWRIGHT_FLAGS ?=
|
||||
|
||||
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
|
||||
ifeq ($(HAS_GO), yes)
|
||||
CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
|
||||
@@ -42,18 +37,13 @@ endif
|
||||
|
||||
TAGS ?=
|
||||
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
||||
CGO_TAGS := sqlite_mattn pam
|
||||
|
||||
CGO_ENABLED ?= 0
|
||||
ifneq (,$(findstring sqlite_mattn,$(TAGS))$(findstring pam,$(TAGS)))
|
||||
ifneq ($(strip $(filter $(CGO_TAGS),$(TAGS))),)
|
||||
CGO_ENABLED = 1
|
||||
endif
|
||||
|
||||
STATIC ?=
|
||||
EXTLDFLAGS ?=
|
||||
ifneq ($(STATIC),)
|
||||
EXTLDFLAGS = -extldflags "-static"
|
||||
endif
|
||||
|
||||
ifeq ($(GOOS),windows)
|
||||
IS_WINDOWS := yes
|
||||
else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows)
|
||||
@@ -62,14 +52,13 @@ else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows)
|
||||
endif
|
||||
endif
|
||||
|
||||
# GOFLAGS and EXTRA_GOFLAGS are for the 'go build' command only
|
||||
ifeq ($(IS_WINDOWS),yes)
|
||||
GOFLAGS := -v -buildmode=exe
|
||||
EXECUTABLE ?= gitea.exe
|
||||
else
|
||||
GOFLAGS := -v
|
||||
EXECUTABLE ?= gitea
|
||||
endif
|
||||
|
||||
# EXTRA_GOFLAGS is for the 'go build' command only
|
||||
EXTRA_GOFLAGS ?=
|
||||
|
||||
ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu)
|
||||
@@ -86,14 +75,20 @@ STORED_VERSION_FILE := VERSION
|
||||
GITHUB_REF_TYPE ?= branch
|
||||
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
ifneq ($(GITHUB_REF_TYPE),branch)
|
||||
# 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"
|
||||
VERSION ?= $(subst v,,$(GITHUB_REF_NAME))
|
||||
GITEA_VERSION ?= $(VERSION)
|
||||
else
|
||||
else ifeq ($(GITHUB_REF_TYPE),branch)
|
||||
ifneq ($(GITHUB_REF_NAME),)
|
||||
# convert branch "release/v1.2" to "1.2-nightly"
|
||||
VERSION ?= $(subst release/v,,$(GITHUB_REF_NAME))-nightly
|
||||
else
|
||||
VERSION ?= main
|
||||
# no branch name info, use git ref name "HEAD" instead
|
||||
VERSION ?= HEAD
|
||||
endif
|
||||
|
||||
STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
|
||||
@@ -102,16 +97,17 @@ else
|
||||
else
|
||||
GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
||||
endif
|
||||
else
|
||||
$(error unsupported ref type $(GITHUB_REF_TYPE))
|
||||
endif
|
||||
|
||||
# if version = "main" then update version to "nightly"
|
||||
# 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)"
|
||||
|
||||
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/riscv64
|
||||
RELEASE_ENV = GO="$(GO)" TAGS="$(TAGS)" LDFLAGS="$(LDFLAGS)" DIST="$(DIST)" VERSION="$(VERSION)"
|
||||
|
||||
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list gitea.dev/modelmigration/...) gitea.dev/tests/integration/migration-test gitea.dev/tests gitea.dev/tests/integration,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list gitea.dev/modelmigration/...)
|
||||
@@ -135,7 +131,7 @@ 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 models modules routers services tests tools
|
||||
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
|
||||
@@ -389,7 +385,7 @@ test-backend: ## test backend files
|
||||
@$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' $(GO_TEST_PACKAGES)
|
||||
|
||||
.PHONY: test-frontend
|
||||
test-frontend: node_modules ## test frontend files
|
||||
test-frontend: playwright ## test frontend files
|
||||
pnpm exec vitest
|
||||
|
||||
.PHONY: test-check
|
||||
@@ -451,7 +447,7 @@ $(GO_LICENSE_FILE): go.mod go.sum
|
||||
GO=$(GO) $(GO) run build/generate-go-licenses.go $(GO_LICENSE_FILE)
|
||||
|
||||
.PHONY: test-integration
|
||||
test-integration:
|
||||
test-integration: $(EXECUTABLE)
|
||||
@# Use a compiled binary: testlogger forwards gitea logs to t.Log, so `go test -v`
|
||||
@# would flood output per passing test. testcache can't help these tests anyway —
|
||||
@# they mutate the work directory, so cache inputs change between runs.
|
||||
@@ -463,7 +459,7 @@ test-integration-compile:
|
||||
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -c -o /dev/null gitea.dev/tests/integration
|
||||
|
||||
.PHONY: test-integration\#%
|
||||
test-integration\#%:
|
||||
test-integration\#%: $(EXECUTABLE)
|
||||
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -run $(subst .,/,$*) gitea.dev/tests/integration
|
||||
|
||||
.PHONY: test-migration
|
||||
@@ -484,11 +480,11 @@ migrations.individual.test\#%:
|
||||
|
||||
.PHONY: playwright
|
||||
playwright: deps-frontend
|
||||
@CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) ./tools/test-e2e.sh install
|
||||
@./tools/playwright.sh $(PLAYWRIGHT_FLAGS) $(PLAYWRIGHT_BROWSERS)
|
||||
|
||||
.PHONY: test-e2e
|
||||
test-e2e: playwright frontend backend
|
||||
@CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) EXECUTABLE=$(EXECUTABLE) ./tools/test-e2e.sh run $(GITEA_TEST_E2E_FLAGS)
|
||||
@CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) EXECUTABLE=$(EXECUTABLE) ./tools/test-e2e.sh $(GITEA_TEST_E2E_FLAGS)
|
||||
|
||||
.PHONY: build
|
||||
build: frontend backend ## build everything
|
||||
@@ -513,38 +509,38 @@ generate-go: $(TAGS_PREREQ)
|
||||
|
||||
.PHONY: security-check
|
||||
security-check:
|
||||
GOEXPERIMENT= go run $(GOVULNCHECK_PACKAGE) -show color ./... || true
|
||||
go run $(GOVULNCHECK_PACKAGE) -show color ./... || true
|
||||
|
||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||
ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)
|
||||
$(error pam support set via TAGS does not support static builds)
|
||||
endif
|
||||
CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@
|
||||
|
||||
.PHONY: release
|
||||
release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-check
|
||||
CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build -v $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
|
||||
$(DIST_DIRS):
|
||||
mkdir -p $(DIST_DIRS)
|
||||
|
||||
# Release builds always use Go's native cross compilation. To cross-compile with CGO,
|
||||
# use "build" target with proper TAGS/LDFLAGS/CGO_CFLAGS to make "$(EXECUTABLE)" target run the "go build" command.
|
||||
.PHONY: release
|
||||
release: frontend release-binaries release-copy release-compress vendor release-sources release-check
|
||||
|
||||
.PHONY: release-binaries
|
||||
release-binaries: | $(DIST_DIRS)
|
||||
@$(RELEASE_ENV) ./tools/build-release.sh
|
||||
|
||||
.PHONY: release-windows
|
||||
release-windows: | $(DIST_DIRS)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
ifeq (,$(findstring gogit,$(TAGS)))
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||
endif
|
||||
@$(RELEASE_ENV) ./tools/build-release.sh windows
|
||||
|
||||
.PHONY: release-linux
|
||||
release-linux: | $(DIST_DIRS)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||
@$(RELEASE_ENV) ./tools/build-release.sh linux
|
||||
|
||||
.PHONY: release-darwin
|
||||
release-darwin: | $(DIST_DIRS)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||
@$(RELEASE_ENV) ./tools/build-release.sh darwin
|
||||
|
||||
.PHONY: release-freebsd
|
||||
release-freebsd: | $(DIST_DIRS)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) .
|
||||
@$(RELEASE_ENV) ./tools/build-release.sh freebsd
|
||||
|
||||
.PHONY: release-copy
|
||||
release-copy: | $(DIST_DIRS)
|
||||
@@ -564,7 +560,7 @@ release-sources: | $(DIST_DIRS)
|
||||
# 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
|
||||
$(eval TRANSFORM := $(shell tar --help | grep -q bsdtar && echo "-s '/^./gitea-src-$(VERSION)/'" || echo "--transform 's|^./|gitea-src-$(VERSION)/|'"))
|
||||
$(eval TRANSFORM := $(shell tar --help | grep -q bsdtar && echo "-s '|^./|gitea-src-$(VERSION)/|'" || echo "--transform 's|^./|gitea-src-$(VERSION)/|'"))
|
||||
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
||||
rm -f $(STORED_VERSION_FILE)
|
||||
|
||||
@@ -589,7 +585,6 @@ deps-tools: ## install tool dependencies
|
||||
$(GO) install $(GXZ_PACKAGE) & \
|
||||
$(GO) install $(MISSPELL_PACKAGE) & \
|
||||
$(GO) install $(SWAGGER_PACKAGE) & \
|
||||
$(GO) install $(XGO_PACKAGE) & \
|
||||
$(GO) install $(GOVULNCHECK_PACKAGE) & \
|
||||
$(GO) install $(ACTIONLINT_PACKAGE) & \
|
||||
wait
|
||||
|
||||
Generated
+15
-20
@@ -184,6 +184,11 @@
|
||||
"path": "github.com/aws/smithy-go/internal/sync/singleflight/LICENSE",
|
||||
"licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/aws/smithy-go/transport/http/protocol/internal/json/internal/stdlib",
|
||||
"path": "github.com/aws/smithy-go/transport/http/protocol/internal/json/internal/stdlib/LICENSE",
|
||||
"licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/aymerick/douceur",
|
||||
"path": "github.com/aymerick/douceur/LICENSE",
|
||||
@@ -444,11 +449,6 @@
|
||||
"path": "github.com/emersion/go-sasl/LICENSE",
|
||||
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 emersion\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/ethantkoenig/rupture",
|
||||
"path": "github.com/ethantkoenig/rupture/LICENSE",
|
||||
"licenseText": "MIT License\n\nCopyright (c) 2018 Ethan Koenig\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/fatih/color",
|
||||
"path": "github.com/fatih/color/LICENSE.md",
|
||||
@@ -944,11 +944,6 @@
|
||||
"path": "github.com/pkg/errors/LICENSE",
|
||||
"licenseText": "Copyright (c) 2015, Dave Cheney \u003cdave@cheney.net\u003e\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/pmezard/go-difflib",
|
||||
"path": "github.com/pmezard/go-difflib/LICENSE",
|
||||
"licenseText": "Copyright (c) 2013, Patrick Mezard\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n The names of its contributors may not be used to endorse or promote\nproducts derived from this software without specific prior written\npermission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/pquerna/otp",
|
||||
"path": "github.com/pquerna/otp/LICENSE",
|
||||
@@ -1059,6 +1054,16 @@
|
||||
"path": "github.com/stretchr/testify/LICENSE",
|
||||
"licenseText": "MIT License\n\nCopyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/stretchr/testify/internal/difflib",
|
||||
"path": "github.com/stretchr/testify/internal/difflib/LICENSE",
|
||||
"licenseText": "Copyright (c) 2013, Patrick Mezard\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n The names of its contributors may not be used to endorse or promote\nproducts derived from this software without specific prior written\npermission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
},
|
||||
{
|
||||
"name": "github.com/stretchr/testify/internal/spew",
|
||||
"path": "github.com/stretchr/testify/internal/spew/LICENSE",
|
||||
"licenseText": "ISC License\n\nCopyright (c) 2012-2016 Dave Collins \u003cdave@davec.name\u003e\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/syndtr/goleveldb",
|
||||
"path": "github.com/syndtr/goleveldb/LICENSE",
|
||||
@@ -1069,11 +1074,6 @@
|
||||
"path": "github.com/tinylib/msgp/LICENSE",
|
||||
"licenseText": "Copyright (c) 2014 Philip Hofer\nPortions Copyright (c) 2009 The Go Authors (license at http://golang.org) where indicated\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
|
||||
},
|
||||
{
|
||||
"name": "github.com/tstranex/u2f",
|
||||
"path": "github.com/tstranex/u2f/LICENSE",
|
||||
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 The Go FIDO U2F Library Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/ulikunitz/xz",
|
||||
"path": "github.com/ulikunitz/xz/LICENSE",
|
||||
@@ -1254,11 +1254,6 @@
|
||||
"path": "gopkg.in/warnings.v0/LICENSE",
|
||||
"licenseText": "Copyright (c) 2016 Péter Surányi.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
},
|
||||
{
|
||||
"name": "gopkg.in/yaml.v3",
|
||||
"path": "gopkg.in/yaml.v3/LICENSE",
|
||||
"licenseText": "\nThis project is covered by two different licenses: MIT and Apache.\n\n#### MIT License ####\n\nThe following files were ported to Go from C files of libyaml, and thus\nare still covered by their original MIT license, with the additional\ncopyright staring in 2011 when the project was ported over:\n\n apic.go emitterc.go parserc.go readerc.go scannerc.go\n writerc.go yamlh.go yamlprivateh.go\n\nCopyright (c) 2006-2010 Kirill Simonov\nCopyright (c) 2006-2011 Kirill Simonov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n### Apache License ###\n\nAll the remaining project files are covered by the Apache license:\n\nCopyright (c) 2011-2019 Canonical Ltd\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
|
||||
},
|
||||
{
|
||||
"name": "modernc.org/libc",
|
||||
"path": "modernc.org/libc/LICENSE",
|
||||
|
||||
+1
-2
@@ -5,7 +5,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/modules/private"
|
||||
"gitea.dev/modules/setting"
|
||||
@@ -49,6 +48,6 @@ func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error {
|
||||
if extra.HasError() {
|
||||
return handleCliResponseExtra(extra)
|
||||
}
|
||||
_, _ = fmt.Printf("%s\n", respText.Text)
|
||||
cprintln(c, respText.Text)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -73,6 +73,6 @@ func runChangePassword(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s's password has been successfully updated!\n", user.Name)
|
||||
cprintf(c, "%s's password has been successfully updated!\n", user.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
// codeql[disable-next-line=go/clear-text-logging]
|
||||
fmt.Printf("generated random password is '%s'\n", password)
|
||||
cprintf(c, "generated random password is '%s'\n", password)
|
||||
} else if userType == user_model.UserTypeIndividual {
|
||||
return errors.New("must set either password or random-password flag")
|
||||
}
|
||||
@@ -228,7 +228,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
|
||||
if err := user_model.CreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
|
||||
return fmt.Errorf("CreateUser: %w", err)
|
||||
}
|
||||
fmt.Printf("New user '%s' has been successfully created!\n", username)
|
||||
cprintf(c, "New user '%s' has been successfully created!\n", username)
|
||||
|
||||
// create the access token
|
||||
if accessTokenScope != "" {
|
||||
@@ -236,7 +236,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
|
||||
if err := auth_model.NewAccessToken(ctx, t); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Access token was successfully created... %s\n", t.Token)
|
||||
cprintf(c, "Access token was successfully created... %s\n", t.Token)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -67,6 +67,6 @@ func runDisableTwoFactor(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Disabled 2FA for user %q (removed %d TOTP and %d WebAuthn credential(s))\n", user.Name, totp, webAuthn)
|
||||
cprintf(c, "Disabled 2FA for user %q (removed %d TOTP and %d WebAuthn credential(s))\n", user.Name, totp, webAuthn)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -88,9 +88,9 @@ func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
|
||||
if c.Bool("raw") {
|
||||
fmt.Printf("%s\n", t.Token)
|
||||
cprintln(c, t.Token)
|
||||
} else {
|
||||
fmt.Printf("Access token was successfully created: %s\n", t.Token)
|
||||
cprintf(c, "Access token was successfully created: %s\n", t.Token)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -6,7 +6,6 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/setting"
|
||||
@@ -59,6 +58,6 @@ func runMustChangePassword(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
|
||||
// codeql[disable-next-line=go/clear-text-logging]
|
||||
fmt.Printf("Updated %d users setting MustChangePassword to %t\n", n, mustChangePassword)
|
||||
cprintf(c, "Updated %d users setting MustChangePassword to %t\n", n, mustChangePassword)
|
||||
return nil
|
||||
}
|
||||
|
||||
+2
-2
@@ -114,8 +114,8 @@ func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
|
||||
|
||||
setting.Database.LogSQL = debug
|
||||
if err := db.InitEngine(ctx); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
cprintln(cmd, err)
|
||||
cprintln(cmd, "Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/log"
|
||||
@@ -40,15 +39,15 @@ func runDoctorConvert(ctx context.Context, cmd *cli.Command) error {
|
||||
log.Fatal("Failed to convert database & table: %v", err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("Converted successfully, please confirm your database's character set is now utf8mb4")
|
||||
cprintln(cmd, "Converted successfully, please confirm your database's character set is now utf8mb4")
|
||||
case setting.Database.Type.IsMSSQL():
|
||||
if err := db.ConvertVarcharToNVarchar(); err != nil {
|
||||
log.Fatal("Failed to convert database from varchar to nvarchar: %v", err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("Converted successfully, please confirm your database's all columns character is NVARCHAR now")
|
||||
cprintln(cmd, "Converted successfully, please confirm your database's all columns character is NVARCHAR now")
|
||||
default:
|
||||
fmt.Println("This command can only be used with a MySQL or MSSQL database")
|
||||
cprintln(cmd, "This command can only be used with a MySQL or MSSQL database")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+8
-8
@@ -150,7 +150,7 @@ func runListDo(c *cli.Command) error {
|
||||
}
|
||||
|
||||
for _, a := range matchedAssetFiles {
|
||||
fmt.Println(a.path)
|
||||
cprintln(c, a.path)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -194,7 +194,7 @@ func runExtractDo(c *cli.Command) error {
|
||||
destdir = c.String("destination")
|
||||
} else if c.Bool("custom") {
|
||||
destdir = setting.CustomPath
|
||||
fmt.Println("Using app.ini at", setting.CustomConf)
|
||||
cprintln(c, "Using app.ini at", setting.CustomConf)
|
||||
}
|
||||
|
||||
fi, err := os.Stat(destdir)
|
||||
@@ -213,13 +213,13 @@ func runExtractDo(c *cli.Command) error {
|
||||
return fmt.Errorf("destination %q is not a directory", destdir)
|
||||
}
|
||||
|
||||
fmt.Printf("Extracting to %s:\n", destdir)
|
||||
cprintf(c, "Extracting to %s:\n", destdir)
|
||||
|
||||
overwrite := c.Bool("overwrite")
|
||||
rename := c.Bool("rename")
|
||||
|
||||
for _, a := range matchedAssetFiles {
|
||||
if err := extractAsset(destdir, a, overwrite, rename); err != nil {
|
||||
if err := extractAsset(c, destdir, a, overwrite, rename); err != nil {
|
||||
// Non-fatal error
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%s: %v\n", a.path, err)
|
||||
}
|
||||
@@ -228,7 +228,7 @@ func runExtractDo(c *cli.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractAsset(d string, a assetFile, overwrite, rename bool) error {
|
||||
func extractAsset(c *cli.Command, d string, a assetFile, overwrite, rename bool) error {
|
||||
dest := filepath.Join(d, filepath.FromSlash(a.path))
|
||||
dir := filepath.Dir(dest)
|
||||
|
||||
@@ -249,7 +249,7 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error {
|
||||
return fmt.Errorf("%s: %w", dest, err)
|
||||
}
|
||||
} else if !overwrite && !rename {
|
||||
fmt.Printf("%s already exists; skipped.\n", dest)
|
||||
cprintf(c, "%s already exists; skipped.\n", dest)
|
||||
return nil
|
||||
} else if !fi.Mode().IsRegular() {
|
||||
return fmt.Errorf("%s already exists, but it's not a regular file", dest)
|
||||
@@ -271,7 +271,7 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error {
|
||||
return fmt.Errorf("%s: %w", dest, err)
|
||||
}
|
||||
|
||||
fmt.Println(dest)
|
||||
cprintln(c, dest)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -306,7 +306,7 @@ func compileCollectPatterns(args []string) (_ []glob.Glob, err error) {
|
||||
pat := make([]glob.Glob, len(args))
|
||||
for i := range args {
|
||||
if pat[i], err = glob.Compile(args[i], '/'); err != nil {
|
||||
return nil, fmt.Errorf("invalid glob patterh %q: %w", args[i], err)
|
||||
return nil, fmt.Errorf("invalid glob pattern %q: %w", args[i], err)
|
||||
}
|
||||
}
|
||||
return pat, nil
|
||||
|
||||
+8
-8
@@ -106,10 +106,10 @@ func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s", internalToken)
|
||||
cprintf(c, "%s", internalToken)
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
fmt.Printf("\n")
|
||||
cprintf(c, "\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -117,10 +117,10 @@ func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
|
||||
|
||||
func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
|
||||
_, jwtSecretBase64 := generate.NewJwtSecretWithBase64()
|
||||
fmt.Printf("%s", jwtSecretBase64)
|
||||
cprintf(c, "%s", jwtSecretBase64)
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
fmt.Printf("\n")
|
||||
cprintf(c, "\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -133,10 +133,10 @@ func runGenerateSecretKey(_ context.Context, c *cli.Command) error {
|
||||
}
|
||||
|
||||
// codeql[disable-next-line=go/clear-text-logging]
|
||||
fmt.Printf("%s", secretKey)
|
||||
cprintf(c, "%s", secretKey)
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
fmt.Printf("\n")
|
||||
cprintf(c, "\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -168,14 +168,14 @@ func runGenerateKeyPair(_ context.Context, c *cli.Command) error {
|
||||
// Check if file exists to prevent overwriting
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
if !confirm(c.Reader, c.Writer, "%s already exists.\nOverwrite (y/n)? ", file) {
|
||||
fmt.Println("Aborting")
|
||||
cprintln(c, "Aborting")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
bits := c.Int("bits")
|
||||
err := ssh.GenKeyPair(file, generate.SSHKeyType(keyType), bits)
|
||||
if err == nil {
|
||||
fmt.Printf("Your SSH key has been saved in %s\n", file)
|
||||
cprintf(c, "Your SSH key has been saved in %s\n", file)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
+10
-4
@@ -1,8 +1,6 @@
|
||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package cmd provides subcommands to the gitea binary - such as "web" or
|
||||
// "admin".
|
||||
package cmd
|
||||
|
||||
import (
|
||||
@@ -22,8 +20,8 @@ import (
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// argsSet checks that all the required arguments are set. args is a list of
|
||||
// arguments that must be set in the passed Context.
|
||||
// argsSet checks that all the required arguments are set.
|
||||
// args is a list of arguments that must be set in the command context.
|
||||
func argsSet(c *cli.Command, args ...string) error {
|
||||
for _, a := range args {
|
||||
if !c.IsSet(a) {
|
||||
@@ -141,3 +139,11 @@ func isValidDefaultSubCommand(cmd *cli.Command) (string, bool) {
|
||||
}
|
||||
return "", true
|
||||
}
|
||||
|
||||
func cprintf(c *cli.Command, format string, args ...any) {
|
||||
_, _ = fmt.Fprintf(c.Writer, format, args...)
|
||||
}
|
||||
|
||||
func cprintln(c *cli.Command, args ...any) {
|
||||
_, _ = fmt.Fprintln(c.Writer, args...)
|
||||
}
|
||||
|
||||
+28
-22
@@ -186,23 +186,24 @@ Gitea or set your environment appropriately.`, "")
|
||||
|
||||
// the environment is set by serv command
|
||||
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
|
||||
username := os.Getenv(repo_module.EnvRepoUsername)
|
||||
reponame := os.Getenv(repo_module.EnvRepoName)
|
||||
ownerName := os.Getenv(repo_module.EnvRepoUsername)
|
||||
repoName := os.Getenv(repo_module.EnvRepoName)
|
||||
userID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
|
||||
deployKeyID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvDeployKeyID), 10, 64)
|
||||
actionsTaskID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvActionsTaskID), 10, 64)
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserID: userID,
|
||||
IsWiki: isWiki,
|
||||
|
||||
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||
GitPushOptions: pushOptions(),
|
||||
PullRequestID: prID,
|
||||
DeployKeyID: deployKeyID,
|
||||
ActionsTaskID: actionsTaskID,
|
||||
IsWiki: isWiki,
|
||||
|
||||
PullRequestID: prID,
|
||||
|
||||
UserID: userID,
|
||||
UserName: os.Getenv(repo_module.EnvPusherName),
|
||||
UserExtDoerData: os.Getenv(repo_module.EnvPusherExtDoerData),
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
@@ -257,7 +258,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
hookOptions.OldCommitIDs = oldCommitIDs
|
||||
hookOptions.NewCommitIDs = newCommitIDs
|
||||
hookOptions.RefFullNames = refFullNames
|
||||
extra := private.HookPreReceive(ctx, username, reponame, hookOptions)
|
||||
extra := private.HookPreReceive(ctx, ownerName, repoName, hookOptions)
|
||||
if extra.HasError() {
|
||||
return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error)
|
||||
}
|
||||
@@ -283,7 +284,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
|
||||
fmt.Fprintf(out, " Checking %d references\n", count)
|
||||
|
||||
extra := private.HookPreReceive(ctx, username, reponame, hookOptions)
|
||||
extra := private.HookPreReceive(ctx, ownerName, repoName, hookOptions)
|
||||
if extra.HasError() {
|
||||
return fail(ctx, extra.UserMsg, "HookPreReceive(last) failed: %v", extra.Error)
|
||||
}
|
||||
@@ -353,18 +354,21 @@ Gitea or set your environment appropriately.`, "")
|
||||
repoName := os.Getenv(repo_module.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
|
||||
pusherName := os.Getenv(repo_module.EnvPusherName)
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserName: pusherName,
|
||||
UserID: pusherID,
|
||||
IsWiki: isWiki,
|
||||
|
||||
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||
GitPushOptions: pushOptions(),
|
||||
PullRequestID: prID,
|
||||
PushTrigger: repo_module.PushTrigger(os.Getenv(repo_module.EnvPushTrigger)),
|
||||
IsWiki: isWiki,
|
||||
|
||||
PullRequestID: prID,
|
||||
PushTrigger: repo_module.PushTrigger(os.Getenv(repo_module.EnvPushTrigger)),
|
||||
|
||||
UserID: pusherID,
|
||||
UserName: os.Getenv(repo_module.EnvPusherName),
|
||||
UserExtDoerData: os.Getenv(repo_module.EnvPusherExtDoerData),
|
||||
}
|
||||
|
||||
oldCommitIDs := make([]string, 0, hookBatchSize)
|
||||
@@ -481,7 +485,6 @@ Gitea or set your environment appropriately.`, "")
|
||||
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
|
||||
repoName := os.Getenv(repo_module.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||
pusherName := os.Getenv(repo_module.EnvPusherName)
|
||||
|
||||
// 1. Version and features negotiation.
|
||||
// S: PKT-LINE(version=1\0push-options atomic...) / PKT-LINE(version=1\n)
|
||||
@@ -553,10 +556,13 @@ Gitea or set your environment appropriately.`, "")
|
||||
// S: ... ...
|
||||
// S: flush-pkt
|
||||
hookOptions := private.HookOptions{
|
||||
UserName: pusherName,
|
||||
UserID: pusherID,
|
||||
IsWiki: isWiki,
|
||||
|
||||
GitPushOptions: make(map[string]string),
|
||||
IsWiki: isWiki,
|
||||
|
||||
UserID: pusherID,
|
||||
UserName: os.Getenv(repo_module.EnvPusherName),
|
||||
UserExtDoerData: os.Getenv(repo_module.EnvPusherExtDoerData),
|
||||
}
|
||||
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
|
||||
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
|
||||
@@ -752,7 +758,7 @@ func writeFlushPktLine(ctx context.Context, out io.Writer) error {
|
||||
func writeDataPktLine(ctx context.Context, out io.Writer, data []byte) error {
|
||||
hexchar := []byte("0123456789abcdef")
|
||||
hex := func(n uint64) byte {
|
||||
return hexchar[(n)&15]
|
||||
return hexchar[n&15]
|
||||
}
|
||||
|
||||
length := uint64(len(data) + 4)
|
||||
|
||||
+3
-4
@@ -5,7 +5,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/modules/private"
|
||||
"gitea.dev/modules/setting"
|
||||
@@ -22,11 +21,11 @@ func runSendMail(ctx context.Context, c *cli.Command) error {
|
||||
|
||||
if !confirmSkipped {
|
||||
if len(body) == 0 {
|
||||
fmt.Println("warning: Content is empty")
|
||||
cprintln(c, "warning: Content is empty")
|
||||
}
|
||||
|
||||
if !confirm(c.Reader, c.Writer, "Proceed with sending email? [Y/n] ") {
|
||||
fmt.Println("The mail was not sent")
|
||||
cprintln(c, "The mail was not sent")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -35,6 +34,6 @@ func runSendMail(ctx context.Context, c *cli.Command) error {
|
||||
if extra.HasError() {
|
||||
return handleCliResponseExtra(extra)
|
||||
}
|
||||
_, _ = fmt.Printf("Sent %s email(s) to all users\n", respText.Text)
|
||||
cprintf(c, "Sent %s email(s) to all users\n", respText.Text)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,60 +5,14 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/private"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func defaultLoggingFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "logger",
|
||||
Usage: `Logger name - will default to "default"`,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "writer",
|
||||
Usage: "Name of the log writer - will default to mode",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "level",
|
||||
Usage: "Logging level for the new logger",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "stacktrace-level",
|
||||
Aliases: []string{"L"},
|
||||
Usage: "Stacktrace logging level",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "flags",
|
||||
Aliases: []string{"F"},
|
||||
Usage: "Flags for the logger",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "expression",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "Matching expression for the logger",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "prefix",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "Prefix for the logger",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "color",
|
||||
Usage: "Use color in the logs",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newLoggingCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "logging",
|
||||
@@ -91,92 +45,6 @@ func newLoggingCommand() *cli.Command {
|
||||
},
|
||||
},
|
||||
Action: runReleaseReopenLogging,
|
||||
}, {
|
||||
Name: "remove",
|
||||
Usage: "Remove a logger",
|
||||
ArgsUsage: "[name] Name of logger to remove",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "debug",
|
||||
}, &cli.StringFlag{
|
||||
Name: "logger",
|
||||
Usage: `Logger name - will default to "default"`,
|
||||
},
|
||||
},
|
||||
Action: runRemoveLogger,
|
||||
}, {
|
||||
Name: "add",
|
||||
Usage: "Add a logger",
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "file",
|
||||
Usage: "Add a file logger",
|
||||
Flags: append(defaultLoggingFlags(), []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "filename",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "Filename for the logger - this must be set.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "rotate",
|
||||
Aliases: []string{"r"},
|
||||
Usage: "Rotate logs",
|
||||
},
|
||||
&cli.Int64Flag{
|
||||
Name: "max-size",
|
||||
Aliases: []string{"s"},
|
||||
Usage: "Maximum size in bytes before rotation",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "daily",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "Rotate logs daily",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "max-days",
|
||||
Aliases: []string{"D"},
|
||||
Usage: "Maximum number of daily logs to keep",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "compress",
|
||||
Aliases: []string{"z"},
|
||||
Usage: "Compress rotated logs",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "compression-level",
|
||||
Aliases: []string{"Z"},
|
||||
Usage: "Compression level to use",
|
||||
},
|
||||
}...),
|
||||
Action: runAddFileLogger,
|
||||
}, {
|
||||
Name: "conn",
|
||||
Usage: "Add a net conn logger",
|
||||
Flags: append(defaultLoggingFlags(), []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "reconnect-on-message",
|
||||
Aliases: []string{"R"},
|
||||
Usage: "Reconnect to host for every message",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "reconnect",
|
||||
Aliases: []string{"r"},
|
||||
Usage: "Reconnect to host when connection is dropped",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "protocol",
|
||||
Aliases: []string{"P"},
|
||||
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "address",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Host address and port to connect to (defaults to :7020)",
|
||||
},
|
||||
}...),
|
||||
Action: runAddConnLogger,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: "log-sql",
|
||||
Usage: "Set LogSQL",
|
||||
@@ -195,107 +63,6 @@ func newLoggingCommand() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func runRemoveLogger(ctx context.Context, c *cli.Command) error {
|
||||
setup(ctx, c.Bool("debug"))
|
||||
logger := c.String("logger")
|
||||
if len(logger) == 0 {
|
||||
logger = log.DEFAULT
|
||||
}
|
||||
writer := c.Args().First()
|
||||
|
||||
extra := private.RemoveLogger(ctx, logger, writer)
|
||||
return handleCliResponseExtra(extra)
|
||||
}
|
||||
|
||||
func runAddConnLogger(ctx context.Context, c *cli.Command) error {
|
||||
setup(ctx, c.Bool("debug"))
|
||||
vals := map[string]any{}
|
||||
mode := "conn"
|
||||
vals["net"] = "tcp"
|
||||
if c.IsSet("protocol") {
|
||||
switch c.String("protocol") {
|
||||
case "udp":
|
||||
vals["net"] = "udp"
|
||||
case "unix":
|
||||
vals["net"] = "unix"
|
||||
}
|
||||
}
|
||||
if c.IsSet("address") {
|
||||
vals["address"] = c.String("address")
|
||||
} else {
|
||||
vals["address"] = ":7020"
|
||||
}
|
||||
if c.IsSet("reconnect") {
|
||||
vals["reconnect"] = c.Bool("reconnect")
|
||||
}
|
||||
if c.IsSet("reconnect-on-message") {
|
||||
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
|
||||
}
|
||||
return commonAddLogger(ctx, c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddFileLogger(ctx context.Context, c *cli.Command) error {
|
||||
setup(ctx, c.Bool("debug"))
|
||||
vals := map[string]any{}
|
||||
mode := "file"
|
||||
if c.IsSet("filename") {
|
||||
vals["filename"] = c.String("filename")
|
||||
} else {
|
||||
return errors.New("filename must be set when creating a file logger")
|
||||
}
|
||||
if c.IsSet("rotate") {
|
||||
vals["rotate"] = c.Bool("rotate")
|
||||
}
|
||||
if c.IsSet("max-size") {
|
||||
vals["maxsize"] = c.Int64("max-size")
|
||||
}
|
||||
if c.IsSet("daily") {
|
||||
vals["daily"] = c.Bool("daily")
|
||||
}
|
||||
if c.IsSet("max-days") {
|
||||
vals["maxdays"] = c.Int("max-days")
|
||||
}
|
||||
if c.IsSet("compress") {
|
||||
vals["compress"] = c.Bool("compress")
|
||||
}
|
||||
if c.IsSet("compression-level") {
|
||||
vals["compressionLevel"] = c.Int("compression-level")
|
||||
}
|
||||
return commonAddLogger(ctx, c, mode, vals)
|
||||
}
|
||||
|
||||
func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[string]any) error {
|
||||
if len(c.String("level")) > 0 {
|
||||
vals["level"] = log.LevelFromString(c.String("level")).String()
|
||||
}
|
||||
if len(c.String("stacktrace-level")) > 0 {
|
||||
vals["stacktraceLevel"] = log.LevelFromString(c.String("stacktrace-level")).String()
|
||||
}
|
||||
if len(c.String("expression")) > 0 {
|
||||
vals["expression"] = c.String("expression")
|
||||
}
|
||||
if len(c.String("prefix")) > 0 {
|
||||
vals["prefix"] = c.String("prefix")
|
||||
}
|
||||
if len(c.String("flags")) > 0 {
|
||||
vals["flags"] = log.FlagsFromString(c.String("flags"))
|
||||
}
|
||||
if c.IsSet("color") {
|
||||
vals["colorize"] = c.Bool("color")
|
||||
}
|
||||
logger := log.DEFAULT
|
||||
if c.IsSet("logger") {
|
||||
logger = c.String("logger")
|
||||
}
|
||||
writer := mode
|
||||
if c.IsSet("writer") {
|
||||
writer = c.String("writer")
|
||||
}
|
||||
|
||||
extra := private.AddLogger(ctx, logger, writer, mode, vals)
|
||||
return handleCliResponseExtra(extra)
|
||||
}
|
||||
|
||||
func runPauseLogging(ctx context.Context, c *cli.Command) error {
|
||||
setup(ctx, c.Bool("debug"))
|
||||
userMsg := private.PauseLogging(ctx)
|
||||
|
||||
+26
-16
@@ -29,6 +29,7 @@ import (
|
||||
"gitea.dev/modules/process"
|
||||
repo_module "gitea.dev/modules/repository"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/services/agit"
|
||||
"gitea.dev/services/lfs"
|
||||
|
||||
"github.com/kballard/go-shellquote"
|
||||
@@ -139,13 +140,13 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
setup(ctx, c.Bool("debug"))
|
||||
|
||||
if setting.SSH.Disabled {
|
||||
println("Gitea: SSH has been disabled")
|
||||
cprintln(c, "Gitea: SSH has been disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.NArg() < 1 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
fmt.Printf("error showing subcommand help: %v\n", err)
|
||||
cprintf(c, "error showing subcommand help: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -171,15 +172,19 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
if err != nil {
|
||||
return fail(ctx, "Key check failed", "Failed to check provided key: %v", err)
|
||||
}
|
||||
var authSuccessMsg string
|
||||
switch key.Type {
|
||||
case asymkey_model.KeyTypeDeploy:
|
||||
println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||
authSuccessMsg = "Hi there! You've successfully authenticated with an SSH deploy key."
|
||||
case asymkey_model.KeyTypePrincipal:
|
||||
println("Hi there! You've successfully authenticated with the principal " + key.Content + ", but Gitea does not provide shell access.")
|
||||
authSuccessMsg = "Hi there! You've successfully authenticated with the SSH principal " + key.Content + "."
|
||||
default:
|
||||
println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||
authSuccessMsg = "Hi there, " + user.Name + "! You've successfully authenticated with the SSH key named " + key.Name + "."
|
||||
}
|
||||
println("If this is unexpected, please log in with password and setup Gitea under another user.")
|
||||
_, _ = fmt.Fprintf(c.ErrWriter, "%s\n%s",
|
||||
authSuccessMsg,
|
||||
"Gitea does not provide shell access. If this is unexpected, please setup Gitea under another SSH user or use container to deploy.",
|
||||
)
|
||||
return nil
|
||||
} else if c.Bool("debug") {
|
||||
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
|
||||
@@ -194,7 +199,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
if git.DefaultFeatures().SupportProcReceive {
|
||||
// for AGit Flow
|
||||
if cmd == "ssh_info" {
|
||||
fmt.Print(`{"type":"agit","version":1}`)
|
||||
cprintf(c, "%s", agit.SshInfoJson)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -256,7 +261,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
if results.IsWiki {
|
||||
return fail(ctx, "LFS Transfer is not supported for wikis", "")
|
||||
}
|
||||
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
|
||||
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, UserExtDoerData: results.UserExtDoerData, RepoID: results.RepoID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -270,7 +275,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
lfsTokenHref := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
||||
|
||||
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
|
||||
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, UserExtDoerData: results.UserExtDoerData, RepoID: results.RepoID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -314,15 +319,20 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
command.Env = append(command.Env, os.Environ()...)
|
||||
command.Env = append(command.Env,
|
||||
repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki),
|
||||
repo_module.EnvRepoName+"="+results.RepoName,
|
||||
|
||||
repo_module.EnvRepoUsername+"="+results.OwnerName,
|
||||
repo_module.EnvRepoName+"="+results.RepoName,
|
||||
repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
|
||||
|
||||
repo_module.EnvKeyID+"="+strconv.FormatInt(results.PublicKeyID, 10),
|
||||
|
||||
repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
|
||||
repo_module.EnvPusherName+"="+results.UserName,
|
||||
repo_module.EnvPusherEmail+"="+results.UserEmail,
|
||||
repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
|
||||
repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
|
||||
repo_module.EnvPusherExtDoerData+"="+results.UserExtDoerData,
|
||||
|
||||
repo_module.EnvPRID+"="+strconv.Itoa(0),
|
||||
repo_module.EnvDeployKeyID+"="+strconv.FormatInt(results.DeployKeyID, 10),
|
||||
repo_module.EnvKeyID+"="+strconv.FormatInt(results.KeyID, 10),
|
||||
|
||||
repo_module.EnvAppURL+"="+setting.AppURL,
|
||||
)
|
||||
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
|
||||
@@ -334,8 +344,8 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
|
||||
// Update user key activity.
|
||||
if results.KeyID > 0 {
|
||||
if err = private.UpdatePublicKeyInRepo(ctx, results.KeyID, results.RepoID); err != nil {
|
||||
if results.PublicKeyID > 0 {
|
||||
if err = private.UpdatePublicKeyInRepo(ctx, results.PublicKeyID, results.RepoID); err != nil {
|
||||
return fail(ctx, "Failed to update public key", "UpdatePublicKeyInRepo: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ var curveStringMap = map[string]tls.CurveID{
|
||||
"p256": tls.CurveP256,
|
||||
"p384": tls.CurveP384,
|
||||
"p521": tls.CurveP521,
|
||||
"mlkem1024": tls.MLKEM1024,
|
||||
"x25519mlkem768": tls.X25519MLKEM768,
|
||||
"secp256r1mlkem768": tls.SecP256r1MLKEM768,
|
||||
"secp384r1mlkem1024": tls.SecP384r1MLKEM1024,
|
||||
|
||||
@@ -1128,7 +1128,6 @@ LEVEL = Info
|
||||
;ALLOW_FORK_WITHOUT_MAXIMUM_LIMIT = true
|
||||
|
||||
;; Allow to fork repositories into the same owner (user or organization)
|
||||
;; This feature is experimental, not fully tested, and may be changed in the future
|
||||
;ALLOW_FORK_INTO_SAME_OWNER = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -2288,6 +2287,18 @@ LEVEL = Info
|
||||
;RUN_AT_START = true
|
||||
;SCHEDULE = @midnight
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Delete action runs older than RUN_RETENTION_DAYS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[cron.cleanup_action_runs]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Deletes nothing while RUN_RETENTION_DAYS is 0
|
||||
;ENABLED = true
|
||||
;RUN_AT_START = false
|
||||
;SCHEDULE = @midnight
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Clean-up deleted branches
|
||||
@@ -3008,16 +3019,20 @@ LEVEL = Info
|
||||
;;
|
||||
;; Default platform to get action plugins, `github` for `https://github.com`, `self` for the current Gitea instance.
|
||||
;DEFAULT_ACTIONS_URL = github
|
||||
;; Logs retention time in days. Old logs will be deleted after this period.
|
||||
;LOG_RETENTION_DAYS = 365
|
||||
;; Log compression type, `none` for no compression, `zstd` for zstd compression.
|
||||
;; Other compression types like `gzip` are NOT supported, since seekable stream is required for log view.
|
||||
;; It's always recommended to use compression when using local disk as log storage if CPU or memory is not a bottleneck.
|
||||
;; And for object storage services like S3, which is billed for requests, it would cause extra 2 times of get requests for each log view.
|
||||
;; But it will save storage space and network bandwidth, so it's still recommended to use compression.
|
||||
;LOG_COMPRESSION = zstd
|
||||
;; Default artifact retention time in days. Artifacts could have their own retention periods by setting the `retention-days` option in `actions/upload-artifact` step.
|
||||
;; Days to keep logs. Old logs will be deleted after this period. 0 means keep forever.
|
||||
;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.
|
||||
;; Artifacts could have their own retention periods by setting the `retention-days` option in `actions/upload-artifact` step.
|
||||
;ARTIFACT_RETENTION_DAYS = 90
|
||||
;; Days to keep completed runs. Old runs and everything under them will be deleted after this period. 0 means keep forever.
|
||||
;RUN_RETENTION_DAYS = 400
|
||||
;; Timeout to stop the task which have running status, but haven't been updated for a long time
|
||||
;ZOMBIE_TASK_TIMEOUT = 10m
|
||||
;; Timeout to stop the tasks which have running status and continuous updates, but don't end for a long time
|
||||
|
||||
@@ -6,7 +6,7 @@ and testing see [development.md](development.md) and [testing.md](testing.md).
|
||||
|
||||
## Background
|
||||
|
||||
The frontend uses [Vue 3](https://vuejs.org/), [Fomantic-UI](https://fomantic-ui.com/) (built on jQuery)
|
||||
The frontend uses [Vue 3](https://vuejs.org/), hard-forked Fomantic-UI (built on jQuery)
|
||||
and [Tailwind CSS](https://tailwindcss.com/). Pages are rendered with Go HTML templates.
|
||||
Source files live in:
|
||||
|
||||
@@ -44,8 +44,10 @@ Gitea uses Vue 3 **without** JSX to keep HTML and JavaScript separate.
|
||||
## Gitea-specific conventions
|
||||
|
||||
- Keep features in their own files or directories.
|
||||
- Use kebab-case for HTML `id`s and classes, ideally with 2-3 feature keywords.
|
||||
- Use kebab-case for HTML `id`s and classes with 2-3 feature keywords.
|
||||
- Prefix classes to avoid short-name conflicts between different frameworks.
|
||||
- Our framework can automatically link "input" and "label" if they are the children of a `.field` element,
|
||||
no need to write `id`/`for` attributes for them unless there are reasons to do so.
|
||||
- Create a new class name when overriding framework styles instead of editing the framework's own classes,
|
||||
or fix the framework's source to fix all cases.
|
||||
- Prefer semantic elements such as `<button>` over generic `<div>`s.
|
||||
@@ -68,17 +70,18 @@ Write class attributes as a single readable unit in templates:
|
||||
## TypeScript
|
||||
|
||||
- Use `import type` for type-only imports.
|
||||
- Prefer `@ts-expect-error` over `@ts-ignore`.
|
||||
- Use the `!` non-null assertion (rather than `?.`/`??`) when a value is known to always exist.
|
||||
- Only mark a function `async` when it actually uses `await` or returns a `Promise`.
|
||||
Avoid async event listeners; if unavoidable, call `e.preventDefault()` before the
|
||||
first `await`. For a deliberately un-awaited call, assign it: `const _promise = asyncFoo()`.
|
||||
Avoid async event listeners; if unavoidable, call `e.preventDefault()` before the first `await`.
|
||||
|
||||
## Data fetching
|
||||
|
||||
Use the `GET`, `POST`, `PUT`, `PATCH`, and `DELETE` wrappers from
|
||||
[`web_src/js/modules/fetch.ts`](../web_src/js/modules/fetch.ts).
|
||||
|
||||
Prefer to use our [`fetch-action.ts`](../web_src/js/modules/fetch-action.ts) framework
|
||||
for form submissions, button clicks and network requests, which provides a consistent UX and error handling.
|
||||
|
||||
## DOM attributes
|
||||
|
||||
Avoid `node.dataset` because of its camel-casing behavior; use `node.getAttribute`
|
||||
@@ -86,7 +89,7 @@ in new code. Never bind user-provided data directly onto DOM nodes.
|
||||
|
||||
## Showing and hiding elements
|
||||
|
||||
- In Vue, use `v-if` and `v-show`.
|
||||
- In Vue, use `v-if` and `v-show`. If an element contains unmanaged DOM, use `v-show` to avoid losing the DOM state.
|
||||
- In Go templates and plain JavaScript, use the `.tw-hidden` class together with the
|
||||
`showElem()`, `hideElem()`, and `toggleElem()` helpers from
|
||||
[`web_src/js/utils/dom.ts`](../web_src/js/utils/dom.ts).
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ go test -run '^TestName$' ./modulepath/
|
||||
make test-backend#TestName
|
||||
```
|
||||
|
||||
Frontend unit tests run with [Vitest](https://vitest.dev/):
|
||||
Frontend unit tests run with [Vitest](https://vitest.dev/) browser mode:
|
||||
|
||||
```bash
|
||||
make test-frontend
|
||||
|
||||
+8
-7
@@ -79,7 +79,7 @@ export default defineConfig([
|
||||
'@eslint-community/eslint-comments/no-unlimited-disable': [2],
|
||||
'@eslint-community/eslint-comments/no-unused-enable': [2],
|
||||
'@eslint-community/eslint-comments/no-use': [0],
|
||||
'@eslint-community/eslint-comments/require-description': [0],
|
||||
'@eslint-community/eslint-comments/require-description': [2, {ignore: ['eslint', 'eslint-enable', 'eslint-env', 'exported', 'global', 'globals']}],
|
||||
'@stylistic/array-bracket-newline': [0],
|
||||
'@stylistic/array-bracket-spacing': [2, 'never'],
|
||||
'@stylistic/array-element-newline': [0],
|
||||
@@ -258,7 +258,7 @@ export default defineConfig([
|
||||
'@typescript-eslint/prefer-function-type': [2],
|
||||
'@typescript-eslint/prefer-includes': [2],
|
||||
'@typescript-eslint/prefer-literal-enum-member': [0],
|
||||
'@typescript-eslint/prefer-namespace-keyword': [2],
|
||||
'@typescript-eslint/prefer-namespace-keyword': [0], // handled by @typescript-eslint/no-namespace
|
||||
'@typescript-eslint/prefer-nullish-coalescing': [0],
|
||||
'@typescript-eslint/prefer-optional-chain': [2, {requireNullish: true}],
|
||||
'@typescript-eslint/prefer-promise-reject-errors': [2],
|
||||
@@ -531,8 +531,8 @@ export default defineConfig([
|
||||
'no-nonoctal-decimal-escape': [2],
|
||||
'no-obj-calls': [2],
|
||||
'no-object-constructor': [2],
|
||||
'no-octal-escape': [2],
|
||||
'no-octal': [2],
|
||||
'no-octal-escape': [0], // parse error under strict mode
|
||||
'no-octal': [0], // parse error under strict mode
|
||||
'no-param-reassign': [0],
|
||||
'no-plusplus': [0],
|
||||
'no-promise-executor-return': [0],
|
||||
@@ -581,7 +581,7 @@ export default defineConfig([
|
||||
'no-useless-call': [2],
|
||||
'no-useless-catch': [2],
|
||||
'no-useless-computed-key': [2],
|
||||
'no-useless-concat': [2],
|
||||
'no-useless-concat': [0], // handled by unicorn/no-useless-concat
|
||||
'no-useless-constructor': [2],
|
||||
'no-useless-escape': [2],
|
||||
'no-useless-rename': [2],
|
||||
@@ -592,7 +592,7 @@ export default defineConfig([
|
||||
'no-with': [0], // handled by no-restricted-syntax
|
||||
'object-shorthand': [2, 'always'],
|
||||
'one-var': [0],
|
||||
'operator-assignment': [2, 'always'],
|
||||
'operator-assignment': [0], // handled by unicorn/operator-assignment
|
||||
'prefer-arrow-callback': [2, {allowNamedFunctions: true, allowUnboundThis: true}],
|
||||
'prefer-const': [2, {destructuring: 'all', ignoreReadBeforeAssign: true}],
|
||||
'prefer-destructuring': [0],
|
||||
@@ -1095,7 +1095,8 @@ export default defineConfig([
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.test.ts', 'web_src/js/test/setup.ts'],
|
||||
files: ['**/*.test.ts', 'web_src/js/vitest.setup.ts'],
|
||||
ignores: ['tests/e2e/**'],
|
||||
plugins: {vitest},
|
||||
languageOptions: {globals: globals.vitest},
|
||||
rules: {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
module gitea.dev
|
||||
|
||||
go 1.26.0
|
||||
go 1.27
|
||||
|
||||
toolchain go1.26.5
|
||||
toolchain go1.27.0
|
||||
|
||||
require (
|
||||
connectrpc.com/connect v1.20.0
|
||||
gitea.com/go-chi/binding v0.0.0-20260414111559-654cea7ac60a
|
||||
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
|
||||
@@ -24,26 +24,25 @@ require (
|
||||
github.com/PuerkitoBio/goquery v1.12.0
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0
|
||||
github.com/alecthomas/chroma/v2 v2.27.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.33
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.36.3
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.36
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.38.1
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/blevesearch/bleve/v2 v2.6.0
|
||||
github.com/bohde/codel v0.2.0
|
||||
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.20260309112543-12416315a635
|
||||
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.5.2
|
||||
github.com/dlclark/regexp2/v2 v2.7.1
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.4
|
||||
github.com/emersion/go-imap v1.2.1
|
||||
github.com/emirpasic/gods v1.18.1
|
||||
github.com/ethantkoenig/rupture v1.0.1
|
||||
github.com/felixge/fgprof v0.9.5
|
||||
github.com/fsnotify/fsnotify v1.10.1
|
||||
github.com/getkin/kin-openapi v0.146.0
|
||||
github.com/getkin/kin-openapi v0.147.0
|
||||
github.com/go-chi/chi/v5 v5.3.1
|
||||
github.com/go-chi/cors v1.2.2
|
||||
github.com/go-co-op/gocron/v2 v2.22.0
|
||||
@@ -60,7 +59,6 @@ require (
|
||||
github.com/google/go-github/v89 v89.0.0
|
||||
github.com/google/licenseclassifier/v2 v2.0.0
|
||||
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/feeds v1.2.0
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/hashicorp/go-version v1.9.0
|
||||
@@ -68,16 +66,16 @@ require (
|
||||
github.com/huandu/xstrings v1.5.0
|
||||
github.com/jhillyerd/enmime/v2 v2.4.1
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/compress v1.19.1
|
||||
github.com/klauspost/compress v1.19.2
|
||||
github.com/lib/pq v1.12.3
|
||||
github.com/markbates/goth v1.82.0
|
||||
github.com/mattn/go-isatty v0.0.24
|
||||
github.com/mattn/go-sqlite3 v1.14.49
|
||||
github.com/mattn/go-sqlite3 v1.14.50
|
||||
github.com/meilisearch/meilisearch-go v0.36.3
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/microsoft/go-mssqldb v1.10.0
|
||||
github.com/minio/minio-go/v7 v7.2.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
|
||||
@@ -91,30 +89,29 @@ require (
|
||||
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.11.1
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.16
|
||||
github.com/urfave/cli-docs/v3 v3.1.0
|
||||
github.com/urfave/cli/v3 v3.10.1
|
||||
github.com/urfave/cli/v3 v3.11.0
|
||||
github.com/wneessen/go-mail v0.8.1
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.8.5
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.55.1
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.58.1
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.5
|
||||
golang.org/x/crypto v0.54.0
|
||||
golang.org/x/image v0.44.0
|
||||
golang.org/x/mod v0.38.0
|
||||
golang.org/x/net v0.57.0
|
||||
golang.org/x/crypto v0.55.0
|
||||
golang.org/x/image v0.45.0
|
||||
golang.org/x/mod v0.40.0
|
||||
golang.org/x/net v0.58.0
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/sync v0.22.0
|
||||
golang.org/x/sys v0.47.0
|
||||
golang.org/x/text v0.40.0
|
||||
google.golang.org/grpc v1.83.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
golang.org/x/text v0.41.0
|
||||
google.golang.org/grpc v1.83.1
|
||||
google.golang.org/protobuf v1.36.12
|
||||
gopkg.in/ini.v1 v1.67.3
|
||||
modernc.org/sqlite v1.56.0
|
||||
modernc.org/sqlite v1.57.0
|
||||
mvdan.cc/xurls/v2 v2.6.0
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.4.1
|
||||
@@ -131,10 +128,10 @@ require (
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/andybalholm/brotli v1.2.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.43.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.34 // indirect
|
||||
github.com/aws/smithy-go v1.27.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.43.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.37 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.37 // indirect
|
||||
github.com/aws/smithy-go v1.27.8 // 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
|
||||
@@ -194,6 +191,7 @@ require (
|
||||
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
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
@@ -250,7 +248,6 @@ require (
|
||||
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/stretchr/objx v0.5.3 // 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
|
||||
@@ -263,13 +260,12 @@ require (
|
||||
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.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.47.0 // indirect
|
||||
golang.org/x/tools v0.49.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.74.4 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
@@ -278,6 +274,9 @@ require (
|
||||
ignore (
|
||||
./.venv
|
||||
./node_modules
|
||||
./public
|
||||
./vendor
|
||||
./web_src
|
||||
)
|
||||
|
||||
// When doing "go get -u ./...", Golang will try to update all dependencies
|
||||
|
||||
@@ -8,8 +8,8 @@ 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-20260414111559-654cea7ac60a h1:JHoBrfuTSF9Ke9aNfSYj1XRPBHjKPgCApVprnt2Am0M=
|
||||
gitea.com/go-chi/binding v0.0.0-20260414111559-654cea7ac60a/go.mod h1:FOsLJIMdpiHzBp3Vby6Wfkdw2ppGscrjgU1IC7E4/zQ=
|
||||
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=
|
||||
@@ -48,10 +48,8 @@ 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.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
|
||||
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
|
||||
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=
|
||||
@@ -61,8 +59,6 @@ github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67
|
||||
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/PuerkitoBio/goquery v1.12.0 h1:pAcL4g3WRXekcB9AU/y1mbKez2dbY2AajVhtkO8RIBo=
|
||||
github.com/PuerkitoBio/goquery v1.12.0/go.mod h1:802ej+gV2y7bbIhOIoPY5sT183ZW0YFofScC4q/hIpQ=
|
||||
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
|
||||
github.com/RoaringBitmap/roaring v0.7.1/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.18.2 h1:oPq3Cgx//iDuJQVp6xSInAKW34J9CEwE5GmLI2z+Eic=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.18.2/go.mod h1:eq4wdNXxtJIS/oikeCzdX1rBzek7ANzbth041hrU8Q4=
|
||||
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
@@ -85,35 +81,30 @@ github.com/andybalholm/cascadia v1.3.4 h1:vM2lgh0Vru9Vwyfm4cQqWP2HHMW0u0+2PAW7Q3
|
||||
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/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
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/aws/aws-sdk-go-v2 v1.43.3 h1:XJIcfv8uDs2ukdQsoAC8/Ebu1ejxwzlayl2ZsiFns2A=
|
||||
github.com/aws/aws-sdk-go-v2 v1.43.3/go.mod h1:70vwSy16txshwG+g55WkpgPKDIByzHI8ccBsOteo3bQ=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.33 h1:/e5V3EWfeDiW6cuRxHsC8gbwko4/vvVYPJR2afBKFFY=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.33/go.mod h1:ZxAmkcyOM9beY/WO9oxp2oVPXiP3rq5N1/p4NbenJdE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.34 h1:vuIfjzoeqhQMGJyOBU3t0ZEjn2jrN8Bbg1N4CgjzM5Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.34/go.mod h1:hP28cN4CPJLZHirdQPrZR50JcLN4ApRJP2tzG8cRlhY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.34 h1:9faHsnqxJ1vDvB4wMZy/ajIDyz5QhllQjjc72RJpXAw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.34/go.mod h1:Yp6nIyejpa23nzlB/LhT63KTla9Jdi06nv/HH/OkAH8=
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.36.3 h1:0y4NzpyfbufOOZkJ54zJHJl/IC/tj/ACd6E8ViCIZZM=
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.36.3/go.mod h1:38Fc43+yPVnGM70Bqh8N7tbUVssQDVwnnFU+49sanPQ=
|
||||
github.com/aws/smithy-go v1.27.6 h1:0zjT8jgK3jbrTT7JJ3EE6JsMhX8JTrZ+f1sEndYDXrA=
|
||||
github.com/aws/smithy-go v1.27.6/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.43.6 h1:RrmFcqCBxkJuf7g1axVo5krB4jM/AO8r5e5oujrgdoQ=
|
||||
github.com/aws/aws-sdk-go-v2 v1.43.6/go.mod h1:tXpPM+v0D1lndmga+HqqLDIzUFJlEeR21aspVklHF00=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.36 h1:84s5xMme6ENYEdKG8rsbSFFg/8+lbHBeM9QYSO0gnDk=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.36/go.mod h1:c46BLdagDLIswjgt+GeQOslXgeS0E6wCacs5yZbxPGk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.37 h1:lznzIOvvbqjfe8UAaciCRJgBgJsxuTROKlhZuXQWfv8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.37/go.mod h1:otfkzyfQeMMLZAqX59GSXTL3o22BR/l6HFaRzzbWSqA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.37 h1:zCEORWo0eU0gDjG+IyApE/2B+ZGG1m+GU7B263XV8ds=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.37/go.mod h1:i6c0PEl3TNOWxRbQ++KQcVenPWS/GoQeiklKhNuqzJ8=
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.38.1 h1:9/0/sqeplR52m0qWjE00wY8r+jkImLOnu6oat5mjG2o=
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.38.1/go.mod h1:ivXqR70vBBk3Snr4IaOABAvuF7ipZpViobAIiAS8AEQ=
|
||||
github.com/aws/smithy-go v1.27.8 h1:FR0dxZfIlV7Z8eh2iHfIofdunw382XsDV3Mxt9nUvRY=
|
||||
github.com/aws/smithy-go v1.27.8/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
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=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bits-and-blooms/bitset v1.1.10/go.mod h1:w0XsmFg8qg6cmpTtJ0z3pKgjTDBMMnI/+I2syrE6XBE=
|
||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bits-and-blooms/bitset v1.24.5 h1:654xBVHc23gJMAgOTkPNoCVfiRxuIOAUnAZFtopqJ4w=
|
||||
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.0.5/go.mod h1:ZjWibgnbRX33c+vBRgla9QhPb4QOjD6fdVJ+R1Bk8LM=
|
||||
github.com/blevesearch/bleve/v2 v2.6.0 h1:Cyd3dd4q5tCbOV8MnKUVRUDYMHOir9xn12NZzXVSEd4=
|
||||
github.com/blevesearch/bleve/v2 v2.6.0/go.mod h1:gLmI8lWgHgrIYf7UpUX7JISI1CaqC6VScu46mHThuAY=
|
||||
github.com/blevesearch/bleve_index_api v1.0.0/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
|
||||
github.com/blevesearch/bleve_index_api v1.3.12 h1:MirVNltwGq8z0PhOgiQp+bKL5qq8OvCxEwOOC7NnHNE=
|
||||
github.com/blevesearch/bleve_index_api v1.3.12/go.mod h1:xvd48t5XMeeioWQ5/jZvgLrV98flT2rdvEJ3l/ki4Ko=
|
||||
github.com/blevesearch/geo v0.2.5 h1:yJg9FX1oRwLnjXSXF+ECHfXFTF4diF02Ca/qUGVjJhE=
|
||||
@@ -124,37 +115,26 @@ github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
|
||||
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
|
||||
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
|
||||
github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA=
|
||||
github.com/blevesearch/mmap-go v1.2.0 h1:l33nNKPFcBjJUMwem6sAYJPUzhUCABoK9FxZDGiFNBI=
|
||||
github.com/blevesearch/mmap-go v1.2.0/go.mod h1:Vd6+20GBhEdwJnU1Xohgt88XCD/CTWcqbCNxkZpyBo0=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.0.1/go.mod h1:lq7yK2jQy1yQjtjTfU931aVqz7pYxEudHaDwOt1tXfU=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.4.7 h1:GlMzW08hcsM3DnLUxhyF/1PcDal1qtvvIuytuph5djw=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.4.7/go.mod h1://IJ7tG3QCf0cWW/aVSXqy77tc1AvLu3fcJLYEvOAFs=
|
||||
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
|
||||
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
|
||||
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
|
||||
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
|
||||
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.1/go.mod h1:MQDVGpHZrpe3Uy26zJBf/a8h0FZY6xJbthIMm8myH2Q=
|
||||
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.0.3/go.mod h1:2u5ax02KeDuNWu4/C+hVQMD6uLN4txH1JbtpaDNLJRo=
|
||||
github.com/blevesearch/vellum v1.0.4/go.mod h1:cMhywHI0de50f7Nj42YgvyD6bFJ2WkNRvNBlNMrEVgY=
|
||||
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.2.0/go.mod h1:gN/a0alGw1FZt/YGTo1G6Z6XpDkeOfujX5exY9sCQQM=
|
||||
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.2.0/go.mod h1:fdjwvCwWWwJW/EYTYGtAp3gBA0geCYGLcVTtJEZnY6A=
|
||||
github.com/blevesearch/zapx/v12 v12.4.3 h1:eElXvAaAX4m04t//CGBQAtHNPA+Q6A1hHZVrN3LSFYo=
|
||||
github.com/blevesearch/zapx/v12 v12.4.3/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58=
|
||||
github.com/blevesearch/zapx/v13 v13.2.0/go.mod h1:o5rAy/lRS5JpAbITdrOHBS/TugWYbkcYZTz6VfEinAQ=
|
||||
github.com/blevesearch/zapx/v13 v13.4.3 h1:qsdhRhaSpVnqDFlRiH9vG5+KJ+dE7KAW9WyZz/KXAiE=
|
||||
github.com/blevesearch/zapx/v13 v13.4.3/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk=
|
||||
github.com/blevesearch/zapx/v14 v14.2.0/go.mod h1:GNgZusc1p4ot040cBQMRGEZobvwjCquiEKYh1xLFK9g=
|
||||
github.com/blevesearch/zapx/v14 v14.4.3 h1:GY4Hecx0C6UTmiNC2pKdeA2rOKiLR5/rwpU9WR51dgM=
|
||||
github.com/blevesearch/zapx/v14 v14.4.3/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
|
||||
github.com/blevesearch/zapx/v15 v15.2.0/go.mod h1:MmQceLpWfME4n1WrBFIwplhWmaQbQqLQARpaKUEOs/A=
|
||||
github.com/blevesearch/zapx/v15 v15.4.3 h1:iJiMJOHrz216jyO6lS0m9RTCEkprUnzvqAI2lc/0/CU=
|
||||
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=
|
||||
@@ -191,8 +171,8 @@ github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20260309112543-12416315a635 h1:RwCfD5XyO8jAermEy6pauh5Q5o6mCvbeNcCPOZlIA5o=
|
||||
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20260309112543-12416315a635/go.mod h1:R+SetERD4+IL7QH0WHp9MLifvITvh9gL3g8vX1j2Fcs=
|
||||
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=
|
||||
@@ -209,10 +189,6 @@ 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/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
|
||||
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=
|
||||
@@ -220,8 +196,6 @@ github.com/couchbase/gomemcached v0.3.4/go.mod h1:pISAjweI42vljCumsJIo7CVhqIMIIP
|
||||
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/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
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=
|
||||
@@ -238,17 +212,15 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k
|
||||
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.5.2 h1:HAsucWRhsqcDzl6Ua9aR8JwYOTzrZyPrF0/FNxJVAI0=
|
||||
github.com/dlclark/regexp2/v2 v2.5.2/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
|
||||
github.com/dlclark/regexp2/v2 v2.7.1 h1:yqDtwI1ptXXvEUNpYTk2lad4jLtAcKqkzepn4savSk4=
|
||||
github.com/dlclark/regexp2/v2 v2.7.1/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=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.4 h1:CHwUbBVVyKWRX9kt5A/OtwhYUJB32DrFp9xzmjR6cac=
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.4/go.mod h1:JWRVKHdVW+dkv6F8p+xGCa6a+TyMrqsFbFkSs/aQkrQ=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
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=
|
||||
@@ -260,8 +232,6 @@ github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTe
|
||||
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/ethantkoenig/rupture v1.0.1 h1:6aAXghmvtnngMgQzy7SMGdicMvkV86V4n9fT0meE5E4=
|
||||
github.com/ethantkoenig/rupture v1.0.1/go.mod h1:Sjqo/nbffZp1pVVXNGhpugIjsWmuS9KiIB4GtpEBur4=
|
||||
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=
|
||||
@@ -272,14 +242,12 @@ 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.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78=
|
||||
github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/getkin/kin-openapi v0.146.0 h1:RA/1RdxrSJW4oc1+6IfnYB6AO9CaGy8GTKPh0k4Ordo=
|
||||
github.com/getkin/kin-openapi v0.146.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY=
|
||||
github.com/getkin/kin-openapi v0.147.0 h1:s+Xsm9gUMPJbgCnABZ2to3zSQQ5A9dyj/zo62VVsldY=
|
||||
github.com/getkin/kin-openapi v0.147.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY=
|
||||
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/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
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.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
@@ -348,7 +316,6 @@ github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EO
|
||||
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.3.2/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=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
@@ -392,9 +359,8 @@ github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFe
|
||||
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/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/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/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=
|
||||
@@ -423,7 +389,6 @@ github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaX
|
||||
github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
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=
|
||||
@@ -432,7 +397,6 @@ github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq
|
||||
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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
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=
|
||||
@@ -462,8 +426,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
|
||||
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.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk=
|
||||
github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8=
|
||||
github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
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=
|
||||
@@ -472,7 +436,6 @@ github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM
|
||||
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
@@ -491,7 +454,6 @@ 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=
|
||||
github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE=
|
||||
github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o=
|
||||
@@ -505,8 +467,8 @@ github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/a
|
||||
github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-shellwords v1.0.13 h1:DC0OMEpGjm6LfNFU4ckYcvbQKyp2vE8atyFGXNtDcf4=
|
||||
github.com/mattn/go-shellwords v1.0.13/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.49 h1:B8jBHC3xhxZgxztrgruTuLucebnULQnx4W7cF7SAE9w=
|
||||
github.com/mattn/go-sqlite3 v1.14.49/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
|
||||
github.com/mattn/go-sqlite3 v1.14.50 h1:dmdFvo1XG4MPzA4IkAmE9upVz/Nj31uRoM5+jC8hYbY=
|
||||
github.com/mattn/go-sqlite3 v1.14.50/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.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk=
|
||||
@@ -525,12 +487,10 @@ 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.2.1 h1:PfBfwvKB/MmqyN8Vb1G9voWisaM9OrLv+WwOvMwS9Dw=
|
||||
github.com/minio/minio-go/v7 v7.2.1/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0=
|
||||
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/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -538,7 +498,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM=
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||
github.com/msteinert/pam/v2 v2.1.0 h1:er5F9TKV5nGFuTt12ubtqPHEUdeBwReP7vd3wovidGY=
|
||||
@@ -581,8 +540,6 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
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=
|
||||
@@ -608,7 +565,6 @@ github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+
|
||||
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/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
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.76 h1:RdDWuvlYBSp+bTrBvaXqJnNEL3VVzsnjo+0psPFgLc4=
|
||||
@@ -619,14 +575,12 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rhysd/actionlint v1.7.12 h1:vQ4GeJN86C0QH+gTUQcs8McmK62OLT3kmakPMtEWYnY=
|
||||
github.com/rhysd/actionlint v1.7.12/go.mod h1:krOUhujIsJusovkaYzQ/VNH8PFexjNKqU0q5XI/4w+g=
|
||||
github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
|
||||
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.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 v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
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=
|
||||
@@ -651,20 +605,12 @@ github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbd
|
||||
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.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||
github.com/stangelandcl/ppmd v0.1.1 h1:c25QazhlWUn5nmR1QOzafKhQxBicAr7GGCKER2aJ8H8=
|
||||
github.com/stangelandcl/ppmd v0.1.1/go.mod h1:Rrv7M+/2P5jYr/GMLhBl7Ug3uJ1bUiVzr5LbbaV6xgY=
|
||||
github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc=
|
||||
github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -681,18 +627,15 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
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 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
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=
|
||||
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
|
||||
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ=
|
||||
github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||
github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ=
|
||||
github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.16 h1:ld6NyySjx5lowVKwJvMRLnW5nxKX/xnpSiFYZ/Lxur0=
|
||||
github.com/ulikunitz/xz v0.5.16/go.mod h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw=
|
||||
@@ -700,9 +643,8 @@ 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.10.1 h1:7Kx9H50hrHbRbyxgO1KP6/BcbiGRz0uYh5YyQ30JEEY=
|
||||
github.com/urfave/cli/v3 v3.10.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/urfave/cli/v3 v3.11.0 h1:P/euJp99kb9p0tlVY+iYTLYYTAQlfl0hR2gUO1Img1Q=
|
||||
github.com/urfave/cli/v3 v3.11.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||
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=
|
||||
@@ -711,7 +653,6 @@ 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/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
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=
|
||||
@@ -731,9 +672,8 @@ 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/v2 v2.55.1 h1:eX0iBhJ9GWP2XqQOH/pkmSvPosFS3rcyFcup/3kHL2k=
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.55.1/go.mod h1:6GcBiCVrZBtOvl6HiahZ3nq5eBs7JMUARJeLN4/RgZU=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.58.1 h1:XMuEYGaruQ3Yu7RFGE4b1fmi//QkAPUisq9LV9jahbA=
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.58.1/go.mod h1:tuYYHZSRj9eKea28W3uySf9bSqfkE2RknDpBdzxdnhk=
|
||||
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.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
@@ -750,13 +690,12 @@ 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.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
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.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.3/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-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
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=
|
||||
@@ -767,27 +706,24 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
||||
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.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
||||
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.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I=
|
||||
golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/image v0.45.0 h1:FMb1nTbH5H9vF55SriQHgFw5GnNL9Jg6L25BwXKzhB0=
|
||||
golang.org/x/image v0.45.0/go.mod h1:n62x/7RqlwXDvGsSU4u6IUTUf6KghUZ9Bt7cG/T9Fx4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
|
||||
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
|
||||
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
|
||||
golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
|
||||
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-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
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.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
@@ -798,13 +734,12 @@ 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.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
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-20200625203802-6e8e738ad208/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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -816,17 +751,13 @@ golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/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-20190813064441-fde4db37ae7a/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-20200202164722-d101bd2416d5/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=
|
||||
@@ -868,37 +799,35 @@ 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.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
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.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
|
||||
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
|
||||
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-20260610212136-7ab31c22f7ad h1:45WmJvIV6C2+O/jjLkPUH+F3aOj/1miDoU2DD0+NWbg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
|
||||
google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
|
||||
google.golang.org/grpc v1.83.1 h1:HIO0+BEtBP6soyqvqC8sNUjZ7bTs+0hFQuFF+RAy++Y=
|
||||
google.golang.org/grpc v1.83.1/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
|
||||
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=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
||||
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
@@ -941,8 +870,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.56.0 h1:/D8e2RfFqoy/Zc6PuC76U28zFwmI/sYx1Kjm4yEn9e0=
|
||||
modernc.org/sqlite v1.56.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=
|
||||
modernc.org/sqlite v1.57.0 h1:qNQP6xnx5M0ISNtlnxoOX0+cD5bJ0/gr9aMmndFczzg=
|
||||
modernc.org/sqlite v1.57.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=
|
||||
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=
|
||||
|
||||
@@ -422,6 +422,10 @@ func prepareMigrationTasks() []*migration {
|
||||
newMigration(346, "Add license_path column to repo_license and backfill", v28.AddLicensePathToRepoLicense),
|
||||
newMigration(347, "Add watch options", v28.AddWatchOptions),
|
||||
newMigration(348, "Recreate email_hash table for SHA256 avatar hashes", v28.RecreateEmailHashTable),
|
||||
newMigration(349, "Expand action_schedule content column", v28.ExpandActionScheduleContent),
|
||||
newMigration(350, "Add published_unix column to release", v28.AddPublishedUnixToRelease),
|
||||
newMigration(351, "Track transfer recipient access grants", v28.AddRecipientAccessGrantedToRepoTransfer),
|
||||
newMigration(352, "Add token columns to deploy_key", v28.AddTokenToDeployKey),
|
||||
}
|
||||
return preparedMigrations
|
||||
}
|
||||
|
||||
@@ -5,17 +5,33 @@ package v1_16
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdh"
|
||||
"encoding/base32"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
"gitea.dev/modules/timeutil"
|
||||
|
||||
"github.com/tstranex/u2f"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
func parseU2FRegistration(raw []byte) (keyHandle, publicKey []byte, err error) {
|
||||
if len(raw) < 69 || raw[0] != 0x05 {
|
||||
return nil, nil, errors.New("invalid u2f registration")
|
||||
}
|
||||
pub, err := ecdh.P256().NewPublicKey(raw[1:66])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
keyHandleLen := int(raw[66])
|
||||
if len(raw) < 67+keyHandleLen {
|
||||
return nil, nil, errors.New("invalid u2f registration key handle")
|
||||
}
|
||||
return raw[67 : 67+keyHandleLen], pub.Bytes(), nil
|
||||
}
|
||||
|
||||
// v208 migration was completely broken
|
||||
func RemigrateU2FCredentials(_ context.Context, x base.EngineMigration) error {
|
||||
// Create webauthnCredential table
|
||||
@@ -112,12 +128,7 @@ func RemigrateU2FCredentials(_ context.Context, x base.EngineMigration) error {
|
||||
}
|
||||
}
|
||||
for _, reg := range regs {
|
||||
parsed := new(u2f.Registration)
|
||||
err = parsed.UnmarshalBinary(reg.Raw)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
pubKey, err := parsed.PubKey.ECDH()
|
||||
keyHandle, publicKey, err := parseU2FRegistration(reg.Raw)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -126,8 +137,8 @@ func RemigrateU2FCredentials(_ context.Context, x base.EngineMigration) error {
|
||||
Name: reg.Name,
|
||||
LowerName: strings.ToLower(reg.Name),
|
||||
UserID: reg.UserID,
|
||||
CredentialID: base32.HexEncoding.EncodeToString(parsed.KeyHandle),
|
||||
PublicKey: pubKey.Bytes(),
|
||||
CredentialID: base32.HexEncoding.EncodeToString(keyHandle),
|
||||
PublicKey: publicKey,
|
||||
AttestationType: "fido-u2f",
|
||||
AAGUID: []byte{},
|
||||
SignCount: reg.Counter,
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
package v1_16
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modelmigration/migrationtest"
|
||||
"gitea.dev/modules/timeutil"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
@@ -73,3 +76,31 @@ func Test_RemigrateU2FCredentials(t *testing.T) {
|
||||
|
||||
assert.Equal(t, expected, got)
|
||||
}
|
||||
|
||||
const u2fRawMessageFormatsExample81Hex = "" +
|
||||
"0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095da" +
|
||||
"f6cd7c03e2e60385d2f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659c" +
|
||||
"baf00b4950f7abfe6660e2e006f76868b772d70c25"
|
||||
|
||||
func Test_parseU2FRegistration(t *testing.T) {
|
||||
raw, err := hex.DecodeString(u2fRawMessageFormatsExample81Hex)
|
||||
require.NoError(t, err)
|
||||
|
||||
keyHandle, publicKey, err := parseU2FRegistration(raw)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25", hex.EncodeToString(keyHandle))
|
||||
assert.Equal(t, "04b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9", hex.EncodeToString(publicKey))
|
||||
|
||||
invalidPoint := slices.Clone(raw)
|
||||
invalidPoint[1] = 0x02
|
||||
|
||||
for name, input := range map[string][]byte{
|
||||
"too short": raw[:68],
|
||||
"bad reserved byte": append([]byte{0x04}, raw[1:]...),
|
||||
"invalid point": invalidPoint,
|
||||
"truncated key handle": raw[:67+int(raw[66])-1],
|
||||
} {
|
||||
_, _, err := parseU2FRegistration(input)
|
||||
assert.Error(t, err, name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
|
||||
func AddWatchOptions(_ context.Context, x base.EngineMigration) error {
|
||||
type Watch struct {
|
||||
PullRequests bool `xorm:"NOT NULL DEFAULT true"`
|
||||
Issues bool `xorm:"NOT NULL DEFAULT true"`
|
||||
Releases bool `xorm:"NOT NULL DEFAULT true"`
|
||||
IncludePullRequests bool `xorm:"NOT NULL DEFAULT true"`
|
||||
IncludeIssues bool `xorm:"NOT NULL DEFAULT true"`
|
||||
IncludeReleases bool `xorm:"NOT NULL DEFAULT true"`
|
||||
}
|
||||
_, err := x.SyncWithOptions(xorm.SyncOptions{
|
||||
IgnoreConstrains: true,
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v28
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
func ExpandActionScheduleContent(ctx context.Context, x base.EngineMigration) error {
|
||||
if !setting.Database.Type.IsMySQL() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return base.ModifyColumn(ctx, x, "action_schedule", &schemas.Column{
|
||||
Name: "content",
|
||||
SQLType: schemas.SQLType{
|
||||
Name: "LONGBLOB",
|
||||
},
|
||||
Length: 0,
|
||||
Nullable: true,
|
||||
DefaultIsEmpty: true,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v28
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modelmigration/migrationtest"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestExpandActionScheduleContent(t *testing.T) {
|
||||
if !setting.Database.Type.IsMySQL() {
|
||||
t.Skip("Only MySQL limits BLOB columns to 65,535 bytes")
|
||||
}
|
||||
|
||||
type ActionSchedule struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Content []byte `xorm:"BLOB"`
|
||||
}
|
||||
|
||||
x, deferable := migrationtest.PrepareTestEnv(t, 0, new(ActionSchedule))
|
||||
defer deferable()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, ExpandActionScheduleContent(t.Context(), x))
|
||||
|
||||
tables := migrationtest.LoadTableSchemasMap(t, x)
|
||||
assert.Equal(t, "LONGBLOB", tables["action_schedule"].GetColumn("content").SQLType.Name)
|
||||
|
||||
content := bytes.Repeat([]byte("x"), 65_536)
|
||||
_, err := x.Insert(&ActionSchedule{Content: content})
|
||||
require.NoError(t, err)
|
||||
|
||||
var stored ActionSchedule
|
||||
has, err := x.Get(&stored)
|
||||
require.NoError(t, err)
|
||||
require.True(t, has)
|
||||
assert.Equal(t, content, stored.Content)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v28
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddPublishedUnixToRelease(_ context.Context, x base.EngineMigration) error {
|
||||
type Release struct {
|
||||
PublishedUnix int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
if _, err := x.SyncWithOptions(xorm.SyncOptions{
|
||||
IgnoreConstrains: true,
|
||||
IgnoreDropIndices: true,
|
||||
}, new(Release)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// existing rows have no recorded publication time, so fall back to their creation time
|
||||
_, err := x.Exec("UPDATE `release` SET published_unix = created_unix WHERE published_unix = 0 AND is_draft = ?", false)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v28
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modelmigration/migrationtest"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddPublishedUnixToRelease(t *testing.T) {
|
||||
type Release struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
|
||||
IsTag bool `xorm:"NOT NULL DEFAULT false"`
|
||||
CreatedUnix int64 `xorm:"INDEX"`
|
||||
}
|
||||
|
||||
x, deferable := migrationtest.PrepareTestEnv(t, 0, new(Release))
|
||||
defer deferable()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := x.Insert(
|
||||
&Release{CreatedUnix: 1000000},
|
||||
&Release{IsDraft: true, CreatedUnix: 2000000},
|
||||
&Release{IsTag: true, CreatedUnix: 3000000},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, AddPublishedUnixToRelease(t.Context(), x))
|
||||
|
||||
var got []struct{ PublishedUnix int64 }
|
||||
require.NoError(t, x.Table("release").OrderBy("id").Find(&got))
|
||||
require.Equal(t, []int64{1000000, 0, 3000000}, []int64{got[0].PublishedUnix, got[1].PublishedUnix, got[2].PublishedUnix},
|
||||
"everything but drafts is backfilled")
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v28
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddRecipientAccessGrantedToRepoTransfer(_ context.Context, x base.EngineMigration) error {
|
||||
type RepoTransfer struct {
|
||||
RecipientAccessGranted bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
_, err := x.SyncWithOptions(xorm.SyncOptions{
|
||||
IgnoreConstrains: true,
|
||||
IgnoreDropIndices: true,
|
||||
}, new(RepoTransfer))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v28
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/modelmigration/base"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddTokenToDeployKey(ctx context.Context, x base.EngineMigration) error {
|
||||
// Drop the old UNIQUE(s) index on (key_id, repo_id). Every token row carries key
|
||||
// id 0, so the pair can no longer be unique. AddDeployKey still checks it in code.
|
||||
indexes, err := x.Dialect().GetIndexes(x.DB(), ctx, "deploy_key")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if idx, ok := indexes["s"]; ok {
|
||||
if _, err := x.Exec(x.Dialect().DropIndexSQL("deploy_key", idx)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
type DeployKey struct {
|
||||
KeyID int64 `xorm:"INDEX"`
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
KeyType int `xorm:"NOT NULL DEFAULT 1"` // every existing row is an SSH key
|
||||
TokenHash string `xorm:"INDEX"`
|
||||
}
|
||||
_, err = x.SyncWithOptions(xorm.SyncOptions{
|
||||
IgnoreConstrains: true,
|
||||
IgnoreDropIndices: true, // the bean only describes the new columns
|
||||
}, new(DeployKey))
|
||||
return err
|
||||
}
|
||||
+52
-49
@@ -8,13 +8,12 @@ package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@@ -23,12 +22,12 @@ import (
|
||||
type ArtifactStatus int64
|
||||
|
||||
const (
|
||||
ArtifactStatusUploadPending ArtifactStatus = iota + 1 // 1, ArtifactStatusUploadPending is the status of an artifact upload that is pending
|
||||
ArtifactStatusUploadConfirmed // 2, ArtifactStatusUploadConfirmed is the status of an artifact upload that is confirmed
|
||||
ArtifactStatusUploadError // 3, ArtifactStatusUploadError is the status of an artifact upload that is errored
|
||||
ArtifactStatusExpired // 4, ArtifactStatusExpired is the status of an artifact that is expired
|
||||
ArtifactStatusPendingDeletion // 5, ArtifactStatusPendingDeletion is the status of an artifact that is pending deletion
|
||||
ArtifactStatusDeleted // 6, ArtifactStatusDeleted is the status of an artifact that is deleted
|
||||
ArtifactStatusUploadPending ArtifactStatus = iota + 1
|
||||
ArtifactStatusUploadConfirmed
|
||||
ArtifactStatusUploadError // unused, kept so the numbering below stays stable
|
||||
ArtifactStatusExpired
|
||||
ArtifactStatusPendingDeletion
|
||||
ArtifactStatusDeleted
|
||||
)
|
||||
|
||||
func (status ArtifactStatus) ToString() string {
|
||||
@@ -87,15 +86,36 @@ type ActionArtifact struct {
|
||||
Status ArtifactStatus `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated index"`
|
||||
ExpiredUnix timeutil.TimeStamp `xorm:"index"` // The time when the artifact will be expired
|
||||
ExpiredUnix timeutil.TimeStamp `xorm:"index"` // 0 means the artifact is kept forever
|
||||
}
|
||||
|
||||
func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPath string, expiredDays int64) (*ActionArtifact, error) {
|
||||
const artifactKeepForever timeutil.TimeStamp = 0
|
||||
|
||||
func artifactExpiry(requested optional.Option[timeutil.TimeStamp]) timeutil.TimeStamp {
|
||||
if requested.Has() {
|
||||
return max(requested.Value(), artifactKeepForever+1)
|
||||
}
|
||||
if setting.Actions.ArtifactRetentionDays <= 0 {
|
||||
return artifactKeepForever
|
||||
}
|
||||
return timeutil.TimeStampNow().Add(timeutil.Day * setting.Actions.ArtifactRetentionDays)
|
||||
}
|
||||
|
||||
// CreateArtifact returns the artifact for the name and path, creating it on first upload and refreshing its expiry either way.
|
||||
func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPath string, expiry optional.Option[timeutil.TimeStamp]) (*ActionArtifact, error) {
|
||||
if err := t.LoadJob(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
artifact, err := getArtifactByNameAndPath(ctx, t.Job.RunID, t.Job.RunAttemptID, artifactName, artifactPath)
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
expiredUnix := artifactExpiry(expiry)
|
||||
|
||||
artifact, exist, err := db.Get[ActionArtifact](ctx, builder.Eq{
|
||||
"run_id": t.Job.RunID, "run_attempt_id": t.Job.RunAttemptID,
|
||||
"artifact_name": artifactName, "artifact_path": artifactPath,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exist {
|
||||
artifact := &ActionArtifact{
|
||||
ArtifactName: artifactName,
|
||||
ArtifactPath: artifactPath,
|
||||
@@ -106,40 +126,24 @@ func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPa
|
||||
OwnerID: t.OwnerID,
|
||||
CommitSHA: t.CommitSHA,
|
||||
Status: ArtifactStatusUploadPending,
|
||||
ExpiredUnix: timeutil.TimeStamp(time.Now().Unix() + timeutil.Day*expiredDays),
|
||||
ExpiredUnix: expiredUnix,
|
||||
}
|
||||
if _, err := db.GetEngine(ctx).Insert(artifact); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return artifact, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := db.GetEngine(ctx).ID(artifact.ID).Cols("expired_unix").Update(&ActionArtifact{
|
||||
ExpiredUnix: timeutil.TimeStamp(time.Now().Unix() + timeutil.Day*expiredDays),
|
||||
}); err != nil {
|
||||
artifact.ExpiredUnix = expiredUnix
|
||||
if err := UpdateArtifact(ctx, artifact, "expired_unix"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
||||
func getArtifactByNameAndPath(ctx context.Context, runID, runAttemptID int64, name, fpath string) (*ActionArtifact, error) {
|
||||
var art ActionArtifact
|
||||
has, err := db.GetEngine(ctx).Where("run_id = ? AND run_attempt_id = ? AND artifact_name = ? AND artifact_path = ?", runID, runAttemptID, name, fpath).Get(&art)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, util.ErrNotExist
|
||||
}
|
||||
return &art, nil
|
||||
}
|
||||
|
||||
// UpdateArtifactByID updates an artifact by id
|
||||
func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) error {
|
||||
art.ID = id
|
||||
_, err := db.GetEngine(ctx).ID(id).AllCols().Update(art)
|
||||
func UpdateArtifact(ctx context.Context, art *ActionArtifact, cols ...string) error {
|
||||
_, err := db.GetEngine(ctx).ID(art.ID).Cols(cols...).Update(art)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -149,7 +153,7 @@ type FindArtifactsOptions struct {
|
||||
RunID int64
|
||||
RunAttemptIDs []int64 // empty means every attempt; pass 0 to target legacy artifacts, which have run_attempt_id=0
|
||||
ArtifactName string
|
||||
Status int
|
||||
Status ArtifactStatus
|
||||
FinalizedArtifactsV4 bool
|
||||
}
|
||||
|
||||
@@ -157,7 +161,7 @@ func (opts FindArtifactsOptions) ToOrders() string {
|
||||
return "id"
|
||||
}
|
||||
|
||||
var _ db.FindOptionsOrder = (*FindArtifactsOptions)(nil)
|
||||
var _ db.FindOptions = (*FindArtifactsOptions)(nil)
|
||||
|
||||
func (opts FindArtifactsOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
@@ -229,7 +233,7 @@ func ListUploadedArtifactsMetaByRunAttempt(ctx context.Context, repoID, runID, r
|
||||
func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) {
|
||||
arts := make([]*ActionArtifact, 0, 10)
|
||||
return arts, db.GetEngine(ctx).
|
||||
Where("expired_unix < ? AND status = ?", timeutil.TimeStamp(time.Now().Unix()), ArtifactStatusUploadConfirmed).Find(&arts)
|
||||
Where("expired_unix > ? AND expired_unix < ? AND status = ?", artifactKeepForever, timeutil.TimeStampNow(), ArtifactStatusUploadConfirmed).Find(&arts)
|
||||
}
|
||||
|
||||
// ListPendingDeleteArtifacts returns all artifacts in pending-delete status.
|
||||
@@ -240,23 +244,24 @@ func ListPendingDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifa
|
||||
Where("status = ?", ArtifactStatusPendingDeletion).Limit(limit).Find(&arts)
|
||||
}
|
||||
|
||||
// SetArtifactExpired sets an artifact to expired
|
||||
func setConfirmedArtifactsStatus(ctx context.Context, status ArtifactStatus, cond builder.Cond) error {
|
||||
_, err := db.GetEngine(ctx).Where(cond).And(builder.Eq{"status": ArtifactStatusUploadConfirmed}).
|
||||
Cols("status").Update(&ActionArtifact{Status: status})
|
||||
return err
|
||||
}
|
||||
|
||||
func SetArtifactExpired(ctx context.Context, artifactID int64) error {
|
||||
_, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusExpired})
|
||||
return err
|
||||
return setConfirmedArtifactsStatus(ctx, ArtifactStatusExpired, builder.Eq{"id": artifactID})
|
||||
}
|
||||
|
||||
// SetArtifactNeedDeleteByID sets an artifact to need-delete by ID, cron job will delete it.
|
||||
func SetArtifactNeedDeleteByID(ctx context.Context, artifactID int64) error {
|
||||
_, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusPendingDeletion})
|
||||
return err
|
||||
return setConfirmedArtifactsStatus(ctx, ArtifactStatusPendingDeletion, builder.Eq{"id": artifactID})
|
||||
}
|
||||
|
||||
// SetArtifactNeedDeleteByRunAttempt sets an artifact to need-delete in a run attempt, cron job will delete it.
|
||||
// runAttemptID may be 0 for legacy artifacts created before ActionRunAttempt existed.
|
||||
func SetArtifactNeedDeleteByRunAttempt(ctx context.Context, runID, runAttemptID int64, name string) error {
|
||||
_, err := db.GetEngine(ctx).Where("run_id=? AND run_attempt_id=? AND artifact_name=? AND status = ?", runID, runAttemptID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusPendingDeletion})
|
||||
return err
|
||||
return setConfirmedArtifactsStatus(ctx, ArtifactStatusPendingDeletion,
|
||||
builder.Eq{"run_id": runID, "run_attempt_id": runAttemptID, "artifact_name": name})
|
||||
}
|
||||
|
||||
// GetArtifactsByRunAttemptAndName returns all artifacts with the given name in the specified run attempt.
|
||||
@@ -269,8 +274,6 @@ func GetArtifactsByRunAttemptAndName(ctx context.Context, runID, runAttemptID in
|
||||
Find(&arts)
|
||||
}
|
||||
|
||||
// SetArtifactDeleted sets an artifact to deleted
|
||||
func SetArtifactDeleted(ctx context.Context, artifactID int64) error {
|
||||
_, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusDeleted})
|
||||
return err
|
||||
return UpdateArtifact(ctx, &ActionArtifact{ID: artifactID, Status: ArtifactStatusDeleted}, "status")
|
||||
}
|
||||
|
||||
@@ -27,14 +27,7 @@ func (attempts ActionRunAttemptList) LoadTriggerUser(ctx context.Context) error
|
||||
return err
|
||||
}
|
||||
for _, attempt := range attempts {
|
||||
if attempt.TriggerUserID == user_model.ActionsUserID {
|
||||
attempt.TriggerUser = user_model.NewActionsUser()
|
||||
} else {
|
||||
attempt.TriggerUser = users[attempt.TriggerUserID]
|
||||
if attempt.TriggerUser == nil {
|
||||
attempt.TriggerUser = user_model.NewGhostUser()
|
||||
}
|
||||
}
|
||||
attempt.TriggerUser = user_model.GetPossibleUserFromMap(attempt.TriggerUserID, users)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@@ -155,10 +156,10 @@ func (opts FindRunJobOptions) ToJoins() []db.JoinFunc {
|
||||
}
|
||||
|
||||
func (opts FindRunJobOptions) ToOrders() string {
|
||||
return string(opts.OrderBy)
|
||||
return util.IfZero(string(opts.OrderBy), "action_run_job.id")
|
||||
}
|
||||
|
||||
var _ db.FindOptionsOrder = FindRunJobOptions{}
|
||||
var _ db.FindOptions = (*FindRunJobOptions)(nil)
|
||||
|
||||
// CountRunJobsByRunAndAttemptID counts the jobs belonging to the given run attempt.
|
||||
// It is used to enforce MaxJobNumPerRun when reusable-workflow expansion inserts new jobs.
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/translation"
|
||||
webhook_module "gitea.dev/modules/webhook"
|
||||
|
||||
@@ -203,3 +204,14 @@ func GetActors(ctx context.Context, repoID int64) ([]*user_model.User, error) {
|
||||
OrderBy(user_model.GetOrderByName()).
|
||||
Find(&actors)
|
||||
}
|
||||
|
||||
// FindOldestRuns returns up to limit runs in the given statuses created before olderThan, lowest id first.
|
||||
func FindOldestRuns(ctx context.Context, statuses []Status, olderThan timeutil.TimeStamp, limit int) ([]*ActionRun, error) {
|
||||
runs := make([]*ActionRun, 0, limit)
|
||||
return runs, db.GetEngine(ctx).
|
||||
Where(builder.In("`action_run`.status", statuses)).
|
||||
And(builder.Lt{"`action_run`.created": olderThan}).
|
||||
OrderBy("`action_run`.`id` ASC").
|
||||
Limit(limit).
|
||||
Find(&runs)
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ type ActionSchedule struct {
|
||||
Ref string
|
||||
CommitSHA string
|
||||
Event webhook_module.HookEventType
|
||||
EventPayload string `xorm:"LONGTEXT"`
|
||||
Content []byte
|
||||
EventPayload string `xorm:"LONGTEXT"`
|
||||
Content []byte `xorm:"LONGBLOB"`
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
||||
@@ -53,6 +53,10 @@ type FindScopedWorkflowSourceOpts struct {
|
||||
SourceRepoID int64
|
||||
}
|
||||
|
||||
func (opts FindScopedWorkflowSourceOpts) ToOrders() string {
|
||||
return "id"
|
||||
}
|
||||
|
||||
func (opts FindScopedWorkflowSourceOpts) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if len(opts.OwnerIDs) > 0 {
|
||||
|
||||
+8
-40
@@ -5,7 +5,6 @@ package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -22,7 +21,6 @@ import (
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru/v2"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@@ -66,21 +64,8 @@ type ActionTask struct {
|
||||
// it only decides whether the runner is reachable, not whether the task should be killed.
|
||||
const taskReportTimeout = time.Minute
|
||||
|
||||
var successfulTokenTaskCache *lru.Cache[string, any]
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(ActionTask), func() error {
|
||||
if setting.SuccessfulTokensCacheSize > 0 {
|
||||
var err error
|
||||
successfulTokenTaskCache, err = lru.New[string, any](setting.SuccessfulTokensCacheSize)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to allocate Task cache: %v", err)
|
||||
}
|
||||
} else {
|
||||
successfulTokenTaskCache = nil
|
||||
}
|
||||
return nil
|
||||
})
|
||||
db.RegisterModel(new(ActionTask))
|
||||
}
|
||||
|
||||
func (task *ActionTask) Duration() time.Duration {
|
||||
@@ -195,21 +180,21 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro
|
||||
}
|
||||
}
|
||||
|
||||
cacheKey := "actions:" + token
|
||||
lastEight := token[len(token)-8:]
|
||||
|
||||
if id := getTaskIDFromCache(token); id > 0 {
|
||||
if cached, _ := auth_model.TokenCache().Get(cacheKey); cached != nil {
|
||||
task := &ActionTask{
|
||||
TokenLastEight: lastEight,
|
||||
}
|
||||
// Re-get the task from the db in case it has been deleted in the intervening period
|
||||
has, err := db.GetEngine(ctx).ID(id).Get(task)
|
||||
has, err := db.GetEngine(ctx).ID(cached.TokenID).Get(task)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if has {
|
||||
if has && util.CryptoConstTimeEqual(task.TokenHash, cached.TokenHash) {
|
||||
return task, nil
|
||||
}
|
||||
successfulTokenTaskCache.Remove(token)
|
||||
auth_model.TokenCache().Remove(cacheKey)
|
||||
}
|
||||
|
||||
var tasks []*ActionTask
|
||||
@@ -223,10 +208,8 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro
|
||||
|
||||
for _, t := range tasks {
|
||||
tempHash := auth_model.HashToken(token, t.TokenSalt)
|
||||
if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
|
||||
if successfulTokenTaskCache != nil {
|
||||
successfulTokenTaskCache.Add(token, t.ID)
|
||||
}
|
||||
if util.CryptoConstTimeEqual(t.TokenHash, tempHash) {
|
||||
auth_model.TokenCache().Add(cacheKey, &auth_model.TokenCacheItem{TokenID: t.ID, TokenHash: t.TokenHash})
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
@@ -671,18 +654,3 @@ func logFileName(repoFullName string, taskID int64) string {
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func getTaskIDFromCache(token string) int64 {
|
||||
if successfulTokenTaskCache == nil {
|
||||
return 0
|
||||
}
|
||||
tInterface, ok := successfulTokenTaskCache.Get(token)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
t, ok := tInterface.(int64)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
@@ -79,6 +79,10 @@ type FindVariablesOpts struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (opts FindVariablesOpts) ToOrders() string {
|
||||
return "name"
|
||||
}
|
||||
|
||||
func (opts FindVariablesOpts) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestCreateOrUpdateIssueNotificationsIgnored(t *testing.T) {
|
||||
// user 4 watches repo 1 and would be notified about issue 1
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
assert.NoError(t, repo_model.WatchIgnoreRepo(t.Context(), user, repo))
|
||||
assert.NoError(t, repo_model.WatchRepoWithOptions(t.Context(), user, repo, repo_model.WatchOptions{Mode: repo_model.WatchModeDont}))
|
||||
|
||||
notified, err := activities_model.CreateOrUpdateIssueNotifications(t.Context(), 1, 0, 2, 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -367,7 +367,7 @@ func (stats *ActivityStats) FillReleases(ctx context.Context, repoID int64, from
|
||||
|
||||
// Published releases list
|
||||
sess := releasesForActivityStatement(ctx, repoID, fromTime)
|
||||
sess.OrderBy("`release`.created_unix DESC")
|
||||
sess.OrderBy("`release`.published_unix DESC")
|
||||
stats.PublishedReleases = make([]*repo_model.Release, 0)
|
||||
if err = sess.Find(&stats.PublishedReleases); err != nil {
|
||||
return err
|
||||
@@ -386,5 +386,5 @@ func (stats *ActivityStats) FillReleases(ctx context.Context, repoID int64, from
|
||||
func releasesForActivityStatement(ctx context.Context, repoID int64, fromTime time.Time) db.Session {
|
||||
return db.GetEngine(ctx).Where("`release`.repo_id = ?", repoID).
|
||||
And("`release`.is_draft = ?", false).
|
||||
And("`release`.created_unix >= ?", fromTime.Unix())
|
||||
And("`release`.published_unix >= ?", fromTime.Unix())
|
||||
}
|
||||
|
||||
@@ -215,67 +215,6 @@ func (err ErrKeyAccessDenied) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrDeployKeyNotExist represents a "DeployKeyNotExist" kind of error.
|
||||
type ErrDeployKeyNotExist struct {
|
||||
ID int64
|
||||
KeyID int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
// IsErrDeployKeyNotExist checks if an error is a ErrDeployKeyNotExist.
|
||||
func IsErrDeployKeyNotExist(err error) bool {
|
||||
_, ok := err.(ErrDeployKeyNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyNotExist) Error() string {
|
||||
return fmt.Sprintf("Deploy key does not exist [id: %d, key_id: %d, repo_id: %d]", err.ID, err.KeyID, err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrDeployKeyAlreadyExist represents a "DeployKeyAlreadyExist" kind of error.
|
||||
type ErrDeployKeyAlreadyExist struct {
|
||||
KeyID int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
// IsErrDeployKeyAlreadyExist checks if an error is a ErrDeployKeyAlreadyExist.
|
||||
func IsErrDeployKeyAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrDeployKeyAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("public key already exists [key_id: %d, repo_id: %d]", err.KeyID, err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrDeployKeyNameAlreadyUsed represents a "DeployKeyNameAlreadyUsed" kind of error.
|
||||
type ErrDeployKeyNameAlreadyUsed struct {
|
||||
RepoID int64
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrDeployKeyNameAlreadyUsed checks if an error is a ErrDeployKeyNameAlreadyUsed.
|
||||
func IsErrDeployKeyNameAlreadyUsed(err error) bool {
|
||||
_, ok := err.(ErrDeployKeyNameAlreadyUsed)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyNameAlreadyUsed) Error() string {
|
||||
return fmt.Sprintf("public key with name already exists [repo_id: %d, name: %s]", err.RepoID, err.Name)
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyNameAlreadyUsed) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrSSHInvalidTokenSignature represents a "ErrSSHInvalidTokenSignature" kind of error.
|
||||
type ErrSSHInvalidTokenSignature struct {
|
||||
Wrapped error
|
||||
|
||||
@@ -75,6 +75,10 @@ type FindGPGKeyOptions struct {
|
||||
IncludeSubKeys bool
|
||||
}
|
||||
|
||||
func (opts FindGPGKeyOptions) ToOrders() string {
|
||||
return "id"
|
||||
}
|
||||
|
||||
func (opts FindGPGKeyOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if !opts.IncludeSubKeys {
|
||||
|
||||
@@ -38,7 +38,7 @@ const (
|
||||
// PublicKey represents a user or deploy SSH public key.
|
||||
type PublicKey struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OwnerID int64 `xorm:"INDEX NOT NULL"`
|
||||
OwnerID int64 `xorm:"INDEX NOT NULL"` // deploy-key doesn't have owner
|
||||
Name string `xorm:"NOT NULL"`
|
||||
Fingerprint string `xorm:"INDEX NOT NULL"`
|
||||
Content string `xorm:"MEDIUMTEXT NOT NULL"`
|
||||
@@ -73,7 +73,7 @@ func (key *PublicKey) OmitEmail() string {
|
||||
return strings.Join(fields[:2], " ")
|
||||
}
|
||||
|
||||
func addKey(ctx context.Context, key *PublicKey) (err error) {
|
||||
func addPublicKey(ctx context.Context, key *PublicKey) (err error) {
|
||||
if len(key.Fingerprint) == 0 {
|
||||
key.Fingerprint, err = CalcFingerprint(key.Content)
|
||||
if err != nil {
|
||||
@@ -89,6 +89,36 @@ func addKey(ctx context.Context, key *PublicKey) (err error) {
|
||||
return appendAuthorizedKeysToFile(key)
|
||||
}
|
||||
|
||||
// FindOrAddDeployPublicKey returns the shared public key that deploy keys of the given content link to, adding it on first use.
|
||||
func FindOrAddDeployPublicKey(ctx context.Context, content string) (*PublicKey, error) {
|
||||
fingerprint, err := CalcFingerprint(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if exist {
|
||||
if pkey.Type != KeyTypeDeploy {
|
||||
return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
|
||||
}
|
||||
return pkey, nil
|
||||
}
|
||||
|
||||
pkey = &PublicKey{
|
||||
Mode: perm.AccessModeNone,
|
||||
Type: KeyTypeDeploy,
|
||||
Name: "(DeployKey)",
|
||||
Content: content,
|
||||
Fingerprint: fingerprint,
|
||||
}
|
||||
if err = addPublicKey(ctx, pkey); err != nil {
|
||||
return nil, fmt.Errorf("addPublicKey: %w", err)
|
||||
}
|
||||
return pkey, nil
|
||||
}
|
||||
|
||||
// AddPublicKey adds new public key to database and authorized_keys file.
|
||||
func AddPublicKey(ctx context.Context, ownerID int64, name, content string, authSourceID int64, verified bool) (*PublicKey, error) {
|
||||
log.Trace(content)
|
||||
@@ -123,7 +153,7 @@ func AddPublicKey(ctx context.Context, ownerID int64, name, content string, auth
|
||||
LoginSourceID: authSourceID,
|
||||
Verified: verified,
|
||||
}
|
||||
if err = addKey(ctx, key); err != nil {
|
||||
if err = addPublicKey(ctx, key); err != nil {
|
||||
return nil, fmt.Errorf("addKey: %w", err)
|
||||
}
|
||||
|
||||
@@ -184,6 +214,10 @@ type FindPublicKeyOptions struct {
|
||||
LoginSourceID int64
|
||||
}
|
||||
|
||||
func (opts FindPublicKeyOptions) ToOrders() string {
|
||||
return "id"
|
||||
}
|
||||
|
||||
func (opts FindPublicKeyOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.OwnerID > 0 {
|
||||
|
||||
@@ -1,206 +0,0 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package asymkey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/perm"
|
||||
"gitea.dev/modules/timeutil"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// ________ .__ ____ __.
|
||||
// \______ \ ____ ______ | | ____ ___.__.| |/ _|____ ___.__.
|
||||
// | | \_/ __ \\____ \| | / _ < | || <_/ __ < | |
|
||||
// | ` \ ___/| |_> > |_( <_> )___ || | \ ___/\___ |
|
||||
// /_______ /\___ > __/|____/\____// ____||____|__ \___ > ____|
|
||||
// \/ \/|__| \/ \/ \/\/
|
||||
//
|
||||
// This file contains functions specific to DeployKeys
|
||||
|
||||
// DeployKey represents deploy key information and its relation with repository.
|
||||
type DeployKey struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
KeyID int64 `xorm:"UNIQUE(s) INDEX"`
|
||||
RepoID int64 `xorm:"UNIQUE(s) INDEX"`
|
||||
Name string
|
||||
Fingerprint string
|
||||
Content string `xorm:"-"`
|
||||
|
||||
Mode perm.AccessMode `xorm:"NOT NULL DEFAULT 1"`
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
||||
HasRecentActivity bool `xorm:"-"`
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
|
||||
func (key *DeployKey) AfterLoad() {
|
||||
key.HasUsed = key.UpdatedUnix > key.CreatedUnix
|
||||
key.HasRecentActivity = key.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow()
|
||||
}
|
||||
|
||||
// GetContent gets associated public key content.
|
||||
func (key *DeployKey) GetContent(ctx context.Context) error {
|
||||
pkey, err := GetPublicKeyByID(ctx, key.KeyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key.Content = pkey.Content
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsReadOnly checks if the key can only be used for read operations, used by template
|
||||
func (key *DeployKey) IsReadOnly() bool {
|
||||
return key.Mode == perm.AccessModeRead
|
||||
}
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(DeployKey))
|
||||
}
|
||||
|
||||
func checkDeployKey(ctx context.Context, keyID, repoID int64, name string) error {
|
||||
// Note: We want error detail, not just true or false here.
|
||||
has, err := db.GetEngine(ctx).
|
||||
Where("key_id = ? AND repo_id = ?", keyID, repoID).
|
||||
Get(new(DeployKey))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrDeployKeyAlreadyExist{keyID, repoID}
|
||||
}
|
||||
|
||||
has, err = db.GetEngine(ctx).
|
||||
Where("repo_id = ? AND name = ?", repoID, name).
|
||||
Get(new(DeployKey))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrDeployKeyNameAlreadyUsed{repoID, name}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addDeployKey adds new key-repo relation.
|
||||
func addDeployKey(ctx context.Context, keyID, repoID int64, name, fingerprint string, mode perm.AccessMode) (*DeployKey, error) {
|
||||
if err := checkDeployKey(ctx, keyID, repoID, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := &DeployKey{
|
||||
KeyID: keyID,
|
||||
RepoID: repoID,
|
||||
Name: name,
|
||||
Fingerprint: fingerprint,
|
||||
Mode: mode,
|
||||
}
|
||||
return key, db.Insert(ctx, key)
|
||||
}
|
||||
|
||||
// AddDeployKey add new deploy key to database and authorized_keys file.
|
||||
func AddDeployKey(ctx context.Context, repoID int64, name, content string, readOnly bool) (*DeployKey, error) {
|
||||
fingerprint, err := CalcFingerprint(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessMode := perm.AccessModeRead
|
||||
if !readOnly {
|
||||
accessMode = perm.AccessModeWrite
|
||||
}
|
||||
|
||||
return db.WithTx2(ctx, func(ctx context.Context) (*DeployKey, error) {
|
||||
pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if exist {
|
||||
if pkey.Type != KeyTypeDeploy {
|
||||
return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
|
||||
}
|
||||
} else {
|
||||
// First time use this deploy key.
|
||||
pkey = &PublicKey{
|
||||
Fingerprint: fingerprint,
|
||||
Mode: accessMode,
|
||||
Type: KeyTypeDeploy,
|
||||
Content: content,
|
||||
Name: name,
|
||||
}
|
||||
if err = addKey(ctx, pkey); err != nil {
|
||||
return nil, fmt.Errorf("addKey: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
key, err := addDeployKey(ctx, pkey.ID, repoID, name, pkey.Fingerprint, accessMode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return key, nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetDeployKeyByID returns deploy key by given ID.
|
||||
func GetDeployKeyByID(ctx context.Context, id int64) (*DeployKey, error) {
|
||||
key, exist, err := db.GetByID[DeployKey](ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !exist {
|
||||
return nil, ErrDeployKeyNotExist{id, 0, 0}
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
|
||||
func GetDeployKeyByRepo(ctx context.Context, keyID, repoID int64) (*DeployKey, error) {
|
||||
key, exist, err := db.Get[DeployKey](ctx, builder.Eq{"key_id": keyID, "repo_id": repoID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !exist {
|
||||
return nil, ErrDeployKeyNotExist{0, keyID, repoID}
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// IsDeployKeyExistByKeyID return true if there is at least one deploykey with the key id
|
||||
func IsDeployKeyExistByKeyID(ctx context.Context, keyID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).
|
||||
Where("key_id = ?", keyID).
|
||||
Get(new(DeployKey))
|
||||
}
|
||||
|
||||
// UpdateDeployKeyCols updates deploy key information in the specified columns.
|
||||
func UpdateDeployKeyCols(ctx context.Context, key *DeployKey, cols ...string) error {
|
||||
_, err := db.GetEngine(ctx).ID(key.ID).Cols(cols...).Update(key)
|
||||
return err
|
||||
}
|
||||
|
||||
// ListDeployKeysOptions are options for ListDeployKeys
|
||||
type ListDeployKeysOptions struct {
|
||||
db.ListOptions
|
||||
RepoID int64
|
||||
KeyID int64
|
||||
Fingerprint string
|
||||
}
|
||||
|
||||
func (opt ListDeployKeysOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opt.RepoID != 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opt.RepoID})
|
||||
}
|
||||
if opt.KeyID != 0 {
|
||||
cond = cond.And(builder.Eq{"key_id": opt.KeyID})
|
||||
}
|
||||
if opt.Fingerprint != "" {
|
||||
cond = cond.And(builder.Eq{"fingerprint": opt.Fingerprint})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
+35
-47
@@ -6,22 +6,16 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru/v2"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
var successfulAccessTokenCache *lru.Cache[string, any]
|
||||
|
||||
// AccessToken represents a personal access token.
|
||||
type AccessToken struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
@@ -46,32 +40,43 @@ func (t *AccessToken) AfterLoad() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(AccessToken), func() error {
|
||||
if setting.SuccessfulTokensCacheSize > 0 {
|
||||
var err error
|
||||
successfulAccessTokenCache, err = lru.New[string, any](setting.SuccessfulTokensCacheSize)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to allocate AccessToken cache: %w", err)
|
||||
}
|
||||
} else {
|
||||
successfulAccessTokenCache = nil
|
||||
}
|
||||
return nil
|
||||
})
|
||||
db.RegisterModel(new(AccessToken))
|
||||
}
|
||||
|
||||
// NewAccessToken creates new access token.
|
||||
func NewAccessToken(ctx context.Context, t *AccessToken) error {
|
||||
// setNewTokenValue generates a fresh random token value and fills in its salt, hash, and last-eight.
|
||||
func (t *AccessToken) setNewTokenValue() {
|
||||
salt := util.CryptoRandomString(10)
|
||||
token := util.CryptoRandomBytes(20)
|
||||
t.TokenSalt = salt
|
||||
t.Token = hex.EncodeToString(token)
|
||||
t.TokenHash = HashToken(t.Token, t.TokenSalt)
|
||||
t.TokenLastEight = t.Token[len(t.Token)-8:]
|
||||
}
|
||||
|
||||
// NewAccessToken creates new access token.
|
||||
func NewAccessToken(ctx context.Context, t *AccessToken) error {
|
||||
t.setNewTokenValue()
|
||||
_, err := db.GetEngine(ctx).Insert(t)
|
||||
return err
|
||||
}
|
||||
|
||||
// RegenerateAccessToken regenerates the token value of an existing access token owned by userID, keeping its name and scope.
|
||||
func RegenerateAccessToken(ctx context.Context, id, userID int64) (*AccessToken, error) {
|
||||
t := &AccessToken{}
|
||||
has, err := db.GetEngine(ctx).Where("id=? AND uid=?", id, userID).Get(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, util.NewNotExistErrorf("access token not found")
|
||||
}
|
||||
|
||||
t.setNewTokenValue()
|
||||
if _, err := db.GetEngine(ctx).ID(t.ID).Cols("token_hash", "token_salt", "token_last_eight").NoAutoTime().Update(t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// DisplayPublicOnly whether to display this as a public-only token.
|
||||
func (t *AccessToken) DisplayPublicOnly() bool {
|
||||
publicOnly, err := t.Scope.PublicOnly()
|
||||
@@ -81,41 +86,26 @@ func (t *AccessToken) DisplayPublicOnly() bool {
|
||||
return publicOnly
|
||||
}
|
||||
|
||||
func getAccessTokenIDFromCache(token string) int64 {
|
||||
if successfulAccessTokenCache == nil {
|
||||
return 0
|
||||
}
|
||||
tInterface, ok := successfulAccessTokenCache.Get(token)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
t, ok := tInterface.(int64)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// GetAccessTokenBySHA returns access token by given token value
|
||||
func GetAccessTokenBySHA(ctx context.Context, token string) (*AccessToken, error) {
|
||||
if len(token) < 8 {
|
||||
return nil, util.NewNotExistErrorf("access token not found")
|
||||
}
|
||||
|
||||
cacheKey := "access:" + token
|
||||
lastEight := token[len(token)-8:]
|
||||
if id := getAccessTokenIDFromCache(token); id > 0 {
|
||||
accessToken := &AccessToken{
|
||||
TokenLastEight: lastEight,
|
||||
}
|
||||
// Re-get the token from the db in case it has been deleted in the intervening period
|
||||
has, err := db.GetEngine(ctx).ID(id).Get(accessToken)
|
||||
if cached, _ := TokenCache().Get(cacheKey); cached != nil {
|
||||
// Re-get the token from the db in case it has been deleted or regenerated in the intervening period
|
||||
accessToken := &AccessToken{}
|
||||
has, err := db.GetEngine(ctx).ID(cached.TokenID).Get(accessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if has {
|
||||
if has && util.CryptoConstTimeEqual(accessToken.TokenHash, cached.TokenHash) {
|
||||
return accessToken, nil
|
||||
}
|
||||
successfulAccessTokenCache.Remove(token)
|
||||
// either the token has been deleted or changed, invalidate the cache
|
||||
TokenCache().Remove(cacheKey)
|
||||
}
|
||||
|
||||
var tokens []AccessToken
|
||||
@@ -128,10 +118,8 @@ func GetAccessTokenBySHA(ctx context.Context, token string) (*AccessToken, error
|
||||
|
||||
for _, t := range tokens {
|
||||
tempHash := HashToken(token, t.TokenSalt)
|
||||
if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
|
||||
if successfulAccessTokenCache != nil {
|
||||
successfulAccessTokenCache.Add(token, t.ID)
|
||||
}
|
||||
if util.CryptoConstTimeEqual(t.TokenHash, tempHash) {
|
||||
TokenCache().Add(cacheKey, &TokenCacheItem{TokenID: t.ID, TokenHash: t.TokenHash})
|
||||
return &t, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +117,46 @@ func TestUpdateAccessToken(t *testing.T) {
|
||||
unittest.AssertExistsAndLoadBean(t, token)
|
||||
}
|
||||
|
||||
func TestRegenerateAccessToken(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
const oldToken = "d2c6c1ba3890b309189a8e618c72a162e4efbf36"
|
||||
|
||||
// prime the successful-lookup cache with the old token value, as a real request would
|
||||
before, err := auth_model.GetAccessTokenBySHA(t.Context(), oldToken)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Token A", before.Name)
|
||||
|
||||
regenerated, err := auth_model.RegenerateAccessToken(t.Context(), before.ID, before.UID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, before.ID, regenerated.ID)
|
||||
assert.Equal(t, before.Name, regenerated.Name)
|
||||
assert.Equal(t, before.Scope, regenerated.Scope)
|
||||
assert.NotEqual(t, before.TokenHash, regenerated.TokenHash)
|
||||
assert.NotEmpty(t, regenerated.Token)
|
||||
|
||||
// the old token value must stop authenticating, even though it was cached as successful above
|
||||
_, err = auth_model.GetAccessTokenBySHA(t.Context(), oldToken)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||
|
||||
// the new token value must authenticate
|
||||
found, err := auth_model.GetAccessTokenBySHA(t.Context(), regenerated.Token)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, before.ID, found.ID)
|
||||
assert.Equal(t, before.UpdatedUnix, found.UpdatedUnix)
|
||||
|
||||
// wrong owner
|
||||
_, err = auth_model.RegenerateAccessToken(t.Context(), before.ID, before.UID+1)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||
|
||||
// nonexistent token
|
||||
_, err = auth_model.RegenerateAccessToken(t.Context(), 100, 100)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||
}
|
||||
|
||||
func TestDeleteAccessTokenByID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
"uuid"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/modules/container"
|
||||
@@ -21,7 +22,6 @@ import (
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
uuid "github.com/google/uuid"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/oauth2"
|
||||
"xorm.io/builder"
|
||||
|
||||
@@ -259,6 +259,10 @@ type FindSourcesOptions struct {
|
||||
LoginType Type
|
||||
}
|
||||
|
||||
func (opts FindSourcesOptions) ToOrders() string {
|
||||
return "name"
|
||||
}
|
||||
|
||||
func (opts FindSourcesOptions) ToConds() builder.Cond {
|
||||
conds := builder.NewCond()
|
||||
if opts.IsActive.Has() {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru/v2"
|
||||
)
|
||||
|
||||
type TokenCacheItem struct {
|
||||
TokenID int64
|
||||
TokenHash string
|
||||
}
|
||||
|
||||
var TokenCache = sync.OnceValue(func() *lru.Cache[string, *TokenCacheItem] {
|
||||
cacheSize := max(setting.SuccessfulTokensCacheSize, 20)
|
||||
c, _ := lru.New[string, *TokenCacheItem](cacheSize) // it only fails when size <= 0
|
||||
return c
|
||||
})
|
||||
+58
-25
@@ -5,39 +5,72 @@ package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
// Iterate iterates all the Bean object
|
||||
func Iterate[Bean any](ctx context.Context, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error {
|
||||
var start int
|
||||
batchSize := setting.Database.IterateBufferSize
|
||||
sess := GetEngine(ctx)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
beans := make([]*Bean, 0, batchSize)
|
||||
if cond != nil {
|
||||
sess = sess.Where(cond)
|
||||
}
|
||||
if err := sess.Limit(batchSize, start).Find(&beans); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(beans) == 0 {
|
||||
return nil
|
||||
}
|
||||
start += len(beans)
|
||||
func iterateTableByColumn[Bean any](ctx context.Context, colName string, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error {
|
||||
table, err := xormEngine.TableInfo(new(Bean))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, bean := range beans {
|
||||
if err := f(ctx, bean); err != nil {
|
||||
return err
|
||||
}
|
||||
var col *schemas.Column
|
||||
if colName == "" {
|
||||
if len(table.PrimaryKeys) != 1 {
|
||||
return fmt.Errorf("table %s has %d primary keys, only the table with exactly one primary key can be iterated", table.Name, len(table.PrimaryKeys))
|
||||
}
|
||||
colName = table.PrimaryKeys[0]
|
||||
}
|
||||
|
||||
col = table.GetColumn(colName)
|
||||
batchSize := setting.Database.IterateBufferSize
|
||||
var lastColValue any
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
beans := make([]*Bean, 0, batchSize)
|
||||
query := GetEngine(ctx).Table(table.Name).Asc(colName)
|
||||
|
||||
batchCond := cond
|
||||
if lastColValue != nil {
|
||||
batchCond = builder.And(cond, builder.Gt{col.Name: lastColValue})
|
||||
}
|
||||
if batchCond != nil {
|
||||
query = query.Where(batchCond)
|
||||
}
|
||||
|
||||
if err := query.Limit(batchSize).Find(&beans); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(beans) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
reflectVal, err := col.ValueOf(beans[len(beans)-1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lastColValue = reflectVal.Interface()
|
||||
|
||||
for _, bean := range beans {
|
||||
if err := f(ctx, bean); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func IterateByColumn[Bean any](ctx context.Context, colName string, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error {
|
||||
return iterateTableByColumn(ctx, colName, cond, f)
|
||||
}
|
||||
|
||||
func Iterate[Bean any](ctx context.Context, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error {
|
||||
return iterateTableByColumn(ctx, "", cond, f)
|
||||
}
|
||||
|
||||
+12
-25
@@ -38,10 +38,7 @@ type ListOptions struct {
|
||||
|
||||
var ListOptionsAll = ListOptions{ListAll: true}
|
||||
|
||||
var (
|
||||
_ Paginator = &ListOptions{}
|
||||
_ FindOptions = ListOptions{}
|
||||
)
|
||||
var _ Paginator = &ListOptions{}
|
||||
|
||||
// GetSkipTake returns the skip and take values
|
||||
func (opts *ListOptions) GetSkipTake() (skip, take int) {
|
||||
@@ -117,6 +114,7 @@ type FindOptions interface {
|
||||
GetPageSize() int
|
||||
IsListAll() bool
|
||||
ToConds() builder.Cond
|
||||
ToOrders() string
|
||||
}
|
||||
|
||||
type JoinFunc func(sess Engine) error
|
||||
@@ -125,10 +123,6 @@ type FindOptionsJoin interface {
|
||||
ToJoins() []JoinFunc
|
||||
}
|
||||
|
||||
type FindOptionsOrder interface {
|
||||
ToOrders() string
|
||||
}
|
||||
|
||||
// Find represents a common find function which accept an options interface
|
||||
func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
|
||||
sess := GetEngine(ctx).Where(opts.ToConds())
|
||||
@@ -140,12 +134,7 @@ func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if orderOpt, ok := opts.(FindOptionsOrder); ok {
|
||||
if order := orderOpt.ToOrders(); order != "" {
|
||||
sess.OrderBy(order)
|
||||
}
|
||||
}
|
||||
|
||||
sess.OrderBy(opts.ToOrders())
|
||||
page, pageSize := opts.GetPage(), opts.GetPageSize()
|
||||
if !opts.IsListAll() && pageSize > 0 {
|
||||
if page == 0 {
|
||||
@@ -167,15 +156,17 @@ func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
|
||||
|
||||
// Count represents a common count function which accept an options interface
|
||||
func Count[T any](ctx context.Context, opts FindOptions) (int64, error) {
|
||||
sess := GetEngine(ctx).Where(opts.ToConds())
|
||||
if joinOpt, ok := opts.(FindOptionsJoin); ok {
|
||||
for _, joinFunc := range joinOpt.ToJoins() {
|
||||
if err := joinFunc(sess); err != nil {
|
||||
return 0, err
|
||||
sess := GetEngine(ctx)
|
||||
if opts != nil {
|
||||
sess.Where(opts.ToConds())
|
||||
if joinOpt, ok := opts.(FindOptionsJoin); ok {
|
||||
for _, joinFunc := range joinOpt.ToJoins() {
|
||||
if err := joinFunc(sess); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var object T
|
||||
return sess.Count(&object)
|
||||
}
|
||||
@@ -194,11 +185,7 @@ func FindAndCount[T any](ctx context.Context, opts FindOptions) ([]*T, int64, er
|
||||
}
|
||||
}
|
||||
}
|
||||
if orderOpt, ok := opts.(FindOptionsOrder); ok {
|
||||
if order := orderOpt.ToOrders(); order != "" {
|
||||
sess.OrderBy(order)
|
||||
}
|
||||
}
|
||||
sess.OrderBy(opts.ToOrders())
|
||||
|
||||
findPageSize := defaultFindSliceSize
|
||||
if pageSize > 0 {
|
||||
|
||||
@@ -18,6 +18,10 @@ type mockListOptions struct {
|
||||
db.ListOptions
|
||||
}
|
||||
|
||||
func (opts mockListOptions) ToOrders() string {
|
||||
return "id"
|
||||
}
|
||||
|
||||
func (opts mockListOptions) IsListAll() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package deploykey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gitea.dev/models/asymkey"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/perm"
|
||||
"gitea.dev/modules/timeutil"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
type KeyType int // SSH public key or HTTP auth token
|
||||
|
||||
const (
|
||||
KeyTypeSSH KeyType = iota + 1
|
||||
KeyTypeToken
|
||||
)
|
||||
|
||||
type DeployKey struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
KeyID int64 `xorm:"INDEX"`
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
|
||||
KeyType KeyType `xorm:"NOT NULL DEFAULT 1"`
|
||||
|
||||
Fingerprint string
|
||||
PublicKey *asymkey.PublicKey `xorm:"-"`
|
||||
|
||||
TokenHash string `xorm:"INDEX"` // sha256 of the token, which carries enough entropy to need no salt
|
||||
Token string `xorm:"-"` // only set when the token is created
|
||||
|
||||
Mode perm.AccessMode `xorm:"NOT NULL DEFAULT 1"`
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
||||
// these methods below are mainly used by templates
|
||||
|
||||
func (key *DeployKey) HasRecentActivity() bool {
|
||||
return key.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow()
|
||||
}
|
||||
|
||||
func (key *DeployKey) HasUsed() bool { return key.UpdatedUnix > key.CreatedUnix }
|
||||
|
||||
func (key *DeployKey) IsReadOnly() bool { return key.Mode == perm.AccessModeRead }
|
||||
|
||||
func (key *DeployKey) IsKeyTypeToken() bool { return key.KeyType == KeyTypeToken }
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(DeployKey))
|
||||
}
|
||||
|
||||
func checkDeployKeyName(ctx context.Context, repoID int64, name string) error {
|
||||
has, err := db.Exist[DeployKey](ctx, builder.Eq{"repo_id": repoID, "name": name})
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrDeployKeyNameAlreadyUsed{repoID, name}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateDeployKeyLastUsed marks the key as used now.
|
||||
func UpdateDeployKeyLastUsed(ctx context.Context, id int64) error {
|
||||
_, err := db.GetEngine(ctx).ID(id).Cols("updated_unix").Update(&DeployKey{UpdatedUnix: timeutil.TimeStampNow()})
|
||||
return err
|
||||
}
|
||||
|
||||
// ListDeployKeysOptions are options for ListDeployKeys
|
||||
type ListDeployKeysOptions struct {
|
||||
db.ListOptions
|
||||
RepoID int64
|
||||
KeyID int64
|
||||
Fingerprint string
|
||||
}
|
||||
|
||||
func (opt ListDeployKeysOptions) ToOrders() string {
|
||||
return "name"
|
||||
}
|
||||
|
||||
func (opt ListDeployKeysOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
cond = cond.And(builder.Eq{"repo_id": opt.RepoID}) // repo ID must be used
|
||||
if opt.KeyID != 0 {
|
||||
cond = cond.And(builder.Eq{"key_id": opt.KeyID})
|
||||
}
|
||||
if opt.Fingerprint != "" {
|
||||
cond = cond.And(builder.Eq{"fingerprint": opt.Fingerprint})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package deploykey
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.dev/models/asymkey"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/perm"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// AddDeployKeySSH add new deploy-key to database and authorized_keys file.
|
||||
func AddDeployKeySSH(ctx context.Context, repoID int64, name, content string, accessMode perm.AccessMode) (*DeployKey, error) {
|
||||
if accessMode != perm.AccessModeRead && accessMode != perm.AccessModeWrite {
|
||||
return nil, util.NewInvalidArgumentErrorf("invalid access mode")
|
||||
}
|
||||
return db.WithTx2(ctx, func(ctx context.Context) (*DeployKey, error) {
|
||||
pkey, err := asymkey.FindOrAddDeployPublicKey(ctx, content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if has, err := db.Exist[DeployKey](ctx, builder.Eq{"repo_id": repoID, "key_id": pkey.ID}); err != nil {
|
||||
return nil, err
|
||||
} else if has {
|
||||
return nil, ErrDeployKeyAlreadyExist{pkey.ID, repoID}
|
||||
}
|
||||
if err := checkDeployKeyName(ctx, repoID, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := &DeployKey{KeyID: pkey.ID, RepoID: repoID, KeyType: KeyTypeSSH, Name: name, Fingerprint: pkey.Fingerprint, Mode: accessMode}
|
||||
return key, db.Insert(ctx, key)
|
||||
})
|
||||
}
|
||||
|
||||
func (key *DeployKey) LoadPublicKey(ctx context.Context) (err error) {
|
||||
if key.PublicKey != nil {
|
||||
return nil
|
||||
}
|
||||
key.PublicKey, err = asymkey.GetPublicKeyByID(ctx, key.KeyID)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDeployKeyByID returns deploy-key by given ID.
|
||||
func GetDeployKeyByID(ctx context.Context, repoID, deployKeyID int64) (*DeployKey, error) {
|
||||
key, exist, err := db.Get[DeployKey](ctx, builder.Eq{"id": deployKeyID, "repo_id": repoID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !exist {
|
||||
return nil, ErrDeployKeyNotExist{deployKeyID, 0, repoID}
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// GetDeployKeyByRepoPublicKey returns deploy-key by given public key ID and repository ID.
|
||||
func GetDeployKeyByRepoPublicKey(ctx context.Context, repoID, publicKeyID int64) (*DeployKey, error) {
|
||||
// the type is part of the condition because every token row carries key id 0
|
||||
key, exist, err := db.Get[DeployKey](ctx, builder.Eq{"key_id": publicKeyID, "repo_id": repoID, "key_type": KeyTypeSSH})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !exist {
|
||||
return nil, ErrDeployKeyNotExist{0, publicKeyID, repoID}
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// IsDeployKeyExistByPublicKeyID return true if there is at least one deploy-key with the key id
|
||||
func IsDeployKeyExistByPublicKeyID(ctx context.Context, keyID int64) (bool, error) {
|
||||
return db.Exist[DeployKey](ctx, builder.Eq{"key_id": keyID, "key_type": KeyTypeSSH})
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package deploykey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/perm"
|
||||
"gitea.dev/modules/base"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
const (
|
||||
DeployTokenPrefix = "gdt_" // lets a secret scanner recognize a leaked token
|
||||
deployTokenLength = 43 // 256 bits of entropy over the 62 alphanumerical characters
|
||||
)
|
||||
|
||||
func (key *DeployKey) generateToken() {
|
||||
key.Token = DeployTokenPrefix + util.CryptoRandomString(deployTokenLength)
|
||||
key.TokenHash = base.EncodeSha256(key.Token)
|
||||
key.Fingerprint = key.Token[:len(DeployTokenPrefix)+2] + "********" + key.Token[len(key.Token)-2:]
|
||||
}
|
||||
|
||||
// AddDeployKeyToken adds a token that authenticates git HTTP requests for one repository.
|
||||
// The plaintext token is only readable on the returned key.
|
||||
func AddDeployKeyToken(ctx context.Context, repoID int64, name string, accessMode perm.AccessMode) (*DeployKey, error) {
|
||||
key := &DeployKey{
|
||||
RepoID: repoID,
|
||||
KeyType: KeyTypeToken,
|
||||
Name: name,
|
||||
Mode: accessMode,
|
||||
}
|
||||
key.generateToken()
|
||||
|
||||
return db.WithTx2(ctx, func(ctx context.Context) (*DeployKey, error) {
|
||||
if err := checkDeployKeyName(ctx, repoID, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, db.Insert(ctx, key)
|
||||
})
|
||||
}
|
||||
|
||||
// RegenerateDeployKeyToken replaces the token value of an existing deploy token, keeping its name and access mode.
|
||||
func RegenerateDeployKeyToken(ctx context.Context, repoID, keyID int64) (*DeployKey, error) {
|
||||
key, err := GetDeployKeyByID(ctx, repoID, keyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if key.KeyType != KeyTypeToken {
|
||||
return nil, ErrDeployKeyNotExist{keyID, 0, repoID}
|
||||
}
|
||||
|
||||
key.generateToken()
|
||||
_, err = db.GetEngine(ctx).ID(key.ID).Cols("token_hash", "fingerprint").NoAutoTime().Update(key)
|
||||
return key, err
|
||||
}
|
||||
|
||||
// VerifyDeployKeyToken returns the deploy-key which the given plaintext token authenticates.
|
||||
func VerifyDeployKeyToken(ctx context.Context, token string) (*DeployKey, error) {
|
||||
if !strings.HasPrefix(token, DeployTokenPrefix) { // spares a query for every password of a normal user
|
||||
return nil, ErrDeployKeyNotExist{}
|
||||
}
|
||||
|
||||
key, exist, err := db.Get[DeployKey](ctx, builder.Eq{"token_hash": base.EncodeSha256(token), "key_type": KeyTypeToken})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !exist {
|
||||
return nil, ErrDeployKeyNotExist{}
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package deploykey
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/perm"
|
||||
"gitea.dev/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDeployToken(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
key, err := AddDeployKeyToken(t.Context(), 1, "ci", perm.AccessModeWrite)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, key.IsReadOnly())
|
||||
assert.Len(t, key.Token, len(DeployTokenPrefix)+deployTokenLength)
|
||||
|
||||
got, err := VerifyDeployKeyToken(t.Context(), key.Token)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, key.ID, got.ID)
|
||||
assert.Empty(t, got.Token, "the token itself is never stored")
|
||||
|
||||
_, err = VerifyDeployKeyToken(t.Context(), "not-a-token")
|
||||
assert.True(t, IsErrDeployKeyNotExist(err))
|
||||
|
||||
_, err = AddDeployKeyToken(t.Context(), 1, "ci", perm.AccessModeWrite)
|
||||
assert.True(t, IsErrDeployKeyNameAlreadyUsed(err))
|
||||
|
||||
regenerated, err := RegenerateDeployKeyToken(t.Context(), 1, key.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, key.Name, regenerated.Name)
|
||||
assert.Equal(t, key.Mode, regenerated.Mode)
|
||||
assert.False(t, unittest.AssertExistsAndLoadBean(t, &DeployKey{ID: key.ID}).HasUsed(), "regenerating is not a use")
|
||||
|
||||
_, err = VerifyDeployKeyToken(t.Context(), key.Token)
|
||||
assert.True(t, IsErrDeployKeyNotExist(err), "the old token stops working")
|
||||
_, err = VerifyDeployKeyToken(t.Context(), regenerated.Token)
|
||||
require.NoError(t, err)
|
||||
|
||||
// an SSH deploy-key has no token to regenerate
|
||||
sshKey := &DeployKey{RepoID: 1, KeyType: KeyTypeSSH, Name: "ssh"}
|
||||
require.NoError(t, db.Insert(t.Context(), sshKey))
|
||||
_, err = RegenerateDeployKeyToken(t.Context(), 1, sshKey.ID)
|
||||
assert.True(t, IsErrDeployKeyNotExist(err))
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package deploykey
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
// ErrDeployKeyNotExist represents a "DeployKeyNotExist" kind of error.
|
||||
type ErrDeployKeyNotExist struct {
|
||||
ID int64
|
||||
KeyID int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
// IsErrDeployKeyNotExist checks if an error is a ErrDeployKeyNotExist.
|
||||
func IsErrDeployKeyNotExist(err error) bool {
|
||||
_, ok := err.(ErrDeployKeyNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyNotExist) Error() string {
|
||||
return fmt.Sprintf("Deploy key does not exist [id: %d, key_id: %d, repo_id: %d]", err.ID, err.KeyID, err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrDeployKeyAlreadyExist represents a "DeployKeyAlreadyExist" kind of error.
|
||||
type ErrDeployKeyAlreadyExist struct {
|
||||
KeyID int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
// IsErrDeployKeyAlreadyExist checks if an error is a ErrDeployKeyAlreadyExist.
|
||||
func IsErrDeployKeyAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrDeployKeyAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("public key already exists [key_id: %d, repo_id: %d]", err.KeyID, err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrDeployKeyNameAlreadyUsed represents a "DeployKeyNameAlreadyUsed" kind of error.
|
||||
type ErrDeployKeyNameAlreadyUsed struct {
|
||||
RepoID int64
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrDeployKeyNameAlreadyUsed checks if an error is a ErrDeployKeyNameAlreadyUsed.
|
||||
func IsErrDeployKeyNameAlreadyUsed(err error) bool {
|
||||
_, ok := err.(ErrDeployKeyNameAlreadyUsed)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyNameAlreadyUsed) Error() string {
|
||||
return fmt.Sprintf("public key with name already exists [repo_id: %d, name: %s]", err.RepoID, err.Name)
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyNameAlreadyUsed) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package deploykey
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dev/models/unittest"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{FixtureFiles: []string{}}) // the tests insert what they assert on
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
is_prerelease: false
|
||||
is_tag: false
|
||||
created_unix: 946684800
|
||||
published_unix: 946684800
|
||||
|
||||
- id: 2
|
||||
repo_id: 40
|
||||
@@ -25,6 +26,7 @@
|
||||
is_prerelease: false
|
||||
is_tag: false
|
||||
created_unix: 946684800
|
||||
published_unix: 946684800
|
||||
|
||||
- id: 3
|
||||
repo_id: 1
|
||||
@@ -39,6 +41,7 @@
|
||||
is_prerelease: false
|
||||
is_tag: true
|
||||
created_unix: 946684800
|
||||
published_unix: 946684800
|
||||
|
||||
- id: 4
|
||||
repo_id: 1
|
||||
@@ -66,6 +69,7 @@
|
||||
is_prerelease: true
|
||||
is_tag: false
|
||||
created_unix: 946684800
|
||||
published_unix: 946684800
|
||||
|
||||
- id: 6
|
||||
repo_id: 57
|
||||
@@ -80,6 +84,7 @@
|
||||
is_prerelease: false
|
||||
is_tag: false
|
||||
created_unix: 946684801
|
||||
published_unix: 946684801
|
||||
|
||||
- id: 7
|
||||
repo_id: 57
|
||||
@@ -94,6 +99,7 @@
|
||||
is_prerelease: false
|
||||
is_tag: false
|
||||
created_unix: 946684802
|
||||
published_unix: 946684802
|
||||
|
||||
- id: 8
|
||||
repo_id: 57
|
||||
@@ -108,6 +114,7 @@
|
||||
is_prerelease: false
|
||||
is_tag: false
|
||||
created_unix: 946684803
|
||||
published_unix: 946684803
|
||||
|
||||
- id: 9
|
||||
repo_id: 57
|
||||
@@ -122,6 +129,7 @@
|
||||
is_prerelease: false
|
||||
is_tag: false
|
||||
created_unix: 946684803
|
||||
published_unix: 946684803
|
||||
|
||||
- id: 10
|
||||
repo_id: 57
|
||||
@@ -136,6 +144,7 @@
|
||||
is_prerelease: false
|
||||
is_tag: false
|
||||
created_unix: 946684803
|
||||
published_unix: 946684803
|
||||
|
||||
- id: 11
|
||||
repo_id: 2
|
||||
@@ -150,5 +159,6 @@
|
||||
is_prerelease: false
|
||||
is_tag: false
|
||||
created_unix: 946684803
|
||||
published_unix: 946684803
|
||||
|
||||
# DO NOT add more test data in the fixtures, test case should prepare their own test data separately and clearly
|
||||
|
||||
@@ -748,4 +748,18 @@
|
||||
config: "{}"
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 113
|
||||
repo_id: 19
|
||||
type: 1
|
||||
config: "{}"
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 114
|
||||
repo_id: 20
|
||||
type: 1
|
||||
config: "{}"
|
||||
created_unix: 946684810
|
||||
|
||||
# DO NOT add more test data in the fixtures, test case should prepare their own test data separately and clearly
|
||||
|
||||
+37
-13
@@ -15,8 +15,12 @@ import (
|
||||
|
||||
asymkey_model "gitea.dev/models/asymkey"
|
||||
"gitea.dev/models/db"
|
||||
access_model "gitea.dev/models/perm/access"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/cache"
|
||||
"gitea.dev/modules/cachegroup"
|
||||
"gitea.dev/modules/commitstatus"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/log"
|
||||
@@ -213,8 +217,8 @@ func (status *CommitStatus) LocaleString(lang translation.Locale) string {
|
||||
return lang.TrString("repo.commitstatus." + status.State.String())
|
||||
}
|
||||
|
||||
// HideActionsURL set `TargetURL` to an empty string if the status comes from Gitea Actions
|
||||
func (status *CommitStatus) HideActionsURL(ctx context.Context) {
|
||||
// hideActionsURL set `TargetURL` to an empty string if the status comes from Gitea Actions
|
||||
func (status *CommitStatus) hideActionsURL(ctx context.Context) {
|
||||
if _, ok := status.cutTargetURLGiteaActionsPrefix(ctx); ok {
|
||||
status.TargetURL = ""
|
||||
}
|
||||
@@ -544,18 +548,38 @@ func HashCommitStatusContext(context string) string {
|
||||
return fmt.Sprintf("%x", sha1.Sum([]byte(context)))
|
||||
}
|
||||
|
||||
// CommitStatusesHideActionsURL hide Gitea Actions urls
|
||||
func CommitStatusesHideActionsURL(ctx context.Context, statuses []*CommitStatus) {
|
||||
idToRepos := make(map[int64]*repo_model.Repository)
|
||||
// CommitStatusesApplyDoerPermission hides the Gitea Actions url of every status whose repository
|
||||
// the doer cannot read the Actions unit of, so the "Details" link does not lead to a 404.
|
||||
func CommitStatusesApplyDoerPermission(ctx context.Context, doer *user_model.User, statuses []*CommitStatus) {
|
||||
for _, status := range statuses {
|
||||
if status == nil {
|
||||
continue
|
||||
if status != nil && !statusRepoCanReadActions(ctx, doer, status) {
|
||||
status.hideActionsURL(ctx)
|
||||
}
|
||||
|
||||
if status.Repo == nil {
|
||||
status.Repo = idToRepos[status.RepoID]
|
||||
}
|
||||
status.HideActionsURL(ctx)
|
||||
idToRepos[status.RepoID] = status.Repo
|
||||
}
|
||||
}
|
||||
|
||||
// SignCommitsApplyDoerPermission is CommitStatusesApplyDoerPermission for a list of commits.
|
||||
func SignCommitsApplyDoerPermission(ctx context.Context, doer *user_model.User, commits []*SignCommitWithStatuses) {
|
||||
var statuses []*CommitStatus
|
||||
for _, commit := range commits {
|
||||
statuses = append(statuses, commit.Status)
|
||||
statuses = append(statuses, commit.Statuses...)
|
||||
}
|
||||
CommitStatusesApplyDoerPermission(ctx, doer, statuses)
|
||||
}
|
||||
|
||||
func statusRepoCanReadActions(ctx context.Context, doer *user_model.User, status *CommitStatus) bool {
|
||||
perm, err := cache.GetWithContextCache(ctx, cachegroup.RepoUserPermission, access_model.RepoUserPermissionCacheKey(status.RepoID, doer),
|
||||
func(ctx context.Context, _ string) (access_model.Permission, error) { // only runs on a cache miss
|
||||
if err := status.loadRepository(ctx); err != nil {
|
||||
return access_model.Permission{}, err
|
||||
}
|
||||
return access_model.GetDoerRepoPermission(ctx, status.Repo, doer)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Error("GetDoerRepoPermission[%d]: %v", status.RepoID, err)
|
||||
return false
|
||||
}
|
||||
return perm.CanRead(unit.TypeActions)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
package git_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
actions_model "gitea.dev/models/actions"
|
||||
"gitea.dev/models/db"
|
||||
git_model "gitea.dev/models/git"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
@@ -233,17 +231,23 @@ func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitStatusesHideActionsURL(t *testing.T) {
|
||||
func TestCommitStatusesApplyDoerPermission(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// repo4 is public and has the actions unit, repo2 is private and owned by someone else
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 791, RepoID: repo.ID})
|
||||
assert.NoError(t, run.LoadAttributes(t.Context()))
|
||||
otherRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||
|
||||
visibleURL := repo.Link() + "/actions/runs/1/jobs/1"
|
||||
statuses := []*git_model.CommitStatus{
|
||||
{
|
||||
RepoID: repo.ID,
|
||||
TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), run.ID),
|
||||
TargetURL: visibleURL,
|
||||
},
|
||||
{
|
||||
RepoID: otherRepo.ID,
|
||||
TargetURL: otherRepo.Link() + "/actions/runs/1/jobs/1",
|
||||
},
|
||||
{
|
||||
RepoID: repo.ID,
|
||||
@@ -251,9 +255,10 @@ func TestCommitStatusesHideActionsURL(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
git_model.CommitStatusesHideActionsURL(t.Context(), statuses)
|
||||
assert.Empty(t, statuses[0].TargetURL)
|
||||
assert.Equal(t, "https://mycicd.org/1", statuses[1].TargetURL)
|
||||
git_model.CommitStatusesApplyDoerPermission(t.Context(), doer, statuses)
|
||||
assert.Equal(t, visibleURL, statuses[0].TargetURL)
|
||||
assert.Empty(t, statuses[1].TargetURL)
|
||||
assert.Equal(t, "https://mycicd.org/1", statuses[2].TargetURL)
|
||||
}
|
||||
|
||||
func TestGetCountLatestCommitStatus(t *testing.T) {
|
||||
|
||||
@@ -124,7 +124,7 @@ func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) (
|
||||
e.Limit(pageSize, start)
|
||||
}
|
||||
lfsLocks := make(LFSLockList, 0, pageSize)
|
||||
return lfsLocks, e.Find(&lfsLocks, &LFSLock{RepoID: repoID})
|
||||
return lfsLocks, e.OrderBy("id").Find(&lfsLocks, &LFSLock{RepoID: repoID})
|
||||
}
|
||||
|
||||
// GetTreePathLock returns LSF lock for the treePath
|
||||
|
||||
@@ -74,6 +74,10 @@ type AssignedIssuesOptions struct {
|
||||
RepoOwnerID int64
|
||||
}
|
||||
|
||||
func (opts *AssignedIssuesOptions) ToOrders() string {
|
||||
return "id"
|
||||
}
|
||||
|
||||
func (opts *AssignedIssuesOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.AssigneeID != 0 {
|
||||
|
||||
@@ -1075,6 +1075,10 @@ type FindCommentsOptions struct {
|
||||
IsPull optional.Option[bool]
|
||||
}
|
||||
|
||||
func (opts FindCommentsOptions) ToOrders() string {
|
||||
return "id"
|
||||
}
|
||||
|
||||
// ToConds implements FindOptions interface
|
||||
func (opts FindCommentsOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/organization"
|
||||
"gitea.dev/models/perm"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
user_model "gitea.dev/models/user"
|
||||
@@ -220,7 +221,7 @@ func applyRepoConditions(sess db.Session, opts *IssuesOptions) {
|
||||
if opts.RepoCond == nil {
|
||||
opts.RepoCond = builder.NewCond()
|
||||
}
|
||||
opts.RepoCond = opts.RepoCond.Or(builder.In("issue.repo_id", builder.Select("id").From("repository").Where(builder.Eq{"is_private": false})))
|
||||
opts.RepoCond = opts.RepoCond.Or(builder.In("issue.repo_id", builder.Select("id").From("repository").Where(repo_model.PublicRepoUnderPublicOwnerCond())))
|
||||
}
|
||||
if opts.RepoCond != nil {
|
||||
sess.And(opts.RepoCond)
|
||||
@@ -287,8 +288,8 @@ func applyConditions(sess db.Session, opts *IssuesOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
// teamUnitsRepoCond returns query condition for those repo id in the special org team with special units access
|
||||
func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Type) builder.Cond {
|
||||
// teamUnitsRepoReaderCond returns query condition for those repo id in the special org team with special units access
|
||||
func teamUnitsRepoReaderCond(id string, userID, orgID, teamID int64, units ...unit.Type) builder.Cond {
|
||||
return builder.In(id,
|
||||
builder.Select("repo_id").From("team_repo").Where(
|
||||
builder.Eq{
|
||||
@@ -316,12 +317,19 @@ func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Typ
|
||||
}),
|
||||
),
|
||||
)).And(
|
||||
builder.In(
|
||||
"team_id", builder.Select("team_id").From("team_unit").Where(
|
||||
builder.Eq{
|
||||
"`team_unit`.org_id": orgID,
|
||||
}.And(
|
||||
builder.In("`team_unit`.type", units),
|
||||
builder.Or(
|
||||
builder.In(
|
||||
"team_id", builder.Select("id").From("team").Where(
|
||||
builder.Eq{"id": teamID}.And(builder.Gt{"authorize": perm.AccessModeNone}),
|
||||
),
|
||||
),
|
||||
builder.In(
|
||||
"team_id", builder.Select("team_id").From("team_unit").Where(
|
||||
builder.Eq{
|
||||
"`team_unit`.org_id": orgID,
|
||||
}.And(
|
||||
builder.In("`team_unit`.type", units),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -338,7 +346,7 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, owner *user_mod
|
||||
}
|
||||
if owner != nil && owner.IsOrganization() {
|
||||
if team != nil {
|
||||
cond = cond.And(teamUnitsRepoCond(repoIDstr, userID, owner.ID, team.ID, unitType)) // special team member repos
|
||||
cond = cond.And(teamUnitsRepoReaderCond(repoIDstr, userID, owner.ID, team.ID, unitType)) // special team member repos
|
||||
} else {
|
||||
cond = cond.And(
|
||||
builder.Or(
|
||||
|
||||
@@ -626,16 +626,7 @@ func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *u
|
||||
unittype = unit.TypePullRequests
|
||||
}
|
||||
for _, team := range teams {
|
||||
if team.HasAdminAccess() {
|
||||
checked = append(checked, team.ID)
|
||||
resolved[issue.Repo.Owner.LowerName+"/"+team.LowerName] = true
|
||||
continue
|
||||
}
|
||||
has, err := db.Exist[organization.TeamUnit](ctx, builder.Eq{"org_id": issue.Repo.Owner.ID, "team_id": team.ID, "`type`": unittype})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get team units (%d): %w", team.ID, err)
|
||||
}
|
||||
if has {
|
||||
if team.UnitEnabled(ctx, unittype) {
|
||||
checked = append(checked, team.ID)
|
||||
resolved[issue.Repo.Owner.LowerName+"/"+team.LowerName] = true
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func CheckIssueWatch(ctx context.Context, user *user_model.User, issue *Issue) (
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if repo_model.IsWatchMode(w.Mode) && util.Iif(issue.IsPull, w.PullRequests, w.Issues) {
|
||||
if repo_model.IsWatchModeWatching(w.Mode) && util.Iif(issue.IsPull, w.IncludePullRequests, w.IncludeIssues) {
|
||||
return true, nil
|
||||
}
|
||||
return IsUserParticipantsOfIssue(ctx, user, issue), nil
|
||||
|
||||
+41
-3
@@ -5,9 +5,11 @@
|
||||
package issues
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -184,11 +186,47 @@ func (l *Label) ExclusiveScope() string {
|
||||
if !l.Exclusive {
|
||||
return ""
|
||||
}
|
||||
lastIndex := strings.LastIndex(l.Name, "/")
|
||||
if lastIndex == -1 || lastIndex == 0 || lastIndex == len(l.Name)-1 {
|
||||
scope, name, found := strings.CutLast(l.Name, "/")
|
||||
if !found || scope == "" || name == "" {
|
||||
return ""
|
||||
}
|
||||
return l.Name[:lastIndex]
|
||||
return scope
|
||||
}
|
||||
|
||||
// CompareLabelForDisplay compares labels for displaying them in dropdowns or lists.
|
||||
// Labels are grouped by their exclusive scope, and labels within the same scope
|
||||
// are sorted by their exclusive order, where unordered labels (order 0) come last.
|
||||
// Labels without a scope are listed first and everything else falls back to name order.
|
||||
func CompareLabelForDisplay(a, b *Label) int {
|
||||
scopeA, scopeB := a.ExclusiveScope(), b.ExclusiveScope()
|
||||
if scopeA != scopeB {
|
||||
if scopeA == "" {
|
||||
return -1
|
||||
}
|
||||
if scopeB == "" {
|
||||
return 1
|
||||
}
|
||||
return strings.Compare(scopeA, scopeB)
|
||||
}
|
||||
if scopeA != "" {
|
||||
orderA, orderB := a.ExclusiveOrder, b.ExclusiveOrder
|
||||
if orderA <= 0 {
|
||||
orderA = math.MaxInt
|
||||
}
|
||||
if orderB <= 0 {
|
||||
orderB = math.MaxInt
|
||||
}
|
||||
if orderA != orderB {
|
||||
return cmp.Compare(orderA, orderB)
|
||||
}
|
||||
}
|
||||
return strings.Compare(a.Name, b.Name)
|
||||
}
|
||||
|
||||
// SortLabelsForDisplay sorts labels in place for displaying them in dropdowns or lists,
|
||||
// grouping them by their exclusive scope and respecting the exclusive order within each scope.
|
||||
func SortLabelsForDisplay(labels []*Label) {
|
||||
slices.SortStableFunc(labels, CompareLabelForDisplay)
|
||||
}
|
||||
|
||||
// NewLabel creates a new label
|
||||
|
||||
@@ -54,6 +54,47 @@ func TestLabel_ExclusiveScope(t *testing.T) {
|
||||
assert.Equal(t, "scope/subscope", label.ExclusiveScope())
|
||||
}
|
||||
|
||||
func TestSortLabelsForDisplay(t *testing.T) {
|
||||
labels := []*issues_model.Label{
|
||||
{Name: "priority/low", Exclusive: true, ExclusiveOrder: 4},
|
||||
{Name: "priority/critical", Exclusive: true, ExclusiveOrder: 1},
|
||||
{Name: "priority/medium", Exclusive: true, ExclusiveOrder: 3},
|
||||
{Name: "priority/high", Exclusive: true, ExclusiveOrder: 2},
|
||||
{Name: "bug"},
|
||||
{Name: "enhancement"},
|
||||
{Name: "kind/question", Exclusive: true},
|
||||
}
|
||||
issues_model.SortLabelsForDisplay(labels)
|
||||
|
||||
names := make([]string, 0, len(labels))
|
||||
for _, l := range labels {
|
||||
names = append(names, l.Name)
|
||||
}
|
||||
assert.Equal(t, []string{
|
||||
"bug",
|
||||
"enhancement",
|
||||
"kind/question",
|
||||
"priority/critical",
|
||||
"priority/high",
|
||||
"priority/medium",
|
||||
"priority/low",
|
||||
}, names)
|
||||
|
||||
// labels without an exclusive order in the same scope are listed last, ordered by name
|
||||
labels = []*issues_model.Label{
|
||||
{Name: "scope/unordered-b", Exclusive: true},
|
||||
{Name: "scope/ordered", Exclusive: true, ExclusiveOrder: 1},
|
||||
{Name: "scope/unordered-a", Exclusive: true},
|
||||
}
|
||||
issues_model.SortLabelsForDisplay(labels)
|
||||
|
||||
names = names[:0]
|
||||
for _, l := range labels {
|
||||
names = append(names, l.Name)
|
||||
}
|
||||
assert.Equal(t, []string{"scope/ordered", "scope/unordered-a", "scope/unordered-b"}, names)
|
||||
}
|
||||
|
||||
func TestNewLabels(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
labels := []*issues_model.Label{
|
||||
|
||||
+13
-9
@@ -1065,19 +1065,23 @@ func (r *Review) GetCodeCommentsCount(ctx context.Context) int {
|
||||
return int(count)
|
||||
}
|
||||
|
||||
// HTMLURL formats a URL-string to the related review issue-comment
|
||||
// HashTag returns unique hash tag for review.
|
||||
func (r *Review) HashTag() string {
|
||||
return fmt.Sprintf("pullrequestreview-%d", r.ID)
|
||||
}
|
||||
|
||||
// HTMLURL formats a URL-string to the review on the pull request page
|
||||
func (r *Review) HTMLURL(ctx context.Context) string {
|
||||
opts := FindCommentsOptions{
|
||||
Type: CommentTypeReview,
|
||||
IssueID: r.IssueID,
|
||||
ReviewID: r.ID,
|
||||
if r.Type != ReviewTypeApprove && r.Type != ReviewTypeComment && r.Type != ReviewTypeReject {
|
||||
return "" // only submitted reviews get a timeline block carrying the anchor
|
||||
}
|
||||
comment := new(Comment)
|
||||
has, err := db.GetEngine(ctx).Where(opts.ToConds()).Get(comment)
|
||||
if err != nil || !has {
|
||||
if err := r.LoadIssue(ctx); err != nil {
|
||||
return ""
|
||||
}
|
||||
return comment.HTMLURL(ctx)
|
||||
if err := r.Issue.LoadRepo(ctx); err != nil {
|
||||
return ""
|
||||
}
|
||||
return r.Issue.HTMLURL(ctx) + "#" + r.HashTag()
|
||||
}
|
||||
|
||||
// RemapExternalUser ExternalUserRemappable interface
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -28,6 +29,15 @@ func TestGetReviewByID(t *testing.T) {
|
||||
assert.True(t, issues_model.IsErrReviewNotExist(err), "IsErrReviewNotExist")
|
||||
}
|
||||
|
||||
func TestReview_HTMLURL(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 1})
|
||||
assert.Equal(t, setting.AppURL+"user2/repo1/pulls/2#pullrequestreview-1", review.HTMLURL(t.Context()))
|
||||
|
||||
pendingReview := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 4})
|
||||
assert.Empty(t, pendingReview.HTMLURL(t.Context()))
|
||||
}
|
||||
|
||||
func TestReview_LoadAttributes(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 1})
|
||||
|
||||
+14
-25
@@ -91,11 +91,6 @@ func (org *Organization) IsOwnedBy(ctx context.Context, uid int64) (bool, error)
|
||||
return IsOrganizationOwner(ctx, org.ID, uid)
|
||||
}
|
||||
|
||||
// IsOrgAdmin returns true if given user is in the owner team or an admin team.
|
||||
func (org *Organization) IsOrgAdmin(ctx context.Context, uid int64) (bool, error) {
|
||||
return IsOrganizationAdmin(ctx, org.ID, uid)
|
||||
}
|
||||
|
||||
// IsOrgMember returns true if given user is member of organization.
|
||||
func (org *Organization) IsOrgMember(ctx context.Context, uid int64) (bool, error) {
|
||||
return IsOrganizationMember(ctx, org.ID, uid)
|
||||
@@ -140,10 +135,6 @@ func (org *Organization) GetMembers(ctx context.Context, doer *user_model.User)
|
||||
|
||||
// HasMemberWithUserID returns true if user with userID is part of the u organisation.
|
||||
func (org *Organization) HasMemberWithUserID(ctx context.Context, userID int64) bool {
|
||||
return org.hasMemberWithUserID(ctx, userID)
|
||||
}
|
||||
|
||||
func (org *Organization) hasMemberWithUserID(ctx context.Context, userID int64) bool {
|
||||
isMember, err := IsOrganizationMember(ctx, org.ID, userID)
|
||||
if err != nil {
|
||||
log.Error("IsOrganizationMember: %v", err)
|
||||
@@ -284,8 +275,8 @@ func (org *Organization) CustomAvatarRelativePath() string {
|
||||
return org.Avatar
|
||||
}
|
||||
|
||||
// UnitPermission returns unit permission
|
||||
func (org *Organization) UnitPermission(ctx context.Context, doer *user_model.User, unitType unit.Type) perm.AccessMode {
|
||||
func (org *Organization) AnyRepoUnitPermission(ctx context.Context, doer *user_model.User, unitType unit.Type) perm.AccessMode {
|
||||
// FIXME: ORG-TEAM-UNIT-MAX-PERMISSION: this function is not right, team can access repo1's code doesn't mean it can access repo2's code
|
||||
if doer != nil {
|
||||
teams, err := GetUserOrgTeams(ctx, org.ID, doer.ID)
|
||||
if err != nil {
|
||||
@@ -299,11 +290,11 @@ func (org *Organization) UnitPermission(ctx context.Context, doer *user_model.Us
|
||||
}
|
||||
|
||||
if len(teams) > 0 {
|
||||
return teams.UnitMaxAccess(unitType)
|
||||
return teams.AnyRepoUnitMaxAccess(ctx, unitType)
|
||||
}
|
||||
}
|
||||
|
||||
if org.Visibility.IsPublic() {
|
||||
if ownerVisibilitySatisfiesDoer(org.AsUser(), doer) {
|
||||
return perm.AccessModeRead
|
||||
}
|
||||
|
||||
@@ -445,8 +436,7 @@ func GetUsersWhoCanCreateOrgRepo(ctx context.Context, orgID int64) (map[int64]*u
|
||||
And("team_user.org_id = ?", orgID).Find(&users)
|
||||
}
|
||||
|
||||
// HasOrgOrUserVisible tells if the given user can see the given org or user
|
||||
func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
|
||||
func ownerVisibilitySatisfiesDoer(orgOrUser, user *user_model.User) bool {
|
||||
// If user is nil, it's an anonymous user/request.
|
||||
// The Ghost user is handled like an anonymous user.
|
||||
if user == nil || user.IsGhost() {
|
||||
@@ -461,18 +451,17 @@ func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User)
|
||||
return true
|
||||
}
|
||||
|
||||
if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(ctx, user.ID) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return orgOrUser.Visibility != structs.VisibleTypePrivate && !user.IsRestricted
|
||||
}
|
||||
|
||||
// HasOrgOrUserVisible tells if the given user can see the given org or user
|
||||
func HasOrgOrUserVisible(ctx context.Context, owner, doer *user_model.User) bool {
|
||||
return ownerVisibilitySatisfiesDoer(owner, doer) ||
|
||||
(doer != nil && OrgFromUser(owner).HasMemberWithUserID(ctx, doer.ID))
|
||||
}
|
||||
|
||||
// HasOrgsVisible tells if the given user can see at least one of the orgs provided
|
||||
func HasOrgsVisible(ctx context.Context, orgs []*Organization, user *user_model.User) bool {
|
||||
if len(orgs) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, org := range orgs {
|
||||
if HasOrgOrUserVisible(ctx, org.AsUser(), user) {
|
||||
return true
|
||||
@@ -596,8 +585,8 @@ func RemoveOrgRepo(ctx context.Context, orgID, repoID int64) error {
|
||||
|
||||
// GetUserTeams returns all teams that belong to user,
|
||||
// and that the user has joined.
|
||||
func (org *Organization) GetUserTeams(ctx context.Context, userID int64, cols ...string) ([]*Team, error) {
|
||||
teams := make([]*Team, 0, org.NumTeams)
|
||||
func (org *Organization) GetUserTeams(ctx context.Context, userID int64, cols ...string) (TeamList, error) {
|
||||
teams := make(TeamList, 0, org.NumTeams)
|
||||
return teams, db.GetEngine(ctx).
|
||||
Where("`team_user`.org_id = ?", org.ID).
|
||||
Join("INNER", "team_user", "`team_user`.team_id = team.id").
|
||||
|
||||
@@ -89,6 +89,9 @@ func DoerViewOtherVisibility(doer, other *user_model.User) structs.VisibleType {
|
||||
if doer.IsAdmin || doer.ID == other.ID {
|
||||
return structs.VisibleTypePrivate
|
||||
}
|
||||
if doer.IsRestricted {
|
||||
return structs.VisibleTypePublic
|
||||
}
|
||||
return structs.VisibleTypeLimited
|
||||
}
|
||||
|
||||
|
||||
@@ -77,8 +77,14 @@ func testLoadOrgListTeams(t *testing.T) {
|
||||
}
|
||||
|
||||
func testDoerViewOtherVisibility(t *testing.T) {
|
||||
viewer := &user_model.User{ID: 1}
|
||||
other := &user_model.User{ID: 2}
|
||||
restrictedViewer := &user_model.User{ID: 3, IsRestricted: true}
|
||||
|
||||
assert.Equal(t, structs.VisibleTypePublic, organization.DoerViewOtherVisibility(nil, nil))
|
||||
assert.Equal(t, structs.VisibleTypeLimited, organization.DoerViewOtherVisibility(&user_model.User{ID: 1}, &user_model.User{ID: 2}))
|
||||
assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(&user_model.User{ID: 1}, &user_model.User{ID: 1}))
|
||||
assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(&user_model.User{ID: 1, IsAdmin: true}, &user_model.User{ID: 2}))
|
||||
assert.Equal(t, structs.VisibleTypeLimited, organization.DoerViewOtherVisibility(viewer, other))
|
||||
assert.Equal(t, structs.VisibleTypePublic, organization.DoerViewOtherVisibility(restrictedViewer, other))
|
||||
assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(viewer, viewer))
|
||||
assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(restrictedViewer, restrictedViewer))
|
||||
assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(&user_model.User{ID: 4, IsAdmin: true, IsRestricted: true}, other))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/organization"
|
||||
"gitea.dev/models/perm"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/setting"
|
||||
@@ -626,3 +628,10 @@ func TestCreateOrganization4(t *testing.T) {
|
||||
assert.True(t, db.IsErrNameReserved(err))
|
||||
unittest.CheckConsistencyFor(t, &organization.Organization{}, &organization.Team{})
|
||||
}
|
||||
|
||||
func TestOrAnyRepoUnitPermission(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()
|
||||
org := organization.Organization{Visibility: structs.VisibleTypeLimited}
|
||||
assert.Equal(t, perm.AccessModeNone, org.AnyRepoUnitPermission(t.Context(), nil, unit.TypeWiki))
|
||||
assert.Equal(t, perm.AccessModeRead, org.AnyRepoUnitPermission(t.Context(), &user_model.User{}, unit.TypeWiki))
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user