mirror of
https://codeberg.org/forgejo-aneksajo/forgejo-aneksajo.git
synced 2025-07-09 08:00:03 +02:00
Compare commits
185 commits
forgejo
...
v10.0.3-gi
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cc83c8c289 | ||
![]() |
be84c51de7 | ||
![]() |
5f1f4f7a87 | ||
![]() |
e741b44667 | ||
![]() |
85812d8b01 | ||
![]() |
24e2a6f41d | ||
![]() |
4521f8f35c | ||
![]() |
ea1ba91e6e | ||
![]() |
0e5e06f75a | ||
![]() |
b1c7251690 | ||
![]() |
795047adac | ||
![]() |
18df280fc1 | ||
![]() |
26ecdc330b | ||
![]() |
9d950c48a0 | ||
![]() |
90fea4b901 | ||
![]() |
1e539805da | ||
![]() |
66e54e33fe | ||
![]() |
195388ba62 | ||
![]() |
57a8ac3a31 | ||
![]() |
a9528fcdbd | ||
![]() |
1c7e2bce53 | ||
![]() |
2d00e46560 | ||
![]() |
a3b7d9242e | ||
![]() |
34af470aa8 | ||
![]() |
21bf0a8fe0 | ||
![]() |
65028f6abf | ||
![]() |
d2c9ac7697 | ||
![]() |
e335b49274 | ||
![]() |
ae30c0fa0e | ||
![]() |
62390abab9 | ||
![]() |
096cd1a6c8 | ||
![]() |
75794e1ab9 | ||
![]() |
383164e808 | ||
![]() |
e05f96cfaf | ||
![]() |
1c84972565 | ||
![]() |
9d902d16c6 | ||
![]() |
fc675b2ede | ||
![]() |
48fdf7540d | ||
![]() |
d8d5ad4a19 | ||
![]() |
28a9ce0083 | ||
![]() |
5da96c5f43 | ||
![]() |
917375146a | ||
![]() |
c4cb418f57 | ||
![]() |
b2dee3ca0e | ||
![]() |
cafb54ba55 | ||
![]() |
0b04df9ef4 | ||
![]() |
dde3f51c72 | ||
![]() |
0f5182d0c6 | ||
![]() |
a35a3b6731 | ||
![]() |
76dfc75ed2 | ||
![]() |
5ec1f7f363 | ||
![]() |
fc5a303b70 | ||
![]() |
8a7dc4ea06 | ||
![]() |
4b3135a859 | ||
![]() |
c082731211 | ||
![]() |
c1e9fd738b | ||
![]() |
400bd08cfe | ||
![]() |
e7d103319e | ||
![]() |
0c0155daf7 | ||
![]() |
40f1e0b1ff | ||
![]() |
c2158b2a1f | ||
![]() |
6e0f449fb9 | ||
![]() |
a830b4de6b | ||
![]() |
6fcb8f646f | ||
![]() |
c7bd6f4a3d | ||
![]() |
2e76237e26 | ||
![]() |
a6a1b11670 | ||
![]() |
b2c3f99901 | ||
![]() |
52fb476fb1 | ||
![]() |
3eacbfead9 | ||
![]() |
932afb2036 | ||
![]() |
cc8a05f693 | ||
![]() |
0fe56e6059 | ||
![]() |
4802c33acb | ||
![]() |
c56bbddf62 | ||
![]() |
a7ae98ff93 | ||
![]() |
0aa872c4e3 | ||
![]() |
ee49a62bed | ||
![]() |
89c4c9c477 | ||
![]() |
bdb78d42b6 | ||
![]() |
6ef900899e | ||
![]() |
a9f0bb9f68 | ||
![]() |
3b4f1b3469 | ||
![]() |
77fc232e5b | ||
![]() |
5a7d70658d | ||
![]() |
5046a10aec | ||
![]() |
77db7655e0 | ||
![]() |
c324910c31 | ||
![]() |
57ad0b868d | ||
![]() |
34d2a8531c | ||
![]() |
d260013a51 | ||
![]() |
3168330425 | ||
![]() |
2491bbfa69 | ||
![]() |
ac01c7a384 | ||
![]() |
b615d41457 | ||
![]() |
184bdef340 | ||
![]() |
27276ff26e | ||
![]() |
1b00bf2d26 | ||
![]() |
69bc17ea35 | ||
![]() |
0db9a24a4b | ||
![]() |
4016f2890d | ||
![]() |
c198cb6e65 | ||
![]() |
114d8975b5 | ||
![]() |
7ee19b4c6c | ||
![]() |
faa263d54a | ||
![]() |
0ecf28f37f | ||
![]() |
d10034f4d8 | ||
![]() |
553fc3cc42 | ||
![]() |
6d7bf7369d | ||
![]() |
eb83b05430 | ||
![]() |
61e345cd36 | ||
![]() |
5c5e1c87ba | ||
![]() |
7546c4acf3 | ||
![]() |
25e81d05f0 | ||
![]() |
054537989f | ||
![]() |
348e0e1fac | ||
![]() |
627634a76e | ||
![]() |
28db11f2e7 | ||
![]() |
6d0bf55f05 | ||
![]() |
2d1e163913 | ||
![]() |
26b7c6b86a | ||
![]() |
05056b8aa2 | ||
![]() |
39843ee2b3 | ||
![]() |
3b7ed0cda2 | ||
![]() |
2b1e74a76f | ||
![]() |
c439e26c33 | ||
![]() |
317559fdd9 | ||
![]() |
b88cd0c111 | ||
![]() |
5326183693 | ||
![]() |
5b6e0ca99c | ||
![]() |
90730e83ba | ||
![]() |
775770ad81 | ||
![]() |
4eb7e0fe08 | ||
![]() |
597d806753 | ||
![]() |
2b6a4137d5 | ||
![]() |
35266133d8 | ||
![]() |
dab3121c65 | ||
![]() |
3ce1a6562d | ||
![]() |
3b5f162fe6 | ||
![]() |
f63e5a1cff | ||
![]() |
25640f201e | ||
![]() |
ef205915cc | ||
![]() |
81d351ce5f | ||
![]() |
cd08097bbb | ||
![]() |
13496203bc | ||
![]() |
8f47560bf7 | ||
![]() |
9e6c3f226c | ||
![]() |
c7c7f69f82 | ||
![]() |
86a09562fd | ||
![]() |
ce5c3e32c1 | ||
![]() |
fa9f6e0cdd | ||
![]() |
172a48be8a | ||
![]() |
4a25a3e154 | ||
![]() |
6266715486 | ||
![]() |
3496c819da | ||
![]() |
4f8d96a6de | ||
![]() |
8d353ad258 | ||
![]() |
865d4f538b | ||
![]() |
ecbbaabfc8 | ||
![]() |
38f058a5f0 | ||
![]() |
1a64ae1dc4 | ||
![]() |
a8cc73fe87 | ||
![]() |
7a84081755 | ||
![]() |
2ac9b16297 | ||
![]() |
2bcbfbc5d4 | ||
![]() |
4999de50c3 | ||
![]() |
eb00a80efc | ||
![]() |
c7c22aae8c | ||
![]() |
0eab84d02e | ||
![]() |
e71fd7d28d | ||
![]() |
055348430e | ||
![]() |
440be42baf | ||
![]() |
f1b98d16c7 | ||
![]() |
662b385596 | ||
![]() |
023aaef2b9 | ||
![]() |
cb745a771a | ||
![]() |
38d2933cc1 | ||
![]() |
5a0c79e6b4 | ||
![]() |
424f85304e | ||
![]() |
382db9e8de | ||
![]() |
0ff66fa3bb | ||
![]() |
d5c5724f44 | ||
![]() |
1da56f0eb8 | ||
![]() |
6a78a71172 | ||
![]() |
f6c442c2fe |
318 changed files with 18477 additions and 4174 deletions
|
@ -13,6 +13,8 @@ runs:
|
||||||
run: |
|
run: |
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
echo "deb http://deb.debian.org/debian/ ${RELEASE} main" > "/etc/apt/sources.list.d/${RELEASE}.list"
|
echo "deb http://deb.debian.org/debian/ ${RELEASE} main" > "/etc/apt/sources.list.d/${RELEASE}.list"
|
||||||
|
wget -O- http://neuro.debian.net/lists/bookworm.de-fzj.libre | tee /etc/apt/sources.list.d/neurodebian.sources.list
|
||||||
|
apt-key adv --recv-keys --keyserver hkps://keyserver.ubuntu.com 0xA5D32F012649A5A9
|
||||||
env:
|
env:
|
||||||
RELEASE: ${{inputs.release}}
|
RELEASE: ${{inputs.release}}
|
||||||
- name: install packages
|
- name: install packages
|
||||||
|
@ -24,6 +26,7 @@ runs:
|
||||||
- name: remove temporary package list to prevent using it in other steps
|
- name: remove temporary package list to prevent using it in other steps
|
||||||
run: |
|
run: |
|
||||||
rm "/etc/apt/sources.list.d/${RELEASE}.list"
|
rm "/etc/apt/sources.list.d/${RELEASE}.list"
|
||||||
|
rm "/etc/apt/sources.list.d/neurodebian.sources.list"
|
||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
env:
|
env:
|
||||||
RELEASE: ${{inputs.release}}
|
RELEASE: ${{inputs.release}}
|
||||||
|
|
|
@ -3,7 +3,7 @@ runs:
|
||||||
steps:
|
steps:
|
||||||
- run: |
|
- run: |
|
||||||
su forgejo -c 'make deps-backend'
|
su forgejo -c 'make deps-backend'
|
||||||
- uses: actions/cache@v4
|
- uses: https://data.forgejo.org/actions/cache@v4
|
||||||
id: cache-backend
|
id: cache-backend
|
||||||
with:
|
with:
|
||||||
path: ${{github.workspace}}/gitea
|
path: ${{github.workspace}}/gitea
|
||||||
|
|
|
@ -27,8 +27,10 @@ runs:
|
||||||
- name: "Get go environment information"
|
- name: "Get go environment information"
|
||||||
id: go-environment
|
id: go-environment
|
||||||
run: |
|
run: |
|
||||||
echo "modcache=$(su ${RUN_AS_USER} -c '/opt/hostedtoolcache/go/${GO_VERSION}/x64/bin/go env GOMODCACHE')" >> "$GITHUB_OUTPUT"
|
chmod 755 $HOME # ensure ${RUN_AS_USER} has permission when go is located in $HOME
|
||||||
echo "cache=$(su ${RUN_AS_USER} -c '/opt/hostedtoolcache/go/${GO_VERSION}/x64/bin/go env GOCACHE')" >> "$GITHUB_OUTPUT"
|
export GOROOT="$(go env GOROOT)"
|
||||||
|
echo "modcache=$(su ${RUN_AS_USER} -c '${GOROOT}/bin/go env GOMODCACHE')" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "cache=$(su ${RUN_AS_USER} -c '${GOROOT}/bin/go env GOCACHE')" >> "$GITHUB_OUTPUT"
|
||||||
env:
|
env:
|
||||||
RUN_AS_USER: ${{ inputs.username }}
|
RUN_AS_USER: ${{ inputs.username }}
|
||||||
GO_VERSION: ${{ steps.go-version.outputs.go-version }}
|
GO_VERSION: ${{ steps.go-version.outputs.go-version }}
|
||||||
|
@ -48,7 +50,7 @@ runs:
|
||||||
|
|
||||||
- name: "Restore Go dependencies from cache or mark for later caching"
|
- name: "Restore Go dependencies from cache or mark for later caching"
|
||||||
id: cache-deps
|
id: cache-deps
|
||||||
uses: actions/cache@v4
|
uses: https://data.forgejo.org/actions/cache@v4
|
||||||
with:
|
with:
|
||||||
key: setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-${{ steps.go-version.outputs.go_version }}-${{ hashFiles('go.sum', 'go.mod') }}
|
key: setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-${{ steps.go-version.outputs.go_version }}-${{ hashFiles('go.sum', 'go.mod') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
|
|
|
@ -19,7 +19,7 @@ runs:
|
||||||
set -ex
|
set -ex
|
||||||
toolchain=$(grep -oP '(?<=toolchain ).+' go.mod)
|
toolchain=$(grep -oP '(?<=toolchain ).+' go.mod)
|
||||||
version=$(go version | cut -d' ' -f3)
|
version=$(go version | cut -d' ' -f3)
|
||||||
if [ "$toolchain" != "$version" ]; then
|
if dpkg --compare-versions ${version#go} lt ${toolchain#go}; then
|
||||||
echo "go version mismatch: $toolchain <> $version"
|
echo "go version too low: $toolchain >= $version"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
41
.forgejo/workflows/build-oci-image.yml
Normal file
41
.forgejo/workflows/build-oci-image.yml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'forgejo'
|
||||||
|
tags:
|
||||||
|
- '*-git-annex*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-oci-image:
|
||||||
|
runs-on: docker
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
type: ["rootful", "rootless"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # fetch the full history so that the Forgejo version is determined properly
|
||||||
|
- name: Determine registry and username
|
||||||
|
id: determine-registry-and-username
|
||||||
|
run: |
|
||||||
|
echo "registry=${GITHUB_SERVER_URL#https://}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "username=${GITHUB_REPOSITORY%/*}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Install Docker
|
||||||
|
run: curl -fsSL https://get.docker.com | sh
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ steps.determine-registry-and-username.outputs.registry }}
|
||||||
|
username: ${{ steps.determine-registry-and-username.outputs.username }}
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ${{ (matrix.type == 'rootful' && 'Dockerfile') || (matrix.type == 'rootless' && 'Dockerfile.rootless') }}
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.determine-registry-and-username.outputs.registry }}/${{ github.repository }}:${{ github.ref_name }}${{ (matrix.type == 'rootful' && ' ') || (matrix.type == 'rootless' && '-rootless') }}
|
|
@ -25,7 +25,7 @@ jobs:
|
||||||
if: vars.ROLE == 'forgejo-coding'
|
if: vars.ROLE == 'forgejo-coding'
|
||||||
runs-on: lxc-bookworm
|
runs-on: lxc-bookworm
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: https://data.forgejo.org/actions/checkout@v4
|
||||||
|
|
||||||
- id: forgejo
|
- id: forgejo
|
||||||
uses: https://data.forgejo.org/actions/setup-forgejo@v2.0.4
|
uses: https://data.forgejo.org/actions/setup-forgejo@v2.0.4
|
||||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
||||||
# root is used for testing, allow it
|
# root is used for testing, allow it
|
||||||
if: vars.ROLE == 'forgejo-integration' || github.repository_owner == 'root'
|
if: vars.ROLE == 'forgejo-integration' || github.repository_owner == 'root'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: https://data.forgejo.org/actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ jobs:
|
||||||
|
|
||||||
- name: build container & release
|
- name: build container & release
|
||||||
if: ${{ secrets.TOKEN != '' }}
|
if: ${{ secrets.TOKEN != '' }}
|
||||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.2.1
|
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4
|
||||||
with:
|
with:
|
||||||
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
||||||
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
||||||
|
@ -183,7 +183,7 @@ jobs:
|
||||||
|
|
||||||
- name: build rootless container
|
- name: build rootless container
|
||||||
if: ${{ secrets.TOKEN != '' }}
|
if: ${{ secrets.TOKEN != '' }}
|
||||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.2.1
|
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4
|
||||||
with:
|
with:
|
||||||
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
||||||
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
||||||
|
|
|
@ -37,11 +37,11 @@ jobs:
|
||||||
container:
|
container:
|
||||||
image: data.forgejo.org/oci/node:20-bookworm
|
image: data.forgejo.org/oci/node:20-bookworm
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: https://data.forgejo.org/actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
show-progress: 'false'
|
show-progress: 'false'
|
||||||
- uses: https://code.forgejo.org/actions/cascading-pr@v2.2.0
|
- uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0
|
||||||
with:
|
with:
|
||||||
origin-url: ${{ env.GITHUB_SERVER_URL }}
|
origin-url: ${{ env.GITHUB_SERVER_URL }}
|
||||||
origin-repo: ${{ github.repository }}
|
origin-repo: ${{ github.repository }}
|
||||||
|
|
|
@ -39,10 +39,10 @@ jobs:
|
||||||
runs-on: lxc-bookworm
|
runs-on: lxc-bookworm
|
||||||
if: vars.DOER != '' && vars.FORGEJO != '' && vars.TO_OWNER != '' && vars.FROM_OWNER != '' && secrets.TOKEN != ''
|
if: vars.DOER != '' && vars.FORGEJO != '' && vars.TO_OWNER != '' && vars.FROM_OWNER != '' && secrets.TOKEN != ''
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: https://data.forgejo.org/actions/checkout@v4
|
||||||
|
|
||||||
- name: copy & sign
|
- name: copy & sign
|
||||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.2.1
|
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.4
|
||||||
with:
|
with:
|
||||||
from-forgejo: ${{ vars.FORGEJO }}
|
from-forgejo: ${{ vars.FORGEJO }}
|
||||||
to-forgejo: ${{ vars.FORGEJO }}
|
to-forgejo: ${{ vars.FORGEJO }}
|
||||||
|
|
|
@ -10,7 +10,6 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
backend-checks:
|
backend-checks:
|
||||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
container:
|
container:
|
||||||
image: 'data.forgejo.org/oci/node:20-bookworm'
|
image: 'data.forgejo.org/oci/node:20-bookworm'
|
||||||
|
@ -27,7 +26,6 @@ jobs:
|
||||||
- run: su forgejo -c 'make --always-make -j$(nproc) lint-backend tidy-check swagger-check fmt-check swagger-validate' # ensure the "go-licenses" make target runs
|
- run: su forgejo -c 'make --always-make -j$(nproc) lint-backend tidy-check swagger-check fmt-check swagger-validate' # ensure the "go-licenses" make target runs
|
||||||
- uses: ./.forgejo/workflows-composite/build-backend
|
- uses: ./.forgejo/workflows-composite/build-backend
|
||||||
frontend-checks:
|
frontend-checks:
|
||||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
container:
|
container:
|
||||||
image: 'data.forgejo.org/oci/node:20-bookworm'
|
image: 'data.forgejo.org/oci/node:20-bookworm'
|
||||||
|
@ -46,7 +44,7 @@ jobs:
|
||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
apt-get -q install -qq -y zstd
|
apt-get -q install -qq -y zstd
|
||||||
- name: "Cache frontend build for playwright testing"
|
- name: "Cache frontend build for playwright testing"
|
||||||
uses: actions/cache/save@v4
|
uses: https://data.forgejo.org/actions/cache/save@v4
|
||||||
with:
|
with:
|
||||||
path: ${{github.workspace}}/public/assets
|
path: ${{github.workspace}}/public/assets
|
||||||
key: frontend-build-${{ github.sha }}
|
key: frontend-build-${{ github.sha }}
|
||||||
|
@ -104,7 +102,7 @@ jobs:
|
||||||
fetch-depth: 20
|
fetch-depth: 20
|
||||||
- uses: ./.forgejo/workflows-composite/setup-env
|
- uses: ./.forgejo/workflows-composite/setup-env
|
||||||
- name: "Restore frontend build"
|
- name: "Restore frontend build"
|
||||||
uses: actions/cache/restore@v4
|
uses: https://data.forgejo.org/actions/cache/restore@v4
|
||||||
id: cache-frontend
|
id: cache-frontend
|
||||||
with:
|
with:
|
||||||
path: ${{github.workspace}}/public/assets
|
path: ${{github.workspace}}/public/assets
|
||||||
|
@ -176,7 +174,6 @@ jobs:
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
TEST_REDIS_SERVER: cacher:${{ matrix.cacher.port }}
|
TEST_REDIS_SERVER: cacher:${{ matrix.cacher.port }}
|
||||||
test-mysql:
|
test-mysql:
|
||||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
needs: [backend-checks, frontend-checks]
|
needs: [backend-checks, frontend-checks]
|
||||||
container:
|
container:
|
||||||
|
@ -199,15 +196,13 @@ jobs:
|
||||||
- name: install dependencies & git >= 2.42
|
- name: install dependencies & git >= 2.42
|
||||||
uses: ./.forgejo/workflows-composite/apt-install-from
|
uses: ./.forgejo/workflows-composite/apt-install-from
|
||||||
with:
|
with:
|
||||||
packages: git git-lfs
|
packages: git git-annex-standalone git-lfs
|
||||||
- uses: ./.forgejo/workflows-composite/build-backend
|
- uses: ./.forgejo/workflows-composite/build-backend
|
||||||
- run: |
|
- run: |
|
||||||
su forgejo -c 'make test-mysql-migration test-mysql'
|
su forgejo -c 'make test-mysql-migration test-mysql'
|
||||||
timeout-minutes: 120
|
|
||||||
env:
|
env:
|
||||||
USE_REPO_TEST_DIR: 1
|
USE_REPO_TEST_DIR: 1
|
||||||
test-pgsql:
|
test-pgsql:
|
||||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
needs: [backend-checks, frontend-checks]
|
needs: [backend-checks, frontend-checks]
|
||||||
container:
|
container:
|
||||||
|
@ -236,17 +231,15 @@ jobs:
|
||||||
- name: install dependencies & git >= 2.42
|
- name: install dependencies & git >= 2.42
|
||||||
uses: ./.forgejo/workflows-composite/apt-install-from
|
uses: ./.forgejo/workflows-composite/apt-install-from
|
||||||
with:
|
with:
|
||||||
packages: git git-lfs
|
packages: git git-annex-standalone git-lfs
|
||||||
- uses: ./.forgejo/workflows-composite/build-backend
|
- uses: ./.forgejo/workflows-composite/build-backend
|
||||||
- run: |
|
- run: |
|
||||||
su forgejo -c 'make test-pgsql-migration test-pgsql'
|
su forgejo -c 'make test-pgsql-migration test-pgsql'
|
||||||
timeout-minutes: 120
|
|
||||||
env:
|
env:
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
USE_REPO_TEST_DIR: 1
|
USE_REPO_TEST_DIR: 1
|
||||||
TEST_LDAP: 1
|
TEST_LDAP: 1
|
||||||
test-sqlite:
|
test-sqlite:
|
||||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
needs: [backend-checks, frontend-checks]
|
needs: [backend-checks, frontend-checks]
|
||||||
container:
|
container:
|
||||||
|
@ -258,25 +251,21 @@ jobs:
|
||||||
- name: install dependencies & git >= 2.42
|
- name: install dependencies & git >= 2.42
|
||||||
uses: ./.forgejo/workflows-composite/apt-install-from
|
uses: ./.forgejo/workflows-composite/apt-install-from
|
||||||
with:
|
with:
|
||||||
packages: git git-lfs
|
packages: git git-annex-standalone git-lfs
|
||||||
- uses: ./.forgejo/workflows-composite/build-backend
|
- uses: ./.forgejo/workflows-composite/build-backend
|
||||||
- run: |
|
- run: |
|
||||||
su forgejo -c 'make test-sqlite-migration test-sqlite'
|
su forgejo -c 'make test-sqlite-migration test-sqlite'
|
||||||
timeout-minutes: 120
|
|
||||||
env:
|
env:
|
||||||
TAGS: sqlite sqlite_unlock_notify
|
TAGS: sqlite sqlite_unlock_notify
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
TEST_TAGS: sqlite sqlite_unlock_notify
|
TEST_TAGS: sqlite sqlite_unlock_notify
|
||||||
USE_REPO_TEST_DIR: 1
|
USE_REPO_TEST_DIR: 1
|
||||||
security-check:
|
security-check:
|
||||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
needs:
|
needs:
|
||||||
- test-sqlite
|
- test-sqlite
|
||||||
- test-pgsql
|
- test-pgsql
|
||||||
- test-mysql
|
- test-mysql
|
||||||
- test-remote-cacher
|
|
||||||
- test-unit
|
|
||||||
container:
|
container:
|
||||||
image: 'data.forgejo.org/oci/node:20-bookworm'
|
image: 'data.forgejo.org/oci/node:20-bookworm'
|
||||||
options: --tmpfs /tmp:exec,noatime
|
options: --tmpfs /tmp:exec,noatime
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -72,6 +72,7 @@ cpu.out
|
||||||
/tests/e2e/reports
|
/tests/e2e/reports
|
||||||
/tests/e2e/test-artifacts
|
/tests/e2e/test-artifacts
|
||||||
/tests/e2e/test-snapshots
|
/tests/e2e/test-snapshots
|
||||||
|
/tests/e2e/.auth
|
||||||
/tests/*.ini
|
/tests/*.ini
|
||||||
/tests/**/*.git/**/*.sample
|
/tests/**/*.git/**/*.sample
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/xx AS xx
|
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.23-alpine3.20 as build-env
|
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.23-alpine3.20 as build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY=${GOPROXY:-direct}
|
ENV GOPROXY=${GOPROXY:-direct}
|
||||||
|
@ -51,7 +51,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
|
||||||
/go/src/code.gitea.io/gitea/environment-to-ini
|
/go/src/code.gitea.io/gitea/environment-to-ini
|
||||||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||||
|
|
||||||
FROM code.forgejo.org/oci/alpine:3.20
|
FROM data.forgejo.org/oci/alpine:3.20
|
||||||
ARG RELEASE_VERSION
|
ARG RELEASE_VERSION
|
||||||
LABEL maintainer="contact@forgejo.org" \
|
LABEL maintainer="contact@forgejo.org" \
|
||||||
org.opencontainers.image.authors="Forgejo" \
|
org.opencontainers.image.authors="Forgejo" \
|
||||||
|
@ -78,6 +78,7 @@ RUN apk --no-cache add \
|
||||||
sqlite \
|
sqlite \
|
||||||
su-exec \
|
su-exec \
|
||||||
gnupg \
|
gnupg \
|
||||||
|
git-annex \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
RUN addgroup \
|
RUN addgroup \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/xx AS xx
|
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.23-alpine3.20 as build-env
|
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.23-alpine3.20 as build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY=${GOPROXY:-direct}
|
ENV GOPROXY=${GOPROXY:-direct}
|
||||||
|
@ -49,7 +49,8 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
|
||||||
/go/src/code.gitea.io/gitea/environment-to-ini
|
/go/src/code.gitea.io/gitea/environment-to-ini
|
||||||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||||
|
|
||||||
FROM code.forgejo.org/oci/alpine:3.20
|
FROM data.forgejo.org/oci/alpine:3.20
|
||||||
|
ARG RELEASE_VERSION
|
||||||
LABEL maintainer="contact@forgejo.org" \
|
LABEL maintainer="contact@forgejo.org" \
|
||||||
org.opencontainers.image.authors="Forgejo" \
|
org.opencontainers.image.authors="Forgejo" \
|
||||||
org.opencontainers.image.url="https://forgejo.org" \
|
org.opencontainers.image.url="https://forgejo.org" \
|
||||||
|
@ -71,6 +72,8 @@ RUN apk --no-cache add \
|
||||||
git \
|
git \
|
||||||
curl \
|
curl \
|
||||||
gnupg \
|
gnupg \
|
||||||
|
openssh-client \
|
||||||
|
git-annex \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
RUN addgroup \
|
RUN addgroup \
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -8,7 +8,7 @@ self := $(location)
|
||||||
@tmpdir=`mktemp --tmpdir -d` ; \
|
@tmpdir=`mktemp --tmpdir -d` ; \
|
||||||
echo Using temporary directory $$tmpdir for test repositories ; \
|
echo Using temporary directory $$tmpdir for test repositories ; \
|
||||||
USE_REPO_TEST_DIR= $(MAKE) -f $(self) --no-print-directory REPO_TEST_DIR=$$tmpdir/ $@ ; \
|
USE_REPO_TEST_DIR= $(MAKE) -f $(self) --no-print-directory REPO_TEST_DIR=$$tmpdir/ $@ ; \
|
||||||
STATUS=$$? ; rm -r "$$tmpdir" ; exit $$STATUS
|
STATUS=$$? ; chmod -R +w "$$tmpdir" && rm -r "$$tmpdir" ; exit $$STATUS
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ else
|
||||||
FORGEJO_VERSION_API ?= $(GITEA_VERSION)+${GITEA_COMPATIBILITY}
|
FORGEJO_VERSION_API ?= $(GITEA_VERSION)+${GITEA_COMPATIBILITY}
|
||||||
else
|
else
|
||||||
# drop the "g" prefix prepended by git describe to the commit hash
|
# drop the "g" prefix prepended by git describe to the commit hash
|
||||||
FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//' | sed 's/\-g/-/')+${GITEA_COMPATIBILITY}
|
FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//' | sed 's/\-g/-/2')+${GITEA_COMPATIBILITY}
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
FORGEJO_VERSION_MAJOR=$(shell echo $(FORGEJO_VERSION) | sed -e 's/\..*//')
|
FORGEJO_VERSION_MAJOR=$(shell echo $(FORGEJO_VERSION) | sed -e 's/\..*//')
|
||||||
|
|
7
assets/go-licenses.json
generated
7
assets/go-licenses.json
generated
|
@ -84,6 +84,11 @@
|
||||||
"path": "gitea.com/lunny/levelqueue/LICENSE",
|
"path": "gitea.com/lunny/levelqueue/LICENSE",
|
||||||
"licenseText": "Copyright (c) 2019 Lunny Xiao\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"
|
"licenseText": "Copyright (c) 2019 Lunny Xiao\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/42wim/httpsig",
|
||||||
|
"path": "github.com/42wim/httpsig/LICENSE",
|
||||||
|
"licenseText": "BSD 3-Clause License\n\nCopyright (c) 2018, go-fed\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\n* Neither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\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/42wim/sshsig",
|
"name": "github.com/42wim/sshsig",
|
||||||
"path": "github.com/42wim/sshsig/LICENSE",
|
"path": "github.com/42wim/sshsig/LICENSE",
|
||||||
|
@ -292,7 +297,7 @@
|
||||||
{
|
{
|
||||||
"name": "github.com/cyphar/filepath-securejoin",
|
"name": "github.com/cyphar/filepath-securejoin",
|
||||||
"path": "github.com/cyphar/filepath-securejoin/LICENSE",
|
"path": "github.com/cyphar/filepath-securejoin/LICENSE",
|
||||||
"licenseText": "Copyright (C) 2014-2015 Docker Inc \u0026 Go Authors. All rights reserved.\nCopyright (C) 2017 SUSE LLC. 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"
|
"licenseText": "Copyright (C) 2014-2015 Docker Inc \u0026 Go Authors. All rights reserved.\nCopyright (C) 2017-2024 SUSE LLC. 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/davecgh/go-spew/spew",
|
"name": "github.com/davecgh/go-spew/spew",
|
||||||
|
|
|
@ -59,9 +59,9 @@ func initRemoveTags() {
|
||||||
oldnew := []string{}
|
oldnew := []string{}
|
||||||
for _, el := range []string{
|
for _, el := range []string{
|
||||||
"email@example.com", "correu@example.com", "epasts@domens.lv", "email@exemplo.com", "eposta@ornek.com", "email@példa.hu", "email@esempio.it",
|
"email@example.com", "correu@example.com", "epasts@domens.lv", "email@exemplo.com", "eposta@ornek.com", "email@példa.hu", "email@esempio.it",
|
||||||
"user", "utente", "lietotājs", "gebruiker", "usuário", "Benutzer", "Bruker",
|
"user", "utente", "lietotājs", "gebruiker", "usuário", "Benutzer", "Bruker", "bruger", "użytkownik",
|
||||||
"server", "servidor", "kiszolgáló", "serveris",
|
"server", "servidor", "kiszolgáló", "serveris",
|
||||||
"label", "etichetta", "etiķete", "rótulo", "Label", "utilizador",
|
"label", "etichetta", "etiķete", "rótulo", "Label", "utilizador", "etiket", "iezīme", "etykieta",
|
||||||
} {
|
} {
|
||||||
oldnew = append(oldnew, "<"+el+">", "REPLACED-TAG")
|
oldnew = append(oldnew, "<"+el+">", "REPLACED-TAG")
|
||||||
}
|
}
|
||||||
|
|
74
cmd/serv.go
74
cmd/serv.go
|
@ -38,6 +38,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
lfsAuthenticateVerb = "git-lfs-authenticate"
|
lfsAuthenticateVerb = "git-lfs-authenticate"
|
||||||
|
gitAnnexShellVerb = "git-annex-shell"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdServ represents the available serv sub-command.
|
// CmdServ represents the available serv sub-command.
|
||||||
|
@ -79,6 +80,7 @@ var (
|
||||||
"git-upload-archive": perm.AccessModeRead,
|
"git-upload-archive": perm.AccessModeRead,
|
||||||
"git-receive-pack": perm.AccessModeWrite,
|
"git-receive-pack": perm.AccessModeWrite,
|
||||||
lfsAuthenticateVerb: perm.AccessModeNone,
|
lfsAuthenticateVerb: perm.AccessModeNone,
|
||||||
|
gitAnnexShellVerb: perm.AccessModeNone, // annex permissions are enforced by GIT_ANNEX_SHELL_READONLY, rather than the Gitea API
|
||||||
}
|
}
|
||||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||||
)
|
)
|
||||||
|
@ -212,6 +214,28 @@ func runServ(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if verb == gitAnnexShellVerb {
|
||||||
|
if !setting.Annex.Enabled {
|
||||||
|
return fail(ctx, "Unknown git command", "git-annex request over SSH denied, git-annex support is disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(words) < 3 {
|
||||||
|
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// git-annex always puts the repo in words[2], unlike most other
|
||||||
|
// git subcommands; and it sometimes names repos like /~/, as if
|
||||||
|
// $HOME should get expanded while also being rooted. e.g.:
|
||||||
|
// git-annex-shell 'configlist' '/~/user/repo'
|
||||||
|
// git-annex-shell 'sendkey' '/user/repo 'key'
|
||||||
|
repoPath = words[2]
|
||||||
|
repoPath = strings.TrimPrefix(repoPath, "/")
|
||||||
|
repoPath = strings.TrimPrefix(repoPath, "~/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent directory traversal attacks
|
||||||
|
repoPath = filepath.Clean("/" + repoPath)[1:]
|
||||||
|
|
||||||
rr := strings.SplitN(repoPath, "/", 2)
|
rr := strings.SplitN(repoPath, "/", 2)
|
||||||
if len(rr) != 2 {
|
if len(rr) != 2 {
|
||||||
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
|
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
|
||||||
|
@ -225,6 +249,18 @@ func runServ(c *cli.Context) error {
|
||||||
// so that username and reponame are not affected.
|
// so that username and reponame are not affected.
|
||||||
repoPath = strings.ToLower(strings.TrimSpace(repoPath))
|
repoPath = strings.ToLower(strings.TrimSpace(repoPath))
|
||||||
|
|
||||||
|
// put the sanitized repoPath back into the argument list for later
|
||||||
|
if verb == gitAnnexShellVerb {
|
||||||
|
// git-annex-shell demands an absolute path
|
||||||
|
absRepoPath, err := filepath.Abs(filepath.Join(setting.RepoRootPath, repoPath))
|
||||||
|
if err != nil {
|
||||||
|
return fail(ctx, "Error locating repoPath", "%v", err)
|
||||||
|
}
|
||||||
|
words[2] = absRepoPath
|
||||||
|
} else {
|
||||||
|
words[1] = repoPath
|
||||||
|
}
|
||||||
|
|
||||||
if alphaDashDotPattern.MatchString(reponame) {
|
if alphaDashDotPattern.MatchString(reponame) {
|
||||||
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
|
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
|
||||||
}
|
}
|
||||||
|
@ -303,21 +339,45 @@ func runServ(c *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var gitcmd *exec.Cmd
|
gitBinVerb, err := exec.LookPath(verb)
|
||||||
gitBinPath := filepath.Dir(git.GitExecutable) // e.g. /usr/bin
|
if err != nil {
|
||||||
gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack
|
|
||||||
if _, err := os.Stat(gitBinVerb); err != nil {
|
|
||||||
// if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git
|
// if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git
|
||||||
// ps: Windows only has "git.exe" in the bin path, so Windows always uses this way
|
// ps: Windows only has "git.exe" in the bin path, so Windows always uses this way
|
||||||
|
// ps: git-annex-shell and other extensions may not necessarily be in gitBinPath,
|
||||||
|
// but '{gitBinPath}/git annex-shell' should be able to find them on $PATH.
|
||||||
verbFields := strings.SplitN(verb, "-", 2)
|
verbFields := strings.SplitN(verb, "-", 2)
|
||||||
if len(verbFields) == 2 {
|
if len(verbFields) == 2 {
|
||||||
// use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ...
|
// use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ...
|
||||||
gitcmd = exec.CommandContext(ctx, git.GitExecutable, verbFields[1], repoPath)
|
gitBinVerb = git.GitExecutable
|
||||||
|
words = append([]string{verbFields[1]}, words...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if gitcmd == nil {
|
|
||||||
// by default, use the verb (it has been checked above by allowedCommands)
|
// by default, use the verb (it has been checked above by allowedCommands)
|
||||||
gitcmd = exec.CommandContext(ctx, gitBinVerb, repoPath)
|
gitcmd := exec.CommandContext(ctx, gitBinVerb, words[1:]...)
|
||||||
|
|
||||||
|
if verb == gitAnnexShellVerb {
|
||||||
|
// This doesn't get its own isolated section like LFS does, because LFS
|
||||||
|
// is handled by internal Gitea routines, but git-annex has to be shelled out
|
||||||
|
// to like other git subcommands, so we need to build up gitcmd.
|
||||||
|
|
||||||
|
// TODO: does this work on Windows?
|
||||||
|
gitcmd.Env = append(gitcmd.Env,
|
||||||
|
// "If set, disallows running git-shell to handle unknown commands."
|
||||||
|
// - git-annex-shell(1)
|
||||||
|
"GIT_ANNEX_SHELL_LIMITED=True",
|
||||||
|
// "If set, git-annex-shell will refuse to run commands
|
||||||
|
// that do not operate on the specified directory."
|
||||||
|
// - git-annex-shell(1)
|
||||||
|
fmt.Sprintf("GIT_ANNEX_SHELL_DIRECTORY=%s", words[2]),
|
||||||
|
)
|
||||||
|
if results.UserMode < perm.AccessModeWrite {
|
||||||
|
// "If set, disallows any action that could modify the git-annex repository."
|
||||||
|
// - git-annex-shell(1)
|
||||||
|
// We set this when the backend API has told us that we don't have write permission to this repo.
|
||||||
|
log.Debug("Setting GIT_ANNEX_SHELL_READONLY=True")
|
||||||
|
gitcmd.Env = append(gitcmd.Env, "GIT_ANNEX_SHELL_READONLY=True")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
process.SetSysProcAttribute(gitcmd)
|
process.SetSysProcAttribute(gitcmd)
|
||||||
|
|
11
cmd/web.go
11
cmd/web.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -247,6 +248,12 @@ func runWeb(ctx *cli.Context) error {
|
||||||
createPIDFile(ctx.String("pid"))
|
createPIDFile(ctx.String("pid"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if setting.Annex.Enabled {
|
||||||
|
if _, err := exec.LookPath("git-annex"); err != nil {
|
||||||
|
log.Fatal("You have enabled git-annex support but git-annex is not installed. Please make sure that Forgejo's PATH contains the git-annex executable.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !setting.InstallLock {
|
if !setting.InstallLock {
|
||||||
if err := serveInstall(ctx); err != nil {
|
if err := serveInstall(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -311,6 +318,10 @@ func listen(m http.Handler, handleRedirector bool) error {
|
||||||
log.Info("LFS server enabled")
|
log.Info("LFS server enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if setting.Annex.Enabled {
|
||||||
|
log.Info("git-annex enabled")
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch setting.Protocol {
|
switch setting.Protocol {
|
||||||
case setting.HTTP:
|
case setting.HTTP:
|
||||||
|
|
|
@ -2678,6 +2678,17 @@ LEVEL = Info
|
||||||
;; Limit the number of concurrent upload/download operations within a batch
|
;; Limit the number of concurrent upload/download operations within a batch
|
||||||
;BATCH_OPERATION_CONCURRENCY = 8
|
;BATCH_OPERATION_CONCURRENCY = 8
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[annex]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;
|
||||||
|
;; Whether git-annex is enabled; defaults to false
|
||||||
|
;ENABLED = false
|
||||||
|
;; Whether to disable p2phttp support; default is the same as repository.DISABLE_HTTP_GIT
|
||||||
|
;DISABLE_P2PHTTP = false
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; settings for packages, will override storage setting
|
;; settings for packages, will override storage setting
|
||||||
|
|
40
go.mod
40
go.mod
|
@ -1,11 +1,11 @@
|
||||||
module code.gitea.io/gitea
|
module code.gitea.io/gitea
|
||||||
|
|
||||||
go 1.23
|
go 1.23.0
|
||||||
|
|
||||||
toolchain go1.23.4
|
toolchain go1.23.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.forgejo.org/f3/gof3/v3 v3.7.0
|
code.forgejo.org/f3/gof3/v3 v3.10.2
|
||||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251
|
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251
|
||||||
code.forgejo.org/forgejo/reply v1.0.2
|
code.forgejo.org/forgejo/reply v1.0.2
|
||||||
code.forgejo.org/go-chi/binding v1.0.0
|
code.forgejo.org/go-chi/binding v1.0.0
|
||||||
|
@ -19,10 +19,10 @@ require (
|
||||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
||||||
github.com/ProtonMail/go-crypto v1.0.0
|
github.com/ProtonMail/go-crypto v1.1.3
|
||||||
github.com/PuerkitoBio/goquery v1.10.0
|
github.com/PuerkitoBio/goquery v1.10.0
|
||||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
|
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
|
||||||
github.com/alecthomas/chroma/v2 v2.14.0
|
github.com/alecthomas/chroma/v2 v2.15.0
|
||||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||||
github.com/blevesearch/bleve/v2 v2.4.4
|
github.com/blevesearch/bleve/v2 v2.4.4
|
||||||
github.com/buildkite/terminal-to-html/v3 v3.16.4
|
github.com/buildkite/terminal-to-html/v3 v3.16.4
|
||||||
|
@ -44,7 +44,7 @@ require (
|
||||||
github.com/go-co-op/gocron v1.37.0
|
github.com/go-co-op/gocron v1.37.0
|
||||||
github.com/go-enry/go-enry/v2 v2.9.1
|
github.com/go-enry/go-enry/v2 v2.9.1
|
||||||
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e
|
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e
|
||||||
github.com/go-git/go-git/v5 v5.11.0
|
github.com/go-git/go-git/v5 v5.13.1
|
||||||
github.com/go-ldap/ldap/v3 v3.4.6
|
github.com/go-ldap/ldap/v3 v3.4.6
|
||||||
github.com/go-openapi/spec v0.20.14
|
github.com/go-openapi/spec v0.20.14
|
||||||
github.com/go-sql-driver/mysql v1.8.1
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
|
@ -53,7 +53,7 @@ require (
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||||
github.com/google/go-github/v64 v64.0.0
|
github.com/google/go-github/v64 v64.0.0
|
||||||
github.com/google/pprof v0.0.0-20241017200806-017d972448fc
|
github.com/google/pprof v0.0.0-20241017200806-017d972448fc
|
||||||
|
@ -85,11 +85,11 @@ require (
|
||||||
github.com/opencontainers/image-spec v1.1.0
|
github.com/opencontainers/image-spec v1.1.0
|
||||||
github.com/pquerna/otp v1.4.0
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/prometheus/client_golang v1.20.5
|
github.com/prometheus/client_golang v1.20.5
|
||||||
github.com/redis/go-redis/v9 v9.7.0
|
github.com/redis/go-redis/v9 v9.7.3
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
||||||
github.com/sassoftware/go-rpmutils v0.4.0
|
github.com/sassoftware/go-rpmutils v0.4.0
|
||||||
github.com/sergi/go-diff v1.3.1
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
||||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
|
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/syndtr/goleveldb v1.0.0
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
|
@ -101,13 +101,13 @@ require (
|
||||||
github.com/yuin/goldmark v1.7.8
|
github.com/yuin/goldmark v1.7.8
|
||||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||||
go.uber.org/mock v0.4.0
|
go.uber.org/mock v0.4.0
|
||||||
golang.org/x/crypto v0.31.0
|
golang.org/x/crypto v0.35.0
|
||||||
golang.org/x/image v0.23.0
|
golang.org/x/image v0.23.0
|
||||||
golang.org/x/net v0.33.0
|
golang.org/x/net v0.36.0
|
||||||
golang.org/x/oauth2 v0.23.0
|
golang.org/x/oauth2 v0.27.0
|
||||||
golang.org/x/sync v0.10.0
|
golang.org/x/sync v0.11.0
|
||||||
golang.org/x/sys v0.28.0
|
golang.org/x/sys v0.30.0
|
||||||
golang.org/x/text v0.21.0
|
golang.org/x/text v0.22.0
|
||||||
google.golang.org/grpc v1.69.2
|
google.golang.org/grpc v1.69.2
|
||||||
google.golang.org/protobuf v1.36.1
|
google.golang.org/protobuf v1.36.1
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
|
@ -131,6 +131,7 @@ require (
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||||
|
github.com/42wim/httpsig v1.2.2 // indirect
|
||||||
github.com/DataDog/zstd v1.5.5 // indirect
|
github.com/DataDog/zstd v1.5.5 // indirect
|
||||||
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect
|
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2 // indirect
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.2 // indirect
|
||||||
|
@ -168,11 +169,11 @@ require (
|
||||||
github.com/cloudflare/circl v1.3.8 // indirect
|
github.com/cloudflare/circl v1.3.8 // indirect
|
||||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
|
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
|
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/envoyproxy/go-control-plane v0.13.1 // indirect
|
github.com/envoyproxy/go-control-plane v0.13.1 // indirect
|
||||||
|
@ -184,7 +185,7 @@ require (
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
github.com/go-git/go-billy/v5 v5.6.1 // indirect
|
||||||
github.com/go-ini/ini v1.67.0 // indirect
|
github.com/go-ini/ini v1.67.0 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
@ -246,13 +247,14 @@ require (
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||||
|
gitlab.com/gitlab-org/api/client-go v0.116.0 // indirect
|
||||||
go.etcd.io/bbolt v1.3.9 // indirect
|
go.etcd.io/bbolt v1.3.9 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.opentelemetry.io/contrib/detectors/gcp v1.31.0 // indirect
|
go.opentelemetry.io/contrib/detectors/gcp v1.31.0 // indirect
|
||||||
|
|
94
go.sum
94
go.sum
|
@ -610,8 +610,8 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS
|
||||||
cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M=
|
cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M=
|
||||||
cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
|
cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
|
||||||
cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw=
|
cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw=
|
||||||
code.forgejo.org/f3/gof3/v3 v3.7.0 h1:ZfuCP8CGm8ZJbWmL+V0pUu3E0X4FCAA7GfRDy/y5/K4=
|
code.forgejo.org/f3/gof3/v3 v3.10.2 h1:EOlv9d8GR7l0BmvZF101O3LUuabb4g5Hw5fKYPiPZlI=
|
||||||
code.forgejo.org/f3/gof3/v3 v3.7.0/go.mod h1:oNhOeqD4DZYjVcNjQXIOdDX9b/1tqxi9ITLS8H9/Csw=
|
code.forgejo.org/f3/gof3/v3 v3.10.2/go.mod h1:qApIHumpBkFkeBEokviO28+HK2WM11IsmMOhmjvCjFQ=
|
||||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU=
|
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU=
|
||||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM=
|
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM=
|
||||||
code.forgejo.org/forgejo/act v1.22.0 h1:NbUf0+vQ48+ddwe4zVkINqnxKYl/to+NUvW7iisPA60=
|
code.forgejo.org/forgejo/act v1.22.0 h1:NbUf0+vQ48+ddwe4zVkINqnxKYl/to+NUvW7iisPA60=
|
||||||
|
@ -651,6 +651,8 @@ gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHq
|
||||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
||||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||||
|
github.com/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA=
|
||||||
|
github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY=
|
||||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 h1:r3qt8PCHnfjOv9PN3H+XXKmDA1dfFMIN1AislhlA/ps=
|
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 h1:r3qt8PCHnfjOv9PN3H+XXKmDA1dfFMIN1AislhlA/ps=
|
||||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121/go.mod h1:Ock8XgA7pvULhIaHGAk/cDnRfNrF9Jey81nPcc403iU=
|
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121/go.mod h1:Ock8XgA7pvULhIaHGAk/cDnRfNrF9Jey81nPcc403iU=
|
||||||
github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U=
|
github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U=
|
||||||
|
@ -674,8 +676,8 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
|
||||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||||
github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
|
github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
|
||||||
github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
|
github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
|
||||||
github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM=
|
github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM=
|
||||||
|
@ -686,11 +688,11 @@ github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm
|
||||||
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
|
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
|
||||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||||
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
|
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
|
||||||
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
|
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||||
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||||
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
|
github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc=
|
||||||
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
|
github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio=
|
||||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||||
|
@ -768,7 +770,6 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
github.com/buildkite/terminal-to-html/v3 v3.16.4 h1:QFYO8IGvRnp7tGgiQb8g9uFU8kY9wOzxsFFx17+yy6Q=
|
github.com/buildkite/terminal-to-html/v3 v3.16.4 h1:QFYO8IGvRnp7tGgiQb8g9uFU8kY9wOzxsFFx17+yy6Q=
|
||||||
github.com/buildkite/terminal-to-html/v3 v3.16.4/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0=
|
github.com/buildkite/terminal-to-html/v3 v3.16.4/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0=
|
||||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
|
||||||
github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0=
|
github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0=
|
||||||
github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=
|
github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=
|
||||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
||||||
|
@ -796,7 +797,6 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
|
||||||
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
|
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
|
||||||
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
@ -817,8 +817,8 @@ github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1Ig
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
@ -837,8 +837,8 @@ github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmW
|
||||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||||
|
@ -849,8 +849,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 h1:dKG8sc7n321deIVRcQtwlMNoBEra7j0qQ8RwxO8RN0w=
|
github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 h1:dKG8sc7n321deIVRcQtwlMNoBEra7j0qQ8RwxO8RN0w=
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.2/go.mod h1:7dvD3GCm7eBw53xZ/lsiq72LqobdMg3ITbMBxnmJmqY=
|
github.com/editorconfig/editorconfig-core-go/v2 v2.6.2/go.mod h1:7dvD3GCm7eBw53xZ/lsiq72LqobdMg3ITbMBxnmJmqY=
|
||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
|
||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
|
||||||
github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
|
github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
|
||||||
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
||||||
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
|
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
|
||||||
|
@ -929,12 +929,12 @@ github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2H
|
||||||
github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
|
github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
|
||||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||||
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
|
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
|
||||||
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
|
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
@ -981,8 +981,8 @@ github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JP
|
||||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
|
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||||
|
@ -1300,8 +1300,8 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||||
|
@ -1342,8 +1342,8 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G
|
||||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
||||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rhysd/actionlint v1.6.27 h1:xxwe8YmveBcC8lydW6GoHMGmB6H/MTqUU60F2p10wjw=
|
github.com/rhysd/actionlint v1.6.27 h1:xxwe8YmveBcC8lydW6GoHMGmB6H/MTqUU60F2p10wjw=
|
||||||
|
@ -1373,8 +1373,8 @@ github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jN
|
||||||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||||
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
|
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
|
||||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
|
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
|
||||||
|
@ -1382,8 +1382,8 @@ github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
|
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
|
||||||
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||||
|
@ -1449,6 +1449,8 @@ github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCR
|
||||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
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/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||||
|
gitlab.com/gitlab-org/api/client-go v0.116.0 h1:Dy534gtZPMrnm3fAcmQRMadrcoUyFO4FQ4rXlSAdHAw=
|
||||||
|
gitlab.com/gitlab-org/api/client-go v0.116.0/go.mod h1:B29OfnZklmaoiR7uHANh9jTyfWEgmXvZLVEnosw2Dx0=
|
||||||
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
||||||
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
@ -1501,12 +1503,10 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -1522,6 +1522,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||||
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
@ -1629,8 +1631,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -1660,8 +1662,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec
|
||||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -1678,8 +1680,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -1769,8 +1771,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
|
@ -1782,8 +1784,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -1802,8 +1804,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
|
|
@ -282,27 +282,22 @@ func UpdateRunner(ctx context.Context, r *ActionRunner, cols ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRunner deletes a runner by given ID.
|
// DeleteRunner deletes a runner by given ID.
|
||||||
func DeleteRunner(ctx context.Context, id int64) error {
|
func DeleteRunner(ctx context.Context, r *ActionRunner) error {
|
||||||
runner, err := GetRunnerByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace the UUID, which was either based on the secret's first 16 bytes or an UUIDv4,
|
// Replace the UUID, which was either based on the secret's first 16 bytes or an UUIDv4,
|
||||||
// with a sequence of 8 0xff bytes followed by the little-endian version of the record's
|
// with a sequence of 8 0xff bytes followed by the little-endian version of the record's
|
||||||
// identifier. This will prevent the deleted record's identifier from colliding with any
|
// identifier. This will prevent the deleted record's identifier from colliding with any
|
||||||
// new record.
|
// new record.
|
||||||
b := make([]byte, 8)
|
b := make([]byte, 8)
|
||||||
binary.LittleEndian.PutUint64(b, uint64(id))
|
binary.LittleEndian.PutUint64(b, uint64(r.ID))
|
||||||
runner.UUID = fmt.Sprintf("ffffffff-ffff-ffff-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x",
|
r.UUID = fmt.Sprintf("ffffffff-ffff-ffff-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x",
|
||||||
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7])
|
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7])
|
||||||
|
|
||||||
err = UpdateRunner(ctx, runner, "UUID")
|
err := UpdateRunner(ctx, r, "UUID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.DeleteByID[ActionRunner](ctx, id)
|
_, err = db.DeleteByID[ActionRunner](ctx, r.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ func TestDeleteRunner(t *testing.T) {
|
||||||
require.NoError(t, unittest.PrepareTestDatabase())
|
require.NoError(t, unittest.PrepareTestDatabase())
|
||||||
before := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID})
|
before := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID})
|
||||||
|
|
||||||
err := DeleteRunner(db.DefaultContext, recordID)
|
err := DeleteRunner(db.DefaultContext, &ActionRunner{ID: recordID})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var after ActionRunner
|
var after ActionRunner
|
||||||
|
|
|
@ -86,7 +86,7 @@ func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariab
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) {
|
func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) {
|
||||||
count, err := db.GetEngine(ctx).ID(variable.ID).Cols("name", "data").
|
count, err := db.GetEngine(ctx).ID(variable.ID).Where("owner_id = ? AND repo_id = ?", variable.OwnerID, variable.RepoID).Cols("name", "data").
|
||||||
Update(&ActionVariable{
|
Update(&ActionVariable{
|
||||||
Name: variable.Name,
|
Name: variable.Name,
|
||||||
Data: variable.Data,
|
Data: variable.Data,
|
||||||
|
@ -94,11 +94,9 @@ func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error)
|
||||||
return count != 0, err
|
return count != 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteVariable(ctx context.Context, id int64) error {
|
func DeleteVariable(ctx context.Context, variableID, ownerID, repoID int64) (bool, error) {
|
||||||
if _, err := db.DeleteByID[ActionVariable](ctx, id); err != nil {
|
count, err := db.GetEngine(ctx).Table("action_variable").Where("id = ? AND owner_id = ? AND repo_id = ?", variableID, ownerID, repoID).Delete()
|
||||||
return err
|
return count != 0, err
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
|
func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
|
||||||
|
|
|
@ -219,8 +219,13 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
|
||||||
return "", 0, fmt.Errorf("ParsePublicKey: %w", err)
|
return "", 0, fmt.Errorf("ParsePublicKey: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pkeyType := pkey.Type()
|
||||||
|
if certPkey, ok := pkey.(*ssh.Certificate); ok {
|
||||||
|
pkeyType = certPkey.Key.Type()
|
||||||
|
}
|
||||||
|
|
||||||
// The ssh library can parse the key, so next we find out what key exactly we have.
|
// The ssh library can parse the key, so next we find out what key exactly we have.
|
||||||
switch pkey.Type() {
|
switch pkeyType {
|
||||||
case ssh.KeyAlgoDSA:
|
case ssh.KeyAlgoDSA:
|
||||||
rawPub := struct {
|
rawPub := struct {
|
||||||
Name string
|
Name string
|
||||||
|
|
|
@ -35,6 +35,7 @@ func Test_SSHParsePublicKey(t *testing.T) {
|
||||||
{"ecdsa-384", false, "ecdsa", 384, "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBINmioV+XRX1Fm9Qk2ehHXJ2tfVxW30ypUWZw670Zyq5GQfBAH6xjygRsJ5wWsHXBsGYgFUXIHvMKVAG1tpw7s6ax9oA+dJOJ7tj+vhn8joFqT+sg3LYHgZkHrfqryRasQ== nocomment"},
|
{"ecdsa-384", false, "ecdsa", 384, "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBINmioV+XRX1Fm9Qk2ehHXJ2tfVxW30ypUWZw670Zyq5GQfBAH6xjygRsJ5wWsHXBsGYgFUXIHvMKVAG1tpw7s6ax9oA+dJOJ7tj+vhn8joFqT+sg3LYHgZkHrfqryRasQ== nocomment"},
|
||||||
{"ecdsa-sk", true, "ecdsa-sk", 256, "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment"},
|
{"ecdsa-sk", true, "ecdsa-sk", 256, "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment"},
|
||||||
{"ed25519-sk", true, "ed25519-sk", 256, "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIE7kM1R02+4ertDKGKEDcKG0s+2vyDDcIvceJ0Gqv5f1AAAABHNzaDo= nocomment"},
|
{"ed25519-sk", true, "ed25519-sk", 256, "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIE7kM1R02+4ertDKGKEDcKG0s+2vyDDcIvceJ0Gqv5f1AAAABHNzaDo= nocomment"},
|
||||||
|
{"ed25519-cert-v01", true, "ed25519", 256, "ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIAlIAPlEj0mYQzQo8Ks0Nm/Ct8ceNkyJSf4DLuF5l7+5AAAAIEuWAoaBo2tT29/oMNnoDfdAPRCIdM2RGapKUhY4nDfLRgPQwfnRoc0AAAABAAAAcHZhdWx0LW9pZGMtNmRhYjdiZDgtNDg5YS00MDFkLTg3ZmItNjdjNTlhMDZkZDkxLTNjNTk2M2YyMGRmMDM3MDkyMzc1YmNiYmNiNzkxY2EyZWIxM2I0NGZhMzc2NTcwMWI0MjMwODU0MWFmNjhkNTgAAAALAAAAB2Zvcmdlam8AAAAAZ6/RUQAAAABn115vAAAAAAAAAAAAAAAAAAACFwAAAAdzc2gtcnNhAAAAAwEAAQAAAgEAySnM/TvD117GyKgOgMatDB2t+fCHORFaWVmH5SaadAzNJ2DfDAauRSLfnim1xdgAOMTzsPEEHH47zyYMjE85o2AiJxrfUBMw3O/7AbNc6+HyLr/txH4+vD9tWQknKnpVWM+3Z9wiHDcOdKRoXCmFZKJH1vxs16GNWjwbrfNiimv7Oi0fadgvTDKX603gpLTuVDXqs9eQFLCONptei86JYBAJqaHvg51k8YUCKt9WFqKAj7BJUWmrDvhv5VFMOsnZieJjqxkoxnpsQNlXfPzxK0vIpJofbYfWwscv/g9WZypHwO1ZR2PqzKm99YrSdr8w5256l0f44vsF0NSP0N7bDQEfYYnRGj8zWTYCBFD+uYF7AxIeaRUpZoTQO8MvCHOLMIDinNgEeCUvNA2v9zHl4BGq+PQjzUKAgJiKj0MZeiCDAmQ22g83ggQlB6BOrBb1fNa/S1cmTbGHQ2oAN358aqkmHVCBhPOyA2Rf65D2M2vzDlUdOsNDUIWAHk7GbwSNGDgcYfTWqtR5fTzp2MJovMh1dDUDXjOvojbhzjJtSy9+rzUYIv18aXdOitzVBgPMWdeVCZFZv4OKF+5MiqxQvedUvfiSjsdxZWLxyT1CJ88G3MzxNMS/Djm86T8h/Oa55bdvFtqpsLfvpIqq0pnXq1V/vF2j1MWwRB5z5Xh/HtEAAAIUAAAADHJzYS1zaGEyLTI1NgAAAgB2I2gzqemQl8/ETxtakALlm/2BpUcbhADcFWuoH6BCPnWHuTSwf3OayM6KXv1PQfL3YFRoi9Afrp8kVFL6DePsmKH+0BUEMz71sZ7v1ty7pwfzibItGnpTbQXhzbEiNYAFoz77rl7oaXF7pV6JNZhj3DVAB5gVA2oN5KRNVxijz+6uyuFJEw1HIl1C7GworvGwZcN7BThTEh3i72/Vntejy9Z8uGVjSFjS0rjRo2oXK1LKN0rVt66p3TmCWHouLkVnOTk0qrhLGlL2HVyo24OYHbkAAObD9b6aMDYlmluk6NsaiTKsSTsvMrbIbjtFQlh7nNyoPhZ0VMwaT1l10pDQ5uxWWZjKGIkz4xM1ZfpBszjJNPo+ivYQnTSjj9LwkbLAT9a/5LawSj80TGcLEMO+0eyPdJsP0wYmOVRFAZeRiBgwb3HrzcF6Wqr8icj1EjYkKSy9YFHGTnFBGknpdh3HGwghRXrCUwAnSM76db9pv4/qowT8LthtJ3dY5Epe0OJ1Tqm+q8bkGH4gB+7uqLSqM5pIHSKLp7lfHQBt1J6xa7H2saiweaWjU+QGTgQ2Lg+uUC5DXJrmm60CeFJ4BoGhUenDlgijbQpjH/l6330PbwefgjWtUK/pqaEA4lCoPyvJ+eF2DbYfPiAIBAFQnhVJJae4AH+XoCt29nb2j30ztg== nocomment"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|
|
@ -52,10 +52,10 @@ type WebAuthnCredential struct {
|
||||||
AAGUID []byte
|
AAGUID []byte
|
||||||
SignCount uint32 `xorm:"BIGINT"`
|
SignCount uint32 `xorm:"BIGINT"`
|
||||||
CloneWarning bool
|
CloneWarning bool
|
||||||
BackupEligible bool `XORM:"NOT NULL DEFAULT false"`
|
BackupEligible bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
BackupState bool `XORM:"NOT NULL DEFAULT false"`
|
BackupState bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
// If legacy is set to true, backup_eligible and backup_state isn't set.
|
// If legacy is set to true, backup_eligible and backup_state isn't set.
|
||||||
Legacy bool `XORM:"NOT NULL DEFAULT true"`
|
Legacy bool `xorm:"NOT NULL DEFAULT true"`
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||||
}
|
}
|
||||||
|
|
23
models/fixtures/PrivateIssueProjects/project.yml
Normal file
23
models/fixtures/PrivateIssueProjects/project.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
title: Org project that contains private and public issues
|
||||||
|
owner_id: 3
|
||||||
|
repo_id: 0
|
||||||
|
is_closed: false
|
||||||
|
creator_id: 2
|
||||||
|
board_type: 1
|
||||||
|
type: 3
|
||||||
|
created_unix: 1738000000
|
||||||
|
updated_unix: 1738000000
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1002
|
||||||
|
title: User project that contains private and public issues
|
||||||
|
owner_id: 2
|
||||||
|
repo_id: 0
|
||||||
|
is_closed: false
|
||||||
|
creator_id: 2
|
||||||
|
board_type: 1
|
||||||
|
type: 1
|
||||||
|
created_unix: 1738000000
|
||||||
|
updated_unix: 1738000000
|
17
models/fixtures/PrivateIssueProjects/project_board.yml
Normal file
17
models/fixtures/PrivateIssueProjects/project_board.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
project_id: 1001
|
||||||
|
title: Triage
|
||||||
|
creator_id: 2
|
||||||
|
default: true
|
||||||
|
created_unix: 1738000000
|
||||||
|
updated_unix: 1738000000
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1002
|
||||||
|
project_id: 1002
|
||||||
|
title: Triage
|
||||||
|
creator_id: 2
|
||||||
|
default: true
|
||||||
|
created_unix: 1738000000
|
||||||
|
updated_unix: 1738000000
|
23
models/fixtures/PrivateIssueProjects/project_issue.yml
Normal file
23
models/fixtures/PrivateIssueProjects/project_issue.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
issue_id: 6
|
||||||
|
project_id: 1001
|
||||||
|
project_board_id: 1001
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1002
|
||||||
|
issue_id: 7
|
||||||
|
project_id: 1002
|
||||||
|
project_board_id: 1002
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1003
|
||||||
|
issue_id: 16
|
||||||
|
project_id: 1001
|
||||||
|
project_board_id: 1001
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1004
|
||||||
|
issue_id: 1
|
||||||
|
project_id: 1002
|
||||||
|
project_board_id: 1002
|
5
models/fixtures/TestPrivateRepoProjects/access.yml
Normal file
5
models/fixtures/TestPrivateRepoProjects/access.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
user_id: 29
|
||||||
|
repo_id: 3
|
||||||
|
mode: 1
|
11
models/fixtures/TestPrivateRepoProjects/project.yml
Normal file
11
models/fixtures/TestPrivateRepoProjects/project.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
title: Org project that contains private issues
|
||||||
|
owner_id: 3
|
||||||
|
repo_id: 0
|
||||||
|
is_closed: false
|
||||||
|
creator_id: 2
|
||||||
|
board_type: 1
|
||||||
|
type: 3
|
||||||
|
created_unix: 1738000000
|
||||||
|
updated_unix: 1738000000
|
|
@ -0,0 +1,8 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
project_id: 1001
|
||||||
|
title: Triage
|
||||||
|
creator_id: 2
|
||||||
|
default: true
|
||||||
|
created_unix: 1738000000
|
||||||
|
updated_unix: 1738000000
|
11
models/fixtures/TestPrivateRepoProjects/project_issue.yml
Normal file
11
models/fixtures/TestPrivateRepoProjects/project_issue.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
issue_id: 6
|
||||||
|
project_id: 1001
|
||||||
|
project_board_id: 1001
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1002
|
||||||
|
issue_id: 15
|
||||||
|
project_id: 1001
|
||||||
|
project_board_id: 1001
|
|
@ -96,3 +96,14 @@
|
||||||
num_issues: 0
|
num_issues: 0
|
||||||
num_closed_issues: 0
|
num_closed_issues: 0
|
||||||
archived_unix: 0
|
archived_unix: 0
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 10
|
||||||
|
repo_id: 3
|
||||||
|
org_id: 0
|
||||||
|
name: repo3label1
|
||||||
|
color: '#112233'
|
||||||
|
exclusive: false
|
||||||
|
num_issues: 0
|
||||||
|
num_closed_issues: 0
|
||||||
|
archived_unix: 0
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
base_branch: branch2
|
base_branch: branch2
|
||||||
merge_base: 985f0301dba5e7b34be866819cd15ad3d8f508ee
|
merge_base: 985f0301dba5e7b34be866819cd15ad3d8f508ee
|
||||||
has_merged: false
|
has_merged: false
|
||||||
|
allow_maintainer_edit: true
|
||||||
|
|
||||||
-
|
-
|
||||||
id: 6
|
id: 6
|
||||||
|
|
|
@ -1,42 +1,49 @@
|
||||||
-
|
-
|
||||||
id: 1
|
id: 1
|
||||||
team_id: 1
|
team_id: 1
|
||||||
|
org_id: 3
|
||||||
type: 1
|
type: 1
|
||||||
access_mode: 4
|
access_mode: 4
|
||||||
|
|
||||||
-
|
-
|
||||||
id: 2
|
id: 2
|
||||||
team_id: 1
|
team_id: 1
|
||||||
|
org_id: 3
|
||||||
type: 2
|
type: 2
|
||||||
access_mode: 4
|
access_mode: 4
|
||||||
|
|
||||||
-
|
-
|
||||||
id: 3
|
id: 3
|
||||||
team_id: 1
|
team_id: 1
|
||||||
|
org_id: 3
|
||||||
type: 3
|
type: 3
|
||||||
access_mode: 4
|
access_mode: 4
|
||||||
|
|
||||||
-
|
-
|
||||||
id: 4
|
id: 4
|
||||||
team_id: 1
|
team_id: 1
|
||||||
|
org_id: 3
|
||||||
type: 4
|
type: 4
|
||||||
access_mode: 4
|
access_mode: 4
|
||||||
|
|
||||||
-
|
-
|
||||||
id: 5
|
id: 5
|
||||||
team_id: 1
|
team_id: 1
|
||||||
|
org_id: 3
|
||||||
type: 5
|
type: 5
|
||||||
access_mode: 4
|
access_mode: 4
|
||||||
|
|
||||||
-
|
-
|
||||||
id: 6
|
id: 6
|
||||||
team_id: 1
|
team_id: 1
|
||||||
|
org_id: 3
|
||||||
type: 6
|
type: 6
|
||||||
access_mode: 4
|
access_mode: 4
|
||||||
|
|
||||||
-
|
-
|
||||||
id: 7
|
id: 7
|
||||||
team_id: 1
|
team_id: 1
|
||||||
|
org_id: 3
|
||||||
type: 7
|
type: 7
|
||||||
access_mode: 4
|
access_mode: 4
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/secret"
|
"code.gitea.io/gitea/modules/secret"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
@ -57,19 +59,38 @@ func MigrateTwoFactorToKeying(x *xorm.Engine) error {
|
||||||
|
|
||||||
oldEncryptionKey := md5.Sum([]byte(setting.SecretKey))
|
oldEncryptionKey := md5.Sum([]byte(setting.SecretKey))
|
||||||
|
|
||||||
return db.Iterate(context.Background(), nil, func(ctx context.Context, bean *auth.TwoFactor) error {
|
messages := make([]string, 0, 100)
|
||||||
|
ids := make([]int64, 0, 100)
|
||||||
|
|
||||||
|
err = db.Iterate(context.Background(), nil, func(ctx context.Context, bean *auth.TwoFactor) error {
|
||||||
decodedStoredSecret, err := base64.StdEncoding.DecodeString(string(bean.Secret))
|
decodedStoredSecret, err := base64.StdEncoding.DecodeString(string(bean.Secret))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
messages = append(messages, fmt.Sprintf("two_factor.id=%d, two_factor.uid=%d: base64.StdEncoding.DecodeString: %v", bean.ID, bean.UID, err))
|
||||||
|
ids = append(ids, bean.ID)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
secretBytes, err := secret.AesDecrypt(oldEncryptionKey[:], decodedStoredSecret)
|
secretBytes, err := secret.AesDecrypt(oldEncryptionKey[:], decodedStoredSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
messages = append(messages, fmt.Sprintf("two_factor.id=%d, two_factor.uid=%d: secret.AesDecrypt: %v", bean.ID, bean.UID, err))
|
||||||
|
ids = append(ids, bean.ID)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
bean.SetSecret(string(secretBytes))
|
bean.SetSecret(string(secretBytes))
|
||||||
_, err = db.GetEngine(ctx).Cols("secret").ID(bean.ID).Update(bean)
|
_, err = db.GetEngine(ctx).Cols("secret").ID(bean.ID).Update(bean)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
if err == nil {
|
||||||
|
if len(ids) > 0 {
|
||||||
|
log.Error("Forgejo migration[25]: The following TOTP secrets were found to be corrupted and removed from the database. TOTP is no longer required to login with the associated users. They should be informed because they will need to visit their security settings and configure TOTP again. No other action is required. See https://codeberg.org/forgejo/forgejo/issues/6637 for more context on the various causes for such a corruption.")
|
||||||
|
for _, message := range messages {
|
||||||
|
log.Error("Forgejo migration[25]: %s", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.GetEngine(context.Background()).In("id", ids).NoAutoCondition().NoAutoTime().Delete(&auth.TwoFactor{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,14 @@ func Test_MigrateTwoFactorToKeying(t *testing.T) {
|
||||||
|
|
||||||
cnt, err := x.Table("two_factor").Count()
|
cnt, err := x.Table("two_factor").Count()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.EqualValues(t, 1, cnt)
|
assert.EqualValues(t, 2, cnt)
|
||||||
|
|
||||||
require.NoError(t, MigrateTwoFactorToKeying(x))
|
require.NoError(t, MigrateTwoFactorToKeying(x))
|
||||||
|
|
||||||
|
cnt, err = x.Table("two_factor").Count()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, cnt)
|
||||||
|
|
||||||
var twofactor auth.TwoFactor
|
var twofactor auth.TwoFactor
|
||||||
_, err = x.Table("two_factor").ID(1).Get(&twofactor)
|
_, err = x.Table("two_factor").ID(1).Get(&twofactor)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -416,20 +416,6 @@ func (issue *Issue) SummaryCardURL() string {
|
||||||
return fmt.Sprintf("%s/summary-card", issue.HTMLURL())
|
return fmt.Sprintf("%s/summary-card", issue.HTMLURL())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (issue *Issue) SummaryCardSize() (int, int) {
|
|
||||||
return 1200, 600
|
|
||||||
}
|
|
||||||
|
|
||||||
func (issue *Issue) SummaryCardWidth() int {
|
|
||||||
width, _ := issue.SummaryCardSize()
|
|
||||||
return width
|
|
||||||
}
|
|
||||||
|
|
||||||
func (issue *Issue) SummaryCardHeight() int {
|
|
||||||
_, height := issue.SummaryCardSize()
|
|
||||||
return height
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link returns the issue's relative URL.
|
// Link returns the issue's relative URL.
|
||||||
func (issue *Issue) Link() string {
|
func (issue *Issue) Link() string {
|
||||||
var path string
|
var path string
|
||||||
|
|
|
@ -7,8 +7,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
project_model "code.gitea.io/gitea/models/project"
|
project_model "code.gitea.io/gitea/models/project"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/optional"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,22 +50,28 @@ func (issue *Issue) ProjectColumnID(ctx context.Context) int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadIssuesFromColumn load issues assigned to this column
|
// LoadIssuesFromColumn load issues assigned to this column
|
||||||
func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column) (IssueList, error) {
|
func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, doer *user_model.User, org *org_model.Organization, isClosed optional.Option[bool]) (IssueList, error) {
|
||||||
issueList, err := Issues(ctx, &IssuesOptions{
|
issueOpts := &IssuesOptions{
|
||||||
ProjectColumnID: b.ID,
|
ProjectColumnID: b.ID,
|
||||||
ProjectID: b.ProjectID,
|
ProjectID: b.ProjectID,
|
||||||
SortType: "project-column-sorting",
|
SortType: "project-column-sorting",
|
||||||
})
|
IsClosed: isClosed,
|
||||||
|
AllPublic: true,
|
||||||
|
}
|
||||||
|
if doer != nil {
|
||||||
|
issueOpts.User = doer
|
||||||
|
issueOpts.Org = org
|
||||||
|
}
|
||||||
|
|
||||||
|
issueList, err := Issues(ctx, issueOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.Default {
|
if b.Default {
|
||||||
issues, err := Issues(ctx, &IssuesOptions{
|
issueOpts.ProjectColumnID = db.NoConditionID
|
||||||
ProjectColumnID: db.NoConditionID,
|
|
||||||
ProjectID: b.ProjectID,
|
issues, err := Issues(ctx, issueOpts)
|
||||||
SortType: "project-column-sorting",
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -78,10 +86,10 @@ func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column) (IssueLi
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadIssuesFromColumnList load issues assigned to the columns
|
// LoadIssuesFromColumnList load issues assigned to the columns
|
||||||
func LoadIssuesFromColumnList(ctx context.Context, bs project_model.ColumnList) (map[int64]IssueList, error) {
|
func LoadIssuesFromColumnList(ctx context.Context, bs project_model.ColumnList, doer *user_model.User, org *org_model.Organization, isClosed optional.Option[bool]) (map[int64]IssueList, error) {
|
||||||
issuesMap := make(map[int64]IssueList, len(bs))
|
issuesMap := make(map[int64]IssueList, len(bs))
|
||||||
for i := range bs {
|
for i := range bs {
|
||||||
il, err := LoadIssuesFromColumn(ctx, bs[i])
|
il, err := LoadIssuesFromColumn(ctx, bs[i], doer, org, isClosed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -160,3 +168,36 @@ func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_mo
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NumIssuesInProjects returns the amount of issues assigned to one of the project
|
||||||
|
// in the list which the doer can access.
|
||||||
|
func NumIssuesInProjects(ctx context.Context, pl []*project_model.Project, doer *user_model.User, org *org_model.Organization, isClosed optional.Option[bool]) (map[int64]int, error) {
|
||||||
|
numMap := make(map[int64]int, len(pl))
|
||||||
|
for _, p := range pl {
|
||||||
|
num, err := NumIssuesInProject(ctx, p, doer, org, isClosed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
numMap[p.ID] = num
|
||||||
|
}
|
||||||
|
|
||||||
|
return numMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumIssuesInProject returns the amount of issues assigned to the project which
|
||||||
|
// the doer can access.
|
||||||
|
func NumIssuesInProject(ctx context.Context, p *project_model.Project, doer *user_model.User, org *org_model.Organization, isClosed optional.Option[bool]) (int, error) {
|
||||||
|
numIssuesInProject := int(0)
|
||||||
|
bs, err := p.GetColumns(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
im, err := LoadIssuesFromColumnList(ctx, bs, doer, org, isClosed)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
for _, il := range im {
|
||||||
|
numIssuesInProject += len(il)
|
||||||
|
}
|
||||||
|
return numIssuesInProject, nil
|
||||||
|
}
|
||||||
|
|
173
models/issues/issue_project_test.go
Normal file
173
models/issues/issue_project_test.go
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package issues_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/models/issues"
|
||||||
|
"code.gitea.io/gitea/models/organization"
|
||||||
|
"code.gitea.io/gitea/models/project"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/optional"
|
||||||
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrivateIssueProjects(t *testing.T) {
|
||||||
|
defer tests.AddFixtures("models/fixtures/PrivateIssueProjects/")()
|
||||||
|
require.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
t.Run("Organization project", func(t *testing.T) {
|
||||||
|
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
||||||
|
orgProject := unittest.AssertExistsAndLoadBean(t, &project.Project{ID: 1001, OwnerID: org.ID})
|
||||||
|
column := unittest.AssertExistsAndLoadBean(t, &project.Column{ID: 1001, ProjectID: orgProject.ID})
|
||||||
|
|
||||||
|
t.Run("Authenticated user", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, user2, org, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, issueList, 2)
|
||||||
|
assert.EqualValues(t, 16, issueList[0].ID)
|
||||||
|
assert.EqualValues(t, 6, issueList[1].ID)
|
||||||
|
|
||||||
|
issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 2, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(true))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 0, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(false))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 2, issuesNum)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Anonymous user", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, nil, org, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, issueList, 1)
|
||||||
|
assert.EqualValues(t, 16, issueList[0].ID)
|
||||||
|
|
||||||
|
issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, nil, org, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, nil, org, optional.Some(true))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 0, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, nil, org, optional.Some(false))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, issuesNum)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("User project", func(t *testing.T) {
|
||||||
|
userProject := unittest.AssertExistsAndLoadBean(t, &project.Project{ID: 1002, OwnerID: user2.ID})
|
||||||
|
column := unittest.AssertExistsAndLoadBean(t, &project.Column{ID: 1002, ProjectID: userProject.ID})
|
||||||
|
|
||||||
|
t.Run("Authenticated user", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, user2, nil, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, issueList, 2)
|
||||||
|
assert.EqualValues(t, 7, issueList[0].ID)
|
||||||
|
assert.EqualValues(t, 1, issueList[1].ID)
|
||||||
|
|
||||||
|
issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 2, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.Some(true))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 0, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.Some(false))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 2, issuesNum)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Anonymous user", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, nil, nil, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, issueList, 1)
|
||||||
|
assert.EqualValues(t, 1, issueList[0].ID)
|
||||||
|
|
||||||
|
issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, userProject, nil, nil, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, nil, nil, optional.Some(true))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 0, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, nil, nil, optional.Some(false))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, issuesNum)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrivateRepoProjects(t *testing.T) {
|
||||||
|
defer tests.AddFixtures("models/fixtures/TestPrivateRepoProjects/")()
|
||||||
|
require.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
||||||
|
orgProject := unittest.AssertExistsAndLoadBean(t, &project.Project{ID: 1001, OwnerID: org.ID})
|
||||||
|
column := unittest.AssertExistsAndLoadBean(t, &project.Column{ID: 1001, ProjectID: orgProject.ID})
|
||||||
|
|
||||||
|
t.Run("Partial access", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29})
|
||||||
|
|
||||||
|
issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, user29, org, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, issueList, 1)
|
||||||
|
assert.EqualValues(t, 6, issueList[0].ID)
|
||||||
|
|
||||||
|
issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, user29, org, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user29, org, optional.Some(true))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 0, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user29, org, optional.Some(false))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, issuesNum)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Full access", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
|
||||||
|
issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, user2, org, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, issueList, 2)
|
||||||
|
assert.EqualValues(t, 15, issueList[0].ID)
|
||||||
|
assert.EqualValues(t, 6, issueList[1].ID)
|
||||||
|
|
||||||
|
issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.None[bool]())
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 2, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(true))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 0, issuesNum)
|
||||||
|
|
||||||
|
issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(false))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 2, issuesNum)
|
||||||
|
})
|
||||||
|
}
|
|
@ -49,6 +49,10 @@ type IssuesOptions struct { //nolint
|
||||||
// prioritize issues from this repo
|
// prioritize issues from this repo
|
||||||
PriorityRepoID int64
|
PriorityRepoID int64
|
||||||
IsArchived optional.Option[bool]
|
IsArchived optional.Option[bool]
|
||||||
|
|
||||||
|
// If combined with AllPublic, then private as well as public issues
|
||||||
|
// that matches the criteria will be returned, if AllPublic is false
|
||||||
|
// only the private issues will be returned.
|
||||||
Org *organization.Organization // issues permission scope
|
Org *organization.Organization // issues permission scope
|
||||||
Team *organization.Team // issues permission scope
|
Team *organization.Team // issues permission scope
|
||||||
User *user_model.User // issues permission scope
|
User *user_model.User // issues permission scope
|
||||||
|
@ -196,7 +200,8 @@ func applyRepoConditions(sess *xorm.Session, opts *IssuesOptions) {
|
||||||
} else if len(opts.RepoIDs) > 1 {
|
} else if len(opts.RepoIDs) > 1 {
|
||||||
opts.RepoCond = builder.In("issue.repo_id", opts.RepoIDs)
|
opts.RepoCond = builder.In("issue.repo_id", opts.RepoIDs)
|
||||||
}
|
}
|
||||||
if opts.AllPublic {
|
// If permission scoping is set, then we set this condition at a later stage.
|
||||||
|
if opts.AllPublic && opts.User == nil {
|
||||||
if opts.RepoCond == nil {
|
if opts.RepoCond == nil {
|
||||||
opts.RepoCond = builder.NewCond()
|
opts.RepoCond = builder.NewCond()
|
||||||
}
|
}
|
||||||
|
@ -268,7 +273,14 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) {
|
||||||
applyLabelsCondition(sess, opts)
|
applyLabelsCondition(sess, opts)
|
||||||
|
|
||||||
if opts.User != nil {
|
if opts.User != nil {
|
||||||
sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.Value()))
|
cond := issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.Value())
|
||||||
|
// If AllPublic was set, then also consider all issues in public
|
||||||
|
// repositories in addition to the private repositories the user has access
|
||||||
|
// to.
|
||||||
|
if opts.AllPublic {
|
||||||
|
cond = cond.Or(builder.In("issue.repo_id", builder.Select("id").From("repository").Where(builder.Eq{"is_private": false})))
|
||||||
|
}
|
||||||
|
sess.And(cond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,6 +341,9 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati
|
||||||
builder.Or(
|
builder.Or(
|
||||||
repo_model.UserOrgUnitRepoCond(repoIDstr, userID, org.ID, unitType), // team member repos
|
repo_model.UserOrgUnitRepoCond(repoIDstr, userID, org.ID, unitType), // team member repos
|
||||||
repo_model.UserOrgPublicUnitRepoCond(userID, org.ID), // user org public non-member repos, TODO: check repo has issues
|
repo_model.UserOrgPublicUnitRepoCond(userID, org.ID), // user org public non-member repos, TODO: check repo has issues
|
||||||
|
builder.And(
|
||||||
|
builder.In("issue.repo_id", builder.Select("id").From("repository").Where(builder.Eq{"owner_id": org.ID})),
|
||||||
|
repo_model.UserAccessRepoCond(repoIDstr, userID)), // user can access org repo in a unit independent way
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,6 +353,17 @@ func GetLabelIDsInRepoByNames(ctx context.Context, repoID int64, labelNames []st
|
||||||
Find(&labelIDs)
|
Find(&labelIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLabelIDsInOrgByNames returns a list of labelIDs by names in a given org.
|
||||||
|
func GetLabelIDsInOrgByNames(ctx context.Context, orgID int64, labelNames []string) ([]int64, error) {
|
||||||
|
labelIDs := make([]int64, 0, len(labelNames))
|
||||||
|
return labelIDs, db.GetEngine(ctx).Table("label").
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
In("name", labelNames).
|
||||||
|
Asc("name").
|
||||||
|
Cols("id").
|
||||||
|
Find(&labelIDs)
|
||||||
|
}
|
||||||
|
|
||||||
// BuildLabelNamesIssueIDsCondition returns a builder where get issue ids match label names
|
// BuildLabelNamesIssueIDsCondition returns a builder where get issue ids match label names
|
||||||
func BuildLabelNamesIssueIDsCondition(labelNames []string) *builder.Builder {
|
func BuildLabelNamesIssueIDsCondition(labelNames []string) *builder.Builder {
|
||||||
return builder.Select("issue_label.issue_id").
|
return builder.Select("issue_label.issue_id").
|
||||||
|
|
|
@ -7,3 +7,12 @@
|
||||||
last_used_passcode:
|
last_used_passcode:
|
||||||
created_unix: 1564253724
|
created_unix: 1564253724
|
||||||
updated_unix: 1564253724
|
updated_unix: 1564253724
|
||||||
|
-
|
||||||
|
id: 2
|
||||||
|
uid: 23
|
||||||
|
secret: badbad
|
||||||
|
scratch_salt: badbad
|
||||||
|
scratch_hash: badbad
|
||||||
|
last_used_passcode:
|
||||||
|
created_unix: 1564253724
|
||||||
|
updated_unix: 1564253724
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright 2024 The Forgejo Authors.
|
// Copyright 2025 The Forgejo Authors.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
package v1_23 //nolint
|
package v1_23 //nolint
|
||||||
|
|
||||||
|
@ -7,26 +7,53 @@ import (
|
||||||
"code.gitea.io/gitea/models/migrations/base"
|
"code.gitea.io/gitea/models/migrations/base"
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GiteaLastDrop(x *xorm.Engine) error {
|
func GiteaLastDrop(x *xorm.Engine) error {
|
||||||
|
tables, err := x.DBMetas()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
|
|
||||||
if err := base.DropTableColumns(sess, "badge", "slug"); err != nil {
|
for _, drop := range []struct {
|
||||||
|
table string
|
||||||
|
column string
|
||||||
|
}{
|
||||||
|
{"badge", "slug"},
|
||||||
|
{"oauth2_application", "skip_secondary_authorization"},
|
||||||
|
{"repository", "default_wiki_branch"},
|
||||||
|
{"repo_unit", "everyone_access_mode"},
|
||||||
|
{"protected_branch", "can_force_push"},
|
||||||
|
{"protected_branch", "enable_force_push_allowlist"},
|
||||||
|
{"protected_branch", "force_push_allowlist_user_i_ds"},
|
||||||
|
{"protected_branch", "force_push_allowlist_team_i_ds"},
|
||||||
|
{"protected_branch", "force_push_allowlist_deploy_keys"},
|
||||||
|
} {
|
||||||
|
var table *schemas.Table
|
||||||
|
found := false
|
||||||
|
|
||||||
|
for _, table = range tables {
|
||||||
|
if table.Name == drop.table {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if table.GetColumn(drop.column) == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := base.DropTableColumns(sess, drop.table, drop.column); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := base.DropTableColumns(sess, "oauth2_application", "skip_secondary_authorization"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := base.DropTableColumns(sess, "repository", "default_wiki_branch"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// the migration v297.go that adds everyone_access_mode exists in Gitea >= v1.22 and the column must be dropped
|
|
||||||
// but it does not exist in Forgejo and a failure to drop the column can be ignored
|
|
||||||
base.DropTableColumns(sess, "repo_unit", "everyone_access_mode")
|
|
||||||
if err := base.DropTableColumns(sess, "protected_branch", "can_force_push", "enable_force_push_allowlist", "force_push_allowlist_user_i_ds", "force_push_allowlist_team_i_ds", "force_push_allowlist_deploy_keys"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
|
|
41
models/migrations/v1_23/v303_test.go
Normal file
41
models/migrations/v1_23/v303_test.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2025 The Forgejo Authors.
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package v1_23 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
migration_tests "code.gitea.io/gitea/models/migrations/test"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"xorm.io/xorm/schemas"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_GiteaLastDrop(t *testing.T) {
|
||||||
|
type Badge struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
Slug string
|
||||||
|
}
|
||||||
|
|
||||||
|
x, deferable := migration_tests.PrepareTestEnv(t, 0, new(Badge))
|
||||||
|
defer deferable()
|
||||||
|
if x == nil || t.Failed() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
getColumn := func() *schemas.Column {
|
||||||
|
tables, err := x.DBMetas()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, tables, 1)
|
||||||
|
table := tables[0]
|
||||||
|
require.Equal(t, "badge", table.Name)
|
||||||
|
return table.GetColumn("slug")
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NotNil(t, getColumn(), "slug column exists")
|
||||||
|
require.NoError(t, GiteaLastDrop(x))
|
||||||
|
require.Nil(t, getColumn(), "slug column was deleted")
|
||||||
|
// idempotent
|
||||||
|
require.NoError(t, GiteaLastDrop(x))
|
||||||
|
}
|
|
@ -57,20 +57,6 @@ func (Column) TableName() string {
|
||||||
return "project_board" // TODO: the legacy table name should be project_column
|
return "project_board" // TODO: the legacy table name should be project_column
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumIssues return counter of all issues assigned to the column
|
|
||||||
func (c *Column) NumIssues(ctx context.Context) int {
|
|
||||||
total, err := db.GetEngine(ctx).Table("project_issue").
|
|
||||||
Where("project_id=?", c.ProjectID).
|
|
||||||
And("project_board_id=?", c.ID).
|
|
||||||
GroupBy("issue_id").
|
|
||||||
Cols("issue_id").
|
|
||||||
Count()
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(total)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Column) GetIssues(ctx context.Context) ([]*ProjectIssue, error) {
|
func (c *Column) GetIssues(ctx context.Context) ([]*ProjectIssue, error) {
|
||||||
issues := make([]*ProjectIssue, 0, 5)
|
issues := make([]*ProjectIssue, 0, 5)
|
||||||
if err := db.GetEngine(ctx).Where("project_id=?", c.ProjectID).
|
if err := db.GetEngine(ctx).Where("project_id=?", c.ProjectID).
|
||||||
|
|
|
@ -34,20 +34,6 @@ func deleteProjectIssuesByProjectID(ctx context.Context, projectID int64) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumIssues return counter of all issues assigned to a project
|
|
||||||
func (p *Project) NumIssues(ctx context.Context) int {
|
|
||||||
c, err := db.GetEngine(ctx).Table("project_issue").
|
|
||||||
Where("project_id=?", p.ID).
|
|
||||||
GroupBy("issue_id").
|
|
||||||
Cols("issue_id").
|
|
||||||
Count()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("NumIssues: %v", err)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumClosedIssues return counter of closed issues assigned to a project
|
// NumClosedIssues return counter of closed issues assigned to a project
|
||||||
func (p *Project) NumClosedIssues(ctx context.Context) int {
|
func (p *Project) NumClosedIssues(ctx context.Context) int {
|
||||||
c, err := db.GetEngine(ctx).Table("project_issue").
|
c, err := db.GetEngine(ctx).Table("project_issue").
|
||||||
|
|
|
@ -97,13 +97,11 @@ func init() {
|
||||||
|
|
||||||
// LoadAttributes load repo and publisher attributes for a release
|
// LoadAttributes load repo and publisher attributes for a release
|
||||||
func (r *Release) LoadAttributes(ctx context.Context) error {
|
func (r *Release) LoadAttributes(ctx context.Context) error {
|
||||||
var err error
|
err := r.LoadRepo(ctx)
|
||||||
if r.Repo == nil {
|
|
||||||
r.Repo, err = GetRepositoryByID(ctx, r.RepoID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if r.Publisher == nil {
|
if r.Publisher == nil {
|
||||||
r.Publisher, err = user_model.GetUserByID(ctx, r.PublisherID)
|
r.Publisher, err = user_model.GetUserByID(ctx, r.PublisherID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -123,6 +121,18 @@ func (r *Release) LoadAttributes(ctx context.Context) error {
|
||||||
return GetReleaseAttachments(ctx, r)
|
return GetReleaseAttachments(ctx, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadRepo load repo attribute for release
|
||||||
|
func (r *Release) LoadRepo(ctx context.Context) error {
|
||||||
|
if r.Repo != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
r.Repo, err = GetRepositoryByID(ctx, r.RepoID)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// LoadArchiveDownloadCount loads the download count for the source archives
|
// LoadArchiveDownloadCount loads the download count for the source archives
|
||||||
func (r *Release) LoadArchiveDownloadCount(ctx context.Context) error {
|
func (r *Release) LoadArchiveDownloadCount(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
|
@ -130,6 +140,25 @@ func (r *Release) LoadArchiveDownloadCount(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTotalDownloadCount returns the summary of all dowload count of files attached to the release
|
||||||
|
func (r *Release) GetTotalDownloadCount(ctx context.Context) (int64, error) {
|
||||||
|
var archiveCount int64
|
||||||
|
if !r.HideArchiveLinks {
|
||||||
|
_, err := db.GetEngine(ctx).SQL("SELECT SUM(count) FROM repo_archive_download_count WHERE release_id = ?", r.ID).Get(&archiveCount)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var attachmentCount int64
|
||||||
|
_, err := db.GetEngine(ctx).SQL("SELECT SUM(download_count) FROM attachment WHERE release_id = ?", r.ID).Get(&attachmentCount)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return archiveCount + attachmentCount, nil
|
||||||
|
}
|
||||||
|
|
||||||
// APIURL the api url for a release. release must have attributes loaded
|
// APIURL the api url for a release. release must have attributes loaded
|
||||||
func (r *Release) APIURL() string {
|
func (r *Release) APIURL() string {
|
||||||
return r.Repo.APIURL() + "/releases/" + strconv.FormatInt(r.ID, 10)
|
return r.Repo.APIURL() + "/releases/" + strconv.FormatInt(r.ID, 10)
|
||||||
|
@ -160,6 +189,20 @@ func (r *Release) Link() string {
|
||||||
return r.Repo.Link() + "/releases/tag/" + util.PathEscapeSegments(r.TagName)
|
return r.Repo.Link() + "/releases/tag/" + util.PathEscapeSegments(r.TagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SummaryCardURL returns the absolute URL to an image providing a summary of the release
|
||||||
|
func (r *Release) SummaryCardURL() string {
|
||||||
|
return fmt.Sprintf("%s/releases/summary-card/%s", r.Repo.HTMLURL(), util.PathEscapeSegments(r.TagName))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisplayName retruns the name of the release
|
||||||
|
func (r *Release) DisplayName() string {
|
||||||
|
if r.IsTag && r.Title == "" {
|
||||||
|
return r.TagName
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Title
|
||||||
|
}
|
||||||
|
|
||||||
// IsReleaseExist returns true if release with given tag name already exists.
|
// IsReleaseExist returns true if release with given tag name already exists.
|
||||||
func IsReleaseExist(ctx context.Context, repoID int64, tagName string) (bool, error) {
|
func IsReleaseExist(ctx context.Context, repoID int64, tagName string) (bool, error) {
|
||||||
if len(tagName) == 0 {
|
if len(tagName) == 0 {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,3 +26,26 @@ func TestMigrate_InsertReleases(t *testing.T) {
|
||||||
err := InsertReleases(db.DefaultContext, r)
|
err := InsertReleases(db.DefaultContext, r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReleaseLoadRepo(t *testing.T) {
|
||||||
|
require.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
release := unittest.AssertExistsAndLoadBean(t, &Release{ID: 1})
|
||||||
|
assert.Nil(t, release.Repo)
|
||||||
|
|
||||||
|
require.NoError(t, release.LoadRepo(db.DefaultContext))
|
||||||
|
|
||||||
|
assert.EqualValues(t, 1, release.Repo.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReleaseDisplayName(t *testing.T) {
|
||||||
|
release := Release{TagName: "TagName"}
|
||||||
|
|
||||||
|
assert.Empty(t, release.DisplayName())
|
||||||
|
|
||||||
|
release.IsTag = true
|
||||||
|
assert.Equal(t, "TagName", release.DisplayName())
|
||||||
|
|
||||||
|
release.Title = "Title"
|
||||||
|
assert.Equal(t, "Title", release.DisplayName())
|
||||||
|
}
|
||||||
|
|
|
@ -327,6 +327,11 @@ func (repo *Repository) HTMLURL() string {
|
||||||
return setting.AppURL + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
|
return setting.AppURL + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SummaryCardURL returns the absolute URL to an image providing a summary of the repo
|
||||||
|
func (repo *Repository) SummaryCardURL() string {
|
||||||
|
return fmt.Sprintf("%s/-/summary-card", repo.HTMLURL())
|
||||||
|
}
|
||||||
|
|
||||||
// CommitLink make link to by commit full ID
|
// CommitLink make link to by commit full ID
|
||||||
// note: won't check whether it's an right id
|
// note: won't check whether it's an right id
|
||||||
func (repo *Repository) CommitLink(commitID string) (result string) {
|
func (repo *Repository) CommitLink(commitID string) (result string) {
|
||||||
|
|
|
@ -166,9 +166,9 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64)
|
||||||
// If isShowFullName is set to true, also include full name prefix search
|
// If isShowFullName is set to true, also include full name prefix search
|
||||||
func GetIssuePostersWithSearch(ctx context.Context, repo *Repository, isPull bool, search string, isShowFullName bool) ([]*user_model.User, error) {
|
func GetIssuePostersWithSearch(ctx context.Context, repo *Repository, isPull bool, search string, isShowFullName bool) ([]*user_model.User, error) {
|
||||||
users := make([]*user_model.User, 0, 30)
|
users := make([]*user_model.User, 0, 30)
|
||||||
var prefixCond builder.Cond = builder.Like{"name", search + "%"}
|
prefixCond := db.BuildCaseInsensitiveLike("name", search+"%")
|
||||||
if isShowFullName {
|
if isShowFullName {
|
||||||
prefixCond = prefixCond.Or(builder.Like{"full_name", "%" + search + "%"})
|
prefixCond = db.BuildCaseInsensitiveLike("full_name", "%"+search+"%")
|
||||||
}
|
}
|
||||||
|
|
||||||
cond := builder.In("`user`.id",
|
cond := builder.In("`user`.id",
|
||||||
|
|
|
@ -126,17 +126,15 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
|
||||||
return e.Where(cond)
|
return e.Where(cond)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2fa filter uses LEFT JOIN to check whether a user has a 2fa record
|
// Check if the user has two factor enabled, which is TOTP or Webauthn.
|
||||||
// While using LEFT JOIN, sometimes the performance might not be good, but it won't be a problem now, such SQL is seldom executed.
|
|
||||||
// There are some possible methods to refactor this SQL in future when we really need to optimize the performance (but not now):
|
|
||||||
// (1) add a column in user table (2) add a setting value in user_setting table (3) use search engines (bleve/elasticsearch)
|
|
||||||
if opts.IsTwoFactorEnabled.Value() {
|
if opts.IsTwoFactorEnabled.Value() {
|
||||||
cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL"))
|
cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL OR webauthn_credential.user_id IS NOT NULL"))
|
||||||
} else {
|
} else {
|
||||||
cond = cond.And(builder.Expr("two_factor.uid IS NULL"))
|
cond = cond.And(builder.Expr("two_factor.uid IS NULL AND webauthn_credential.user_id IS NULL"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.Join("LEFT OUTER", "two_factor", "two_factor.uid = `user`.id").
|
return e.Join("LEFT OUTER", "two_factor", "two_factor.uid = `user`.id").
|
||||||
|
Join("LEFT OUTER", "webauthn_credential", "webauthn_credential.user_id = `user`.id").
|
||||||
Where(cond)
|
Where(cond)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -842,48 +842,46 @@ func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
|
||||||
|
|
||||||
// VerifyUserActiveCode verifies that the code is valid for the given purpose for this user.
|
// VerifyUserActiveCode verifies that the code is valid for the given purpose for this user.
|
||||||
// If delete is specified, the token will be deleted.
|
// If delete is specified, the token will be deleted.
|
||||||
func VerifyUserAuthorizationToken(ctx context.Context, code string, purpose auth.AuthorizationPurpose, delete bool) (*User, error) {
|
func VerifyUserAuthorizationToken(ctx context.Context, code string, purpose auth.AuthorizationPurpose) (user *User, deleteToken func() error, err error) {
|
||||||
lookupKey, validator, found := strings.Cut(code, ":")
|
lookupKey, validator, found := strings.Cut(code, ":")
|
||||||
if !found {
|
if !found {
|
||||||
return nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
authToken, err := auth.FindAuthToken(ctx, lookupKey, purpose)
|
authToken, err := auth.FindAuthToken(ctx, lookupKey, purpose)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, util.ErrNotExist) {
|
if errors.Is(err, util.ErrNotExist) {
|
||||||
return nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if authToken.IsExpired() {
|
if authToken.IsExpired() {
|
||||||
return nil, auth.DeleteAuthToken(ctx, authToken)
|
return nil, nil, auth.DeleteAuthToken(ctx, authToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawValidator, err := hex.DecodeString(validator)
|
rawValidator, err := hex.DecodeString(validator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if subtle.ConstantTimeCompare([]byte(authToken.HashedValidator), []byte(auth.HashValidator(rawValidator))) == 0 {
|
if subtle.ConstantTimeCompare([]byte(authToken.HashedValidator), []byte(auth.HashValidator(rawValidator))) == 0 {
|
||||||
return nil, errors.New("validator doesn't match")
|
return nil, nil, errors.New("validator doesn't match")
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := GetUserByID(ctx, authToken.UID)
|
u, err := GetUserByID(ctx, authToken.UID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if IsErrUserNotExist(err) {
|
if IsErrUserNotExist(err) {
|
||||||
return nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if delete {
|
deleteToken = func() error {
|
||||||
if err := auth.DeleteAuthToken(ctx, authToken); err != nil {
|
return auth.DeleteAuthToken(ctx, authToken)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return u, nil
|
return u, deleteToken, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateUser check if user is valid to insert / update into database
|
// ValidateUser check if user is valid to insert / update into database
|
||||||
|
|
|
@ -222,7 +222,7 @@ func TestSearchUsers(t *testing.T) {
|
||||||
[]int64{1041, 37})
|
[]int64{1041, 37})
|
||||||
|
|
||||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
|
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
|
||||||
[]int64{24})
|
[]int64{24, 32})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmailNotificationPreferences(t *testing.T) {
|
func TestEmailNotificationPreferences(t *testing.T) {
|
||||||
|
@ -741,13 +741,13 @@ func TestVerifyUserAuthorizationToken(t *testing.T) {
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
|
||||||
t.Run("Wrong purpose", func(t *testing.T) {
|
t.Run("Wrong purpose", func(t *testing.T) {
|
||||||
u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.PasswordReset, false)
|
u, _, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.PasswordReset)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Nil(t, u)
|
assert.Nil(t, u)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("No delete", func(t *testing.T) {
|
t.Run("No delete", func(t *testing.T) {
|
||||||
u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation, false)
|
u, _, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.EqualValues(t, user.ID, u.ID)
|
assert.EqualValues(t, user.ID, u.ID)
|
||||||
|
|
||||||
|
@ -757,9 +757,10 @@ func TestVerifyUserAuthorizationToken(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Delete", func(t *testing.T) {
|
t.Run("Delete", func(t *testing.T) {
|
||||||
u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation, true)
|
u, deleteToken, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.EqualValues(t, user.ID, u.ID)
|
assert.EqualValues(t, user.ID, u.ID)
|
||||||
|
require.NoError(t, deleteToken())
|
||||||
|
|
||||||
authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation)
|
authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation)
|
||||||
require.ErrorIs(t, err, util.ErrNotExist)
|
require.ErrorIs(t, err, util.ErrNotExist)
|
||||||
|
|
256
modules/annex/annex.go
Normal file
256
modules/annex/annex.go
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Unlike modules/lfs, which operates mainly on git.Blobs, this operates on git.TreeEntrys.
|
||||||
|
// The motivation for this is that TreeEntrys have an easy pointer to the on-disk repo path,
|
||||||
|
// while blobs do not (in fact, if building with TAGS=gogit, blobs might exist only in a mock
|
||||||
|
// filesystem, living only in process RAM). We must have the on-disk path to do anything
|
||||||
|
// useful with git-annex because all of its interesting data is on-disk under .git/annex/.
|
||||||
|
|
||||||
|
package annex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/typesniffer"
|
||||||
|
|
||||||
|
"gopkg.in/ini.v1" //nolint:depguard // This import is forbidden in favor of using the setting module, but we need ini parsing for something other than Forgejo settings
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrBlobIsNotAnnexed occurs if a blob does not contain a valid annex key
|
||||||
|
var ErrBlobIsNotAnnexed = errors.New("not a git-annex pointer")
|
||||||
|
|
||||||
|
func PrivateInit(ctx context.Context, repoPath string) error {
|
||||||
|
if _, _, err := git.NewCommand(ctx, "config", "annex.private", "true").RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, _, err := git.NewCommand(ctx, "annex", "init").RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LookupKey(blob *git.Blob) (string, error) {
|
||||||
|
stdout, _, err := git.NewCommand(git.DefaultContext, "annex", "lookupkey", "--ref").AddDynamicArguments(blob.ID.String()).RunStdString(&git.RunOpts{Dir: blob.Repo().Path})
|
||||||
|
if err != nil {
|
||||||
|
return "", ErrBlobIsNotAnnexed
|
||||||
|
}
|
||||||
|
key := strings.TrimSpace(stdout)
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupKeyBatch runs git annex lookupkey --batch --ref
|
||||||
|
func LookupKeyBatch(ctx context.Context, shasToBatchReader *io.PipeReader, lookupKeyBatchWriter *io.PipeWriter, wg *sync.WaitGroup, repoPath string) {
|
||||||
|
defer wg.Done()
|
||||||
|
defer shasToBatchReader.Close()
|
||||||
|
defer lookupKeyBatchWriter.Close()
|
||||||
|
|
||||||
|
stderr := new(bytes.Buffer)
|
||||||
|
var errbuf strings.Builder
|
||||||
|
if err := git.NewCommand(ctx, "annex", "lookupkey", "--batch", "--ref").Run(&git.RunOpts{
|
||||||
|
Dir: repoPath,
|
||||||
|
Stdout: lookupKeyBatchWriter,
|
||||||
|
Stdin: shasToBatchReader,
|
||||||
|
Stderr: stderr,
|
||||||
|
}); err != nil {
|
||||||
|
_ = lookupKeyBatchWriter.CloseWithError(fmt.Errorf("git annex lookupkey --batch --ref [%s]: %w - %s", repoPath, err, errbuf.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyFromToBatch runs git -c annex.hardlink=true annex copy --batch-keys --from <remote> --to <remote>
|
||||||
|
func CopyFromToBatch(ctx context.Context, from, to string, keysToCopyReader *io.PipeReader, wg *sync.WaitGroup, repoPath string) {
|
||||||
|
defer wg.Done()
|
||||||
|
defer keysToCopyReader.Close()
|
||||||
|
|
||||||
|
stdout := new(bytes.Buffer)
|
||||||
|
stderr := new(bytes.Buffer)
|
||||||
|
var errbuf strings.Builder
|
||||||
|
if err := git.NewCommand(ctx, "-c", "annex.hardlink=true", "annex", "copy", "--batch-keys", "--from").AddDynamicArguments(from).AddArguments("--to").AddDynamicArguments(to).Run(&git.RunOpts{
|
||||||
|
Dir: repoPath,
|
||||||
|
Stdout: stdout,
|
||||||
|
Stdin: keysToCopyReader,
|
||||||
|
Stderr: stderr,
|
||||||
|
}); err != nil {
|
||||||
|
_ = keysToCopyReader.CloseWithError(fmt.Errorf("git annex copy --batch-keys --from <remote> --to <remote> [%s]: %w - %s", repoPath, err, errbuf.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContentLocationFromKey(repoPath, key string) (string, error) {
|
||||||
|
contentLocation, _, err := git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "contentlocation").AddDynamicArguments(key).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("in %s: %s does not seem to be a valid annexed file: %w", repoPath, key, err)
|
||||||
|
}
|
||||||
|
contentLocation = strings.TrimSpace(contentLocation)
|
||||||
|
contentLocation = path.Clean("/" + contentLocation)[1:] // prevent directory traversals
|
||||||
|
contentLocation = path.Join(repoPath, contentLocation)
|
||||||
|
|
||||||
|
return contentLocation, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the absolute path of the content pointed to by the annex pointer stored in the git object
|
||||||
|
// errors if the content is not found in this repo
|
||||||
|
func ContentLocation(blob *git.Blob) (string, error) {
|
||||||
|
key, err := LookupKey(blob)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ContentLocationFromKey(blob.Repo().Path, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a stream open to the annex content
|
||||||
|
func Content(blob *git.Blob) (*os.File, error) {
|
||||||
|
contentLocation, err := ContentLocation(blob)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.Open(contentLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// whether the object appears to be a valid annex pointer
|
||||||
|
// does *not* verify if the content is actually in this repo;
|
||||||
|
// for that, use ContentLocation()
|
||||||
|
func IsAnnexed(blob *git.Blob) (bool, error) {
|
||||||
|
if !setting.Annex.Enabled {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupKey is written to only return well-formed keys
|
||||||
|
// so the test is just to see if it errors
|
||||||
|
_, err := LookupKey(blob)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, ErrBlobIsNotAnnexed) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathIsAnnexRepo determines if repoPath is a git-annex enabled repository
|
||||||
|
func PathIsAnnexRepo(repoPath string) bool {
|
||||||
|
_, _, err := git.NewCommand(git.DefaultContext, "config", "annex.uuid").RunStdString(&git.RunOpts{Dir: repoPath})
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAnnexRepo determines if repo is a git-annex enabled repository
|
||||||
|
func IsAnnexRepo(repo *git.Repository) bool {
|
||||||
|
_, _, err := git.NewCommand(repo.Ctx, "config", "annex.uuid").RunStdString(&git.RunOpts{Dir: repo.Path})
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
uuid2repoPathCache = make(map[string]string)
|
||||||
|
repoPath2uuidCache = make(map[string]string)
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init() error {
|
||||||
|
if !setting.Annex.Enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !setting.Annex.DisableP2PHTTP {
|
||||||
|
log.Info("Populating the git-annex UUID cache with existing repositories")
|
||||||
|
start := time.Now()
|
||||||
|
if err := updateUUID2RepoPathCache(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info("Populating the git-annex UUID cache took %v", time.Since(start))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUUID2RepoPathCache() error {
|
||||||
|
configFiles, err := filepath.Glob(filepath.Join(setting.RepoRootPath, "*", "*", "config"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, configFile := range configFiles {
|
||||||
|
repoPath := strings.TrimSuffix(configFile, "/config")
|
||||||
|
_, ok := repoPath2uuidCache[repoPath]
|
||||||
|
if ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
config, err := ini.Load(configFile)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
repoUUID := config.Section("annex").Key("uuid").Value()
|
||||||
|
if repoUUID != "" {
|
||||||
|
uuid2repoPathCache[repoUUID] = repoPath
|
||||||
|
repoPath2uuidCache[repoPath] = repoUUID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func repoPathFromUUIDCache(uuid string) (string, error) {
|
||||||
|
if repoPath, ok := uuid2repoPathCache[uuid]; ok {
|
||||||
|
return repoPath, nil
|
||||||
|
}
|
||||||
|
// If the cache didn't contain an entry for the UUID then update the cache and try again
|
||||||
|
if err := updateUUID2RepoPathCache(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if repoPath, ok := uuid2repoPathCache[uuid]; ok {
|
||||||
|
return repoPath, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no repository known for UUID '%s'", uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkValidity(uuid, repoPath string) (bool, error) {
|
||||||
|
stdout, _, err := git.NewCommand(git.DefaultContext, "config", "annex.uuid").RunStdString(&git.RunOpts{Dir: repoPath})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
repoUUID := strings.TrimSpace(stdout)
|
||||||
|
return uuid == repoUUID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeCachedEntries(uuid, repoPath string) {
|
||||||
|
delete(uuid2repoPathCache, uuid)
|
||||||
|
delete(repoPath2uuidCache, repoPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UUID2RepoPath(uuid string) (string, error) {
|
||||||
|
// Get the current cache entry for the UUID
|
||||||
|
repoPath, err := repoPathFromUUIDCache(uuid)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Check if it is still up-to-date
|
||||||
|
valid, err := checkValidity(uuid, repoPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
|
// If it isn't, remove the cache entry and try again
|
||||||
|
removeCachedEntries(uuid, repoPath)
|
||||||
|
return UUID2RepoPath(uuid)
|
||||||
|
}
|
||||||
|
// Otherwise just return the cached entry
|
||||||
|
return repoPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GuessContentType guesses the content type of the annexed blob.
|
||||||
|
func GuessContentType(blob *git.Blob) (typesniffer.SniffedType, error) {
|
||||||
|
r, err := Content(blob)
|
||||||
|
if err != nil {
|
||||||
|
return typesniffer.SniffedType{}, err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
return typesniffer.DetectContentTypeFromReader(r)
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/annex"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
|
@ -101,6 +102,12 @@ func Int64sToStrings(ints []int64) []string {
|
||||||
|
|
||||||
// EntryIcon returns the octicon class for displaying files/directories
|
// EntryIcon returns the octicon class for displaying files/directories
|
||||||
func EntryIcon(entry *git.TreeEntry) string {
|
func EntryIcon(entry *git.TreeEntry) string {
|
||||||
|
isAnnexed, _ := annex.IsAnnexed(entry.Blob())
|
||||||
|
if isAnnexed {
|
||||||
|
// Show git-annex files as binary files to differentiate them from non-annexed files
|
||||||
|
// TODO: find a more suitable icon, maybe something related to git-annex
|
||||||
|
return "file-binary"
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case entry.IsLink():
|
case entry.IsLink():
|
||||||
te, _, err := entry.FollowLink()
|
te, _, err := entry.FollowLink()
|
||||||
|
|
|
@ -5,6 +5,7 @@ package card
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"io"
|
"io"
|
||||||
|
@ -35,12 +36,19 @@ type Card struct {
|
||||||
Img *image.RGBA
|
Img *image.RGBA
|
||||||
Font *truetype.Font
|
Font *truetype.Font
|
||||||
Margin int
|
Margin int
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
}
|
}
|
||||||
|
|
||||||
var fontCache = sync.OnceValues(func() (*truetype.Font, error) {
|
var fontCache = sync.OnceValues(func() (*truetype.Font, error) {
|
||||||
return truetype.Parse(goregular.TTF)
|
return truetype.Parse(goregular.TTF)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// DefaultSize returns the default size for a card
|
||||||
|
func DefaultSize() (int, int) {
|
||||||
|
return 1200, 600
|
||||||
|
}
|
||||||
|
|
||||||
// NewCard creates a new card with the given dimensions in pixels
|
// NewCard creates a new card with the given dimensions in pixels
|
||||||
func NewCard(width, height int) (*Card, error) {
|
func NewCard(width, height int) (*Card, error) {
|
||||||
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||||
|
@ -55,6 +63,8 @@ func NewCard(width, height int) (*Card, error) {
|
||||||
Img: img,
|
Img: img,
|
||||||
Font: font,
|
Font: font,
|
||||||
Margin: 0,
|
Margin: 0,
|
||||||
|
Width: width,
|
||||||
|
Height: height,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,14 +77,14 @@ func (c *Card) Split(vertical bool, percentage int) (*Card, *Card) {
|
||||||
mid := (bounds.Dx() * percentage / 100) + bounds.Min.X
|
mid := (bounds.Dx() * percentage / 100) + bounds.Min.X
|
||||||
subleft := c.Img.SubImage(image.Rect(bounds.Min.X, bounds.Min.Y, mid, bounds.Max.Y)).(*image.RGBA)
|
subleft := c.Img.SubImage(image.Rect(bounds.Min.X, bounds.Min.Y, mid, bounds.Max.Y)).(*image.RGBA)
|
||||||
subright := c.Img.SubImage(image.Rect(mid, bounds.Min.Y, bounds.Max.X, bounds.Max.Y)).(*image.RGBA)
|
subright := c.Img.SubImage(image.Rect(mid, bounds.Min.Y, bounds.Max.X, bounds.Max.Y)).(*image.RGBA)
|
||||||
return &Card{Img: subleft, Font: c.Font},
|
return &Card{Img: subleft, Font: c.Font, Width: subleft.Bounds().Dx(), Height: subleft.Bounds().Dy()},
|
||||||
&Card{Img: subright, Font: c.Font}
|
&Card{Img: subright, Font: c.Font, Width: subright.Bounds().Dx(), Height: subright.Bounds().Dy()}
|
||||||
}
|
}
|
||||||
mid := (bounds.Dy() * percentage / 100) + bounds.Min.Y
|
mid := (bounds.Dy() * percentage / 100) + bounds.Min.Y
|
||||||
subtop := c.Img.SubImage(image.Rect(bounds.Min.X, bounds.Min.Y, bounds.Max.X, mid)).(*image.RGBA)
|
subtop := c.Img.SubImage(image.Rect(bounds.Min.X, bounds.Min.Y, bounds.Max.X, mid)).(*image.RGBA)
|
||||||
subbottom := c.Img.SubImage(image.Rect(bounds.Min.X, mid, bounds.Max.X, bounds.Max.Y)).(*image.RGBA)
|
subbottom := c.Img.SubImage(image.Rect(bounds.Min.X, mid, bounds.Max.X, bounds.Max.Y)).(*image.RGBA)
|
||||||
return &Card{Img: subtop, Font: c.Font},
|
return &Card{Img: subtop, Font: c.Font, Width: subtop.Bounds().Dx(), Height: subtop.Bounds().Dy()},
|
||||||
&Card{Img: subbottom, Font: c.Font}
|
&Card{Img: subbottom, Font: c.Font, Width: subbottom.Bounds().Dx(), Height: subbottom.Bounds().Dy()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMargin sets the margins for the card
|
// SetMargin sets the margins for the card
|
||||||
|
@ -244,9 +254,14 @@ func (c *Card) fetchExternalImage(url string) (image.Image, bool) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go expects a absolute URL, so we must change a relative to an absolute one
|
||||||
|
if !strings.Contains(url, "://") {
|
||||||
|
url = fmt.Sprintf("%s%s", setting.AppURL, strings.TrimPrefix(url, "/"))
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := client.Get(url)
|
resp, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("error when fetching external image from %s: %w", url, err)
|
log.Warn("error when fetching external image from %s: %v", url, err)
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
@ -321,3 +336,8 @@ func (c *Card) DrawExternalImage(url string) {
|
||||||
}
|
}
|
||||||
c.DrawImage(image)
|
c.DrawImage(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DrawRect draws a rect with the given color
|
||||||
|
func (c *Card) DrawRect(startX, startY, endX, endY int, color color.Color) {
|
||||||
|
draw.Draw(c.Img, image.Rect(startX, startY, endX, endY), &image.Uniform{color}, image.Point{}, draw.Src)
|
||||||
|
}
|
||||||
|
|
|
@ -126,6 +126,10 @@ func (b *blobReader) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Blob) Repo() *Repository {
|
||||||
|
return b.repo
|
||||||
|
}
|
||||||
|
|
||||||
// Name returns name of the tree entry this blob object was created from (or empty string)
|
// Name returns name of the tree entry this blob object was created from (or empty string)
|
||||||
func (b *Blob) Name() string {
|
func (b *Blob) Name() string {
|
||||||
return b.name
|
return b.name
|
||||||
|
|
|
@ -457,12 +457,13 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests
|
// AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests
|
||||||
|
// It also re-enables git-credential(1), which is used to test git-annex's HTTP support
|
||||||
func AllowLFSFiltersArgs() TrustedCmdArgs {
|
func AllowLFSFiltersArgs() TrustedCmdArgs {
|
||||||
// Now here we should explicitly allow lfs filters to run
|
// Now here we should explicitly allow lfs filters to run
|
||||||
filteredLFSGlobalArgs := make(TrustedCmdArgs, len(globalCommandArgs))
|
filteredLFSGlobalArgs := make(TrustedCmdArgs, len(globalCommandArgs))
|
||||||
j := 0
|
j := 0
|
||||||
for _, arg := range globalCommandArgs {
|
for _, arg := range globalCommandArgs {
|
||||||
if strings.Contains(string(arg), "lfs") {
|
if strings.Contains(string(arg), "lfs") || strings.Contains(string(arg), "credential") {
|
||||||
j--
|
j--
|
||||||
} else {
|
} else {
|
||||||
filteredLFSGlobalArgs[j] = arg
|
filteredLFSGlobalArgs[j] = arg
|
||||||
|
|
|
@ -106,3 +106,36 @@ func BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader *io.PipeReader, s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlobsLessThanOrEqual32KiBFromCatFileBatchCheck reads a pipeline from cat-file --batch-check and returns the blobs <=32KiB in size
|
||||||
|
func BlobsLessThanOrEqual32KiBFromCatFileBatchCheck(catFileCheckReader *io.PipeReader, shasToBatchWriter *io.PipeWriter, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
defer catFileCheckReader.Close()
|
||||||
|
scanner := bufio.NewScanner(catFileCheckReader)
|
||||||
|
defer func() {
|
||||||
|
_ = shasToBatchWriter.CloseWithError(scanner.Err())
|
||||||
|
}()
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if len(line) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Split(line, " ")
|
||||||
|
if len(fields) < 3 || fields[1] != "blob" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
size, _ := strconv.Atoi(fields[2])
|
||||||
|
if size > 32*1024 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
toWrite := []byte(fields[0] + "\n")
|
||||||
|
for len(toWrite) > 0 {
|
||||||
|
n, err := shasToBatchWriter.Write(toWrite)
|
||||||
|
if err != nil {
|
||||||
|
_ = catFileCheckReader.CloseWithError(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
toWrite = toWrite[n:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
25
modules/markup/external/external.go
vendored
25
modules/markup/external/external.go
vendored
|
@ -12,6 +12,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/annex"
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
|
@ -86,8 +87,22 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
|
||||||
commands = strings.Fields(command)
|
commands = strings.Fields(command)
|
||||||
args = commands[1:]
|
args = commands[1:]
|
||||||
)
|
)
|
||||||
|
isAnnexed, _ := annex.IsAnnexed(ctx.Blob)
|
||||||
if p.IsInputFile {
|
// if a renderer wants to read a file, and we have annexed content, we can
|
||||||
|
// provide the annex key file location directly to the renderer. git-annex
|
||||||
|
// takes care of having that location be read-only, so no critical
|
||||||
|
// protection layer is needed. Moreover, the file readily exists, and
|
||||||
|
// expensive temporary files can be avoided, also allowing an operator
|
||||||
|
// to raise MAX_DISPLAY_FILE_SIZE without much negative impact.
|
||||||
|
if p.IsInputFile && isAnnexed {
|
||||||
|
// look for annexed content, will be empty, if there is none
|
||||||
|
annexContentLocation, _ := annex.ContentLocation(ctx.Blob)
|
||||||
|
// we call the renderer, even if there is no annex content present.
|
||||||
|
// showing the pointer file content is not much use, and a topical
|
||||||
|
// renderer might be able to produce something useful from the
|
||||||
|
// filename alone (present in ENV)
|
||||||
|
args = append(args, annexContentLocation)
|
||||||
|
} else if p.IsInputFile {
|
||||||
// write to temp file
|
// write to temp file
|
||||||
f, err := os.CreateTemp("", "gitea_input")
|
f, err := os.CreateTemp("", "gitea_input")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -130,6 +145,12 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
|
||||||
os.Environ(),
|
os.Environ(),
|
||||||
"GITEA_PREFIX_SRC="+ctx.Links.SrcLink(),
|
"GITEA_PREFIX_SRC="+ctx.Links.SrcLink(),
|
||||||
"GITEA_PREFIX_RAW="+ctx.Links.RawLink(),
|
"GITEA_PREFIX_RAW="+ctx.Links.RawLink(),
|
||||||
|
// also communicate the relative path of the to-be-rendered item.
|
||||||
|
// this enables the renderer to make use of the original file name
|
||||||
|
// and path, e.g., to make rendering or dtype-detection decisions
|
||||||
|
// that go beyond the originally matched extension. Even if the
|
||||||
|
// content is directly streamed to STDIN
|
||||||
|
"GITEA_RELATIVE_PATH="+ctx.RelativePath,
|
||||||
)
|
)
|
||||||
if !p.IsInputFile {
|
if !p.IsInputFile {
|
||||||
cmd.Stdin = input
|
cmd.Stdin = input
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -77,6 +78,16 @@ func newFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca
|
||||||
|
|
||||||
commitSha := node.Data[m[4]:m[5]]
|
commitSha := node.Data[m[4]:m[5]]
|
||||||
filePath := node.Data[m[6]:m[7]]
|
filePath := node.Data[m[6]:m[7]]
|
||||||
|
urlFullSource := urlFull
|
||||||
|
if strings.HasSuffix(filePath, "?display=source") {
|
||||||
|
filePath = strings.TrimSuffix(filePath, "?display=source")
|
||||||
|
} else if Type(filePath) != "" {
|
||||||
|
urlFullSource = node.Data[m[0]:m[6]] + filePath + "?display=source#" + node.Data[m[8]:m[1]]
|
||||||
|
}
|
||||||
|
filePath, err := url.QueryUnescape(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
hash := node.Data[m[8]:m[9]]
|
hash := node.Data[m[8]:m[9]]
|
||||||
|
|
||||||
preview.start = m[0]
|
preview.start = m[0]
|
||||||
|
@ -113,7 +124,7 @@ func newFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca
|
||||||
titleBuffer.WriteString(" – ")
|
titleBuffer.WriteString(" – ")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = html.Render(titleBuffer, createLink(urlFull, filePath, "muted"))
|
err = html.Render(titleBuffer, createLink(urlFullSource, filePath, "muted"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to render filepathLink: %v", err)
|
log.Error("failed to render filepathLink: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1026,4 +1026,138 @@ func TestRender_FilePreview(t *testing.T) {
|
||||||
localMetas,
|
localMetas,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
commitFileURL := util.URLJoin(markup.TestRepoURL, "src", "commit", "c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be", "path", "to", "file.md")
|
||||||
|
|
||||||
|
t.Run("rendered file with ?display=source", func(t *testing.T) {
|
||||||
|
testRender(
|
||||||
|
commitFileURL+"?display=source"+"#L1-L2",
|
||||||
|
`<p></p>`+
|
||||||
|
`<div class="file-preview-box">`+
|
||||||
|
`<div class="header">`+
|
||||||
|
`<div>`+
|
||||||
|
`<a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be/path/to/file.md?display=source#L1-L2" class="muted" rel="nofollow">path/to/file.md</a>`+
|
||||||
|
`</div>`+
|
||||||
|
`<span class="text small grey">`+
|
||||||
|
`Lines 1 to 2 in <a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be" class="text black" rel="nofollow">c991312</a>`+
|
||||||
|
`</span>`+
|
||||||
|
`</div>`+
|
||||||
|
`<div class="ui table">`+
|
||||||
|
`<table class="file-preview">`+
|
||||||
|
`<tbody>`+
|
||||||
|
`<tr>`+
|
||||||
|
`<td class="lines-num"><span data-line-number="1"></span></td>`+
|
||||||
|
`<td class="lines-code chroma"><code class="code-inner"><span class="gh"># A`+"\n"+`</span></code></td>`+
|
||||||
|
`</tr>`+
|
||||||
|
`<tr>`+
|
||||||
|
`<td class="lines-num"><span data-line-number="2"></span></td>`+
|
||||||
|
`<td class="lines-code chroma"><code class="code-inner"><span class="gh"></span>B`+"\n"+`</code></td>`+
|
||||||
|
`</tr>`+
|
||||||
|
`</tbody>`+
|
||||||
|
`</table>`+
|
||||||
|
`</div>`+
|
||||||
|
`</div>`+
|
||||||
|
`<p></p>`,
|
||||||
|
localMetas,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("rendered file without ?display=source", func(t *testing.T) {
|
||||||
|
testRender(
|
||||||
|
commitFileURL+"#L1-L2",
|
||||||
|
`<p></p>`+
|
||||||
|
`<div class="file-preview-box">`+
|
||||||
|
`<div class="header">`+
|
||||||
|
`<div>`+
|
||||||
|
`<a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be/path/to/file.md?display=source#L1-L2" class="muted" rel="nofollow">path/to/file.md</a>`+
|
||||||
|
`</div>`+
|
||||||
|
`<span class="text small grey">`+
|
||||||
|
`Lines 1 to 2 in <a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be" class="text black" rel="nofollow">c991312</a>`+
|
||||||
|
`</span>`+
|
||||||
|
`</div>`+
|
||||||
|
`<div class="ui table">`+
|
||||||
|
`<table class="file-preview">`+
|
||||||
|
`<tbody>`+
|
||||||
|
`<tr>`+
|
||||||
|
`<td class="lines-num"><span data-line-number="1"></span></td>`+
|
||||||
|
`<td class="lines-code chroma"><code class="code-inner"><span class="gh"># A`+"\n"+`</span></code></td>`+
|
||||||
|
`</tr>`+
|
||||||
|
`<tr>`+
|
||||||
|
`<td class="lines-num"><span data-line-number="2"></span></td>`+
|
||||||
|
`<td class="lines-code chroma"><code class="code-inner"><span class="gh"></span>B`+"\n"+`</code></td>`+
|
||||||
|
`</tr>`+
|
||||||
|
`</tbody>`+
|
||||||
|
`</table>`+
|
||||||
|
`</div>`+
|
||||||
|
`</div>`+
|
||||||
|
`<p></p>`,
|
||||||
|
localMetas,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
commitFileURL = util.URLJoin(markup.TestRepoURL, "src", "commit", "190d9492934af498c3f669d6a2431dc5459e5b20", "path", "to", "file.go")
|
||||||
|
|
||||||
|
t.Run("normal file with ?display=source", func(t *testing.T) {
|
||||||
|
testRender(
|
||||||
|
commitFileURL+"?display=source"+"#L2-L3",
|
||||||
|
`<p></p>`+
|
||||||
|
`<div class="file-preview-box">`+
|
||||||
|
`<div class="header">`+
|
||||||
|
`<div>`+
|
||||||
|
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go?display=source#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||||
|
`</div>`+
|
||||||
|
`<span class="text small grey">`+
|
||||||
|
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||||
|
`</span>`+
|
||||||
|
`</div>`+
|
||||||
|
`<div class="ui table">`+
|
||||||
|
`<table class="file-preview">`+
|
||||||
|
`<tbody>`+
|
||||||
|
`<tr>`+
|
||||||
|
`<td class="lines-num"><span data-line-number="2"></span></td>`+
|
||||||
|
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
|
||||||
|
`</tr>`+
|
||||||
|
`<tr>`+
|
||||||
|
`<td class="lines-num"><span data-line-number="3"></span></td>`+
|
||||||
|
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
|
||||||
|
`</tr>`+
|
||||||
|
`</tbody>`+
|
||||||
|
`</table>`+
|
||||||
|
`</div>`+
|
||||||
|
`</div>`+
|
||||||
|
`<p></p>`,
|
||||||
|
localMetas,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
commitFileURL = util.URLJoin(markup.TestRepoURL, "src", "commit", "eeb243c3395e1921c5d90e73bd739827251fc99d", "path", "to", "file%20%23.txt")
|
||||||
|
|
||||||
|
t.Run("file with strange characters in name", func(t *testing.T) {
|
||||||
|
testRender(
|
||||||
|
commitFileURL+"#L1",
|
||||||
|
`<p></p>`+
|
||||||
|
`<div class="file-preview-box">`+
|
||||||
|
`<div class="header">`+
|
||||||
|
`<div>`+
|
||||||
|
`<a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d/path/to/file%20%23.txt#L1" class="muted" rel="nofollow">path/to/file #.txt</a>`+
|
||||||
|
`</div>`+
|
||||||
|
`<span class="text small grey">`+
|
||||||
|
`Line 1 in <a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d" class="text black" rel="nofollow">eeb243c</a>`+
|
||||||
|
`</span>`+
|
||||||
|
`</div>`+
|
||||||
|
`<div class="ui table">`+
|
||||||
|
`<table class="file-preview">`+
|
||||||
|
`<tbody>`+
|
||||||
|
`<tr>`+
|
||||||
|
`<td class="lines-num"><span data-line-number="1"></span></td>`+
|
||||||
|
`<td class="lines-code chroma"><code class="code-inner">A`+"\n"+`</code></td>`+
|
||||||
|
`</tr>`+
|
||||||
|
`</tbody>`+
|
||||||
|
`</table>`+
|
||||||
|
`</div>`+
|
||||||
|
`</div>`+
|
||||||
|
`<p></p>`,
|
||||||
|
localMetas,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
|
mdutil "code.gitea.io/gitea/modules/markup/markdown/util"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/yuin/goldmark/ast"
|
"github.com/yuin/goldmark/ast"
|
||||||
|
@ -19,7 +20,7 @@ func (g *ASTTransformer) transformHeading(_ *markup.RenderContext, v *ast.Headin
|
||||||
v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value)))
|
v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
txt := v.Lines().Value(reader.Source())
|
txt := mdutil.Text(v, reader.Source())
|
||||||
header := markup.Header{
|
header := markup.Header{
|
||||||
Text: util.UnsafeBytesToString(txt),
|
Text: util.UnsafeBytesToString(txt),
|
||||||
Level: v.Level,
|
Level: v.Level,
|
||||||
|
|
|
@ -152,8 +152,8 @@ func HelloWorld() {
|
||||||
}
|
}
|
||||||
#+end_src
|
#+end_src
|
||||||
`, `<div class="src src-go">
|
`, `<div class="src src-go">
|
||||||
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints "Hello World"
|
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints "Hello World"</span>
|
||||||
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
|
<span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
|
||||||
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">)</span>
|
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">)</span>
|
||||||
<span class="p">}</span></code></pre>
|
<span class="p">}</span></code></pre>
|
||||||
</div>`)
|
</div>`)
|
||||||
|
|
|
@ -75,6 +75,10 @@ type RenderContext struct {
|
||||||
Metas map[string]string
|
Metas map[string]string
|
||||||
DefaultLink string
|
DefaultLink string
|
||||||
GitRepo *git.Repository
|
GitRepo *git.Repository
|
||||||
|
// reporting the target blob that is to-be-rendered enables
|
||||||
|
// deeper inspection in the handler for external renderer
|
||||||
|
// (i.e., more targeted handling of annexed files)
|
||||||
|
Blob *git.Blob
|
||||||
ShaExistCache map[string]bool
|
ShaExistCache map[string]bool
|
||||||
cancelFn func()
|
cancelFn func()
|
||||||
SidebarTocNode ast.Node
|
SidebarTocNode ast.Node
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
x•ŽANÃ0EYû³GB;a U=D9€=þ&–ÚÙÓr} 7èêÞÓÓëåÒŒBœ^¬´¤˜yY8Ï:AІX}<7D>R×XkÎs"î;uìFº®9x” Œ ÊEdÐ’%Í~**Zß3\ºÙvíô9Й>nÿ8Žfxkû=<3D>[9K”%L>®ôêÙ{§<>7Ãs–;aÕvý4ÛhXOûH·Ô“þÕ†ûð`KÑ
|
|
@ -0,0 +1 @@
|
||||||
|
x•ŽKŠ1@]çµ$¿J¥aæz€JRÁ@w+éØsýõ®ÞâñàåÛ²´ÖÛÃè"@VL&J3%f-ÑGDÒq2>FçjBOEݹË:ÀgÃ\1¤œ¦ê¦’kÀêªEM6DÔ,Ÿ\‚âǸÞ:\6é¾OülmÈ©;Ï|ƒ!GäŒE‚£6Z«üzòY¥Î²
¨m¸wÙ›üÂÿi‘.x-o³ò"›úŒLÌ
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1 +1 @@
|
||||||
4c1aaf56bcb9f39dcf65f3f250726850aed13cd6
|
eeb243c3395e1921c5d90e73bd739827251fc99d
|
||||||
|
|
|
@ -40,6 +40,7 @@ type ServCommandResults struct {
|
||||||
UserName string
|
UserName string
|
||||||
UserEmail string
|
UserEmail string
|
||||||
UserID int64
|
UserID int64
|
||||||
|
UserMode perm.AccessMode
|
||||||
OwnerName string
|
OwnerName string
|
||||||
RepoName string
|
RepoName string
|
||||||
RepoID int64
|
RepoID int64
|
||||||
|
|
|
@ -32,7 +32,7 @@ var (
|
||||||
// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
|
// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
|
||||||
issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\'|\")([#!][0-9]+)(?:\s|$|\)|\]|\'|\"|[:;,.?!]\s|[:;,.?!]$)`)
|
issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\'|\")([#!][0-9]+)(?:\s|$|\)|\]|\'|\"|[:;,.?!]\s|[:;,.?!]$)`)
|
||||||
// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
|
// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
|
||||||
issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\"|\')([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$)|\"|\')`)
|
issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\"|\')([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$)|\"|\'|,)`)
|
||||||
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
|
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
|
||||||
// e.g. org/repo#12345
|
// e.g. org/repo#12345
|
||||||
crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
||||||
|
|
|
@ -466,6 +466,7 @@ func TestRegExp_issueAlphanumericPattern(t *testing.T) {
|
||||||
"ABC-123:",
|
"ABC-123:",
|
||||||
"\"ABC-123\"",
|
"\"ABC-123\"",
|
||||||
"'ABC-123'",
|
"'ABC-123'",
|
||||||
|
"ABC-123, unknown PR",
|
||||||
}
|
}
|
||||||
falseTestCases := []string{
|
falseTestCases := []string{
|
||||||
"RC-08",
|
"RC-08",
|
||||||
|
|
|
@ -47,7 +47,7 @@ func AesDecrypt(key, text []byte) ([]byte, error) {
|
||||||
cfb.XORKeyStream(text, text)
|
cfb.XORKeyStream(text, text)
|
||||||
data, err := base64.StdEncoding.DecodeString(string(text))
|
data, err := base64.StdEncoding.DecodeString(string(text))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("AesDecrypt invalid decrypted base64 string: %w", err)
|
return nil, fmt.Errorf("AesDecrypt invalid decrypted base64 string: %w - it can be caused by a change of the [security].SECRET_KEY setting or a database corruption - `forgejo doctor check --run check-db-consistency --fix` will get rid of orphaned rows found in the `two_factor` table and may fix this problem if they are the one with the invalid content", err)
|
||||||
}
|
}
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
25
modules/setting/annex.go
Normal file
25
modules/setting/annex.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Annex represents the configuration for git-annex
|
||||||
|
var Annex = struct {
|
||||||
|
Enabled bool `ini:"ENABLED"`
|
||||||
|
DisableP2PHTTP bool `ini:"DISABLE_P2PHTTP"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
func loadAnnexFrom(rootCfg ConfigProvider) {
|
||||||
|
sec := rootCfg.Section("annex")
|
||||||
|
if err := sec.MapTo(&Annex); err != nil {
|
||||||
|
log.Fatal("Failed to map Annex settings: %v", err)
|
||||||
|
}
|
||||||
|
if !sec.HasKey("DISABLE_P2PHTTP") {
|
||||||
|
// If DisableP2PHTTP is not explicitly set then use DisableHTTPGit as its default
|
||||||
|
Annex.DisableP2PHTTP = Repository.DisableHTTPGit
|
||||||
|
}
|
||||||
|
}
|
|
@ -138,6 +138,11 @@ func CompileEmailGlobList(sec ConfigSection, keys ...string) (globs []glob.Glob)
|
||||||
return globs
|
return globs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadServiceSetting loads the service settings
|
||||||
|
func LoadServiceSetting() {
|
||||||
|
loadServiceFrom(CfgProvider)
|
||||||
|
}
|
||||||
|
|
||||||
func loadServiceFrom(rootCfg ConfigProvider) {
|
func loadServiceFrom(rootCfg ConfigProvider) {
|
||||||
sec := rootCfg.Section("service")
|
sec := rootCfg.Section("service")
|
||||||
Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
|
Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
|
||||||
|
|
|
@ -153,6 +153,7 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error {
|
||||||
loadCamoFrom(cfg)
|
loadCamoFrom(cfg)
|
||||||
loadI18nFrom(cfg)
|
loadI18nFrom(cfg)
|
||||||
loadGitFrom(cfg)
|
loadGitFrom(cfg)
|
||||||
|
loadAnnexFrom(cfg)
|
||||||
loadMirrorFrom(cfg)
|
loadMirrorFrom(cfg)
|
||||||
loadMarkupFrom(cfg)
|
loadMarkupFrom(cfg)
|
||||||
loadQuotaFrom(cfg)
|
loadQuotaFrom(cfg)
|
||||||
|
|
|
@ -103,6 +103,10 @@ func NewFuncMap() template.FuncMap {
|
||||||
"AppVer": func() string {
|
"AppVer": func() string {
|
||||||
return setting.AppVer
|
return setting.AppVer
|
||||||
},
|
},
|
||||||
|
"AppVerNoMetadata": func() string {
|
||||||
|
version, _, _ := strings.Cut(setting.AppVer, "+")
|
||||||
|
return version
|
||||||
|
},
|
||||||
"AppDomain": func() string { // documented in mail-templates.md
|
"AppDomain": func() string { // documented in mail-templates.md
|
||||||
return setting.Domain
|
return setting.Domain
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
@ -41,10 +43,48 @@ func Remove(name string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveAll removes the named file or (empty) directory with at most 5 attempts.
|
// MakeWritable recursively makes the named directory writable.
|
||||||
|
func MakeWritable(name string) error {
|
||||||
|
return filepath.WalkDir(name, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
// NB: this is called WalkDir but it works on a single file too
|
||||||
|
if err == nil {
|
||||||
|
info, err := d.Info()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't try chmod'ing symlinks (will fail with broken symlinks)
|
||||||
|
if info.Mode()&os.ModeSymlink != os.ModeSymlink {
|
||||||
|
// 0200 == u+w, in octal unix permission notation
|
||||||
|
err = os.Chmod(path, info.Mode()|0o200)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAll removes the named file or directory with at most 5 attempts.
|
||||||
func RemoveAll(name string) error {
|
func RemoveAll(name string) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
|
// Do chmod -R +w to help ensure the removal succeeds.
|
||||||
|
// In particular, in the git-annex case, this handles
|
||||||
|
// https://git-annex.branchable.com/internals/lockdown/ :
|
||||||
|
//
|
||||||
|
// > (The only bad consequence of this is that rm -rf .git
|
||||||
|
// > doesn't work unless you first run chmod -R +w .git)
|
||||||
|
|
||||||
|
err = MakeWritable(name)
|
||||||
|
if err != nil {
|
||||||
|
// try again
|
||||||
|
<-time.After(100 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
err = os.RemoveAll(name)
|
err = os.RemoveAll(name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue