mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-29 17:19:41 +09:00
Compare commits
70
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
678b5aba30 | ||
|
|
bd5f881c51 | ||
|
|
d3d57dd9b4 | ||
|
|
1bbd127a1a | ||
|
|
f803f8e269 | ||
|
|
8401fe7c54 | ||
|
|
c12e92c21d | ||
|
|
aab3242f7b | ||
|
|
f452c369ac | ||
|
|
c5c991b1a4 | ||
|
|
362539b78e | ||
|
|
66a3723cbb | ||
|
|
27e33b7ba1 | ||
|
|
761470c01d | ||
|
|
c0bbd82cd4 | ||
|
|
7fd34ff033 | ||
|
|
1e682a26eb | ||
|
|
3b3a06e06f | ||
|
|
545ed92354 | ||
|
|
49ef93940a | ||
|
|
308a6f12ae | ||
|
|
97078b96cf | ||
|
|
6507f1fd94 | ||
|
|
0964899799 | ||
|
|
550efdcdfd | ||
|
|
b96bd22372 | ||
|
|
a74f618ade | ||
|
|
2b89e2ac97 | ||
|
|
26bff7f47e | ||
|
|
582217a0da | ||
|
|
a46e331637 | ||
|
|
580cc26d63 | ||
|
|
e3d83bcf9c | ||
|
|
44f927eacf | ||
|
|
35413d5b65 | ||
|
|
e797a27d4e | ||
|
|
4b15260277 | ||
|
|
18fdc77130 | ||
|
|
2c2691b969 | ||
|
|
08d4abbb46 | ||
|
|
e4ef995f2a | ||
|
|
7fdfb8d642 | ||
|
|
d4c4142123 | ||
|
|
f7fd510224 | ||
|
|
38a5824753 | ||
|
|
e8d2c493bb | ||
|
|
b09920a537 | ||
|
|
c6184ed184 | ||
|
|
8909958055 | ||
|
|
638e4bce09 | ||
|
|
a031454586 | ||
|
|
c52a07dcfe | ||
|
|
b6ef881a9f | ||
|
|
6240d8bf89 | ||
|
|
9cb2719fab | ||
|
|
e8654c7e06 | ||
|
|
67a6bd7fc0 | ||
|
|
77e221ffaf | ||
|
|
458c11bd68 | ||
|
|
3d2bbd25ec | ||
|
|
7745720292 | ||
|
|
d46d0540d0 | ||
|
|
e449018730 | ||
|
|
e1cdb71845 | ||
|
|
a64131e22d | ||
|
|
0f0a38c1b9 | ||
|
|
535f791166 | ||
|
|
b34a09be38 | ||
|
|
6f2e328c85 | ||
|
|
55983320ed |
@@ -9,10 +9,10 @@ inputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
- uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
- name: Build regular image
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ inputs.platform }}
|
||||
@@ -20,7 +20,7 @@ runs:
|
||||
file: Dockerfile
|
||||
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
|
||||
- name: Build rootless image
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ inputs.platform }}
|
||||
|
||||
@@ -16,34 +16,34 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- if: ${{ github.workflow == 'cache-seeder' }}
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
|
||||
- if: ${{ github.workflow != 'cache-seeder' }}
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
|
||||
restore-keys: gomod-${{ runner.os }}-${{ runner.arch }}
|
||||
- if: ${{ github.workflow == 'cache-seeder' && inputs.lint-cache != 'true' }}
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: gobuild-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
|
||||
- if: ${{ github.workflow != 'cache-seeder' || inputs.lint-cache == 'true' }}
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: gobuild-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
|
||||
restore-keys: gobuild-${{ runner.os }}-${{ runner.arch }}
|
||||
- if: ${{ inputs.lint-cache == 'true' && github.workflow == 'cache-seeder' }}
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ~/.cache/golangci-lint
|
||||
key: golint-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum', '.golangci.yml') }}
|
||||
- if: ${{ inputs.lint-cache == 'true' && github.workflow != 'cache-seeder' }}
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ~/.cache/golangci-lint
|
||||
key: golint-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum', '.golangci.yml') }}
|
||||
|
||||
@@ -13,10 +13,10 @@ runs:
|
||||
- if: ${{ inputs.cache == 'true' }}
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
node-version: 26
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- if: ${{ inputs.cache != 'true' }}
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
node-version: 26
|
||||
|
||||
@@ -21,12 +21,12 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- uses: renovatebot/github-action@6d859fc95779be83a0335ca704879b47e5d79641 # v46.1.16
|
||||
- uses: renovatebot/github-action@dd5302ec17783b2fc721b19ae7209b57b1587765 # v46.1.17
|
||||
with:
|
||||
renovate-version: ${{ env.RENOVATE_VERSION }}
|
||||
configurationFile: renovate.json5
|
||||
token: ${{ secrets.RENOVATE_TOKEN }}
|
||||
env:
|
||||
RENOVATE_BINARY_SOURCE: install # auto-install go/node toolchains needed by post-upgrade tasks.
|
||||
RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^make (tidy|svg)$"]'
|
||||
RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^make (tidy|svg|generate-codemirror-languages)$"]'
|
||||
RENOVATE_REPOSITORIES: '["go-gitea/gitea"]'
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.base.sha }}
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
node-version: 26
|
||||
# Labels are only synced after the title lints, so an invalid title never reaches the label diff.
|
||||
- run: node ./tools/ci-tools.ts lint-pr-title
|
||||
env:
|
||||
|
||||
@@ -6,40 +6,30 @@ on:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runner: [ubuntu-24.04, ubuntu-24.04-arm]
|
||||
runs-on: ${{ matrix.runner }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
|
||||
- name: Install snapcraft
|
||||
run: sudo snap install snapcraft --classic
|
||||
|
||||
- name: Authenticate snapcraft
|
||||
shell: bash
|
||||
run: snapcraft login --with <(printf '%s' "$SNAPCRAFT_STORE_CREDENTIALS")
|
||||
|
||||
- name: Remote build
|
||||
run: |
|
||||
snapcraft remote-build \
|
||||
--launchpad-accept-public-upload \
|
||||
--build-for=amd64,arm64,armhf
|
||||
|
||||
- name: List built snaps
|
||||
run: find . -maxdepth 1 -type f -name '*.snap' -print
|
||||
|
||||
- name: Upload and release snapcraft nightly build
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
for snap in ./*.snap; do
|
||||
echo "Uploading $snap to edge"
|
||||
snapcraft upload --release="latest/edge" "$snap"
|
||||
done
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1.3.0
|
||||
id: build
|
||||
- uses: snapcore/action-publish@214b86e5ca036ead1668c79afb81e550e6c54d40 # v1.2.0
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||
with:
|
||||
snap: ${{ steps.build.outputs.snap }}
|
||||
release: latest/edge
|
||||
|
||||
@@ -23,12 +23,7 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- uses: ./.github/actions/node-setup
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
@@ -61,7 +56,7 @@ jobs:
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b # v6.2.0
|
||||
uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
@@ -83,8 +78,8 @@ jobs:
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
- uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
env:
|
||||
@@ -92,7 +87,7 @@ jobs:
|
||||
run: |
|
||||
REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
|
||||
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
id: meta
|
||||
with:
|
||||
images: |-
|
||||
@@ -102,7 +97,7 @@ jobs:
|
||||
type=raw,value=${{ steps.clean_name.outputs.branch }}
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
|
||||
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
id: meta_rootless
|
||||
with:
|
||||
images: |-
|
||||
@@ -116,18 +111,18 @@ jobs:
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Login to GHCR using PAT
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: build regular docker image
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
@@ -137,7 +132,7 @@ jobs:
|
||||
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
|
||||
cache-to: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful,mode=max
|
||||
- name: build rootless docker image
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
|
||||
@@ -24,12 +24,7 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- uses: ./.github/actions/node-setup
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
@@ -62,7 +57,7 @@ jobs:
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b # v6.2.0
|
||||
uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
@@ -94,9 +89,9 @@ jobs:
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
|
||||
- uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
id: meta
|
||||
with:
|
||||
images: |-
|
||||
@@ -109,7 +104,7 @@ jobs:
|
||||
type=semver,pattern={{version}}
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
|
||||
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
id: meta_rootless
|
||||
with:
|
||||
images: |-
|
||||
@@ -125,18 +120,18 @@ jobs:
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Login to GHCR using PAT
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: build regular container image
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
@@ -144,7 +139,7 @@ jobs:
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
- name: build rootless container image
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
|
||||
@@ -27,12 +27,7 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- uses: ./.github/actions/node-setup
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
@@ -65,7 +60,7 @@ jobs:
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b # v6.2.0
|
||||
uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1
|
||||
with:
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
@@ -97,9 +92,9 @@ jobs:
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||
- run: git fetch --unshallow --quiet --tags --force
|
||||
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
|
||||
- uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
|
||||
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
id: meta
|
||||
with:
|
||||
images: |-
|
||||
@@ -116,7 +111,7 @@ jobs:
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
|
||||
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
id: meta_rootless
|
||||
with:
|
||||
images: |-
|
||||
@@ -137,18 +132,18 @@ jobs:
|
||||
annotations: |
|
||||
org.opencontainers.image.authors="maintainers@gitea.io"
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Login to GHCR using PAT
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4 # v4.3.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: build regular container image
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
@@ -156,7 +151,7 @@ jobs:
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
- name: build rootless container image
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
||||
|
||||
-303
@@ -4,309 +4,6 @@ This changelog goes through the changes that have been made in each release
|
||||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
|
||||
## [1.27.0](https://github.com/go-gitea/gitea/releases/tag/v1.27.0) - 2026-07-13
|
||||
|
||||
* BREAKING
|
||||
* Feat(actions)!: improve support for reusable workflows (#37478)
|
||||
* Use Content-Security-Policy: script nonce (#37232)
|
||||
|
||||
* SECURITY
|
||||
* Fix: various security fixes (#38406) (#38426)
|
||||
* Fix(security): harden access checks and migration validation (#38324) (#38400)
|
||||
* Fix: enforce public-only token scope and harden push options / locale parsing (#38323) (#38399)
|
||||
* Fix(pull): re-evaluate review official flag on target branch change (#38319) (#38402)
|
||||
* Fix(api): stop leaking private repo metadata after access revocation (#38321) (#38390)
|
||||
* Fix(lfs): require proof of possession for cross-repo objects (#38322) (#38389)
|
||||
* Fix(mirror): disable HTTP redirects on pull mirror sync (#38320) (#38367)
|
||||
* Fix: golang html template url escaping (#38363) (#38369)
|
||||
* Fix(release): validate web attachment renames against allowed types (#38314) (#38328)
|
||||
* Fix(release): gate draft release attachments on web download endpoints (#38318) (#38325)
|
||||
* Fix(deps): update module github.com/go-git/go-git/v5 to v5.19.1 [security] (#37786)
|
||||
* Fix(oauth): restrict introspection to the token's client (#38042)
|
||||
* Fix(api): don't expose private org membership via public_members (#38145)
|
||||
* Fix(actions): deny fork-PR cross-repo access via collaborative owner (#38214)
|
||||
* Fix(migrations): prevent path traversal in repository restore (#38215)
|
||||
|
||||
* FEATURES
|
||||
* Feat(actions): add workflow status badge modal (#38196)
|
||||
* Feat(actions): support owner-level and global scoped workflows (#38154)
|
||||
* Feat(api): support ref suffixes in compare (#38148)
|
||||
* Feat(actions): implement `jobs.<job_id>.continue-on-error` (#38100)
|
||||
* Feat(actions): show run status on browser tab favicon (#38071)
|
||||
* Feat(api): add token introspection and self-deletion endpoint (#37995)
|
||||
* Feat(api): add q parameter to list branches API for server-side filtering (#37982)
|
||||
* Feat(repo): split repository creation limit into user and org scopes (#37872)
|
||||
* Feat(actions): bulk delete, disable and enable runners in admin UI (#37869)
|
||||
* Feat(actions): List workflows that were executed once but got removed from the default branch (#37835)
|
||||
* Feat(org): add team visibility so org members can discover teams (#37680)
|
||||
* Feat: add raw diff/patch endpoint for repository comparisons (#37632)
|
||||
* Feat: Add avatar stacks (#37594)
|
||||
* Feat(actions): add job summaries (GITHUB_STEP_SUMMARY) (#37500)
|
||||
* Feat(web): Add Jupyter Notebook (.ipynb) Rendering Support (#37433)
|
||||
* Support for Custom URI Schemes in OAuth2 Redirect URIs (#37356)
|
||||
* Feat(orgs): Add search bar for organization members tab page (#37347)
|
||||
* Feat(api): Add assignees APIs (#37330)
|
||||
* Feat(api): Add GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs (#37196)
|
||||
* Serve OpenAPI 3.0 spec at /openapi.v1.json (#37038)
|
||||
* Add project column picker to issue and pull request sidebar (#37037)
|
||||
* Allow multiple projects per issue and pull requests (#36784)
|
||||
* Feat(ui): add "follow rename" to file commit history list (#34994)
|
||||
* Feat(ssh): auto generate additional ssh keys (#33974)
|
||||
|
||||
* ENHANCEMENTS
|
||||
* Enhance(actions): only create filtered-out workflow commit status for required contexts (#38371) (#38385)
|
||||
* Enhance: allow builtin default git config options to be overridden (#38172)
|
||||
* Enhance: allow MathML core elements (#38034)
|
||||
* Enhance(markup): improve issue title rendering (#37908)
|
||||
* Enhance(actions): set descriptive browser tab title on run view (#37870)
|
||||
* Enhance: Migrate remaining gopkg.in/yaml.v3 usages to go.yaml.in/yaml/v4 (#37866)
|
||||
* Enhance(actions): show workflow name from YAML instead of filename (#37833)
|
||||
* Feat(actions): add before/after to PR synchronize event payload (#37827)
|
||||
* Enhance(actions): add branch filters to run list (#37826)
|
||||
* Enhance(actions): Make Summary UI more beautiful with more infos (#37824)
|
||||
* Feat: add copy button to action step header, improve other copy buttons (#37744)
|
||||
* Fix(icon): use repo-forked icon to display forks count (#37731)
|
||||
* Feat(api): add sort and order query parameters to job list endpoints (#37672)
|
||||
* Feat(api): add last_sync to repository API (#37566)
|
||||
* Enhance: Adjust Workflow Graph styling (#37497)
|
||||
* Improve code editor text selection and clean up lint enablement (#37474)
|
||||
* Add mirror auth updates to repo edit API and settings (#37468)
|
||||
* Replace `olivere/elastic` with REST API client, add OpenSearch support (#37411)
|
||||
* Feat: Add default PR branch update style setting (#37410)
|
||||
* Fix inconsistent disabled styling on logged-out repo header buttons (#37406)
|
||||
* Allow fast-forward-only merge when signed commits are required (#37335)
|
||||
* Enhance styling in actions page (#37323)
|
||||
* Fix: improve actions status icons and texts (#37206)
|
||||
* Make Markdown fenced code block work with more syntaxes (#37154)
|
||||
* Fix: Sort action run jobs by JobID and Name with matrix examples (#37046)
|
||||
* Add API endpoint to reply to pull request review comments (#36683)
|
||||
|
||||
* PERFORMANCE
|
||||
* Perf(actions): debounce runner heartbeat writes and throttle task picks (#38281) (#38368)
|
||||
* Perf(web): sort the action_run query by a repo-scoped index when possible (#38155)
|
||||
* Perf: Various performance regression fixes (#38078)
|
||||
* Perf: extend action `c_u` index to include `created_unix` for faster dashboard feeds (#38076)
|
||||
* Batch-load related data in actions run, job, and task API endpoints (#37032)
|
||||
|
||||
* BUGFIXES
|
||||
* Fix(util): reject invalid characters between time-estimate units (#38416) (#38423)
|
||||
* Fix: represent a deleted assignee team as a Ghost team (#38413) (#38419)
|
||||
* Fix(turnstile): route CAPTCHA verification through the configured proxy (#38412) (#38420)
|
||||
* Fix: refresh pull request merge box when the commit status is pending (#38410) (#38411)
|
||||
* Fix: actions task state concurrent update (#38405) (#38409)
|
||||
* Fix(actions): keep workflow run trailing on one row with long branch names (#38382) (#38403)
|
||||
* Fix(web): use locale-aware date formatting for contribution calendar tooltips (#38398) (#38401)
|
||||
* Fix: co-author detection (#38392) (#38397)
|
||||
* Fix: incorrect co-author detection on commit page (#38386) (#38387)
|
||||
* Fix(ui): restore commits table column widths (#38379) (#38383)
|
||||
* Fix: minio init check (#38355) (#38361)
|
||||
* Fix: org project view assignee list (#38357) (#38360)
|
||||
* Fix(actions): release claimed task if context is cancelled during `FetchTask` (#38343) (#38347)
|
||||
* Fix(actions): make runner list pagination order deterministic (#38313) (#38327)
|
||||
* Fix: Improve since/until when counting commits for X-Total-Count (#38243) (#38304)
|
||||
* Fix(actions): prevent chevron overlap with log text when timestamps are enabled (#38227) (#38307)
|
||||
* Fix(workflows): branch protection status checks fail when workflow uses on: paths filter (#38237) (#38302)
|
||||
* Fix(oauth2): persist linkAccountData during auto-link 2FA flow (#38274) (#38295)
|
||||
* Fix(actions): allow Actions bot to push to protected branches (#38284) (#38293)
|
||||
* Fix(actions): include all aggregable run statuses in status filter (#38280) (#38287)
|
||||
* Fix(archiver): use serializable repo-archive queue payload (#38273) (#38283)
|
||||
* Fix: update npm dependencies, fix misc issues (#38257)
|
||||
* Fix(api): respect since/until when counting commits for X-Total-Count (#38204)
|
||||
* Fix: codemirror regressions (#38248)
|
||||
* Fix(api): support HEAD requests on all API GET endpoints (#38245)
|
||||
* Fix(actions): Cleanup workflow status badge code (#38241)
|
||||
* Fix(web): Correctly align the "disabled" label on larger workflow names (#38240)
|
||||
* Fix(actions): don't swallow HTML entities into linkified URLs (#38239)
|
||||
* Fix(packages): accept npm "repository" and "bin" in string form (#38236)
|
||||
* Fix(actions): fix 500 error when canceling a canceling task (#38223)
|
||||
* Fix(deps): update module golang.org/x/image to v0.43.0 [security] (#38219)
|
||||
* Fix(mssql): convert legacy DATETIME columns to DATETIME2 (#38216)
|
||||
* Fix(api): deny private org member enumeration via /members (#38213)
|
||||
* Fix(actions): ensure all waiting jobs get runners in large workflows (#38200)
|
||||
* Fix(deps): update go dependencies (#38194)
|
||||
* Fix(deps): update npm dependencies (#38193)
|
||||
* Fix(cli): default must-change-password to false for bot users (#38175)
|
||||
* Fix(actions): show run index in run view and fix summary graph height (#38165)
|
||||
* Fix: csp (#38162)
|
||||
* Fix(deps): update npm dependencies (#38123)
|
||||
* Fix(mssql): expand legacy issue and comment long-text columns (#38120)
|
||||
* Fix(packages): validate debian distribution and component names (#38116)
|
||||
* Fix(packages): validate module version in goproxy ParsePackage (#38104)
|
||||
* Fix(deps): update dependency esbuild to v0.28.1 [security] (#38097)
|
||||
* Fix: git push hook post receive (#38089)
|
||||
* Fix(ui): prevent commit status popup overflowing its row (#38081)
|
||||
* Fix: validate gem name in rubygems parseMetadataFile (#38061)
|
||||
* Fix: commit display name (#38057)
|
||||
* Fix: csp regressions (#38047)
|
||||
* Fix: api error message (#38031)
|
||||
* Fix(deps): update npm dependencies (#38029)
|
||||
* Fix: pgsql lint (#38022)
|
||||
* Fix(indexer): fix assignee filters in issue search (#38021)
|
||||
* Fix: various dropdown problems (#38020)
|
||||
* Fix: refactor git error handling and make archive streaming handle non-existing commit id (#38007)
|
||||
* Fix: raise git required version to 2.13 (#37996)
|
||||
* Fix: remove "no-transfrom" from the cache-control header (#37985)
|
||||
* Fix(deps): update module github.com/google/go-github/v87 to v88 (#37971)
|
||||
* Fix: use committer time where ever possible as default (#37969)
|
||||
* Fix(deps): update npm dependencies, remove nolyfill (#37968)
|
||||
* Fix(deps): update go dependencies (#37967)
|
||||
* Fix(pull): preserve squash message trailers and additional commit messages (#37954)
|
||||
* Fix(deps): update module golang.org/x/image to v0.41.0 [security] (#37904)
|
||||
* Fix: support ##[command] log prefix in action run UI (#37882)
|
||||
* Fix(deps): update module github.com/google/go-github/v86 to v87 (#37845)
|
||||
* Fix(deps): update npm dependencies (#37844)
|
||||
* Fix(deps): update go dependencies (#37841)
|
||||
* Fix(frontend): resolve Vite assets by manifest source path (#37836)
|
||||
* Fix(locales): Replace hardcoded strings (#37788)
|
||||
* Fix(packages): render markdown links relative to linked repo (#37676)
|
||||
* Fix: persist mirror repository metadata (#37519)
|
||||
* Fix cmd tests by mocking builtin paths (#37369)
|
||||
* Add `form-fetch-action` to some forms, fix "fetch action" resp bug (#37305)
|
||||
* Feat: execute post run cleanup when workflow is cancelled (#37275)
|
||||
* Fix `relative-time` error and improve global error handler (#37241)
|
||||
* Refactor flash message and remove SanitizeHTML template func (#37179)
|
||||
|
||||
* TESTING
|
||||
* Test(e2e): fix race in pdf file render test (#38380) (#38381)
|
||||
* Test: compare key file contents instead of `FileInfo` in `TestInitKeys` (#38330) (#38331)
|
||||
* Test: speed up two tests (#37905)
|
||||
* Test: Fix random failure test (#37887)
|
||||
* Test: fix flaky `issue-comment` close test (#37880)
|
||||
* Test: enable WAL for sqlite integration tests (#37861)
|
||||
* Test: fix flaky `TestResourceIndex` and reduce its runtime (#37847)
|
||||
* Test: run `TestAPIRepoMigrate` offline via a local clone source (#37817)
|
||||
* Ci: shard tests and reduce redundant work (#37618)
|
||||
* Test(e2e): run playwright via container (#37300)
|
||||
* Remove external service dependencies in migration tests (#36866)
|
||||
|
||||
* BUILD
|
||||
* Fix(actions): authenticate snapcraft before nightly remote build (#38252)
|
||||
* Ci: cap Elasticsearch heap in db-tests (#37816)
|
||||
* Build(snap): publish nightly version to snapcraft via actions (#37814)
|
||||
* Ci: split pgsql shards into plain jobs, dedupe setup actions (#37802)
|
||||
* Ci: narrow files-changed frontend filter (#37749)
|
||||
* Ci: add `zizmor` to `lint-actions` (#37720)
|
||||
* Chore: clean up "contrib" dir (#37690)
|
||||
* Fix: snap build (main branch) (#37685)
|
||||
* Ci: Also lint json5 files (#37659)
|
||||
* Feat(editor): broaden language detection in web code editor (#37619)
|
||||
* Build: update pnpm to v11 (#37591)
|
||||
* Refactor(deps): migrate from `nektos/act` fork to `gitea/runner` (#37557)
|
||||
* Refactor: lint bare `fill`/`stroke` colors, add vars for git graph color series (#37543)
|
||||
* Update go js py dependencies (#37525)
|
||||
* Ci: lint PR titles with commitlint (#37498)
|
||||
* Chore: upgrade Go version in devcontainer image to 1.26 (#37374)
|
||||
* Update GitHub Actions to latest major versions (#37313)
|
||||
* Update go js dependencies (#37312)
|
||||
* Fail vite build on rolldown warnings via NODE_ENV=test (#37270)
|
||||
* Remove htmx (#37224)
|
||||
* Replace custom Go formatter with `golangci-lint fmt` (#37194)
|
||||
* Refactor htmx and fetch-action related code (#37186)
|
||||
* Integrate renovate bot for all dependency updates (#37050)
|
||||
* Build(sign): move to sigstore (#38250)
|
||||
|
||||
* DOCS
|
||||
* Docs: update changelog for 1.26.3 & 1.26.4 (#38178)
|
||||
* Docs: fix duplicated word in foreachref doc comment (#38161)
|
||||
* Docs: Clarify criteria for becoming a merger (#38113)
|
||||
* Docs: Publish TOC Election Result 2026 (#38111)
|
||||
* Docs: mark openapi3 as autogenerated in attributes (#37963)
|
||||
* Docs: add development setup guide (#37960)
|
||||
|
||||
* MISC
|
||||
* Revert(sign): restore gpg (#38251)
|
||||
* Refactor: replace legacy `delete-button` with `link-action` (#38143)
|
||||
* Refactor(actions): read runner capabilities from proto field (#38068)
|
||||
* Refactor(api): clarify APIError message usage and fix legacy lint error (#38012)
|
||||
* Refactor: Use db.Get[] instead of db.GetEngine(ctx).Get(bean) to avoid zero value fetching wrong database record (#37977)
|
||||
* Fix(deps): update go dependencies (#37851)
|
||||
* Ci: Fix sync PR labels from the conventional-commit title (#37784) (#37825)
|
||||
* Ci: tweak `files-changed`, add `free-disk-space` (#37819)
|
||||
* Fix(deps): update module golang.org/x/crypto to v0.52.0 [security] (#37806)
|
||||
* Test(e2e): add comment, release, star, PR and fork tests (#37800)
|
||||
* Chore: simplify issue and pull request templates (#37799)
|
||||
* Chore: Update giteabot to fix failure when backport (#37789)
|
||||
* Fix(api): handle partial failures in push mirror synchronization gracefully (#37782)
|
||||
* Fix(deps): update module gitlab.com/gitlab-org/api/client-go/v2 to v2.26.0 (#37771)
|
||||
* Ci: split giteabot workflow (#37770)
|
||||
* Fix(deps): update npm dependencies (#37768)
|
||||
* Refactor(waitgroup): replace Add/Done goroutines with WaitGroup.Go (#37764)
|
||||
* Fix(deps): update module google.golang.org/grpc to v1.81.1 (#37762)
|
||||
* Ci: fix cache-related issues (#37761)
|
||||
* Chore: fix tests (#37760)
|
||||
* Fix(deps): update module github.com/google/go-github/v85 to v86 (#37754)
|
||||
* Fix(deps): update npm dependencies (#37753)
|
||||
* Fix(deps): update go dependencies (#37752)
|
||||
* Chore(deps): update action dependencies (#37751)
|
||||
* Fix(markup): wrap indented code blocks for the code-copy button (#37748)
|
||||
* Chore(db): introduce db.Session and db.EngineMigration interfaces (#37746)
|
||||
* Feat(web): also display PR counts in repo list (#37739)
|
||||
* Refactor(glob): use strings.Builder for regexp compilation (#37730)
|
||||
* Chore(doctor): remove four obsolete doctor check implementations (#37728)
|
||||
* Refactor(org): simplify owner-team org repo creation logic (#37727)
|
||||
* Refactor: move `workflowpattern` into `modules/actions` (#37717)
|
||||
* Chore: clean up tests (#37715)
|
||||
* Style: misc UI fixes (#37691)
|
||||
* Ci: add shellcheck linter (#37682)
|
||||
* Fix: catch and fix more lint problems (#37674)
|
||||
* Fix(deps): update dependency mermaid to v11.15.0 [security], add e2e test (#37662)
|
||||
* Fix(deps): update npm dependencies (#37647)
|
||||
* Ci(renovate): update Go import paths on major bumps (#37641)
|
||||
* Fix(deps): update go dependencies (major) (#37639)
|
||||
* Chore(deps): update action dependencies (major) (#37638)
|
||||
* Fix(deps): update module code.gitea.io/sdk/gitea to v0.25.0 (#37637)
|
||||
* Fix(deps): update npm dependencies (#37636)
|
||||
* Refactor(log): replace log.Critical with log.Error (#37624)
|
||||
* Build(deps): bump fast-uri from 3.1.0 to 3.1.2 (#37616)
|
||||
* Feat(oauth): Support AWS Cognito OAuth2 provider (#37607)
|
||||
* Chore(deps): update action dependencies (#37603)
|
||||
* Ci: allow `chore` type in PR title lint (#37575)
|
||||
* Refactor: only reset a database table when the table's data was changed (#37573)
|
||||
* Ci: increase renovate frequency and fix RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS (#37565)
|
||||
* Refactor: use modernc sqlite driver as default (#37562)
|
||||
* Docs: fix 4 typos in CHANGELOG.md (#37549)
|
||||
* Fix(deps): update go dependencies (#37541)
|
||||
* Chore(deps): update action dependencies (#37540)
|
||||
* Refactor pull request view (6) (#37522)
|
||||
* Fix: redirect early CLI console logger to stderr (#37507)
|
||||
* Refactor "flex-list" to "flex-divided-list" (#37505)
|
||||
* Refactor compare diff/pull page (1) (#37481)
|
||||
* Refactor pull request view (4) (#37451)
|
||||
* Update 1.26.1 changelog in main (#37442)
|
||||
* Refactor: use named `Permission` field in `Repository` struct instead of anonymous embedding (#37441)
|
||||
* Refactor: serve site manifest via `/assets/site-manifest.json` endpoint (#37405)
|
||||
* Remove IsValidExternalURL/IsAPIURL and use IsValidURL at call sites (#37364)
|
||||
* Update `Block a user` form (#37359)
|
||||
* Move review request functions to a standalone file (#37358)
|
||||
* Feat(security): set X-Content-Type-Options: nosniff by default (#37354)
|
||||
* Enable strict TypeScript, add `errorMessage` helper (#37292)
|
||||
* Refactor frontend `tw-justify-between` layouts to `flex-left-right` (#37291)
|
||||
* Update Nix flake (#37284)
|
||||
* Fix Repository transferring page (#37277)
|
||||
* Remove `SubmitEvent` polyfill (#37276)
|
||||
* Remove dead code identified by `deadcode` tool (#37271)
|
||||
* Upgrade go-git to v5.18.0 (#37268)
|
||||
* Don't add useless labels which will bother changelog generation (#37267)
|
||||
* Move heatmap to first-party code (#37262)
|
||||
* Tests/integration: simplify code (#37249)
|
||||
* Add pagination and search box to org teams list (#37245)
|
||||
* Remove error returns from crypto random helpers and callers (#37240)
|
||||
* Add `ExternalIDClaim` option for OAuth2 OIDC auth source (#37229)
|
||||
* Refactor: simplify ParseCatFileTreeLine and catBatchParseTreeEntries (#37210)
|
||||
* Refactor "htmx" to "fetch action" (#37208)
|
||||
* Update go js py dependencies (#37204)
|
||||
* Add comment for the design of "user activity time" (#37195)
|
||||
* Remove outdated RunUser logic (#37180)
|
||||
* Models/fixtures: add "DO NOT add more test data" comment to all yml fixture files (#37150)
|
||||
* Update javascript dependencies (#37142)
|
||||
* Update go dependencies (#37141)
|
||||
* Frontport changelog of v1.26.0-rc0 (#37138)
|
||||
* Introduce `ActionRunAttempt` to represent each execution of a run (#37119)
|
||||
* Workflow Artifact Info Hover (#37100)
|
||||
* Extend issue context popup beyond markdown content (#36908)
|
||||
* Add bulk repository deletion for organizations (#36763)
|
||||
* Feat: Add bypass allowlist for branch protection (#36514)
|
||||
|
||||
## [1.26.4](https://github.com/go-gitea/gitea/releases/tag/1.26.4) - 2026-06-21
|
||||
|
||||
* SECURITY
|
||||
|
||||
@@ -41,7 +41,6 @@ Jimmy Praet <jimmy.praet@telenet.be> (@jpraet)
|
||||
Leon Hofmeister <dev.lh@web.de> (@delvh)
|
||||
Wim <wim@42.be> (@42wim)
|
||||
Jason Song <i@wolfogre.com> (@wolfogre)
|
||||
Yarden Shoham <git@yardenshoham.com> (@yardenshoham)
|
||||
Yu Tian <zettat123@gmail.com> (@Zettat123)
|
||||
Dong Ge <gedong_1994@163.com> (@sillyguodong)
|
||||
Xinyi Gong <hestergong@gmail.com> (@HesterG)
|
||||
|
||||
@@ -12,13 +12,13 @@ COMMA := ,
|
||||
XGO_VERSION := go-1.26.x
|
||||
|
||||
AIR_PACKAGE ?= github.com/air-verse/air@v1.65.3 # renovate: datasource=go
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.7.0 # renovate: datasource=go
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.8.0 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 # renovate: datasource=go
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15 # renovate: datasource=go
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.8.0 # renovate: datasource=go
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.34.1 # renovate: datasource=go
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.35.0 # renovate: datasource=go
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@v1.9.0 # renovate: datasource=go
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.4.0 # renovate: datasource=go
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.5.0 # renovate: datasource=go
|
||||
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 # renovate: datasource=go
|
||||
SHELLCHECK_IMAGE ?= docker.io/koalaman/shellcheck:v0.11.0@sha256:61862eba1fcf09a484ebcc6feea46f1782532571a34ed51fedf90dd25f925a8d # renovate: datasource=docker
|
||||
|
||||
@@ -231,7 +231,9 @@ endif
|
||||
generate-swagger: $(SWAGGER_SPEC) $(OPENAPI3_SPEC) ## generate the swagger spec from code comments
|
||||
|
||||
$(SWAGGER_SPEC): $(GO_SOURCES) $(SWAGGER_SPEC_INPUT)
|
||||
$(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)'
|
||||
@output="$$($(GO) run $(SWAGGER_PACKAGE) generate spec --enable-allof-compounding --skip-enum-desc --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)' 2>&1)" || { printf '%s\n' "$$output" >&2; exit 1; }; \
|
||||
warnings="$$(printf '%s\n' "$$output" | grep -v '^go: ')"; \
|
||||
if [ -n "$$warnings" ]; then printf '%s\n' "$$warnings" >&2; exit 1; fi
|
||||
|
||||
.PHONY: swagger-check
|
||||
swagger-check: generate-swagger
|
||||
@@ -246,9 +248,11 @@ swagger-check: generate-swagger
|
||||
swagger-validate: ## check if the swagger spec is valid
|
||||
@# swagger "validate" requires that the "basePath" must start with a slash, but we are using Golang template "{{...}}"
|
||||
@$(SED_INPLACE) -E -e 's|"basePath":( *)"(.*)"|"basePath":\1"/\2"|g' './$(SWAGGER_SPEC)' # add a prefix slash to basePath
|
||||
@# FIXME: there are some warnings
|
||||
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
|
||||
@$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath
|
||||
@output="$$($(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)' 2>&1)"; status=$$?; \
|
||||
$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)'; \
|
||||
printf '%s\n' "$$output" | grep -v '^go: '; \
|
||||
[ $$status -eq 0 ] || exit $$status; \
|
||||
case "$$output" in *WARNING:*) exit 1;; esac
|
||||
|
||||
.PHONY: generate-openapi3
|
||||
generate-openapi3: $(OPENAPI3_SPEC) ## generate the OpenAPI 3.0 spec from the Swagger 2.0 spec
|
||||
|
||||
Generated
+8
@@ -436,6 +436,7 @@
|
||||
"jsonl",
|
||||
"mcmeta",
|
||||
"sarif",
|
||||
"slnlaunch",
|
||||
"tact",
|
||||
"tfstate",
|
||||
"topojson",
|
||||
@@ -691,10 +692,17 @@
|
||||
"extensions": [
|
||||
"ini",
|
||||
"cnf",
|
||||
"container",
|
||||
"dof",
|
||||
"lektorproject",
|
||||
"mount",
|
||||
"network",
|
||||
"prefs",
|
||||
"properties",
|
||||
"service",
|
||||
"socket",
|
||||
"target",
|
||||
"timer",
|
||||
"url",
|
||||
"conf"
|
||||
],
|
||||
|
||||
@@ -18,6 +18,7 @@ func newUserCommand() *cli.Command {
|
||||
microcmdUserDelete(),
|
||||
newUserGenerateAccessTokenCommand(),
|
||||
microcmdUserMustChangePassword(),
|
||||
microcmdUserDisableTwoFactor(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
auth_model "gitea.dev/models/auth"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func microcmdUserDisableTwoFactor() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "disable-2fa",
|
||||
Usage: "Disable two-factor authentication for a user",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "username",
|
||||
Aliases: []string{"u"},
|
||||
Usage: "Username of the user to disable 2FA for",
|
||||
},
|
||||
&cli.Int64Flag{
|
||||
Name: "id",
|
||||
Usage: "ID of the user to disable 2FA for",
|
||||
},
|
||||
},
|
||||
Action: runDisableTwoFactor,
|
||||
}
|
||||
}
|
||||
|
||||
func runDisableTwoFactor(ctx context.Context, c *cli.Command) error {
|
||||
if !c.IsSet("id") && !c.IsSet("username") {
|
||||
return errors.New("either --id or --username must be provided")
|
||||
}
|
||||
|
||||
if !setting.IsInTesting {
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var user *user_model.User
|
||||
var err error
|
||||
if c.IsSet("id") {
|
||||
user, err = user_model.GetUserByID(ctx, c.Int64("id"))
|
||||
} else {
|
||||
user, err = user_model.GetUserByName(ctx, c.String("username"))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// When both selectors are given, make sure they refer to the same user.
|
||||
if c.IsSet("id") && c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
|
||||
return fmt.Errorf("the user with id %d is %q, which does not match the provided username %q", user.ID, user.Name, c.String("username"))
|
||||
}
|
||||
|
||||
totp, webAuthn, err := auth_model.DisableTwoFactor(ctx, user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Disabled 2FA for user %q (removed %d TOTP and %d WebAuthn credential(s))\n", user.Name, totp, webAuthn)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDisableTwoFactorCommand(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}, &auth_model.TwoFactor{}, &auth_model.WebAuthnCredential{}))
|
||||
}()
|
||||
|
||||
t.Run("disable TOTP and WebAuthn", func(t *testing.T) {
|
||||
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "tfuser", "--email", "tfuser@gitea.local", "--random-password"}))
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "tfuser"})
|
||||
|
||||
// Enroll TOTP.
|
||||
tf := &auth_model.TwoFactor{UID: user.ID}
|
||||
require.NoError(t, tf.SetSecret("test-secret"))
|
||||
_, err := tf.GenerateScratchToken()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, auth_model.NewTwoFactor(ctx, tf))
|
||||
|
||||
// Register a WebAuthn credential.
|
||||
_, err = auth_model.CreateCredential(ctx, user.ID, "test-key", &webauthn.Credential{ID: []byte("test-cred-id")})
|
||||
require.NoError(t, err)
|
||||
|
||||
has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, has)
|
||||
|
||||
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--username", "tfuser"}))
|
||||
|
||||
// Both factors must be gone afterwards.
|
||||
has, err = auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
})
|
||||
|
||||
t.Run("disable by id", func(t *testing.T) {
|
||||
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "iduser", "--email", "iduser@gitea.local", "--random-password"}))
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "iduser"})
|
||||
|
||||
tf := &auth_model.TwoFactor{UID: user.ID}
|
||||
require.NoError(t, tf.SetSecret("test-secret"))
|
||||
require.NoError(t, auth_model.NewTwoFactor(ctx, tf))
|
||||
|
||||
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--id", strconv.FormatInt(user.ID, 10)}))
|
||||
|
||||
has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
})
|
||||
|
||||
t.Run("no enrollment is a no-op", func(t *testing.T) {
|
||||
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "plainuser", "--email", "plainuser@gitea.local", "--random-password"}))
|
||||
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--username", "plainuser"}))
|
||||
})
|
||||
|
||||
t.Run("id and username must match when both given", func(t *testing.T) {
|
||||
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "matchuser", "--email", "matchuser@gitea.local", "--random-password"}))
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "matchuser"})
|
||||
id := strconv.FormatInt(user.ID, 10)
|
||||
|
||||
// Matching id + username is accepted.
|
||||
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--id", id, "--username", "matchuser"}))
|
||||
|
||||
// Mismatched id + username is rejected.
|
||||
cmd := microcmdUserDisableTwoFactor()
|
||||
cmd.Writer, cmd.ErrWriter = io.Discard, io.Discard
|
||||
err := cmd.Run(ctx, []string{"disable-2fa", "--id", id, "--username", "someotheruser"})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "does not match the provided username")
|
||||
})
|
||||
|
||||
t.Run("failure cases", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "user does not exist",
|
||||
args: []string{"disable-2fa", "--username", "nonexistentuser"},
|
||||
expectedErr: "user does not exist",
|
||||
},
|
||||
{
|
||||
name: "neither id nor username",
|
||||
args: []string{"disable-2fa"},
|
||||
expectedErr: "either --id or --username must be provided",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := microcmdUserDisableTwoFactor()
|
||||
cmd.Writer, cmd.ErrWriter = io.Discard, io.Discard
|
||||
err := cmd.Run(ctx, tc.args)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.expectedErr)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -536,13 +536,6 @@ INTERNAL_TOKEN =
|
||||
;; Leave it empty to apply the default policy, or set it to "unset" to disable Content-Security-Policy.
|
||||
;CONTENT_SECURITY_POLICY_GENERAL =
|
||||
|
||||
;; Webhook and oauth2 clients can only call allowed hosts for security reasons. Comma separated list, eg: external, 192.168.1.0/24, *.mydomain.com
|
||||
;; Built-in: loopback (for localhost), private (for LAN/intranet), external (for public hosts on internet), * (for all hosts)
|
||||
;; CIDR list: 1.2.3.0/8, 2001:db8::/32
|
||||
;; Wildcard hosts: *.mydomain.com, 192.168.100.*
|
||||
;; This list is enforced on direct connections only. When an HTTP proxy is configured, restricting the proxied target is the proxy server's responsibility.
|
||||
;ALLOWED_HOST_LIST = external
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
[camo]
|
||||
@@ -1198,6 +1191,7 @@ LEVEL = Info
|
||||
;; Default source for the pull request title when opening a new PR.
|
||||
;; "first-commit" uses the oldest commit's summary.
|
||||
;; "auto" uses commit's summary if the PR only has one commit, normalizes the branch name if multiple commits.
|
||||
;; "branch-name" always uses the PR's branch name.
|
||||
;DEFAULT_TITLE_SOURCE = auto
|
||||
;;
|
||||
;; Delay mergeable check until page view or API access, for pull requests that have not been updated in the specified days when their base branches get updated.
|
||||
@@ -1761,6 +1755,13 @@ LEVEL = Info
|
||||
;; Deliver timeout in seconds
|
||||
;DELIVER_TIMEOUT = 5
|
||||
;;
|
||||
;; Webhook can only call allowed hosts for security reasons. Comma separated list, eg: external, 192.168.1.0/24, *.mydomain.com
|
||||
;; Built-in: loopback (for localhost), private (for LAN/intranet), external (for public hosts on internet), * (for all hosts)
|
||||
;; CIDR list: 1.2.3.0/8, 2001:db8::/32
|
||||
;; Wildcard hosts: *.mydomain.com, 192.168.100.*
|
||||
;; Since 1.15.7. Default to * for 1.15.x, external for 1.16 and later
|
||||
;ALLOWED_HOST_LIST = external
|
||||
;;
|
||||
;; Allow insecure certification
|
||||
;SKIP_TLS_VERIFY = false
|
||||
;;
|
||||
|
||||
@@ -95,8 +95,15 @@ However, if there are no objections from maintainers, the PR can be merged with
|
||||
### Commit messages
|
||||
|
||||
Mergers are required to rewrite the PR title and the first comment (the summary) when necessary so the squash commit message is clear.
|
||||
Usually the Pull Request description and commit message body should not be empty, unless the title is already clear enough or the description would be a copy of the comments in code.
|
||||
|
||||
The final commit message should not hedge: replace phrases like `hopefully, <x> won't happen anymore` with definite wording.
|
||||
The final commit message:
|
||||
|
||||
- should match the code changes.
|
||||
- should only keep true co-authors, false-positive co-authors should be removed.
|
||||
- should not hedge: replace phrases like `hopefully, <x> won't happen anymore` with definite wording.
|
||||
- should not contain hidden information like `<!-- -->` or extra information after the description's divider `----`.
|
||||
- should not contain unrelated contents (e.g.: Release Notes, Configuration, etc.) from a Renovate update PR.
|
||||
|
||||
#### PR Co-authors
|
||||
|
||||
@@ -158,9 +165,16 @@ Any account with write access (including bots and TOC members) **must** use [2FA
|
||||
Mergers are the maintainers who carry out the final merge of approved PRs. Their responsibilities, described throughout this guide, are:
|
||||
|
||||
- Merging PRs from the [merge queue](#getting-prs-merged) in order, once a PR has `lgtm/done`, no open discussions, and no merge conflicts.
|
||||
- Rewriting the PR title and summary so the squash [commit message](#commit-messages) is clear, removing false-positive co-authors while keeping every true co-author.
|
||||
- Rewriting the PR title and description prior to the merge, making the [commit message](#commit-messages) clear.\
|
||||
In particular, mergers should edit the PR description.\
|
||||
Mergers should **not** edit the actual commit message except to remove unnecessary information. Because of that, even if users are looking at the PR, they can understand what changed.
|
||||
- Assigning the correct labels (including `type/…`) needed for changelog and backport decisions.
|
||||
- Agreeing, together with the owners, on when a release is ready (see [release management](release-management.md)).
|
||||
- Merging a PR also means the PR looks good to the merger and is approved by the merger.
|
||||
|
||||
If a merger violates these merge guides more than 3 times in the past 365 days
|
||||
(e.g.: merge with unresolved reviews without TOC decision to ignore the review, merge with garbage commit messages),
|
||||
they may lose their merging privileges for at least three months.
|
||||
|
||||
#### Becoming a merger
|
||||
|
||||
@@ -200,20 +214,20 @@ random.seed("Gitea TOC <YEAR> Election")
|
||||
random.choice([<CANDIDATE_1>, <CANDIDATE_2>, ...])
|
||||
```
|
||||
|
||||
The result of this script needs then to be published in the TOC election issue to ensure transparency of the process.
|
||||
The result of this script needs then to be published in the TOC election issue to ensure transparency of the process.
|
||||
|
||||
### Current TOC members
|
||||
|
||||
- 2026-06-14 ~ 2026-12-31
|
||||
- Company
|
||||
- [Jason Song](https://gitea.com/wolfogre) <i@wolfogre.com>
|
||||
- [Yu Tian](https://gitea.com/Zettat123) <zettat123@gmail.com>
|
||||
- [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
||||
- [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.com>
|
||||
- Community
|
||||
- [bircni](https://gitea.com/bircni) <bircni@icloud.com>
|
||||
- [delvh](https://gitea.com/delvh) <dev.lh@web.de>
|
||||
- [TheFox0x7](https://gitea.com/TheFox0x7) <thefox0x7@gmail.com>
|
||||
|
||||
|
||||
|
||||
### Previous TOC/owners members
|
||||
|
||||
@@ -227,7 +241,7 @@ Here's the history of the owners and the time they served:
|
||||
- [Andrew Thornton](https://gitea.com/zeripath) - [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872), 2023
|
||||
- [6543](https://gitea.com/6543) - 2023, 2025
|
||||
- [John Olheiser](https://gitea.com/jolheiser) - 2023, 2024
|
||||
- [Jason Song](https://gitea.com/wolfogre) - 2023
|
||||
- [Jason Song](https://gitea.com/wolfogre) - 2023, 2025
|
||||
|
||||
## Governance Compensation
|
||||
|
||||
|
||||
+23
-14
@@ -1,6 +1,4 @@
|
||||
import arrayFunc from 'eslint-plugin-array-func';
|
||||
import comments from '@eslint-community/eslint-plugin-eslint-comments';
|
||||
import deMorgan from 'eslint-plugin-de-morgan';
|
||||
import globals from 'globals';
|
||||
import importPlugin from 'eslint-plugin-import-x';
|
||||
import playwright from 'eslint-plugin-playwright';
|
||||
@@ -15,7 +13,6 @@ import vue from 'eslint-plugin-vue';
|
||||
import vueScopedCss from 'eslint-plugin-vue-scoped-css';
|
||||
import wc from 'eslint-plugin-wc';
|
||||
import {defineConfig, globalIgnores} from 'eslint/config';
|
||||
import type {ESLint} from 'eslint';
|
||||
|
||||
import unescapedHtmlLiteral from './tools/eslint-rules/unescaped-html-literal.ts';
|
||||
|
||||
@@ -64,10 +61,8 @@ export default defineConfig([
|
||||
'@eslint-community/eslint-comments': comments,
|
||||
'@stylistic': stylistic,
|
||||
'@typescript-eslint': typescriptPlugin.plugin,
|
||||
'array-func': arrayFunc,
|
||||
'de-morgan': deMorgan,
|
||||
'gitea': {rules: {'unescaped-html-literal': unescapedHtmlLiteral}},
|
||||
'import-x': importPlugin as unknown as ESLint.Plugin, // https://github.com/un-ts/eslint-plugin-import-x/issues/203
|
||||
'import-x': importPlugin,
|
||||
regexp,
|
||||
sonarjs,
|
||||
unicorn,
|
||||
@@ -278,12 +273,6 @@ export default defineConfig([
|
||||
'@typescript-eslint/unified-signatures': [2],
|
||||
'accessor-pairs': [2],
|
||||
'array-callback-return': [2, {checkForEach: true}],
|
||||
'array-func/avoid-reverse': [2],
|
||||
'array-func/from-map': [2],
|
||||
'array-func/no-unnecessary-this-arg': [2],
|
||||
'array-func/prefer-array-from': [2],
|
||||
'array-func/prefer-flat-map': [0], // handled by unicorn/prefer-array-flat-map
|
||||
'array-func/prefer-flat': [0], // handled by unicorn/prefer-array-flat
|
||||
'arrow-body-style': [0],
|
||||
'block-scoped-var': [2],
|
||||
'camelcase': [0],
|
||||
@@ -294,8 +283,6 @@ export default defineConfig([
|
||||
'consistent-this': [0],
|
||||
'constructor-super': [2],
|
||||
'curly': [0],
|
||||
'de-morgan/no-negated-conjunction': [2],
|
||||
'de-morgan/no-negated-disjunction': [2],
|
||||
'default-case-last': [2],
|
||||
'default-case': [0],
|
||||
'default-param-last': [0],
|
||||
@@ -745,6 +732,7 @@ export default defineConfig([
|
||||
'unicorn/consistent-json-file-read': [2],
|
||||
'unicorn/consistent-optional-chaining': [2],
|
||||
'unicorn/consistent-template-literal-escape': [2],
|
||||
'unicorn/consistent-tuple-labels': [2],
|
||||
'unicorn/custom-error-definition': [0],
|
||||
'unicorn/default-export-style': [2],
|
||||
'unicorn/dom-node-dataset': [2, {preferAttributes: true}],
|
||||
@@ -778,6 +766,7 @@ export default defineConfig([
|
||||
'unicorn/no-array-sort-for-min-max': [2],
|
||||
'unicorn/no-array-splice': [0],
|
||||
'unicorn/no-asterisk-prefix-in-documentation-comments': [0],
|
||||
'unicorn/no-async-promise-finally': [2],
|
||||
'unicorn/no-await-expression-member': [0],
|
||||
'unicorn/no-await-in-promise-methods': [2],
|
||||
'unicorn/no-blob-to-file': [2],
|
||||
@@ -814,8 +803,10 @@ export default defineConfig([
|
||||
'unicorn/no-invalid-fetch-options': [2],
|
||||
'unicorn/no-invalid-file-input-accept': [2],
|
||||
'unicorn/no-invalid-remove-event-listener': [2],
|
||||
'unicorn/no-invalid-well-known-symbol-methods': [2],
|
||||
'unicorn/no-keyword-prefix': [0],
|
||||
'unicorn/no-late-current-target-access': [2],
|
||||
'unicorn/no-late-event-control': [2],
|
||||
'unicorn/no-lonely-if': [2],
|
||||
'unicorn/no-loop-iterable-mutation': [2],
|
||||
'unicorn/no-magic-array-flat-depth': [0],
|
||||
@@ -852,9 +843,11 @@ export default defineConfig([
|
||||
'unicorn/no-uncalled-method': [2],
|
||||
'unicorn/no-undeclared-class-members': [2],
|
||||
'unicorn/no-unnecessary-array-flat-depth': [2],
|
||||
'unicorn/no-unnecessary-array-flat-map': [2],
|
||||
'unicorn/no-unnecessary-array-splice-count': [2],
|
||||
'unicorn/no-unnecessary-await': [2],
|
||||
'unicorn/no-unnecessary-boolean-comparison': [2],
|
||||
'unicorn/no-unnecessary-fetch-options': [0],
|
||||
'unicorn/no-unnecessary-global-this': [0],
|
||||
'unicorn/no-unnecessary-nested-ternary': [2],
|
||||
'unicorn/no-unnecessary-polyfills': [2],
|
||||
@@ -867,6 +860,7 @@ export default defineConfig([
|
||||
'unicorn/no-unreadable-object-destructuring': [0],
|
||||
'unicorn/no-unsafe-buffer-conversion': [2],
|
||||
'unicorn/no-unsafe-dom-html': [0],
|
||||
'unicorn/no-unsafe-promise-all-settled-values': [2],
|
||||
'unicorn/no-unsafe-property-key': [0],
|
||||
'unicorn/no-unsafe-string-replacement': [0],
|
||||
'unicorn/no-unused-array-method-return': [2],
|
||||
@@ -896,13 +890,17 @@ export default defineConfig([
|
||||
'unicorn/number-literal-case': [0],
|
||||
'unicorn/numeric-separators-style': [0],
|
||||
'unicorn/operator-assignment': [2],
|
||||
'unicorn/prefer-abort-signal-any': [2],
|
||||
'unicorn/prefer-abort-signal-timeout': [2],
|
||||
'unicorn/prefer-add-event-listener': [2],
|
||||
'unicorn/prefer-add-event-listener-options': [2],
|
||||
'unicorn/prefer-aggregate-error': [2],
|
||||
'unicorn/prefer-array-find': [0], // handled by @typescript-eslint/prefer-find
|
||||
'unicorn/prefer-array-flat': [2],
|
||||
'unicorn/prefer-array-flat-map': [2],
|
||||
'unicorn/prefer-array-from-async': [2],
|
||||
'unicorn/prefer-array-from-map': [2],
|
||||
'unicorn/prefer-array-from-range': [2],
|
||||
'unicorn/prefer-array-index-of': [2],
|
||||
'unicorn/prefer-array-iterable-methods': [2],
|
||||
'unicorn/prefer-array-last-methods': [2],
|
||||
@@ -912,6 +910,7 @@ export default defineConfig([
|
||||
'unicorn/prefer-await': [2],
|
||||
'unicorn/prefer-bigint-literals': [2],
|
||||
'unicorn/prefer-blob-reading-methods': [2],
|
||||
'unicorn/prefer-block-statement-over-iife': [2],
|
||||
'unicorn/prefer-boolean-return': [2],
|
||||
'unicorn/prefer-class-fields': [2],
|
||||
'unicorn/prefer-classlist-toggle': [2],
|
||||
@@ -924,15 +923,18 @@ export default defineConfig([
|
||||
'unicorn/prefer-dom-node-append': [2],
|
||||
'unicorn/prefer-dom-node-html-methods': [0],
|
||||
'unicorn/prefer-dom-node-remove': [2],
|
||||
'unicorn/prefer-dom-node-replace-children': [2],
|
||||
'unicorn/prefer-dom-node-text-content': [2],
|
||||
'unicorn/prefer-early-return': [0],
|
||||
'unicorn/prefer-else-if': [2],
|
||||
'unicorn/prefer-error-is-error': [0],
|
||||
'unicorn/prefer-event-target': [2],
|
||||
'unicorn/prefer-export-from': [0],
|
||||
'unicorn/prefer-flat-math-min-max': [2],
|
||||
'unicorn/prefer-get-or-insert-computed': [2],
|
||||
'unicorn/prefer-global-number-constants': [2],
|
||||
'unicorn/prefer-global-this': [0],
|
||||
'unicorn/prefer-group-by': [2],
|
||||
'unicorn/prefer-has-check': [2],
|
||||
'unicorn/prefer-hoisting-branch-code': [2],
|
||||
'unicorn/prefer-https': [0], // false-positives on namespace and schema URIs
|
||||
@@ -942,6 +944,7 @@ export default defineConfig([
|
||||
'unicorn/prefer-includes-over-repeated-comparisons': [0], // too opinionated
|
||||
'unicorn/prefer-iterable-in-constructor': [2],
|
||||
'unicorn/prefer-iterator-concat': [0], // too opinionated
|
||||
'unicorn/prefer-iterator-helpers': [2],
|
||||
'unicorn/prefer-iterator-to-array': [2],
|
||||
'unicorn/prefer-iterator-to-array-at-end': [2],
|
||||
'unicorn/prefer-keyboard-event-key': [2],
|
||||
@@ -966,9 +969,11 @@ export default defineConfig([
|
||||
'unicorn/prefer-object-destructuring-defaults': [2],
|
||||
'unicorn/prefer-object-from-entries': [2],
|
||||
'unicorn/prefer-object-iterable-methods': [2],
|
||||
'unicorn/prefer-observer-apis': [2],
|
||||
'unicorn/prefer-optional-catch-binding': [2],
|
||||
'unicorn/prefer-path2d': [2],
|
||||
'unicorn/prefer-private-class-fields': [0],
|
||||
'unicorn/prefer-promise-try': [2],
|
||||
'unicorn/prefer-promise-with-resolvers': [2],
|
||||
'unicorn/prefer-prototype-methods': [0],
|
||||
'unicorn/prefer-query-selector': [2],
|
||||
@@ -979,10 +984,12 @@ export default defineConfig([
|
||||
'unicorn/prefer-response-static-json': [2],
|
||||
'unicorn/prefer-scoped-selector': [0],
|
||||
'unicorn/prefer-set-has': [0],
|
||||
'unicorn/prefer-set-methods': [0],
|
||||
'unicorn/prefer-set-size': [2],
|
||||
'unicorn/prefer-short-arrow-method': [2],
|
||||
'unicorn/prefer-simple-condition-first': [0],
|
||||
'unicorn/prefer-simple-sort-comparator': [2],
|
||||
'unicorn/prefer-simplified-conditions': [2],
|
||||
'unicorn/prefer-single-array-predicate': [2],
|
||||
'unicorn/prefer-single-call': [2],
|
||||
'unicorn/prefer-single-object-destructuring': [2],
|
||||
@@ -1002,6 +1009,7 @@ export default defineConfig([
|
||||
'unicorn/prefer-switch': [0],
|
||||
'unicorn/prefer-temporal': [0],
|
||||
'unicorn/prefer-ternary': [0],
|
||||
'unicorn/prefer-toggle-attribute': [2],
|
||||
'unicorn/prefer-top-level-await': [0],
|
||||
'unicorn/prefer-type-error': [0],
|
||||
'unicorn/prefer-type-literal-last': [0],
|
||||
@@ -1010,6 +1018,7 @@ export default defineConfig([
|
||||
'unicorn/prefer-unicode-code-point-escapes': [0],
|
||||
'unicorn/prefer-url-can-parse': [2],
|
||||
'unicorn/prefer-url-href': [2],
|
||||
'unicorn/prefer-url-search-parameters': [2],
|
||||
'unicorn/prefer-while-loop-condition': [2],
|
||||
'unicorn/prevent-abbreviations': [0],
|
||||
'unicorn/relative-url-style': [2],
|
||||
|
||||
Generated
+3
-3
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1776877367,
|
||||
"narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=",
|
||||
"lastModified": 1782723713,
|
||||
"narHash": "sha256-oPXCU/SSUokcGaJREHibG1CBX3+s/W7orDWQOZDsEeQ=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0726a0ecb6d4e08f6adced58726b95db924cef57",
|
||||
"rev": "b5aa0fbd538984f6e3d201be0005b4463d8b09f8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
# only bump toolchain versions here
|
||||
go = pkgs.go_1_26;
|
||||
nodejs = pkgs.nodejs_24;
|
||||
nodejs = pkgs.nodejs_26;
|
||||
python3 = pkgs.python314;
|
||||
pnpm = pkgs.pnpm_10;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ require (
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||
gitea.dev/actions-proto-go v0.6.0
|
||||
gitea.dev/sdk v1.1.0
|
||||
gitea.dev/sdk v1.2.0
|
||||
github.com/42wim/httpsig v1.2.4
|
||||
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0
|
||||
@@ -24,8 +24,8 @@ require (
|
||||
github.com/PuerkitoBio/goquery v1.12.0
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.10.0
|
||||
github.com/alecthomas/chroma/v2 v2.27.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.24
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.4
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.25
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.5
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/blevesearch/bleve/v2 v2.6.0
|
||||
github.com/bohde/codel v0.2.0
|
||||
@@ -68,8 +68,8 @@ require (
|
||||
github.com/huandu/xstrings v1.5.0
|
||||
github.com/jhillyerd/enmime/v2 v2.4.1
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/compress v1.18.6
|
||||
github.com/klauspost/cpuid/v2 v2.3.0
|
||||
github.com/klauspost/compress v1.19.0
|
||||
github.com/klauspost/cpuid/v2 v2.4.0
|
||||
github.com/lib/pq v1.12.3
|
||||
github.com/markbates/goth v1.82.0
|
||||
github.com/mattn/go-isatty v0.0.22
|
||||
@@ -78,7 +78,7 @@ require (
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/microsoft/go-mssqldb v1.10.0
|
||||
github.com/minio/minio-go/v7 v7.2.0
|
||||
github.com/minio/minio-go/v7 v7.2.1
|
||||
github.com/msteinert/pam/v2 v2.1.0
|
||||
github.com/niklasfasching/go-org v1.9.1
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
@@ -96,12 +96,12 @@ require (
|
||||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.15
|
||||
github.com/urfave/cli-docs/v3 v3.1.0
|
||||
github.com/urfave/cli/v3 v3.10.0
|
||||
github.com/urfave/cli/v3 v3.10.1
|
||||
github.com/wneessen/go-mail v0.7.3
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.8.2
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.42.0
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.44.0
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.5
|
||||
golang.org/x/crypto v0.53.0
|
||||
golang.org/x/image v0.43.0
|
||||
@@ -111,14 +111,14 @@ require (
|
||||
golang.org/x/sync v0.21.0
|
||||
golang.org/x/sys v0.46.0
|
||||
golang.org/x/text v0.38.0
|
||||
google.golang.org/grpc v1.81.1
|
||||
google.golang.org/grpc v1.82.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/ini.v1 v1.67.3
|
||||
modernc.org/sqlite v1.53.0
|
||||
mvdan.cc/xurls/v2 v2.6.0
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20260301104140-add494e31dab
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.3.11
|
||||
xorm.io/xorm v1.4.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -28,8 +28,8 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGq
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||
gitea.dev/actions-proto-go v0.6.0 h1:gjllYQ5vmwlkqOeofTQu5qKTZpmf7kWsafoHvoPCSzY=
|
||||
gitea.dev/actions-proto-go v0.6.0/go.mod h1:p4RX+D9oqiEEzzkPMXscw2CmaGuYFPWFc6xIOmDNDqs=
|
||||
gitea.dev/sdk v1.1.0 h1:wLlz03WkLEiXa2bQpO1JQBTlYf7tQI2neYtZK1kU+TE=
|
||||
gitea.dev/sdk v1.1.0/go.mod h1:Zfl+EZXdsGGCLkryDfsmvYrQo6GKMl4U3BJA8Beu+cs=
|
||||
gitea.dev/sdk v1.2.0 h1:avRtJl/nKCGispgSalo9czoZM9Rto1awnE0caNAoXGo=
|
||||
gitea.dev/sdk v1.2.0/go.mod h1:rfh5oNdIK24cbCREwIn1tqWKQW+IICXFGWJyebuOAOE=
|
||||
github.com/42wim/httpsig v1.2.4 h1:mI5bH0nm4xn7K18fo1K3okNDRq8CCJ0KbBYWyA6r8lU=
|
||||
github.com/42wim/httpsig v1.2.4/go.mod h1:yKsYfSyTBEohkPik224QPFylmzEBtda/kjyIAJjh3ps=
|
||||
github.com/42wim/sshsig v0.0.0-20260317195500-b9f38cf0d432 h1:3Fcz1QzlS7Jv4FT2KI3cHNSZL+KPN3dXxurn9f3YL/Y=
|
||||
@@ -94,14 +94,14 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.24 h1:2hQqYCV9yqyePQ9o6dCrZc/zO8U3TwPr9mIKlZnPu/I=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.24/go.mod h1:IDwpACtwqHLISdzfwUUNq4P9DsB/h5BLg4FwJPNfqFY=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.25 h1:TzPVjfUZ1hsKafvYE+DIzKXIik2KufQxsPHanlkttbo=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.25/go.mod h1:K4hw0buguVvtC74HnVfTRr0LzQQHAWPqJbBU9QGk2Pg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA=
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.4 h1:Uu+wqrOXozYYvaxcNIqjFsMTjoIJIZDN3R0f70ZIjyQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.4/go.mod h1:pYrBdL1tMTZO7PaKRsa1cTUB8HtQh3fFM3zJHGhTQcE=
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.5 h1:mY0qCJuWfbRxok5sRkGxehMGshSYAVIskDvPE4zIZwM=
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.34.5/go.mod h1:pYrBdL1tMTZO7PaKRsa1cTUB8HtQh3fFM3zJHGhTQcE=
|
||||
github.com/aws/smithy-go v1.27.2 h1:y9NPmSE6am6LjEFPfqHqG/jJk7AauQvhCJONKh7kpzk=
|
||||
github.com/aws/smithy-go v1.27.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
@@ -413,8 +413,6 @@ github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pw
|
||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
|
||||
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||
github.com/graph-gophers/graphql-go v1.10.2 h1:HXu6Wu5klCH4ALn1fQHVI20cjEIa4wftavHIgbLA4Fo=
|
||||
github.com/graph-gophers/graphql-go v1.10.2/go.mod h1:AsADheC4CCFwd8n1/QbkduTlHgYYMsRgtPihYVAlEsk=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -471,12 +469,12 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
|
||||
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
|
||||
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ=
|
||||
github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
||||
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
|
||||
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
@@ -534,8 +532,8 @@ github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI
|
||||
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.2.0 h1:RCJM0R1XOsRs+A3x3UCaf3ZYbByDaLjFeAi+YCQEPhs=
|
||||
github.com/minio/minio-go/v7 v7.2.0/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0=
|
||||
github.com/minio/minio-go/v7 v7.2.1 h1:PfBfwvKB/MmqyN8Vb1G9voWisaM9OrLv+WwOvMwS9Dw=
|
||||
github.com/minio/minio-go/v7 v7.2.1/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0=
|
||||
github.com/minio/minlz v1.1.1 h1:OGmft1V6AnI/Wme332U6bhG54nxEan+VFgkD7lat4KM=
|
||||
github.com/minio/minlz v1.1.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
@@ -709,8 +707,8 @@ github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
github.com/urfave/cli-docs/v3 v3.1.0 h1:Sa5xm19IpE5gpm6tZzXdfjdFxn67PnEsE4dpXF7vsKw=
|
||||
github.com/urfave/cli-docs/v3 v3.1.0/go.mod h1:59d+5Hz1h6GSGJ10cvcEkbIe3j233t4XDqI72UIx7to=
|
||||
github.com/urfave/cli/v3 v3.10.0 h1:0aU8yOObVDMkM13Cj4G+zb4P0PdeJMec65f81Ak1ioM=
|
||||
github.com/urfave/cli/v3 v3.10.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||
github.com/urfave/cli/v3 v3.10.1 h1:7Kx9H50hrHbRbyxgO1KP6/BcbiGRz0uYh5YyQ30JEEY=
|
||||
github.com/urfave/cli/v3 v3.10.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/wneessen/go-mail v0.7.3 h1:g3DravXC5SMlVdboFrQA8Jx95A8sOzoBeS5F+vzNRK0=
|
||||
github.com/wneessen/go-mail v0.7.3/go.mod h1:QGhBX0yNbc1J+Mkjcu7z2rpj4B4l+BmDY8gYznPC9sk=
|
||||
@@ -740,8 +738,8 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.42.0 h1:Bq5YIYgUJVbt4Hbh7ibBwNR4SNEafsyDVhIXl7dXDdg=
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.42.0/go.mod h1:SKUbKSS59KPt6WeGNJoYF8HDaf/rFMUSITlftj/HkLg=
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.44.0 h1:Vtv2WKC8p9BAygu5VCZlZUwDhTQ7UMlS3PErXyjUmeY=
|
||||
gitlab.com/gitlab-org/api/client-go/v2 v2.44.0/go.mod h1:pTbeBowtVA+0/ZExWEZYUGOrpu5qlRN5ZyOUf27BnVY=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||
@@ -898,8 +896,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad h1:45WmJvIV6C2+O/jjLkPUH+F3aOj/1miDoU2DD0+NWbg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
|
||||
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||
google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU=
|
||||
google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -964,5 +962,5 @@ strk.kbt.io/projects/go/libravatar v0.0.0-20260301104140-add494e31dab h1:3IZDVyI
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20260301104140-add494e31dab/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
|
||||
xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo=
|
||||
xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
xorm.io/xorm v1.3.11 h1:i4tlVUASogb0ZZFJHA7dZqoRU2pUpUsutnNdaOlFyMI=
|
||||
xorm.io/xorm v1.3.11/go.mod h1:cs0ePc8O4a0jD78cNvD+0VFwhqotTvLQZv372QsDw7Q=
|
||||
xorm.io/xorm v1.4.1 h1:m7QlNd0eBGb31IV4Q/ow0Du83rtdC1CiwlvJZGvYde8=
|
||||
xorm.io/xorm v1.4.1/go.mod h1:cs0ePc8O4a0jD78cNvD+0VFwhqotTvLQZv372QsDw7Q=
|
||||
|
||||
@@ -99,10 +99,6 @@ type FindRunJobOptions struct {
|
||||
UpdatedBefore timeutil.TimeStamp
|
||||
ConcurrencyGroup string
|
||||
OrderBy db.SearchOrderBy
|
||||
// AccessibleRepoIDsSubQuery, when non-nil, restricts results to the repo IDs selected by the
|
||||
// subquery (the caller's accessible repos). A nil value means no restriction. Using a subquery
|
||||
// instead of a materialized ID slice avoids exceeding DB parameter limits for large owners.
|
||||
AccessibleRepoIDsSubQuery *builder.Builder
|
||||
}
|
||||
|
||||
var JobOrderByMap = map[string]map[string]db.SearchOrderBy{
|
||||
@@ -136,9 +132,6 @@ func (opts FindRunJobOptions) ToConds() builder.Cond {
|
||||
}
|
||||
cond = cond.And(builder.Eq{"`action_run_job`.concurrency_group": opts.ConcurrencyGroup})
|
||||
}
|
||||
if opts.AccessibleRepoIDsSubQuery != nil {
|
||||
cond = cond.And(builder.In("`action_run_job`.repo_id", opts.AccessibleRepoIDsSubQuery))
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
|
||||
@@ -70,10 +70,6 @@ type FindRunOptions struct {
|
||||
Status []Status
|
||||
ConcurrencyGroup string
|
||||
CommitSHA string
|
||||
// AccessibleRepoIDsSubQuery, when non-nil, restricts results to the repo IDs selected by the
|
||||
// subquery (the caller's accessible repos). A nil value means no restriction. Using a subquery
|
||||
// instead of a materialized ID slice avoids exceeding DB parameter limits for large owners.
|
||||
AccessibleRepoIDsSubQuery *builder.Builder
|
||||
}
|
||||
|
||||
func (opts FindRunOptions) ToConds() builder.Cond {
|
||||
@@ -105,9 +101,6 @@ func (opts FindRunOptions) ToConds() builder.Cond {
|
||||
if opts.CommitSHA != "" {
|
||||
cond = cond.And(builder.Eq{"`action_run`.commit_sha": opts.CommitSHA})
|
||||
}
|
||||
if opts.AccessibleRepoIDsSubQuery != nil {
|
||||
cond = cond.And(builder.In("`action_run`.repo_id", opts.AccessibleRepoIDsSubQuery))
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
|
||||
@@ -36,18 +36,7 @@ import (
|
||||
|
||||
const ssh2keyStart = "---- BEGIN SSH2 PUBLIC KEY ----"
|
||||
|
||||
const (
|
||||
// the longest RSA key ssh-keygen allows to generate is 16384 bits (2048 bytes), we still relax the limit a little here
|
||||
maxKeyBinaryBytes = 4096
|
||||
maxKeyContentBase64Bytes = maxKeyBinaryBytes * 4 / 3
|
||||
maxKeyContentExtraBytes = 4 * 1024 // header, footer, comment
|
||||
maxKeyContentBytes = maxKeyContentBase64Bytes + maxKeyContentExtraBytes
|
||||
)
|
||||
|
||||
func extractTypeFromBase64Key(key string) (string, error) {
|
||||
if len(key) > maxKeyContentBase64Bytes {
|
||||
return "", util.NewInvalidArgumentErrorf("SSH public key base64 is too long")
|
||||
}
|
||||
b, err := base64.StdEncoding.DecodeString(key)
|
||||
if err != nil || len(b) < 4 {
|
||||
return "", fmt.Errorf("invalid key format: %w", err)
|
||||
@@ -63,10 +52,6 @@ func extractTypeFromBase64Key(key string) (string, error) {
|
||||
|
||||
// parseKeyString parses any key string in OpenSSH or SSH2 format to clean OpenSSH string (RFC4253).
|
||||
func parseKeyString(content string) (string, error) {
|
||||
if len(content) > maxKeyContentBytes {
|
||||
return "", util.NewInvalidArgumentErrorf("SSH public key content is too long")
|
||||
}
|
||||
|
||||
// remove whitespace at start and end
|
||||
content = strings.TrimSpace(content)
|
||||
|
||||
@@ -78,8 +63,6 @@ func parseKeyString(content string) (string, error) {
|
||||
// Transform all legal line endings to a single "\n".
|
||||
content = strings.NewReplacer("\r\n", "\n", "\r", "\n").Replace(content)
|
||||
|
||||
var b strings.Builder
|
||||
b.Grow(len(content))
|
||||
lines := strings.Split(content, "\n")
|
||||
continuationLine := false
|
||||
|
||||
@@ -91,10 +74,9 @@ func parseKeyString(content string) (string, error) {
|
||||
if continuationLine || strings.ContainsAny(line, ":-") {
|
||||
continuationLine = strings.HasSuffix(line, "\\")
|
||||
} else {
|
||||
b.WriteString(line)
|
||||
keyContent += line
|
||||
}
|
||||
}
|
||||
keyContent = b.String()
|
||||
|
||||
t, err := extractTypeFromBase64Key(keyContent)
|
||||
if err != nil {
|
||||
|
||||
@@ -473,20 +473,10 @@ func runErr(t *testing.T, stdin []byte, args ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicKeysAreExternallyManaged(t *testing.T) {
|
||||
func Test_PublicKeysAreExternallyManaged(t *testing.T) {
|
||||
key1 := unittest.AssertExistsAndLoadBean(t, &PublicKey{ID: 1})
|
||||
externals, err := PublicKeysAreExternallyManaged(t.Context(), []*PublicKey{key1})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, externals, 1)
|
||||
assert.False(t, externals[0])
|
||||
}
|
||||
|
||||
// TestCheckPublicKeyStringOversized tests if oversized SSH2 public key strings are rejected before triggering costly operations.
|
||||
func TestCheckPublicKeyStringOversized(t *testing.T) {
|
||||
_, err := parseKeyString(strings.Repeat("a", maxKeyContentBytes+1))
|
||||
assert.ErrorContains(t, err, "SSH public key content is too long")
|
||||
|
||||
content := "---- BEGIN SSH2 PUBLIC KEY ----\n" + strings.Repeat("a", maxKeyContentBase64Bytes+1) + "\n--- END SSH2 PUBLIC KEY ----"
|
||||
_, err = parseKeyString(content)
|
||||
assert.ErrorContains(t, err, "SSH public key base64 is too long")
|
||||
}
|
||||
|
||||
@@ -304,36 +304,6 @@ func (s AccessTokenScope) PublicOnly() (bool, error) {
|
||||
return bitmap.hasScope(AccessTokenScopePublicOnly)
|
||||
}
|
||||
|
||||
// CanCreateChildScope reports whether a request authenticated by this (parent) scope may mint a token
|
||||
// carrying the child scope. It rejects any grantable scope the parent does not hold, closing the
|
||||
// scope-escalation path. public-only is a restriction rather than a grantable permission, so it is
|
||||
// ignored here (a child may always be public-only); EnforcePublicOnlyFrom handles carrying it down.
|
||||
func (s AccessTokenScope) CanCreateChildScope(child AccessTokenScope) (bool, error) {
|
||||
requested := child.StringSlice()
|
||||
scopes := make([]AccessTokenScope, 0, len(requested))
|
||||
for _, sc := range requested {
|
||||
childScope := AccessTokenScope(sc)
|
||||
if childScope == AccessTokenScopePublicOnly {
|
||||
continue
|
||||
}
|
||||
scopes = append(scopes, childScope)
|
||||
}
|
||||
return s.HasScope(scopes...)
|
||||
}
|
||||
|
||||
// EnforcePublicOnlyFrom adds the public-only restriction to s when the authorizing parent scope is
|
||||
// public-only, so a public-only token cannot mint a child token that drops the restriction.
|
||||
func (s AccessTokenScope) EnforcePublicOnlyFrom(parent AccessTokenScope) (AccessTokenScope, error) {
|
||||
publicOnly, err := parent.PublicOnly()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !publicOnly {
|
||||
return s, nil
|
||||
}
|
||||
return AccessTokenScope(string(s) + "," + string(AccessTokenScopePublicOnly)).Normalize()
|
||||
}
|
||||
|
||||
// HasScope returns true if the string has the given scope
|
||||
func (s AccessTokenScope) HasScope(scopes ...AccessTokenScope) (bool, error) {
|
||||
bitmap, err := s.parse()
|
||||
|
||||
@@ -89,26 +89,3 @@ func TestAccessTokenScope_HasScope(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessTokenScope_EnforcePublicOnlyFrom(t *testing.T) {
|
||||
tests := []struct {
|
||||
in AccessTokenScope
|
||||
parent AccessTokenScope
|
||||
out AccessTokenScope
|
||||
}{
|
||||
// public-only parent forces the restriction onto the minted scope
|
||||
{"write:user", "write:user,public-only", "public-only,write:user"},
|
||||
// already public-only stays public-only
|
||||
{"public-only,read:user", "public-only", "public-only,read:user"},
|
||||
// non-public-only parent leaves the scope untouched
|
||||
{"write:user", "write:user", "write:user"},
|
||||
{"all", "all", "all"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(string(test.parent)+"->"+string(test.in), func(t *testing.T) {
|
||||
got, err := test.in.EnforcePublicOnlyFrom(test.parent)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.out, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,3 +195,18 @@ func HasTwoFactorOrWebAuthn(ctx context.Context, id int64) (bool, error) {
|
||||
}
|
||||
return HasWebAuthnRegistrationsByUID(ctx, id)
|
||||
}
|
||||
|
||||
// DisableTwoFactor removes every two-factor method of the given user atomically,
|
||||
// returning the number of TOTP records and WebAuthn credentials removed.
|
||||
// It is a no-op for a user that has no 2FA enrolled.
|
||||
func DisableTwoFactor(ctx context.Context, uid int64) (totp, webAuthn int64, err error) {
|
||||
err = db.WithTx(ctx, func(ctx context.Context) error {
|
||||
var e error
|
||||
if totp, e = db.GetEngine(ctx).Where("uid = ?", uid).Delete(&TwoFactor{}); e != nil {
|
||||
return e
|
||||
}
|
||||
webAuthn, e = db.GetEngine(ctx).Where("user_id = ?", uid).Delete(&WebAuthnCredential{})
|
||||
return e
|
||||
})
|
||||
return totp, webAuthn, err
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/models/unittest"
|
||||
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -45,3 +46,37 @@ func TestTwoFactorValidateAndConsumeTOTP(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestDisableTwoFactor(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
ctx := t.Context()
|
||||
|
||||
const uid = 1000 // a uid with no user/2FA fixtures
|
||||
|
||||
// Enroll TOTP and register a WebAuthn credential.
|
||||
tfa := &auth_model.TwoFactor{UID: uid}
|
||||
require.NoError(t, tfa.SetSecret("test-secret"))
|
||||
require.NoError(t, auth_model.NewTwoFactor(ctx, tfa))
|
||||
_, err := auth_model.CreateCredential(ctx, uid, "test-key", &webauthn.Credential{ID: []byte("test-cred-id")})
|
||||
require.NoError(t, err)
|
||||
|
||||
has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, uid)
|
||||
require.NoError(t, err)
|
||||
require.True(t, has)
|
||||
|
||||
// Both records are removed and counted separately.
|
||||
totp, webAuthn, err := auth_model.DisableTwoFactor(ctx, uid)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 1, totp)
|
||||
assert.EqualValues(t, 1, webAuthn)
|
||||
|
||||
has, err = auth_model.HasTwoFactorOrWebAuthn(ctx, uid)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
|
||||
// A second call on a user without 2FA is a no-op.
|
||||
totp, webAuthn, err = auth_model.DisableTwoFactor(ctx, uid)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 0, totp)
|
||||
assert.EqualValues(t, 0, webAuthn)
|
||||
}
|
||||
|
||||
+30
-25
@@ -27,7 +27,6 @@ import (
|
||||
"gitea.dev/modules/markup"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/references"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/structs"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/translation"
|
||||
@@ -627,18 +626,11 @@ func UpdateCommentAttachments(ctx context.Context, c *Comment, uuids []string) e
|
||||
return nil
|
||||
}
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
issue, err := GetIssueByID(ctx, c.IssueID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
|
||||
}
|
||||
for i := range attachments {
|
||||
if err := validateAttachmentForIssue(ctx, issue, attachments[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
attachments[i].IssueID = c.IssueID
|
||||
attachments[i].CommentID = c.ID
|
||||
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
|
||||
@@ -651,18 +643,36 @@ func UpdateCommentAttachments(ctx context.Context, c *Comment, uuids []string) e
|
||||
}
|
||||
|
||||
// LoadAssigneeUserAndTeam if comment.Type is CommentTypeAssignees, then load assignees
|
||||
func (c *Comment) LoadAssigneeUserAndTeam(ctx context.Context) (err error) {
|
||||
func (c *Comment) LoadAssigneeUserAndTeam(ctx context.Context) error {
|
||||
var err error
|
||||
|
||||
if c.AssigneeID > 0 && c.Assignee == nil {
|
||||
_, c.Assignee, err = user_model.GetPossibleUserByID(ctx, c.AssigneeID)
|
||||
c.Assignee, err = user_model.GetUserByID(ctx, c.AssigneeID)
|
||||
if err != nil {
|
||||
if !user_model.IsErrUserNotExist(err) {
|
||||
return err
|
||||
}
|
||||
c.Assignee = user_model.NewGhostUser()
|
||||
}
|
||||
} else if c.AssigneeTeamID > 0 && c.AssigneeTeam == nil {
|
||||
if err = c.LoadIssue(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.AssigneeTeamID > 0 && c.AssigneeTeam == nil {
|
||||
_, c.AssigneeTeam, err = organization.GetPossibleTeamByID(ctx, c.AssigneeTeamID)
|
||||
if err != nil {
|
||||
|
||||
if err = c.Issue.LoadRepo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.Issue.Repo.LoadOwner(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Issue.Repo.Owner.IsOrganization() {
|
||||
c.AssigneeTeam, err = organization.GetTeamByID(ctx, c.AssigneeTeamID)
|
||||
if err != nil && !organization.IsErrTeamNotExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -785,7 +795,8 @@ func (c *Comment) MetaSpecialDoerTr(locale translation.Locale) template.HTML {
|
||||
}
|
||||
|
||||
func (c *Comment) TimelineRequestedReviewTr(locale translation.Locale, createdStr template.HTML) template.HTML {
|
||||
if c.Assignee != nil {
|
||||
if c.AssigneeID > 0 {
|
||||
// it guarantees LoadAssigneeUserAndTeam has been called, and c.Assignee is Ghost user but not nil if the user doesn't exist
|
||||
if c.RemovedAssignee {
|
||||
if c.PosterID == c.AssigneeID {
|
||||
return locale.Tr("repo.issues.review.remove_review_request_self", createdStr)
|
||||
@@ -794,20 +805,14 @@ func (c *Comment) TimelineRequestedReviewTr(locale translation.Locale, createdSt
|
||||
}
|
||||
return locale.Tr("repo.issues.review.add_review_request", c.Assignee.GetDisplayName(), createdStr)
|
||||
}
|
||||
teamName := "Ghost Team"
|
||||
if c.AssigneeTeam != nil {
|
||||
if c.RemovedAssignee {
|
||||
return locale.Tr("repo.issues.review.remove_review_request", c.AssigneeTeam.Name, createdStr)
|
||||
}
|
||||
return locale.Tr("repo.issues.review.add_review_request", c.AssigneeTeam.Name, createdStr)
|
||||
teamName = c.AssigneeTeam.Name
|
||||
}
|
||||
|
||||
// impossible fallback
|
||||
assigneePrompt := fmt.Sprintf("(AssigneeID=%d, AssigneeTeamID=%d)", c.AssigneeID, c.AssigneeTeam.ID)
|
||||
setting.PanicInDevOrTesting("unknown timeline pull request review event comment: id=%d, %s", c.ID, assigneePrompt)
|
||||
if c.RemovedAssignee {
|
||||
return locale.Tr("repo.issues.review.remove_review_request", assigneePrompt, createdStr)
|
||||
return locale.Tr("repo.issues.review.remove_review_request", teamName, createdStr)
|
||||
}
|
||||
return locale.Tr("repo.issues.review.add_review_request", assigneePrompt, createdStr)
|
||||
return locale.Tr("repo.issues.review.add_review_request", teamName, createdStr)
|
||||
}
|
||||
|
||||
// CreateComment creates comment with context
|
||||
|
||||
@@ -45,27 +45,12 @@ func TestCreateComment(t *testing.T) {
|
||||
unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
|
||||
}
|
||||
|
||||
func TestLoadAssigneeUserAndTeam_DeletedTeamBecomesGhostTeam(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 15})
|
||||
comment := &issues_model.Comment{
|
||||
Type: issues_model.CommentTypeAssignees,
|
||||
IssueID: issue.ID,
|
||||
AssigneeTeamID: 999999, // non-existing team ID
|
||||
}
|
||||
assert.NoError(t, comment.LoadAssigneeUserAndTeam(t.Context()))
|
||||
assert.NotNil(t, comment.AssigneeTeam)
|
||||
assert.EqualValues(t, -1, comment.AssigneeTeam.ID)
|
||||
}
|
||||
|
||||
func Test_UpdateCommentAttachment(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1})
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
|
||||
attachment := repo_model.Attachment{
|
||||
RepoID: issue.RepoID, // must match the comment's repo, else the cross-repo guard rejects it
|
||||
Name: "test.txt",
|
||||
Name: "test.txt",
|
||||
}
|
||||
assert.NoError(t, db.Insert(t.Context(), &attachment))
|
||||
|
||||
|
||||
@@ -263,46 +263,14 @@ func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo *
|
||||
return err
|
||||
}
|
||||
|
||||
// validateAttachmentForIssue rejects a foreign or already-linked attachment before it is linked to
|
||||
// issue: a known UUID could otherwise re-link (and expose) another repo's private attachment. A
|
||||
// legacy attachment predating repo_id-on-upload is adopted into the issue's repo.
|
||||
func validateAttachmentForIssue(ctx context.Context, issue *Issue, attachment *repo_model.Attachment) error {
|
||||
if attachment.RepoID == 0 && attachment.CreatedUnix < repo_model.LegacyAttachmentMissingRepoIDCutoff {
|
||||
attachment.RepoID = issue.RepoID
|
||||
if err := repo_model.UpdateAttachmentByUUID(ctx, attachment, "repo_id"); err != nil {
|
||||
return fmt.Errorf("update attachment repo_id [id: %d]: %w", attachment.ID, err)
|
||||
}
|
||||
}
|
||||
if attachment.RepoID != issue.RepoID {
|
||||
return util.NewPermissionDeniedErrorf("attachment belongs to a different repository")
|
||||
}
|
||||
if attachment.IssueID != 0 && attachment.IssueID != issue.ID {
|
||||
return util.NewPermissionDeniedErrorf("attachment is already linked to another issue")
|
||||
}
|
||||
if attachment.ReleaseID != 0 {
|
||||
return util.NewPermissionDeniedErrorf("attachment is already linked to a release")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateIssueAttachments update attachments by UUIDs for the issue
|
||||
func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string) (err error) {
|
||||
if len(uuids) == 0 {
|
||||
return nil
|
||||
}
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
issue, err := GetIssueByID(ctx, issueID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
|
||||
}
|
||||
for i := range attachments {
|
||||
if err := validateAttachmentForIssue(ctx, issue, attachments[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
attachments[i].IssueID = issueID
|
||||
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
|
||||
return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package issues_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
issues_model "gitea.dev/models/issues"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unittest"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUpdateIssueAttachmentsCrossRepo(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// attachment id 2 belongs to repo 2 / issue 4; issue 1 lives in repo 1
|
||||
issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
|
||||
foreign := unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: 2})
|
||||
require.NotEqual(t, issue1.RepoID, foreign.RepoID)
|
||||
|
||||
// re-linking a foreign repo's attachment by UUID must be rejected
|
||||
err := issues_model.UpdateIssueAttachments(t.Context(), issue1.ID, []string{foreign.UUID})
|
||||
assert.ErrorIs(t, err, util.ErrPermissionDenied)
|
||||
|
||||
// the foreign attachment must be left untouched
|
||||
reloaded := unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: 2})
|
||||
assert.Equal(t, foreign.IssueID, reloaded.IssueID)
|
||||
}
|
||||
+6
-13
@@ -6,7 +6,6 @@ package issues
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -28,6 +27,12 @@ type ErrRepoLabelNotExist struct {
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
// IsErrRepoLabelNotExist checks if an error is a RepoErrLabelNotExist.
|
||||
func IsErrRepoLabelNotExist(err error) bool {
|
||||
_, ok := err.(ErrRepoLabelNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrRepoLabelNotExist) Error() string {
|
||||
return fmt.Sprintf("label does not exist [label_id: %d, repo_id: %d]", err.LabelID, err.RepoID)
|
||||
}
|
||||
@@ -307,18 +312,6 @@ func GetLabelInRepoByName(ctx context.Context, repoID int64, labelName string) (
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// GetLabelInRepoOrOrgByID returns the label with labelID scoped to the repo, falling back to the
|
||||
// repo's owning organization when ownerIsOrg is set. It returns ErrRepoLabelNotExist /
|
||||
// ErrOrgLabelNotExist when the label is in neither scope, so a foreign-but-existing label ID is
|
||||
// indistinguishable from a nonexistent one (no cross-repo enumeration oracle).
|
||||
func GetLabelInRepoOrOrgByID(ctx context.Context, repoID, ownerID int64, ownerIsOrg bool, labelID int64) (*Label, error) {
|
||||
label, err := GetLabelInRepoByID(ctx, repoID, labelID)
|
||||
if err != nil && errors.Is(err, util.ErrNotExist) && ownerIsOrg {
|
||||
return GetLabelInOrgByID(ctx, ownerID, labelID)
|
||||
}
|
||||
return label, err
|
||||
}
|
||||
|
||||
// GetLabelInRepoByID returns a label by ID in given repository.
|
||||
func GetLabelInRepoByID(ctx context.Context, repoID, labelID int64) (*Label, error) {
|
||||
if labelID <= 0 || repoID <= 0 {
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -95,10 +94,10 @@ func TestGetLabelInRepoByName(t *testing.T) {
|
||||
assert.Equal(t, "label1", label.Name)
|
||||
|
||||
_, err = issues_model.GetLabelInRepoByName(t.Context(), 1, "")
|
||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||
assert.True(t, issues_model.IsErrRepoLabelNotExist(err))
|
||||
|
||||
_, err = issues_model.GetLabelInRepoByName(t.Context(), unittest.NonexistentID, "nonexistent")
|
||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||
assert.True(t, issues_model.IsErrRepoLabelNotExist(err))
|
||||
}
|
||||
|
||||
func TestGetLabelInRepoByNames(t *testing.T) {
|
||||
@@ -132,10 +131,10 @@ func TestGetLabelInRepoByID(t *testing.T) {
|
||||
assert.EqualValues(t, 1, label.ID)
|
||||
|
||||
_, err = issues_model.GetLabelInRepoByID(t.Context(), 1, -1)
|
||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||
assert.True(t, issues_model.IsErrRepoLabelNotExist(err))
|
||||
|
||||
_, err = issues_model.GetLabelInRepoByID(t.Context(), unittest.NonexistentID, unittest.NonexistentID)
|
||||
assert.ErrorIs(t, err, util.ErrNotExist)
|
||||
assert.True(t, issues_model.IsErrRepoLabelNotExist(err))
|
||||
}
|
||||
|
||||
func TestGetLabelsInRepoByIDs(t *testing.T) {
|
||||
|
||||
@@ -6,7 +6,6 @@ package organization
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -93,15 +92,6 @@ func (t *Team) IsPublic() bool { return t.Visibility.IsPublic() }
|
||||
func (t *Team) IsLimited() bool { return t.Visibility.IsLimited() }
|
||||
func (t *Team) IsPrivate() bool { return t.Visibility.IsPrivate() }
|
||||
|
||||
const (
|
||||
ghostTeamID = -1
|
||||
ghostTeamName = "(deleted team)"
|
||||
)
|
||||
|
||||
func newGhostTeam() *Team {
|
||||
return &Team{ID: ghostTeamID, Name: ghostTeamName, LowerName: ghostTeamName}
|
||||
}
|
||||
|
||||
// CanNonMemberReadMeta reports whether a non-member, non-owner doer may read
|
||||
// the team's metadata, based on the team's visibility tier and the parent org's
|
||||
// visibility. Privileged callers (site admins, org owners, team members) are
|
||||
@@ -280,17 +270,6 @@ func GetTeamByID(ctx context.Context, teamID int64) (*Team, error) {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func GetPossibleTeamByID(ctx context.Context, teamID int64) (int64, *Team, error) {
|
||||
t, err := GetTeamByID(ctx, teamID)
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
t = newGhostTeam()
|
||||
return t.ID, t, nil
|
||||
} else if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return t.ID, t, nil
|
||||
}
|
||||
|
||||
// IncrTeamRepoNum increases the number of repos for the given team by 1
|
||||
func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
|
||||
_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
|
||||
|
||||
@@ -310,17 +310,11 @@ func userOrgTeamRepoBuilder(userID int64) *builder.Builder {
|
||||
}
|
||||
|
||||
// userOrgTeamUnitRepoBuilder returns repo ids where user's teams can access the special unit.
|
||||
// A team grants the unit either through an explicit team_unit row (access_mode > none) or by being an
|
||||
// admin/owner team (team.authorize >= admin), which grants every unit regardless of team_unit rows —
|
||||
// mirroring the HasAdminAccess() short-circuit in access.GetIndividualUserRepoPermission.
|
||||
func userOrgTeamUnitRepoBuilder(userID int64, unitType unit.Type) *builder.Builder {
|
||||
return userOrgTeamRepoBuilder(userID).
|
||||
Join("INNER", "team", "`team`.id = `team_repo`.team_id").
|
||||
Join("LEFT", "team_unit", builder.Expr("`team_unit`.team_id = `team_repo`.team_id AND `team_unit`.`type` = ?", unitType)).
|
||||
Where(builder.Or(
|
||||
builder.Gte{"`team`.authorize": int(perm.AccessModeAdmin)},
|
||||
builder.Gt{"`team_unit`.`access_mode`": int(perm.AccessModeNone)},
|
||||
))
|
||||
Join("INNER", "team_unit", "`team_unit`.team_id = `team_repo`.team_id").
|
||||
Where(builder.Eq{"`team_unit`.`type`": unitType}).
|
||||
And(builder.Gt{"`team_unit`.`access_mode`": int(perm.AccessModeNone)})
|
||||
}
|
||||
|
||||
// userOrgTeamUnitRepoCond returns a condition to select repo ids where user's teams can access the special unit.
|
||||
@@ -332,7 +326,7 @@ func userOrgTeamUnitRepoCond(idStr string, userID int64, unitType unit.Type) bui
|
||||
func UserOrgUnitRepoCond(idStr string, userID, orgID int64, unitType unit.Type) builder.Cond {
|
||||
return builder.In(idStr,
|
||||
userOrgTeamUnitRepoBuilder(userID, unitType).
|
||||
And(builder.Eq{"`team`.org_id": orgID}),
|
||||
And(builder.Eq{"`team_unit`.org_id": orgID}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -761,40 +755,6 @@ func FindUserCodeAccessibleOwnerRepoIDs(ctx context.Context, ownerID int64, user
|
||||
))
|
||||
}
|
||||
|
||||
// PublicRepoUnderPublicOwnerCond restricts to public repos whose owner is publicly visible: the
|
||||
// "genuinely public" set a public-only token or an anonymous caller may see (a public repo under a
|
||||
// limited/private owner is not publicly reachable and must be excluded).
|
||||
func PublicRepoUnderPublicOwnerCond() builder.Cond {
|
||||
return builder.And(
|
||||
builder.Eq{"`repository`.is_private": false},
|
||||
builder.In("`repository`.owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"visibility": structs.VisibleTypePublic})),
|
||||
)
|
||||
}
|
||||
|
||||
// UserActionsAccessibleOwnerRepoCond selects the repos owned by ownerID whose Actions `user` may read.
|
||||
// It is used to list an org/user's Actions runs and jobs (see the callers in routers/api/v1/shared).
|
||||
// - owner_id = ownerID: only that owner's repos.
|
||||
// - AccessibleRepositoryCondition(user, TypeActions): only repos whose Actions the user can read
|
||||
// (admin/owner teams are handled inside it; a site admin is not, callers must skip the filter for one).
|
||||
// - publicOnly (a public-only token): additionally limit to public repos under a public owner.
|
||||
func UserActionsAccessibleOwnerRepoCond(ownerID int64, user *user_model.User, publicOnly bool) builder.Cond {
|
||||
cond := builder.NewCond().And(
|
||||
builder.Eq{"`repository`.owner_id": ownerID},
|
||||
AccessibleRepositoryCondition(user, unit.TypeActions),
|
||||
)
|
||||
if publicOnly {
|
||||
cond = cond.And(PublicRepoUnderPublicOwnerCond())
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
// FindUserActionsAccessibleOwnerRepoIDsSubQuery returns a subquery selecting the repository IDs the user
|
||||
// can see for the given owner. Callers embed it in an `IN (...)` condition so that a large owner does not
|
||||
// materialize every repo ID into the SQL statement, which could exceed database parameter limits.
|
||||
func FindUserActionsAccessibleOwnerRepoIDsSubQuery(ownerID int64, user *user_model.User, publicOnly bool) *builder.Builder {
|
||||
return builder.Select("id").From("repository").Where(UserActionsAccessibleOwnerRepoCond(ownerID, user, publicOnly))
|
||||
}
|
||||
|
||||
// GetUserRepositories returns a list of repositories of given user.
|
||||
func GetUserRepositories(ctx context.Context, opts SearchRepoOptions) (RepositoryList, int64, error) {
|
||||
if len(opts.OrderBy) == 0 {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
"gitea.dev/models/db"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/optional"
|
||||
@@ -467,50 +466,3 @@ func TestSearchRepositoryByTopicName(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindUserActionsAccessibleOwnerRepoIDs(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
// user2 is on org3's owner team, so it can access org3's private repo3 (which has the actions unit)
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
// org3 is a public org owning repo3 (private) and repo32 (public), both with the actions unit
|
||||
const orgID = 3
|
||||
|
||||
all, err := repo_model.SearchRepositoryIDsByCondition(t.Context(), repo_model.UserActionsAccessibleOwnerRepoCond(orgID, user, false))
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, all, int64(3), "without public-only the private repo's actions are listed")
|
||||
|
||||
publicOnly, err := repo_model.SearchRepositoryIDsByCondition(t.Context(), repo_model.UserActionsAccessibleOwnerRepoCond(orgID, user, true))
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, publicOnly, int64(3), "a public-only token must not list a private repo's actions")
|
||||
assert.Contains(t, publicOnly, int64(32), "a public repo under a public owner stays listed")
|
||||
}
|
||||
|
||||
// TestUserOrgUnitRepoCondTeamAuthorize pins the team.authorize behavior of userOrgTeamUnitRepoBuilder
|
||||
// (exercised through UserOrgUnitRepoCond): an admin/owner team grants every unit even without an explicit
|
||||
// team_unit row, while a non-admin team only grants a unit it has an explicit row for. This guards both
|
||||
// directions — hiding repos from admin-team members, and over-broadening a plain team's access.
|
||||
func TestUserOrgUnitRepoCondTeamAuthorize(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
accessibleRepoIDs := func(userID, orgID int64, unitType unit.Type) []int64 {
|
||||
ids, err := repo_model.SearchRepositoryIDsByCondition(t.Context(),
|
||||
repo_model.UserOrgUnitRepoCond("`repository`.id", userID, orgID, unitType))
|
||||
require.NoError(t, err)
|
||||
return ids
|
||||
}
|
||||
|
||||
// Case A: user18 is only on org17's owner team (team5, authorize=owner), linked to the private repo24
|
||||
// but with no Actions team_unit row. The owner authorize must still grant it, mirroring the runtime
|
||||
// HasAdminAccess() short-circuit in access.GetIndividualUserRepoPermission.
|
||||
assert.Contains(t, accessibleRepoIDs(18, 17, unit.TypeActions), int64(24),
|
||||
"an owner team grants a unit it has no explicit team_unit row for")
|
||||
|
||||
// Cases B and C share one subject so the team_unit row is the only difference: user4 is only on org3's
|
||||
// write team (team2, authorize=write, non-admin), linked to the private repo3. team2 has an explicit
|
||||
// Projects row but none for Actions.
|
||||
assert.Contains(t, accessibleRepoIDs(4, 3, unit.TypeProjects), int64(3),
|
||||
"a non-admin team grants a unit it has an explicit team_unit row for")
|
||||
assert.NotContains(t, accessibleRepoIDs(4, 3, unit.TypeActions), int64(3),
|
||||
"a non-admin team must NOT grant a unit it has no team_unit row for")
|
||||
}
|
||||
|
||||
@@ -46,8 +46,9 @@ func (opts *StarredReposOptions) ToConds() builder.Cond {
|
||||
// only include private repos the actor can still access, so metadata does not leak after access revocation
|
||||
cond = cond.And(AccessibleRepositoryCondition(opts.Actor, unit.TypeInvalid))
|
||||
} else {
|
||||
// a public repo under a limited/private owner is not publicly reachable, so exclude it too
|
||||
cond = cond.And(PublicRepoUnderPublicOwnerCond())
|
||||
cond = cond.And(builder.Eq{
|
||||
"repository.is_private": false,
|
||||
})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
@@ -95,8 +96,9 @@ func (opts *WatchedReposOptions) ToConds() builder.Cond {
|
||||
// only include private repos the actor can still access, so metadata does not leak after access revocation
|
||||
cond = cond.And(AccessibleRepositoryCondition(opts.Actor, unit.TypeInvalid))
|
||||
} else {
|
||||
// a public repo under a limited/private owner is not publicly reachable, so exclude it too
|
||||
cond = cond.And(PublicRepoUnderPublicOwnerCond())
|
||||
cond = cond.And(builder.Eq{
|
||||
"repository.is_private": false,
|
||||
})
|
||||
}
|
||||
return cond.And(builder.Neq{
|
||||
"watch.mode": WatchModeDont,
|
||||
|
||||
@@ -84,40 +84,3 @@ func testUserRepoGetIssuePostersWithSearch(t *testing.T) {
|
||||
require.Len(t, users, 1)
|
||||
assert.Equal(t, "user2", users[0].Name)
|
||||
}
|
||||
|
||||
func TestStarredWatchedReposExcludeNonPublicOwners(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
const viewerID = 2
|
||||
// repo1: public repo under a public owner; repo38: public repo under a limited org (not publicly reachable)
|
||||
const publicOwnerRepo, limitedOwnerRepo = 1, 38
|
||||
|
||||
require.NoError(t, db.Insert(t.Context(), &repo_model.Star{UID: viewerID, RepoID: publicOwnerRepo}))
|
||||
require.NoError(t, db.Insert(t.Context(), &repo_model.Star{UID: viewerID, RepoID: limitedOwnerRepo}))
|
||||
require.NoError(t, db.Insert(t.Context(), &repo_model.Watch{UserID: viewerID, RepoID: publicOwnerRepo, Mode: repo_model.WatchModeNormal}))
|
||||
require.NoError(t, db.Insert(t.Context(), &repo_model.Watch{UserID: viewerID, RepoID: limitedOwnerRepo, Mode: repo_model.WatchModeNormal}))
|
||||
|
||||
listOpts := db.ListOptions{Page: 1, PageSize: 50}
|
||||
|
||||
starred, err := repo_model.GetStarredRepos(t.Context(), &repo_model.StarredReposOptions{
|
||||
ListOptions: listOpts, StarrerID: viewerID, IncludePrivate: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, repoIDs(starred), int64(limitedOwnerRepo), "a public repo under a limited owner must be hidden from a public star listing")
|
||||
assert.Contains(t, repoIDs(starred), int64(publicOwnerRepo), "a public repo under a public owner stays visible")
|
||||
|
||||
watched, _, err := repo_model.GetWatchedRepos(t.Context(), &repo_model.WatchedReposOptions{
|
||||
ListOptions: listOpts, WatcherID: viewerID, IncludePrivate: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, repoIDs(watched), int64(limitedOwnerRepo), "a public repo under a limited owner must be hidden from a public watch listing")
|
||||
assert.Contains(t, repoIDs(watched), int64(publicOwnerRepo), "a public repo under a public owner stays visible")
|
||||
}
|
||||
|
||||
func repoIDs(repos []*repo_model.Repository) []int64 {
|
||||
ids := make([]int64, len(repos))
|
||||
for i, r := range repos {
|
||||
ids[i] = r.ID
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
@@ -4,14 +4,8 @@
|
||||
package openid
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.dev/modules/hostmatcher"
|
||||
"gitea.dev/modules/proxy"
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/yohcop/openid-go"
|
||||
)
|
||||
|
||||
@@ -25,23 +19,11 @@ import (
|
||||
var (
|
||||
nonceStore = openid.NewSimpleNonceStore()
|
||||
discoveryCache = newTimedDiscoveryCache(24 * time.Hour)
|
||||
|
||||
// openIDInstance does discovery/verification via an SSRF-protected client, so a user-supplied
|
||||
// OpenID identifier can't reach internal/loopback/reserved addresses. It honors the operator's
|
||||
// [security] ALLOWED_HOST_LIST (empty defaults to "external"), matching the avatar/webhook/migration
|
||||
// clients, and validates the proxy path too. Lazy: reads proxy/settings once.
|
||||
openIDInstance = sync.OnceValue(func() *openid.OpenID {
|
||||
allowList := hostmatcher.ParseHostMatchList("security.ALLOWED_HOST_LIST", setting.Security.AllowedHostList)
|
||||
return openid.NewOpenID(&http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: hostmatcher.NewHTTPTransport("openid", allowList, nil, proxy.Proxy(), setting.Proxy.ProxyURLFixed, nil),
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
// Verify handles response from OpenID provider
|
||||
func Verify(fullURL string) (id string, err error) {
|
||||
return openIDInstance().Verify(fullURL, discoveryCache, nonceStore)
|
||||
return openid.Verify(fullURL, discoveryCache, nonceStore)
|
||||
}
|
||||
|
||||
// Normalize normalizes an OpenID URI
|
||||
@@ -51,5 +33,5 @@ func Normalize(url string) (id string, err error) {
|
||||
|
||||
// RedirectURL redirects browser
|
||||
func RedirectURL(id, callbackURL, realm string) (string, error) {
|
||||
return openIDInstance().RedirectURL(id, callbackURL, realm)
|
||||
return openid.RedirectURL(id, callbackURL, realm)
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package openid
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOpenIDDiscoveryBlocksInternalHost(t *testing.T) {
|
||||
var reached atomic.Bool
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
reached.Store(true)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
// RedirectURL performs server-side discovery of the identifier URL; a loopback URL
|
||||
// must be refused at dial time instead of reaching the internal server
|
||||
_, err := RedirectURL(srv.URL, "http://example.com/callback", "http://example.com/")
|
||||
require.Error(t, err)
|
||||
assert.False(t, reached.Load(), "OpenID discovery must not reach an internal/loopback host")
|
||||
}
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// sha1Pattern can be used to determine if a string is an valid sha
|
||||
// sha1Pattern can be used to determine if a string is a valid sha
|
||||
var sha1Pattern = regexp.MustCompile(`^[0-9a-f]{4,40}$`)
|
||||
|
||||
// sha256Pattern can be used to determine if a string is an valid sha
|
||||
// sha256Pattern can be used to determine if a string is a valid sha
|
||||
var sha256Pattern = regexp.MustCompile(`^[0-9a-f]{4,64}$`)
|
||||
|
||||
type ObjectFormat interface {
|
||||
|
||||
@@ -5,10 +5,8 @@ package hostmatcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -65,17 +63,3 @@ func NewDialContext(usage string, allowList, blockList *HostMatchList, proxy *ur
|
||||
return dialer.DialContext(ctx, network, addrOrHost)
|
||||
}
|
||||
}
|
||||
|
||||
// NewHTTPTransport builds an http.Transport that validates the request target against the allow/block
|
||||
// lists on the direct-dial path (DialContext). When an HTTP proxy is configured the proxy resolves and
|
||||
// dials the target itself, so restricting the proxied target is the proxy server's responsibility, not
|
||||
// Gitea's. proxyFunc selects the proxy URL per request (the http.Transport.Proxy selector, e.g.
|
||||
// proxy.Proxy()); proxyURLFixed is the fixed proxy address the dialer must always permit; tlsConfig may
|
||||
// be nil. blockList may be nil for callers that only maintain an allow-list.
|
||||
func NewHTTPTransport(usage string, allowList, blockList *HostMatchList, proxyFunc func(*http.Request) (*url.URL, error), proxyURLFixed *url.URL, tlsConfig *tls.Config) *http.Transport {
|
||||
return &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
Proxy: proxyFunc,
|
||||
DialContext: NewDialContext(usage, allowList, blockList, proxyURLFixed),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ func validateOptions(field *api.IssueFormField, idx int) error {
|
||||
|
||||
options, ok := field.Attributes["options"].([]any)
|
||||
if !ok || len(options) == 0 {
|
||||
return position.Errorf("'options' is required and should be a array")
|
||||
return position.Errorf("'options' is required and should be an array")
|
||||
}
|
||||
|
||||
for optIdx, option := range options {
|
||||
@@ -270,7 +270,7 @@ func validateDropdownDefault(position errorPosition, attributes map[string]any)
|
||||
options, ok := attributes["options"].([]any)
|
||||
if !ok {
|
||||
// should not happen
|
||||
return position.Errorf("'options' is required and should be a array")
|
||||
return position.Errorf("'options' is required and should be an array")
|
||||
}
|
||||
if defaultValue < 0 || defaultValue >= len(options) {
|
||||
return position.Errorf("the value of 'default' is out of range")
|
||||
|
||||
@@ -268,7 +268,7 @@ body:
|
||||
attributes:
|
||||
label: "a"
|
||||
`,
|
||||
wantErr: "body[0](dropdown): 'options' is required and should be a array",
|
||||
wantErr: "body[0](dropdown): 'options' is required and should be an array",
|
||||
},
|
||||
{
|
||||
name: "dropdown invalid options",
|
||||
|
||||
@@ -51,7 +51,7 @@ var globalVars = sync.OnceValue(func() *globalVarsType {
|
||||
v := &globalVarsType{}
|
||||
// NOTE: All below regex matching do not perform any extra validation.
|
||||
// Thus a link is produced even if the linked entity does not exist.
|
||||
// While fast, this is also incorrect and lead to false positives.
|
||||
// While fast, this is also incorrect and leads to false positives.
|
||||
// TODO: fix invalid linking issue (update: stale TODO, what issues? maybe no TODO anymore)
|
||||
|
||||
// valid chars in encoded path and parameter: [-+~_%.a-zA-Z0-9/]
|
||||
|
||||
@@ -54,7 +54,7 @@ func (st *Sanitizer) createDefaultPolicy() *bluemonday.Policy {
|
||||
// Allow 'color' and 'background-color' properties for the style attribute on text elements.
|
||||
policy.AllowStyles("color", "background-color").OnElements("div", "span", "p", "tr", "th", "td")
|
||||
|
||||
policy.AllowAttrs("src", "autoplay", "controls").OnElements("video")
|
||||
policy.AllowAttrs("src", "autoplay", "controls", "muted", "loop", "playsinline").OnElements("video")
|
||||
|
||||
// Native support of "<picture><source media=... srcset=...><img src=...></picture>"
|
||||
// ATTENTION: it only works with "auto" theme, because "media" query doesn't work with the theme chosen by end user manually.
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/modules/packages"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/modules/validation"
|
||||
|
||||
@@ -47,11 +46,6 @@ var (
|
||||
namePattern = regexp.MustCompile(`\A[a-zA-Z0-9@._+-]+\z`)
|
||||
// (epoch:pkgver-pkgrel)
|
||||
versionPattern = regexp.MustCompile(`\A(?:\d:)?[\w.+~]+(?:-[-\w.+~]+)?\z`)
|
||||
|
||||
// caps on the accumulated package file list (vars so tests can lower them); far above
|
||||
// any legitimate package, but low enough to stop metadata amplification
|
||||
maxFileEntries = 100000
|
||||
maxFileNameBytes = 16 * 1024 * 1024
|
||||
)
|
||||
|
||||
type Package struct {
|
||||
@@ -130,7 +124,7 @@ func ParsePackage(r io.Reader) (*Package, error) {
|
||||
}
|
||||
|
||||
var p *Package
|
||||
files := packages.NewBoundedFileList(maxFileEntries, maxFileNameBytes)
|
||||
files := make([]string, 0, 10)
|
||||
|
||||
tr := tar.NewReader(inner)
|
||||
for {
|
||||
@@ -153,9 +147,7 @@ func ParsePackage(r io.Reader) (*Package, error) {
|
||||
return nil, err
|
||||
}
|
||||
} else if !strings.HasPrefix(filename, ".") {
|
||||
if err := files.Add(hd.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, hd.Name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +155,7 @@ func ParsePackage(r io.Reader) (*Package, error) {
|
||||
return nil, ErrMissingPKGINFOFile
|
||||
}
|
||||
|
||||
p.FileMetadata.Files = files.Files()
|
||||
p.FileMetadata.Files = files
|
||||
p.FileCompressionExtension = compressionType
|
||||
|
||||
return p, nil
|
||||
|
||||
@@ -10,9 +10,6 @@ import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modules/test"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/ulikunitz/xz"
|
||||
@@ -170,25 +167,3 @@ func TestParsePackageInfo(t *testing.T) {
|
||||
assert.ElementsMatch(t, []string{"usr/bin/paket1"}, p.FileMetadata.Backup)
|
||||
})
|
||||
}
|
||||
|
||||
// TestParsePackageTooManyFiles ensures the accumulated file list is bounded to prevent
|
||||
// metadata amplification from a package with a huge number of (tiny) file entries.
|
||||
func TestParsePackageTooManyFiles(t *testing.T) {
|
||||
defer test.MockVariableValue(&maxFileEntries, 3)()
|
||||
buf := test.WriteTarCompression(func(w io.Writer) io.WriteCloser { return gzip.NewWriter(w) }, map[string]string{
|
||||
"file1": "content1",
|
||||
".PKGINFO": string(createPKGINFOContent(packageName, packageVersion)),
|
||||
})
|
||||
_, err := ParsePackage(buf)
|
||||
assert.NoError(t, err)
|
||||
|
||||
buf = test.WriteTarCompression(func(w io.Writer) io.WriteCloser { return gzip.NewWriter(w) }, map[string]string{
|
||||
"file1": "content1",
|
||||
"file2": "content2",
|
||||
"file3": "content3",
|
||||
"file4": "content4",
|
||||
".PKGINFO": string(createPKGINFOContent(packageName, packageVersion)),
|
||||
})
|
||||
_, err = ParsePackage(buf)
|
||||
assert.ErrorIs(t, err, util.ErrInvalidArgument)
|
||||
}
|
||||
|
||||
@@ -135,10 +135,7 @@ func ParsePackage(r io.Reader) (*Package, error) {
|
||||
return nil, GlobalVars().ErrUnsupportedCompression
|
||||
}
|
||||
|
||||
// bound the decompressed control archive: it holds only the small control file
|
||||
// and maintainer scripts, so a much larger stream is a decompression bomb
|
||||
const maxControlTarSize = 32 * 1024 * 1024
|
||||
tr := tar.NewReader(io.LimitReader(inner, maxControlTarSize))
|
||||
tr := tar.NewReader(inner)
|
||||
for {
|
||||
hd, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
@@ -171,7 +168,6 @@ func ParseControlFile(r io.Reader) (*Package, error) {
|
||||
key := ""
|
||||
var depends strings.Builder
|
||||
var control strings.Builder
|
||||
var description strings.Builder
|
||||
|
||||
// https://www.debian.org/doc/debian-policy/ch-controlfields.html#syntax-of-control-files
|
||||
s := bufio.NewScanner(r)
|
||||
@@ -193,13 +189,10 @@ func ParseControlFile(r io.Reader) (*Package, error) {
|
||||
control.WriteString(line)
|
||||
control.WriteByte('\n')
|
||||
|
||||
// a leading space or tab marks a folded continuation line that belongs to the previous field
|
||||
// (identified by key), not a new "Key: value" pair; only the multi-line fields append here.
|
||||
// Continuation lines may themselves contain a colon, so they must not be re-split on ":".
|
||||
if line[0] == ' ' || line[0] == '\t' {
|
||||
switch key {
|
||||
case "Description":
|
||||
description.WriteString(line)
|
||||
p.Metadata.Description += line
|
||||
case "Depends":
|
||||
depends.WriteString(trimmed)
|
||||
}
|
||||
@@ -226,8 +219,7 @@ func ParseControlFile(r io.Reader) (*Package, error) {
|
||||
p.Metadata.Maintainer = a.Name
|
||||
}
|
||||
case "Description":
|
||||
description.Reset()
|
||||
description.WriteString(value)
|
||||
p.Metadata.Description = value
|
||||
case "Depends":
|
||||
depends.WriteString(value)
|
||||
case "Homepage":
|
||||
@@ -251,8 +243,6 @@ func ParseControlFile(r io.Reader) (*Package, error) {
|
||||
return nil, GlobalVars().ErrInvalidArchitecture
|
||||
}
|
||||
|
||||
p.Metadata.Description = description.String()
|
||||
|
||||
dependencies := strings.Split(depends.String(), ",")
|
||||
for i := range dependencies {
|
||||
dependencies[i] = strings.TrimSpace(dependencies[i])
|
||||
|
||||
@@ -232,15 +232,3 @@ func TestValidateDistributionOrComponent(t *testing.T) {
|
||||
assert.True(t, IsValidDistributionOrComponent(name), "good=%q", name)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseControlFileMultilineDescription verifies a multi-line Description is assembled in order
|
||||
// (the parser accumulates it in a strings.Builder); it guards the assembled value, not its timing.
|
||||
func TestParseControlFileMultilineDescription(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("Package: testpkg\nVersion: 1.0\nArchitecture: amd64\nDescription: short summary\n more details\n even more\n")
|
||||
|
||||
p, err := ParseControlFile(&buf)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, p)
|
||||
assert.Equal(t, "short summary more details even more", p.Metadata.Description)
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package packages
|
||||
|
||||
import "gitea.dev/modules/util"
|
||||
|
||||
// BoundedFileList accumulates file names from a package archive while enforcing caps on the number of
|
||||
// entries and their total name length, returning an error once either cap would be exceeded.
|
||||
type BoundedFileList struct {
|
||||
files []string
|
||||
nameBytes int
|
||||
maxFiles int
|
||||
maxBytes int
|
||||
}
|
||||
|
||||
// NewBoundedFileList creates a BoundedFileList with the given caps; a non-positive cap falls back to the
|
||||
// corresponding default.
|
||||
func NewBoundedFileList(maxFiles, maxNameBytes int) *BoundedFileList {
|
||||
return &BoundedFileList{maxFiles: maxFiles, maxBytes: maxNameBytes}
|
||||
}
|
||||
|
||||
// Add appends name, returning util.ErrInvalidArgument once the entry count or accumulated byte length
|
||||
// would exceed the configured cap.
|
||||
func (b *BoundedFileList) Add(name string) error {
|
||||
if len(b.files) >= b.maxFiles || b.nameBytes+len(name) > b.maxBytes {
|
||||
return util.NewInvalidArgumentErrorf("package contains too many file entries")
|
||||
}
|
||||
b.nameBytes += len(name)
|
||||
b.files = append(b.files, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Files returns the accumulated file names.
|
||||
func (b *BoundedFileList) Files() []string {
|
||||
return b.files
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -54,37 +53,12 @@ func dialContextInternalAPI(ctx context.Context, network, address string) (conn
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// internalAPIConnectionIsLocal reports whether the internal API transport connects to a local target,
|
||||
// where the self-signed local certificate cannot be verified so skipping verification is safe. It mirrors
|
||||
// what dialContextInternalAPI actually dials: a unix socket whenever Protocol is HTTPUnix (always local,
|
||||
// whatever LOCAL_ROOT_URL says), otherwise the LOCAL_ROOT_URL host directly. A non-loopback LOCAL_ROOT_URL
|
||||
// is a real network hop, so its certificate must be verified, else the internal token can be MITM'd. An
|
||||
// unparseable LOCAL_ROOT_URL is a hard misconfiguration and fails closed (verify).
|
||||
func internalAPIConnectionIsLocal(protocol setting.Scheme, localURL string) bool {
|
||||
if protocol == setting.HTTPUnix {
|
||||
return true
|
||||
}
|
||||
u, err := url.Parse(localURL)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
host := u.Hostname()
|
||||
if host == "localhost" {
|
||||
return true
|
||||
}
|
||||
ip := net.ParseIP(host)
|
||||
return ip != nil && ip.IsLoopback()
|
||||
}
|
||||
|
||||
var internalAPITransport = sync.OnceValue(func() http.RoundTripper {
|
||||
return &http.Transport{
|
||||
DialContext: dialContextInternalAPI,
|
||||
TLSClientConfig: &tls.Config{
|
||||
// Skip verification only for a local target (unix socket, or a loopback LOCAL_ROOT_URL), where the
|
||||
// self-signed local cert can't be verified anyway; a non-loopback LOCAL_ROOT_URL is a real network
|
||||
// hop and must be verified so the internal token can't be MITM'd. When verifying, Go's default
|
||||
// ServerName (the dialed LOCAL_ROOT_URL host) is already correct, so it is not overridden.
|
||||
InsecureSkipVerify: internalAPIConnectionIsLocal(setting.Protocol, setting.LocalURL),
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: setting.Domain,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package private
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestInternalAPIConnectionIsLocal(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
protocol setting.Scheme
|
||||
localURL string
|
||||
want bool
|
||||
}{
|
||||
// HTTPUnix always dials the unix socket (a local target), whatever LOCAL_ROOT_URL says
|
||||
{"unix socket", setting.HTTPUnix, "https://gitea.example.com/", true},
|
||||
{"localhost", setting.HTTP, "http://localhost:3000/", true},
|
||||
{"loopback ipv4", setting.HTTPS, "https://127.0.0.1:3000/", true},
|
||||
{"loopback ipv6", setting.HTTPS, "https://[::1]:3000/", true},
|
||||
// a non-loopback LOCAL_ROOT_URL is a real network hop and must be verified
|
||||
{"remote host", setting.HTTPS, "https://gitea.internal:443/", false},
|
||||
{"remote ip", setting.HTTPS, "https://10.0.0.5:3000/", false},
|
||||
// an unparseable LOCAL_ROOT_URL is a hard misconfiguration; fail closed to verification
|
||||
{"invalid url", setting.HTTPS, "://bad", false},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
assert.Equal(t, c.want, internalAPIConnectionIsLocal(c.protocol, c.localURL))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ var (
|
||||
|
||||
// NOTE: All below regex matching do not perform any extra validation.
|
||||
// Thus a link is produced even if the linked entity does not exist.
|
||||
// While fast, this is also incorrect and lead to false positives.
|
||||
// While fast, this is also incorrect and leads to false positives.
|
||||
// TODO: fix invalid linking issue
|
||||
|
||||
// mentionPattern matches all mentions in the form of "@user" or "@org/team"
|
||||
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
const (
|
||||
RepoPRTitleSourceFirstCommit = "first-commit"
|
||||
RepoPRTitleSourceAuto = "auto"
|
||||
RepoPRTitleSourceBranchName = "branch-name"
|
||||
)
|
||||
|
||||
// ItemsPerPage maximum items per page in forks, watchers and stars of a repo
|
||||
|
||||
@@ -20,11 +20,9 @@ var Security = struct {
|
||||
XContentTypeOptions string
|
||||
|
||||
ContentSecurityPolicyGeneral string // it only supports empty (default policy) or "unset", maybe it can support more in the future
|
||||
AllowedHostList string
|
||||
}{
|
||||
XFrameOptions: "SAMEORIGIN",
|
||||
XContentTypeOptions: "nosniff",
|
||||
AllowedHostList: "external",
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -10,20 +10,13 @@ import (
|
||||
)
|
||||
|
||||
func TestLoadSecurityFrom(t *testing.T) {
|
||||
assert.Equal(t, "SAMEORIGIN", Security.XFrameOptions)
|
||||
assert.Equal(t, "nosniff", Security.XContentTypeOptions)
|
||||
assert.Equal(t, "external", Security.AllowedHostList)
|
||||
|
||||
cfg, err := NewConfigProviderFromData(`[security]
|
||||
X_FRAME_OPTIONS = DENY
|
||||
X_CONTENT_TYPE_OPTIONS = unset
|
||||
ALLOWED_HOST_LIST = foo
|
||||
CONTENT_SECURITY_POLICY_GENERAL = "script-src *; foo"
|
||||
`)
|
||||
CONTENT_SECURITY_POLICY_GENERAL = "script-src *; foo"`)
|
||||
assert.NoError(t, err)
|
||||
loadSecurityFrom(cfg)
|
||||
assert.Equal(t, "DENY", Security.XFrameOptions)
|
||||
assert.Equal(t, "unset", Security.XContentTypeOptions)
|
||||
assert.Equal(t, "foo", Security.AllowedHostList)
|
||||
assert.Equal(t, `"script-src *`, Security.ContentSecurityPolicyGeneral) // holy shit ini package bug
|
||||
}
|
||||
|
||||
@@ -34,10 +34,7 @@ func loadWebhookFrom(rootCfg ConfigProvider) {
|
||||
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
|
||||
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
|
||||
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
|
||||
|
||||
deprecatedSetting(rootCfg, "webhook", "ALLOWED_HOST_LIST", "security", "ALLOWED_HOST_LIST", "v28.0.0")
|
||||
Webhook.AllowedHostList = sec.Key("ALLOWED_HOST_LIST").MustString(Security.AllowedHostList)
|
||||
|
||||
Webhook.AllowedHostList = sec.Key("ALLOWED_HOST_LIST").MustString("")
|
||||
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork", "packagist"}
|
||||
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
|
||||
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")
|
||||
|
||||
@@ -19,7 +19,6 @@ type CreateUserOption struct {
|
||||
// The full display name of the user
|
||||
FullName string `json:"full_name" binding:"MaxSize(100)"`
|
||||
// required: true
|
||||
// swagger:strfmt email
|
||||
Email string `json:"email" binding:"Required;Email;MaxSize(254)"`
|
||||
// The plain text password for the user
|
||||
Password string `json:"password" binding:"MaxSize(255)"`
|
||||
|
||||
@@ -133,7 +133,6 @@ type IssueAssigneesOption struct {
|
||||
// EditDeadlineOption options for creating a deadline
|
||||
type EditDeadlineOption struct {
|
||||
// required:true
|
||||
// swagger:strfmt date-time
|
||||
Deadline *time.Time `json:"due_date"`
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ const (
|
||||
type NotifySubjectType string
|
||||
|
||||
const (
|
||||
// NotifySubjectIssue a issue is subject of an notification
|
||||
// NotifySubjectIssue an issue is subject of a notification
|
||||
NotifySubjectIssue NotifySubjectType = "Issue"
|
||||
// NotifySubjectPull a pull is subject of an notification
|
||||
NotifySubjectPull NotifySubjectType = "Pull"
|
||||
|
||||
@@ -54,10 +54,10 @@ type CreateTeamOption struct {
|
||||
// Whether the team has access to all repositories in the organization
|
||||
IncludesAllRepositories bool `json:"includes_all_repositories"`
|
||||
Permission RepoWritePermission `json:"permission"`
|
||||
// example: ["repo.actions","repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.ext_wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"]
|
||||
// example: ["repo.actions","repo.packages","repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"]
|
||||
// Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.
|
||||
Units []string `json:"units"`
|
||||
// example: {"repo.actions","repo.packages","repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"}
|
||||
// example: {"repo.actions":"read","repo.packages":"read","repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"}
|
||||
UnitsMap map[string]string `json:"units_map"`
|
||||
// Whether the team can create repositories in the organization
|
||||
CanCreateOrgRepo bool `json:"can_create_org_repo"`
|
||||
|
||||
@@ -12,20 +12,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/proxy"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
// httpClient returns an HTTP client that honors Gitea's proxy configuration.
|
||||
var httpClient = util.OnceValue[*http.Client]{
|
||||
Func: func() *http.Client {
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
transport.Proxy = proxy.Proxy()
|
||||
return &http.Client{Transport: transport}
|
||||
},
|
||||
}
|
||||
|
||||
// Response is the structure of JSON returned from API
|
||||
type Response struct {
|
||||
Success bool `json:"success"`
|
||||
@@ -51,7 +40,7 @@ func Verify(ctx context.Context, response string) (bool, error) {
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
resp, err := httpClient.Value().Do(req)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to send CAPTCHA response: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package turnstile
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHTTPClientHonorsProxy(t *testing.T) {
|
||||
proxyURL, err := url.Parse("http://proxy.example.com:3128")
|
||||
require.NoError(t, err)
|
||||
|
||||
defer test.MockVariableValue(&setting.Proxy.Enabled, true)()
|
||||
defer test.MockVariableValue(&setting.Proxy.ProxyURL, proxyURL.String())()
|
||||
defer test.MockVariableValue(&setting.Proxy.ProxyURLFixed, proxyURL)()
|
||||
defer test.MockVariableValue(&setting.Proxy.ProxyHosts, []string{"**"})()
|
||||
httpClient.Reset()
|
||||
transport, ok := httpClient.Value().Transport.(*http.Transport)
|
||||
require.True(t, ok)
|
||||
require.NotNil(t, transport.Proxy)
|
||||
|
||||
// The Turnstile verification request must be routed through the configured proxy.
|
||||
req := httptest.NewRequest(http.MethodPost, "https://any.example.com", nil)
|
||||
got, err := transport.Proxy(req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
assert.Equal(t, proxyURL.String(), got.String())
|
||||
}
|
||||
+1
-9
@@ -23,21 +23,13 @@ func (e ErrURISchemeNotSupported) Error() string {
|
||||
|
||||
// Open open a local file or a remote file
|
||||
func Open(uriStr string) (io.ReadCloser, error) {
|
||||
return OpenWithClient(uriStr, http.DefaultClient)
|
||||
}
|
||||
|
||||
// OpenWithClient opens a local file or a remote file, using the given (non-nil) HTTP client
|
||||
// for http/https URLs. Callers that must confine remote access (e.g. to defeat SSRF via
|
||||
// redirects) should pass a client whose transport validates the peer at dial time; Open
|
||||
// passes http.DefaultClient.
|
||||
func OpenWithClient(uriStr string, client *http.Client) (io.ReadCloser, error) {
|
||||
u, err := url.Parse(uriStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "http", "https":
|
||||
f, err := client.Get(uriStr)
|
||||
f, err := http.Get(uriStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,18 +4,10 @@
|
||||
package uri
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestReadURI(t *testing.T) {
|
||||
@@ -25,45 +17,3 @@ func TestReadURI(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
defer f.Close()
|
||||
}
|
||||
|
||||
// TestOpenWithClientValidatesRedirectTarget verifies OpenWithClient routes the
|
||||
// whole request chain (including redirects) through the provided client, so a
|
||||
// client whose transport refuses to dial an internal target blocks a redirect to
|
||||
// it — whereas the default client (old Open behavior) follows it.
|
||||
func TestOpenWithClientValidatesRedirectTarget(t *testing.T) {
|
||||
var internalHit atomic.Bool
|
||||
internal := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
internalHit.Store(true)
|
||||
_, _ = w.Write([]byte("secret"))
|
||||
}))
|
||||
defer internal.Close()
|
||||
|
||||
front := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, internal.URL, http.StatusFound)
|
||||
}))
|
||||
defer front.Close()
|
||||
|
||||
internalAddr := strings.TrimPrefix(internal.URL, "http://")
|
||||
|
||||
// a client that refuses to dial the internal target, mimicking the migration
|
||||
// hostmatcher dialer that re-validates every hop
|
||||
blockingClient := &http.Client{Transport: &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
if addr == internalAddr {
|
||||
return nil, errors.New("blocked internal address")
|
||||
}
|
||||
return (&net.Dialer{}).DialContext(ctx, network, addr)
|
||||
},
|
||||
}}
|
||||
|
||||
_, err := OpenWithClient(front.URL, blockingClient)
|
||||
require.Error(t, err)
|
||||
assert.False(t, internalHit.Load(), "the redirect target must not be reached through the validating client")
|
||||
|
||||
// the default client (the previous behavior) follows the redirect to the internal target
|
||||
internalHit.Store(false)
|
||||
rc, err := Open(front.URL)
|
||||
require.NoError(t, err)
|
||||
_ = rc.Close()
|
||||
assert.True(t, internalHit.Load(), "sanity check: the default client follows the redirect")
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type onceValueResult[T any] struct {
|
||||
value T
|
||||
panic any
|
||||
}
|
||||
|
||||
// OnceValue is similar to Golang's "sync.OnceValue", but can be reset.
|
||||
type OnceValue[T any] struct {
|
||||
Func func() T
|
||||
mu sync.Mutex
|
||||
res atomic.Pointer[onceValueResult[T]]
|
||||
}
|
||||
|
||||
func (o *OnceValue[T]) Value() T {
|
||||
res := o.res.Load()
|
||||
if res == nil {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
res = o.res.Load()
|
||||
if res == nil {
|
||||
res = &onceValueResult[T]{}
|
||||
defer func() {
|
||||
res.panic = recover()
|
||||
o.res.Store(res)
|
||||
if res.panic != nil {
|
||||
panic(res.panic)
|
||||
}
|
||||
}()
|
||||
res.value = o.Func()
|
||||
}
|
||||
}
|
||||
if res.panic != nil {
|
||||
panic(res.panic)
|
||||
}
|
||||
return res.value
|
||||
}
|
||||
|
||||
func (o *OnceValue[T]) Reset() {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
o.res.Store(nil)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOnceValue(t *testing.T) {
|
||||
t.Run("RepeatCall", func(t *testing.T) {
|
||||
callCount := 0
|
||||
o := OnceValue[int]{Func: func() int {
|
||||
callCount++
|
||||
return 42
|
||||
}}
|
||||
assert.Equal(t, 42, o.Value())
|
||||
assert.Equal(t, 42, o.Value())
|
||||
assert.Equal(t, 1, callCount)
|
||||
o.Reset()
|
||||
assert.Equal(t, 42, o.Value())
|
||||
assert.Equal(t, 2, callCount)
|
||||
assert.Equal(t, 42, o.Value())
|
||||
assert.Equal(t, 2, callCount)
|
||||
})
|
||||
|
||||
t.Run("Panic", func(t *testing.T) {
|
||||
callCount := 0
|
||||
doPanic := true
|
||||
o := OnceValue[int]{Func: func() int {
|
||||
callCount++
|
||||
if doPanic {
|
||||
panic("some error")
|
||||
}
|
||||
return 42
|
||||
}}
|
||||
assert.PanicsWithValue(t, "some error", func() { o.Value() })
|
||||
assert.PanicsWithValue(t, "some error", func() { o.Value() })
|
||||
assert.Equal(t, 1, callCount)
|
||||
doPanic = false
|
||||
o.Reset()
|
||||
assert.Equal(t, 42, o.Value())
|
||||
assert.Equal(t, 2, callCount)
|
||||
assert.Equal(t, 42, o.Value())
|
||||
assert.Equal(t, 2, callCount)
|
||||
})
|
||||
}
|
||||
@@ -40,7 +40,6 @@ var timeStrGlobalVars = sync.OnceValue(func() *timeStrGlobalVarsType {
|
||||
})
|
||||
|
||||
func TimeEstimateParse(timeStr string) (int64, error) {
|
||||
timeStr = strings.TrimSpace(timeStr)
|
||||
if timeStr == "" {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -52,13 +51,7 @@ func TimeEstimateParse(timeStr string) (int64, error) {
|
||||
if matches[0][0] != 0 || matches[len(matches)-1][1] != len(timeStr) {
|
||||
return 0, fmt.Errorf("invalid time string: %s", timeStr)
|
||||
}
|
||||
prevEnd := 0
|
||||
for _, match := range matches {
|
||||
// only whitespace may separate two units, otherwise the string contains invalid content like "1h x 2m"
|
||||
if strings.TrimSpace(timeStr[prevEnd:match[0]]) != "" {
|
||||
return 0, fmt.Errorf("invalid time string: %s", timeStr)
|
||||
}
|
||||
prevEnd = match[1]
|
||||
amount, err := strconv.ParseInt(timeStr[match[2]:match[3]], 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid time string: %v", err)
|
||||
|
||||
@@ -22,9 +22,6 @@ func TestTimeStr(t *testing.T) {
|
||||
{"1s", 1, false},
|
||||
{"1h 1m 1s", 3600 + 60 + 1, false},
|
||||
{"1d1x", 0, true},
|
||||
{"1h 2x 3m", 0, true},
|
||||
{"1h_2m", 0, true},
|
||||
{"1h,1m", 0, true},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"forks": "Forks",
|
||||
"activities": "Aktivitäten",
|
||||
"pull_requests": "Pull-Requests",
|
||||
"issues": "Probleme",
|
||||
"issues": "Issues",
|
||||
"milestones": "Meilensteine",
|
||||
"ok": "OK",
|
||||
"cancel": "Abbrechen",
|
||||
@@ -1101,7 +1101,7 @@
|
||||
"repo.migrate_items_wiki": "Wiki",
|
||||
"repo.migrate_items_milestones": "Meilensteine",
|
||||
"repo.migrate_items_labels": "Labels",
|
||||
"repo.migrate_items_issues": "Probleme",
|
||||
"repo.migrate_items_issues": "Issues",
|
||||
"repo.migrate_items_pullrequests": "Pull-Requests",
|
||||
"repo.migrate_items_merge_requests": "Merge-Requests",
|
||||
"repo.migrate_items_releases": "Veröffentlichungen",
|
||||
@@ -1178,7 +1178,7 @@
|
||||
"repo.find_tag": "Tag finden",
|
||||
"repo.branches": "Branches",
|
||||
"repo.tags": "Tags",
|
||||
"repo.issues": "Probleme",
|
||||
"repo.issues": "Issues",
|
||||
"repo.pulls": "Pull-Requests",
|
||||
"repo.projects": "Projekte",
|
||||
"repo.packages": "Pakete",
|
||||
@@ -2300,7 +2300,7 @@
|
||||
"repo.settings.event_repository": "Repository",
|
||||
"repo.settings.event_repository_desc": "Repository erstellt oder gelöscht.",
|
||||
"repo.settings.event_header_issue": "Issue Ereignisse",
|
||||
"repo.settings.event_issues": "Probleme",
|
||||
"repo.settings.event_issues": "Issues",
|
||||
"repo.settings.event_issues_desc": "Issue geöffnet, geschlossen, wieder geöffnet, bearbeitet oder gelöscht.",
|
||||
"repo.settings.event_issue_assign": "Issue zugewiesen",
|
||||
"repo.settings.event_issue_assign_desc": "Issue zugewiesen oder Zuweisung entfernt.",
|
||||
@@ -3104,7 +3104,7 @@
|
||||
"admin.repos.owner": "Besitzer",
|
||||
"admin.repos.name": "Name",
|
||||
"admin.repos.private": "Privat",
|
||||
"admin.repos.issues": "Probleme",
|
||||
"admin.repos.issues": "Issues",
|
||||
"admin.repos.size": "Größe",
|
||||
"admin.repos.lfs_size": "LFS-Größe",
|
||||
"admin.packages.package_manage_panel": "Paketverwaltung",
|
||||
@@ -3779,6 +3779,7 @@
|
||||
"actions.runs.commit": "Commit",
|
||||
"actions.runs.run_details": "Run Details",
|
||||
"actions.runs.workflow_file": "Workflow-Datei",
|
||||
"actions.runs.workflow_file_no_permission": "Keine Berechtigung zum Anzeigen der Workflow-Datei",
|
||||
"actions.runs.scheduled": "Geplant",
|
||||
"actions.runs.pushed_by": "gepusht von",
|
||||
"actions.runs.invalid_workflow_helper": "Die Workflow-Konfigurationsdatei ist ungültig. Bitte überprüfe Deine Konfigurationsdatei: %s",
|
||||
@@ -3835,7 +3836,33 @@
|
||||
"actions.workflow.scope_owner": "Besitzer",
|
||||
"actions.workflow.scope_global": "Global",
|
||||
"actions.workflow.required": "Erforderlich",
|
||||
"actions.workflow.scoped_required_cannot_disable": "Dieser Scoped Workflow ist erforderlich und kann nicht deaktiviert werden.",
|
||||
"actions.scoped_workflows": "Scoped Workflows",
|
||||
"actions.scoped_workflows.desc_org": "Repositories als Scoped Workflow Quellen registrieren. Workflow-Dateien unter den Workflow-Verzeichnissen eines Quell-Repositorys laufen in jedem Projektarchiv dieser Organisation, im eigenen Kontext des Projektarchivs.",
|
||||
"actions.scoped_workflows.desc_user": "Repositories als Scoped Workflow Quellen registrieren. Workflow-Dateien unter den Workflow-Verzeichnissen eines Quell-Repositorys laufen in jedem Projektarchiv dieser Organisation, im eigenen Kontext des Projektarchivs.",
|
||||
"actions.scoped_workflows.desc_global": "Repositories als Scoped Workflow-Quellen registrieren. Workflow-Dateien unter den Workflow-Verzeichnissen eines Quellcode-Repositorys laufen auf jedem Projektarchiv in dieser Instanz im eigenen Kontext des Projektarchivs. Da Quellen auf Instanzenebene auf den Ereignissen jedes Projektarchivs ausgewertet werden, kann die Registrierung bei großen Instanzen Overhead hinzufügen.",
|
||||
"actions.scoped_workflows.add_help": "Um Scoped Workflows aus einem Repository zu erstellen, übertrage die Workflow-Dateien unter <code>%s</code> in seinem Standard Branch, dann registrieren Sie das Projektarchiv als Quelle unten.",
|
||||
"actions.scoped_workflows.security_note": "Der Workflow-Inhalt eines Quell-Repositorys wird in jedem Repository ausgeführt, für das er gilt. Die Skripte der einzelnen Schritte sowie deren Ausgabe werden in den Actions-Logs des jeweiligen Repositorys gespeichert und sind für alle sichtbar, die die Actions des konsumierenden Repositorys einsehen können. Das Registrieren eines privaten Repositorys als Quelle legt daher dessen Workflow-Logik über diese Logs offen. Registriere nur Repositorys, deren Workflow-Inhalte mit allen konsumierenden Repositorys geteilt werden dürfen. Wenn ein scoped Workflow einen wiederverwendbaren Workflow aus einem privaten Repository referenziert, stelle sicher, dass jedes konsumierende Repository darauf Lesezugriff hat – andernfalls schlägt der Workflow dort fehl.",
|
||||
"actions.scoped_workflows.source.add": "Quell-Repository hinzufügen",
|
||||
"actions.scoped_workflows.source.add_success": "Quell-Repository hinzugefügt.",
|
||||
"actions.scoped_workflows.source.remove_success": "Quell-Repository entfernt.",
|
||||
"actions.scoped_workflows.source.not_found": "Repository nicht gefunden.",
|
||||
"actions.scoped_workflows.required.update_success": "Erforderliche Workflows aktualisiert.",
|
||||
"actions.scoped_workflows.required.label": "Workflows als erforderlich markieren (ein erforderlicher Workflow kann nicht durch Repositories deaktiviert werden):",
|
||||
"actions.scoped_workflows.required.patterns": "Erforderliche Statusüberprüfungsmuster",
|
||||
"actions.scoped_workflows.required.patterns_aria": "Erforderliche Statusüberprüfungsmuster für %s",
|
||||
"actions.scoped_workflows.required.patterns_note": "nur erzwungen während der Workflow benötigt wird",
|
||||
"actions.scoped_workflows.required.patterns_hint": "Markieren Sie den Workflow als erforderlich, um seine Statusüberprüfungsmuster zu konfigurieren.",
|
||||
"actions.scoped_workflows.required.patterns_help": "Ein Statusüberprüfungsmuster (glob) pro Zeile. Ein Pull-Request kann erst zusammengeführt werden, wenn ein Status mit jedem Muster übereinstimmt. Dies wird für jeden Zielzweig erzwungen, der eine Schutzregel hat, auch für einen mit einer eigenen Statusüberprüfung; ein Zielzweig ohne Schutzregel ist nicht ausgeschaltet.",
|
||||
"actions.scoped_workflows.required.patterns_empty": "Jeder benötigte Workflow benötigt mindestens ein Statusüberprüfungsmuster.",
|
||||
"actions.scoped_workflows.required.missing_file": "nicht mehr im Quelltext",
|
||||
"actions.scoped_workflows.required.expected_contexts": "Erwartete Statusüberprüfung (eine Prüfung, die zu einem Muster passt)",
|
||||
"actions.scoped_workflows.required.no_status_contexts": "Dieser Workflow postet keine Status Checks, ihn als Anforderung zu markieren würde jede Pull Request blockieren. Deaktiviere die Anforderung.",
|
||||
"actions.scoped_workflows.no_files": "Im Standard-Branch wurden keine Scoped Workflow-Dateien gefunden.",
|
||||
"actions.workflow.run": "Workflow ausführen",
|
||||
"actions.workflow.create_status_badge": "Status Badge erstellen",
|
||||
"actions.workflow.status_badge": "Status Badge",
|
||||
"actions.workflow.status_badge_url": "Badge-URL",
|
||||
"actions.workflow.not_found": "Workflow '%s' wurde nicht gefunden.",
|
||||
"actions.workflow.run_success": "Workflow '%s' erfolgreich ausgeführt.",
|
||||
"actions.workflow.from_ref": "Nutze Workflow von",
|
||||
|
||||
+214
-169
@@ -10,7 +10,7 @@
|
||||
"sign_out": "Déconnexion",
|
||||
"sign_up": "S'inscrire",
|
||||
"link_account": "Lier un Compte",
|
||||
"register": "S'inscrire",
|
||||
"register": "S’inscrire",
|
||||
"version": "Version",
|
||||
"powered_by": "Propulsé par %s",
|
||||
"page": "Page",
|
||||
@@ -80,8 +80,8 @@
|
||||
"cancel": "Annuler",
|
||||
"retry": "Réessayez",
|
||||
"rerun": "Relancer",
|
||||
"rerun_all": "Relancer toutes les tâches",
|
||||
"rerun_failed": "Relancer les tâches échouées",
|
||||
"rerun_all": "Relancer toutes les missions.",
|
||||
"rerun_failed": "Relancer les missions échouées.",
|
||||
"save": "Enregistrer",
|
||||
"add": "Ajouter",
|
||||
"add_all": "Tout Ajouter",
|
||||
@@ -165,7 +165,7 @@
|
||||
"search.fuzzy_tooltip": "Inclure également les résultats proches de la recherche",
|
||||
"search.words": "Mots",
|
||||
"search.words_tooltip": "Inclure uniquement les résultats qui correspondent exactement aux mots recherchés",
|
||||
"search.regexp": "Regexp",
|
||||
"search.regexp": "Expression régulière",
|
||||
"search.regexp_tooltip": "Inclure uniquement les résultats qui correspondent à l’expression régulière recherchée",
|
||||
"search.exact": "Exact",
|
||||
"search.exact_tooltip": "Inclure uniquement les résultats qui correspondent exactement au terme de recherche",
|
||||
@@ -185,7 +185,7 @@
|
||||
"search.tag_kind": "Chercher des étiquettes…",
|
||||
"search.tag_tooltip": "Cherchez des étiquettes correspondantes. Utilisez « % » pour rechercher n’importe quelle suite de nombres.",
|
||||
"search.commit_kind": "Chercher des révisions…",
|
||||
"search.runner_kind": "Chercher des exécuteurs…",
|
||||
"search.runner_kind": "Chercher des opérateurs…",
|
||||
"search.no_results": "Aucun résultat correspondant trouvé.",
|
||||
"search.issue_kind": "Recherche de tickets…",
|
||||
"search.pull_kind": "Recherche de demandes d’ajouts…",
|
||||
@@ -289,7 +289,7 @@
|
||||
"install.smtp_port": "Port SMTP",
|
||||
"install.smtp_from": "Envoyer les courriels en tant que",
|
||||
"install.smtp_from_invalid": "L’adresse « Envoyer le courriel sous » est invalide",
|
||||
"install.smtp_from_helper": "Adresse courriel utilisée par Gitea. Utilisez directement votre adresse ou la forme « Nom <email@example.com> ».",
|
||||
"install.smtp_from_helper": "Adresse courriel utilisée par Gitea. Utilisez directement votre adresse ou la forme « Nom <courriel@exemple.com> ».",
|
||||
"install.mailer_user": "Utilisateur SMTP",
|
||||
"install.mailer_password": "Mot de passe SMTP",
|
||||
"install.register_confirm": "Exiger la confirmation du courriel lors de l’inscription",
|
||||
@@ -308,7 +308,7 @@
|
||||
"install.require_sign_in_view_popup": "Limiter l’accès aux pages aux utilisateurs connectés. Les visiteurs ne verront que les pages de connexion et d’inscription.",
|
||||
"install.admin_setting_desc": "La création d'un compte administrateur est facultative. Le premier utilisateur enregistré deviendra automatiquement un administrateur le cas échéant.",
|
||||
"install.admin_title": "Paramètres de compte administrateur",
|
||||
"install.admin_name": "Nom d’utilisateur administrateur",
|
||||
"install.admin_name": "Nom de l’administrateur",
|
||||
"install.admin_password": "Mot de passe",
|
||||
"install.confirm_password": "Confirmez le mot de passe",
|
||||
"install.admin_email": "Courriel",
|
||||
@@ -505,10 +505,10 @@
|
||||
"mail.repo.actions.run.failed": "L’exécution a échoué",
|
||||
"mail.repo.actions.run.succeeded": "L’exécution a réussi",
|
||||
"mail.repo.actions.run.cancelled": "L’exécution a été annulée",
|
||||
"mail.repo.actions.jobs.all_succeeded": "Tous les tâches ont réussi.",
|
||||
"mail.repo.actions.jobs.all_failed": "Toutes les tâches ont échoué.",
|
||||
"mail.repo.actions.jobs.some_not_successful": "Certaines tâches n’ont pas réussi.",
|
||||
"mail.repo.actions.jobs.all_cancelled": "Toutes les tâches ont bien été annulés.",
|
||||
"mail.repo.actions.jobs.all_succeeded": "Tous les missions ont réussi.",
|
||||
"mail.repo.actions.jobs.all_failed": "Toutes les missions ont échoué.",
|
||||
"mail.repo.actions.jobs.some_not_successful": "Certaines missions n’ont pas réussi.",
|
||||
"mail.repo.actions.jobs.all_cancelled": "Toutes les missions ont bien été annulées.",
|
||||
"mail.team_invite.subject": "%[1]s vous a invité à rejoindre l’organisation %[2]s",
|
||||
"mail.team_invite.text_1": "%[1]s vous a invité à rejoindre l’équipe %[2]s dans l’organisation %[3]s.",
|
||||
"mail.team_invite.text_2": "Veuillez cliquer sur le lien suivant pour rejoindre l'équipe :",
|
||||
@@ -916,7 +916,7 @@
|
||||
"settings.passcode_invalid": "Le mot de passe est invalide. Réessayez.",
|
||||
"settings.twofa_enrolled": "L’authentification à deux facteurs a été activée pour votre compte. Gardez votre clé de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois.",
|
||||
"settings.twofa_failed_get_secret": "Impossible d'obtenir le secret.",
|
||||
"settings.webauthn_desc": "Les clés de sécurité sont des dispositifs matériels contenant des clés cryptographiques. Elles peuvent être utilisées pour l’authentification à deux facteurs. La clé de sécurité doit supporter le standard <a rel=\"noreferrer\" target=\"_blank\" href=\"%s\">WebAuthn Authenticator</a>.",
|
||||
"settings.webauthn_desc": "Les clés de sécurité sont des dispositifs matériels contenant des clés cryptographiques. Elles peuvent être utilisées pour l’authentification à deux facteurs. Gitea requière le support de l’<a rel=\"noreferrer\" target=\"_blank\" href=\"%s\">API Web Authentication</a>.",
|
||||
"settings.webauthn_register_key": "Ajouter une clé de sécurité",
|
||||
"settings.webauthn_nickname": "Pseudonyme",
|
||||
"settings.webauthn_delete_key": "Retirer la clé de sécurité",
|
||||
@@ -944,8 +944,8 @@
|
||||
"settings.email_notifications.disable": "Ne pas notifier",
|
||||
"settings.email_notifications.submit": "Définir les préférences de courriel",
|
||||
"settings.email_notifications.andyourown": "Inclure vos propres notifications",
|
||||
"settings.email_notifications.actions.desc": "Notification pour les executions de workflows sur les dépôts configurés avec les <a target=\"_blank\" href=\"%s\">Actions Gitea</a>.",
|
||||
"settings.email_notifications.actions.failure_only": "Ne notifier que pour les exécutions échouées",
|
||||
"settings.email_notifications.actions.desc": "Notifier les procédures des dépôts configurés avec les <a target=\"_blank\" href=\"%s\">Actions Gitea</a>.",
|
||||
"settings.email_notifications.actions.failure_only": "Ne notifier que procédures échouées",
|
||||
"settings.visibility": "Visibilité de l'utilisateur",
|
||||
"settings.visibility.public": "Publique",
|
||||
"settings.visibility.public_tooltip": "Visible par tout le monde",
|
||||
@@ -992,7 +992,7 @@
|
||||
"repo.repo_desc": "Description",
|
||||
"repo.repo_desc_helper": "Décrire brièvement votre dépôt",
|
||||
"repo.repo_no_desc": "Aucune description fournie",
|
||||
"repo.repo_lang": "Langue",
|
||||
"repo.repo_lang": "Langues",
|
||||
"repo.repo_gitignore_helper": "Sélectionner quelques .gitignore prédéfinies",
|
||||
"repo.repo_gitignore_helper_desc": "De nombreux outils et compilateurs génèrent des fichiers résiduels qui n'ont pas besoin d'être supervisés par git. Composez un .gitignore à l’aide de cette liste des languages de programmation courants.",
|
||||
"repo.issue_labels": "Jeu de labels pour les tickets",
|
||||
@@ -1041,7 +1041,7 @@
|
||||
"repo.stars": "Favoris",
|
||||
"repo.reactions_more": "et %d de plus",
|
||||
"repo.reactions": "Réactions",
|
||||
"repo.unit_disabled": "L'administrateur du site a désactivé cette section du dépôt.",
|
||||
"repo.unit_disabled": "L’administrateur du site a désactivé cette section du dépôt.",
|
||||
"repo.language_other": "Autre",
|
||||
"repo.adopt_search": "Entrez un nom d’utilisateur pour rechercher les dépôts dépossédés… (laissez vide pour tous trouver)",
|
||||
"repo.adopt_preexisting_label": "Adopter les fichiers",
|
||||
@@ -1106,9 +1106,9 @@
|
||||
"repo.migrate_items_merge_requests": "Demandes de fusion",
|
||||
"repo.migrate_items_releases": "Publications",
|
||||
"repo.migrate_repo": "Migrer le dépôt",
|
||||
"repo.migrate.clone_address": "Migrer/Cloner depuis une URL",
|
||||
"repo.migrate.clone_address": "Migrer depuis une URL",
|
||||
"repo.migrate.clone_address_desc": "L’URL ou le lien « Git clone » d’un dépôt existant",
|
||||
"repo.migrate.github_token_desc": "Vous pouvez mettre un ou plusieurs jetons séparés par des virgules ici pour rendre la migration plus rapide et contourner la limite de débit de l’API GitHub. ATTENTION : abuser de cette fonctionnalité peut enfreindre la politique du fournisseur de service et entraîner un blocage de votre compte.",
|
||||
"repo.migrate.github_token_desc": "Vous pouvez mettre un ou plusieurs jetons séparés par des virgules ici pour rendre la migration plus rapide et contourner la limite de débit de l’API GitHub. Attention : abuser de cette fonctionnalité peut enfreindre la politique du fournisseur de service et entraîner un blocage de votre compte.",
|
||||
"repo.migrate.clone_local_path": "ou un chemin serveur local",
|
||||
"repo.migrate.permission_denied": "Vous n'êtes pas autorisé à importer des dépôts locaux.",
|
||||
"repo.migrate.permission_denied_blocked": "Vous ne pouvez pas importer depuis des domaines bannis, veuillez demander à votre administrateur de vérifier les paramètres ALLOWED_DOMAINS, ALLOW_LOCALNETWORKS ou BLOCKED_DOMAINS.",
|
||||
@@ -1165,7 +1165,7 @@
|
||||
"repo.clone_this_repo": "Cloner ce dépôt",
|
||||
"repo.cite_this_repo": "Citer ce dépôt",
|
||||
"repo.create_new_repo_command": "Création d'un nouveau dépôt en ligne de commande",
|
||||
"repo.push_exist_repo": "Soumission d'un dépôt existant par ligne de commande",
|
||||
"repo.push_exist_repo": "Soumission d’un dépôt existant par ligne de commande",
|
||||
"repo.empty_message": "Ce dépôt n’a pas de contenu.",
|
||||
"repo.broken_message": "Les données git de ce dépôt ne peuvent pas être lues. Contactez l'administrateur de cette instance ou supprimez ce dépôt.",
|
||||
"repo.no_branch": "Ce dépôt n’a aucune branche.",
|
||||
@@ -1294,7 +1294,7 @@
|
||||
"repo.editor.file_changed_while_editing": "Le contenu du fichier a changé depuis que vous avez commencé à éditer. <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">Cliquez ici</a> pour voir les changements ou <strong>soumettez de nouveau</strong> pour les écraser.",
|
||||
"repo.editor.file_already_exists": "Un fichier nommé \"%s\" existe déjà dans ce dépôt.",
|
||||
"repo.editor.commit_id_not_matching": "L’ID de la révision ne correspond pas à l’ID lorsque vous avez commencé à éditer. Faites une révision dans une branche de correctif puis fusionnez.",
|
||||
"repo.editor.push_out_of_date": "Cet envoi semble être obsolète.",
|
||||
"repo.editor.push_out_of_date": "Cette soumission semble être obsolète.",
|
||||
"repo.editor.commit_empty_file_header": "Réviser un fichier vide",
|
||||
"repo.editor.commit_empty_file_text": "Le fichier que vous allez réviser est vide. Continuer ?",
|
||||
"repo.editor.no_changes_to_show": "Il n’y a aucune modification à afficher.",
|
||||
@@ -1307,7 +1307,7 @@
|
||||
"repo.editor.upload_files_to_dir": "Téléverser les fichiers vers \"%s\"",
|
||||
"repo.editor.cannot_commit_to_protected_branch": "Impossible de créer une révision sur la branche protégée \"%s\".",
|
||||
"repo.editor.no_commit_to_branch": "Impossible de réviser cette branche car :",
|
||||
"repo.editor.user_no_push_to_branch": "L'utilisateur ne peut pas pousser vers la branche",
|
||||
"repo.editor.user_no_push_to_branch": "L’utilisateur ne peut pas soumettre sur la branche",
|
||||
"repo.editor.require_signed_commit": "Cette branche nécessite une révision signée",
|
||||
"repo.editor.cherry_pick": "Picorer %s vers:",
|
||||
"repo.editor.revert": "Rétablir %s sur:",
|
||||
@@ -1317,10 +1317,11 @@
|
||||
"repo.editor.fork_create_description": "Vous ne pouvez pas modifier ce dépôt directement. Cependant, vous pouvez bifurquer ce dépôt, et créer une demande d’ajout avec vos contributions.",
|
||||
"repo.editor.fork_edit_description": "Vous ne pouvez pas modifier ce dépôt directement. Les modifications seront écrites sur une bifurcation <b>%s</b>, vous permettant de faire une demande d’ajout.",
|
||||
"repo.editor.fork_not_editable": "Vous avez bifurqué ce dépôt mais votre copie n’est pas modifiable.",
|
||||
"repo.editor.fork_failed_to_push_branch": "Impossible de pousser la branche %s vers votre dépôt.",
|
||||
"repo.editor.fork_failed_to_push_branch": "Impossible de soumettre la branche %s vers votre dépôt.",
|
||||
"repo.editor.fork_branch_exists": "La branche « %s » existe déjà dans votre bifurcation, veuillez choisir un nouveau nom.",
|
||||
"repo.commits.desc": "Naviguer dans l'historique des modifications.",
|
||||
"repo.commits.commits": "Révisions",
|
||||
"repo.commits.history_enable_follow_renames": "Inclure les renommages",
|
||||
"repo.commits.no_commits": "Pas de révisions en commun. \"%s\" et \"%s\" ont des historiques entièrement différents.",
|
||||
"repo.commits.nothing_to_compare": "Ces révisions sont équivalentes.",
|
||||
"repo.commits.search.tooltip": "Vous pouvez utiliser les mots-clés \"author:\", \"committer:\", \"after:\", ou \"before:\" pour filtrer votre recherche, ex.: \"revert author:Alice before:2019-01-13\".",
|
||||
@@ -1332,8 +1333,8 @@
|
||||
"repo.commits.older": "Précédemment",
|
||||
"repo.commits.newer": "Récemment",
|
||||
"repo.commits.signed_by": "Signé par",
|
||||
"repo.commits.signed_by_untrusted_user": "Signature provenant d'un utilisateur dilletant",
|
||||
"repo.commits.signed_by_untrusted_user_unmatched": "Signature discordante de l'auteur de la révision et provenant d'un utilisateur dilletant",
|
||||
"repo.commits.signed_by_untrusted_user": "Signé en dilettante par",
|
||||
"repo.commits.signed_by_untrusted_user_unmatched": "Signé, sans en être l’auteur, par",
|
||||
"repo.commits.gpg_key_id": "ID de la clé GPG",
|
||||
"repo.commits.ssh_key_fingerprint": "Empreinte numérique de la clé SSH",
|
||||
"repo.commits.view_path": "Voir à ce point de l'historique",
|
||||
@@ -1621,7 +1622,7 @@
|
||||
"repo.issues.lock.notice_2": "- Vous et les autres collaborateurs ayant accès à ce dépôt peuvent toujours laisser des commentaires que d’autres peuvent voir.",
|
||||
"repo.issues.lock.notice_3": "- Vous pouvez toujours déverrouiller ce ticket à l'avenir.",
|
||||
"repo.issues.unlock.notice_1": "- Tout le monde sera de nouveau en mesure de commenter ce ticket.",
|
||||
"repo.issues.unlock.notice_2": "- Vous pouvez toujours verrouiller ce ticket à l'avenir.",
|
||||
"repo.issues.unlock.notice_2": "- Vous pouvez toujours verrouiller ce ticket à l’avenir.",
|
||||
"repo.issues.lock.reason": "Motif de verrouillage",
|
||||
"repo.issues.lock.title": "Verrouiller la conversation sur ce ticket.",
|
||||
"repo.issues.unlock.title": "Déverrouiller la conversation sur ce ticket.",
|
||||
@@ -1817,9 +1818,9 @@
|
||||
"repo.pulls.is_checking": "Recherche de conflits de fusion…",
|
||||
"repo.pulls.is_ancestor": "Cette branche est déjà présente dans la branche ciblée. Il n'y a rien à fusionner.",
|
||||
"repo.pulls.is_empty": "Les changements sur cette branche sont déjà sur la branche cible. Cette révision sera vide.",
|
||||
"repo.pulls.required_status_check_failed": "Certains contrôles requis n'ont pas réussi.",
|
||||
"repo.pulls.required_status_check_missing": "Certains contrôles requis sont manquants.",
|
||||
"repo.pulls.required_status_check_administrator": "En tant qu'administrateur, vous pouvez toujours fusionner cette requête de pull.",
|
||||
"repo.pulls.required_status_check_failed": "Certains signaux requis n'ont pas réussi.",
|
||||
"repo.pulls.required_status_check_missing": "Certains signaux requis sont manquants.",
|
||||
"repo.pulls.required_status_check_administrator": "En tant qu’administrateur, vous pouvez fusionner cette demande d’ajout.",
|
||||
"repo.pulls.required_status_check_bypass_allowlist": "Vous êtes autorisé à contourner les règles de protection pour cette fusion.",
|
||||
"repo.pulls.blocked_by_approvals": "Cette demande d’ajout n’est pas suffisamment approuvée. %d approbations obtenues sur %d.",
|
||||
"repo.pulls.blocked_by_approvals_whitelisted": "Cette demande d’ajout n’a pas encore assez d’approbations. %d sur %d approbations de la part des utilisateurs ou équipes sur la liste autorisée.",
|
||||
@@ -1843,7 +1844,7 @@
|
||||
"repo.pulls.no_merge_desc": "Cette demande d’ajout ne peut être fusionnée car toutes les options de fusion du dépôt sont désactivées.",
|
||||
"repo.pulls.no_merge_helper": "Activez des options de fusion dans les paramètres du dépôt ou fusionnez la demande manuellement.",
|
||||
"repo.pulls.no_merge_wip": "Cette demande d’ajout ne peut pas être fusionnée car elle est marquée en chantier.",
|
||||
"repo.pulls.no_merge_not_ready": "Cette demande d’ajout n’est pas prête à être fusionnée, vérifiez les évaluations et le contrôle qualité.",
|
||||
"repo.pulls.no_merge_not_ready": "Cette demande d’ajout n’est pas prête à être fusionnée, vérifiez les évaluations et les signaux.",
|
||||
"repo.pulls.no_merge_access": "Vous n'êtes pas autorisé⋅e à fusionner cette demande d'ajout.",
|
||||
"repo.pulls.merge_pull_request": "Créer une révision de fusion",
|
||||
"repo.pulls.rebase_merge_pull_request": "Rebaser puis rattraper",
|
||||
@@ -1867,19 +1868,19 @@
|
||||
"repo.pulls.push_rejected_summary": "Message de rejet complet",
|
||||
"repo.pulls.push_rejected_no_message": "Échec de la fusion : la soumission a été rejetée sans raison. Contrôler les déclencheurs Git pour ce dépôt.",
|
||||
"repo.pulls.open_unmerged_pull_exists": "Vous ne pouvez pas rouvrir ceci car la demande d’ajout #%d, en attente, a des propriétés identiques.",
|
||||
"repo.pulls.status_checking": "Certains contrôles sont en attente",
|
||||
"repo.pulls.status_checks_success": "Tous les contrôles ont réussi",
|
||||
"repo.pulls.status_checks_warning": "Quelques vérifications ont signalé des avertissements",
|
||||
"repo.pulls.status_checks_failure_required": "Des vérifications obligatoires ont échoué",
|
||||
"repo.pulls.status_checks_failure_optional": "Des vérifications optionnelles ont échoué",
|
||||
"repo.pulls.status_checks_error": "Quelques vérifications ont signalé des erreurs",
|
||||
"repo.pulls.status_checking": "Certains signaux sont en attente",
|
||||
"repo.pulls.status_checks_success": "Tous les signaux ont réussi",
|
||||
"repo.pulls.status_checks_warning": "Des signaux déclarent des avertissements",
|
||||
"repo.pulls.status_checks_failure_required": "Des signaux requis ont échoués",
|
||||
"repo.pulls.status_checks_failure_optional": "Des signaux optionnels ont échoués",
|
||||
"repo.pulls.status_checks_error": "Des signaux rapportent des erreurs",
|
||||
"repo.pulls.status_checks_requested": "Requis",
|
||||
"repo.pulls.status_checks_details": "Détails",
|
||||
"repo.pulls.status_checks_hide_all": "Masquer toutes les vérifications",
|
||||
"repo.pulls.status_checks_hide_all": "Masquer les signaux",
|
||||
"repo.pulls.status_checks_show_all": "Afficher toutes les vérifications",
|
||||
"repo.pulls.status_checks_approve_all": "Accepter tous les flux de travail",
|
||||
"repo.pulls.status_checks_need_approvals": "%d flux de travail en attente d’approbation",
|
||||
"repo.pulls.status_checks_need_approvals_helper": "Ce flux de travail ne s’exécutera qu’après l’approbation par le mainteneur du dépôt.",
|
||||
"repo.pulls.status_checks_approve_all": "Approuver toutes les procédures",
|
||||
"repo.pulls.status_checks_need_approvals": "%d procédure(s) en attente d’approbation",
|
||||
"repo.pulls.status_checks_need_approvals_helper": "Cette procédure ne s’executera qu’après l’approbation par le mainteneur du dépôt.",
|
||||
"repo.pulls.update_branch": "Actualiser la branche par fusion",
|
||||
"repo.pulls.update_branch_rebase": "Actualiser la branche par rebasage",
|
||||
"repo.pulls.update_branch_success": "La mise à jour de la branche a réussi",
|
||||
@@ -1963,8 +1964,8 @@
|
||||
"repo.ext_wiki.desc": "Lier un wiki externe.",
|
||||
"repo.wiki": "Wiki",
|
||||
"repo.wiki.welcome": "Bienvenue sur le Wiki.",
|
||||
"repo.wiki.welcome_desc": "Le wiki vous permet d'écrire ou de partager de la documentation avec vos collaborateurs.",
|
||||
"repo.wiki.desc": "Écrire et partager de la documentation avec vos collaborateurs.",
|
||||
"repo.wiki.welcome_desc": "Le wiki vous permet de rédiger et partager de la documentation avec des collaborateurs.",
|
||||
"repo.wiki.desc": "Rédiger et partager de la documentation avec des collaborateurs.",
|
||||
"repo.wiki.create_first_page": "Créer la première page",
|
||||
"repo.wiki.page": "Page",
|
||||
"repo.wiki.filter_page": "Filtrer la page",
|
||||
@@ -1986,7 +1987,7 @@
|
||||
"repo.wiki.pages": "Pages",
|
||||
"repo.wiki.last_updated": "Dernière mise à jour: %s",
|
||||
"repo.wiki.page_name_desc": "Entrez un nom pour cette page Wiki. Certains noms spéciaux sont « Home », « _Sidebar » et « _Footer ».",
|
||||
"repo.wiki.original_git_entry_tooltip": "Voir le fichier Git original au lieu d'utiliser un lien convivial.",
|
||||
"repo.wiki.original_git_entry_tooltip": "Voir le fichier Git original au lieu d’utiliser un lien convivial.",
|
||||
"repo.activity": "Activité",
|
||||
"repo.activity.navbar.pulse": "Impulsion",
|
||||
"repo.activity.navbar.code_frequency": "Fréquence du code",
|
||||
@@ -2066,23 +2067,23 @@
|
||||
"repo.settings.public_access": "Accès public",
|
||||
"repo.settings.public_access_desc": "Configurer les permissions des visiteurs publics remplaçant les valeurs par défaut de ce dépôt.",
|
||||
"repo.settings.public_access.docs.not_set": "Non défini : ne donne aucune permission supplémentaire. Les règles du dépôt et les permissions des utilisateurs font foi.",
|
||||
"repo.settings.public_access.docs.anonymous_read": "Lecture anonyme : les utilisateurs qui ne sont pas connectés peuvent consulter la ressource.",
|
||||
"repo.settings.public_access.docs.everyone_read": "Consultation collective : tous les utilisateurs connectés peuvent consulter la ressource. Mettre les tickets et demandes d’ajouts en accès public signifie que les utilisateurs connectés peuvent en créer.",
|
||||
"repo.settings.public_access.docs.everyone_write": "Participation collective : tous les utilisateurs connectés ont la permission d’écrire sur la ressource. Seule le Wiki supporte cette autorisation.",
|
||||
"repo.settings.public_access.docs.anonymous_read": "Accès anonyme : les visiteurs non connectés peuvent consulter cette section.",
|
||||
"repo.settings.public_access.docs.everyone_read": "Consultation collective : tous les utilisateurs connectés peuvent consulter cette section. Pour les Tickets et les Demandes d’ajouts, cela permet aussi aux utilisateurs connectés d’en créer.",
|
||||
"repo.settings.public_access.docs.everyone_write": "Participation collective : tous les utilisateurs connectés peuvent participer à la section. Seul le Wiki supporte cette permission.",
|
||||
"repo.settings.collaboration": "Collaborateurs",
|
||||
"repo.settings.collaboration.admin": "Administrateur",
|
||||
"repo.settings.collaboration.write": "Écriture",
|
||||
"repo.settings.collaboration.read": "Lecture",
|
||||
"repo.settings.collaboration.owner": "Propriétaire",
|
||||
"repo.settings.collaboration.undefined": "Indéfini",
|
||||
"repo.settings.collaboration.per_unit": "Permissions de ressource",
|
||||
"repo.settings.collaboration.per_unit": "Permissions de section",
|
||||
"repo.settings.hooks": "Déclencheurs web",
|
||||
"repo.settings.githooks": "Déclencheurs Git",
|
||||
"repo.settings.basic_settings": "Paramètres de base",
|
||||
"repo.settings.mirror_settings": "Réglages Miroir",
|
||||
"repo.settings.mirror_settings.docs": "Configurez votre dépôt pour synchroniser automatiquement les révisions, étiquettes et branches avec un autre dépôt.",
|
||||
"repo.settings.mirror_settings.docs.disabled_pull_mirror.instructions": "Configurez votre projet pour soumettre automatiquement les révisions, étiquettes et branches vers un autre dépôt. Les miroirs ont été désactivés par l'administrateur de votre site.",
|
||||
"repo.settings.mirror_settings.docs.disabled_push_mirror.instructions": "Configurez votre projet pour synchroniser automatiquement les révisions, étiquettes et branches d'un autre dépôt.",
|
||||
"repo.settings.mirror_settings.docs.disabled_pull_mirror.instructions": "Configurez votre projet pour soumettre automatiquement les révisions, étiquettes et branches vers un autre dépôt. Les miroirs ont été désactivés par l’administrateur de votre site.",
|
||||
"repo.settings.mirror_settings.docs.disabled_push_mirror.instructions": "Configurez votre projet pour synchroniser automatiquement les révisions, étiquettes et branches d’un autre dépôt.",
|
||||
"repo.settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning": "Pour l’instant, cela ne peut être fait que dans le menu « Nouvelle migration ». Pour plus d’informations, veuillez consulter :",
|
||||
"repo.settings.mirror_settings.docs.disabled_push_mirror.info": "Les miroirs push ont été désactivés par l’administrateur de votre site.",
|
||||
"repo.settings.mirror_settings.docs.no_new_mirrors": "Votre dépôt se synchronise avec un dépôt distant. Vous ne pouvez pas créer de nouveaux miroirs pour le moment.",
|
||||
@@ -2201,11 +2202,13 @@
|
||||
"repo.settings.trust_model.default.desc": "Utiliser le niveau de confiance configuré par défaut pour cette instance Gitea.",
|
||||
"repo.settings.trust_model.collaborator": "Collaborateur",
|
||||
"repo.settings.trust_model.collaborator.long": "Collaborateur : ne se fier qu'aux signatures des collaborateurs du dépôt",
|
||||
"repo.settings.trust_model.collaborator.desc": "La signature d’une révision est dite « fiable » si elle correspond à un collaborateur du dépôt, indépendamment de son auteur. À défaut, si elle correspond à l’auteur de la révision, elle sera « dilettante », et « discordante » sinon.",
|
||||
"repo.settings.trust_model.collaborator.desc": "Une révision est réputée authentifiée si elle est signée par un collaborateur du dépôt. Si elle n‘est que signée par son auteur, elle sera réputée dilettante, et discordante sinon.",
|
||||
"repo.settings.trust_model.committer": "Auteur",
|
||||
"repo.settings.trust_model.committer.long": "Auteur : ne se fier qu’aux signatures des auteurs des révisions (mimique GitHub en forçant Gitea à co-signer ses révisions).",
|
||||
"repo.settings.trust_model.committer.desc": "Une révision est réputée authentifiée si elle est signée par son auteur, et discordante si les signatures diffèrent. Cela force Gitea à signer ses propres révisions en créditant l’auteur original en pied de révision \"Co-authored-by:\" et \"Co-committed-by:\". La clé par défaut de Gitea doit correspondre à celle d’un utilisateur existant.",
|
||||
"repo.settings.trust_model.collaboratorcommitter": "Collaborateur et Auteur",
|
||||
"repo.settings.trust_model.collaboratorcommitter.long": "Collaborateur et Auteur : ne se fier qu'aux signatures des auteurs collaborant au dépôt",
|
||||
"repo.settings.trust_model.collaboratorcommitter.desc": "Une révision est réputée authentifiée si est elle signée par son auteur étant lui-même collaborateur du dépôt. Si elle n‘est que signée par son auteur, elle sera réputée dilettante, et discordante sinon. Cela force Gitea à signer ses propres révisions en créditant l’auteur original en pied de révision \"Co-authored-by:\". La clé par défaut de Gitea doit correspondre à celle d’un utilisateur existant.",
|
||||
"repo.settings.wiki_delete": "Supprimer les données du Wiki",
|
||||
"repo.settings.wiki_delete_desc": "Supprimer les données du wiki d'un dépôt est permanent. Cette action est irréversible.",
|
||||
"repo.settings.wiki_delete_notices_1": "- Ceci supprimera de manière permanente et désactivera le wiki de dépôt pour %s.",
|
||||
@@ -2218,7 +2221,7 @@
|
||||
"repo.settings.delete_notices_fork_1": "- Les bifurcations de ce dépôt deviendront indépendants après suppression.",
|
||||
"repo.settings.deletion_success": "Le dépôt a été supprimé.",
|
||||
"repo.settings.update_settings_success": "Les options du dépôt ont été mises à jour.",
|
||||
"repo.settings.update_settings_no_unit": "Impossible de désactiver toutes les fonctionnalités d'un dépôt. Vous ne pourrez gère l'utiliser.",
|
||||
"repo.settings.update_settings_no_unit": "Si vous désactivez toutes les sections du dépôt, vous ne pourrez gère plus l’utiliser.",
|
||||
"repo.settings.confirm_delete": "Supprimer le dépôt",
|
||||
"repo.settings.add_collaborator": "Ajouter un collaborateur",
|
||||
"repo.settings.add_collaborator_success": "Le collaborateur a été ajouté.",
|
||||
@@ -2292,7 +2295,7 @@
|
||||
"repo.settings.event_release": "Publication",
|
||||
"repo.settings.event_release_desc": "Publication publiée, mise à jour ou supprimée.",
|
||||
"repo.settings.event_push": "Soumission",
|
||||
"repo.settings.event_force_push": "Poussée forcée",
|
||||
"repo.settings.event_force_push": "Soumission forcée",
|
||||
"repo.settings.event_push_desc": "Soumission Git.",
|
||||
"repo.settings.event_repository": "Dépôt",
|
||||
"repo.settings.event_repository_desc": "Dépôt créé ou supprimé.",
|
||||
@@ -2326,11 +2329,11 @@
|
||||
"repo.settings.event_pull_request_review_request_desc": "Création ou suppresion de demandes d’évaluation.",
|
||||
"repo.settings.event_pull_request_approvals": "Approbations de demande d'ajout",
|
||||
"repo.settings.event_pull_request_merge": "Fusion de demande d'ajout",
|
||||
"repo.settings.event_header_workflow": "Événements du flux de travail",
|
||||
"repo.settings.event_workflow_run": "Exécution du flux de travail",
|
||||
"repo.settings.event_workflow_run_desc": "Tâche du flux de travail Gitea Actions ajoutée, en attente, en cours ou terminée.",
|
||||
"repo.settings.event_workflow_job": "Tâches du flux de travail",
|
||||
"repo.settings.event_workflow_job_desc": "Tâches du flux de travail Gitea Actions en file d’attente, en attente, en cours ou terminée.",
|
||||
"repo.settings.event_header_workflow": "Événements de procédure",
|
||||
"repo.settings.event_workflow_run": "Exécution de procédure",
|
||||
"repo.settings.event_workflow_run_desc": "Exécution des procédures des Actions Gitea ajoutée, en attente, en cours ou terminées.",
|
||||
"repo.settings.event_workflow_job": "Missions de la procédure",
|
||||
"repo.settings.event_workflow_job_desc": "Les missions ajoutées, en attente, en cours ou terminées des Actions Gitea.",
|
||||
"repo.settings.event_package": "Paquet",
|
||||
"repo.settings.event_package_desc": "Paquet créé ou supprimé.",
|
||||
"repo.settings.branch_filter": "Filtre de branche",
|
||||
@@ -2390,44 +2393,44 @@
|
||||
"repo.settings.protected_branch_can_push_no": "Vous ne pouvez pas soumettre",
|
||||
"repo.settings.branch_protection": "Paramètres de protection de branches pour la branche <b>%s</b>",
|
||||
"repo.settings.protect_this_branch": "Activer la protection de branche",
|
||||
"repo.settings.protect_this_branch_desc": "Empêche les suppressions et limite les poussées et fusions sur cette branche.",
|
||||
"repo.settings.protect_this_branch_desc": "Empêche la suppression et Git de soumettre et fusionner sur cette branche.",
|
||||
"repo.settings.protect_disable_push": "Désactiver la soumission",
|
||||
"repo.settings.protect_disable_push_desc": "Aucune soumission ne sera possible sur cette branche.",
|
||||
"repo.settings.protect_disable_force_push": "Désactiver les poussés forcées",
|
||||
"repo.settings.protect_disable_force_push_desc": "Aucune poussée forcée ne sera possible sur cette branche.",
|
||||
"repo.settings.protect_disable_force_push": "Désactiver la soumission forcée",
|
||||
"repo.settings.protect_disable_force_push_desc": "Aucune soumission forcée ne sera possible sur cette branche.",
|
||||
"repo.settings.protect_enable_push": "Activer la soumission",
|
||||
"repo.settings.protect_enable_push_desc": "Toute personne ayant un accès en écriture sera autorisée à soumettre sur cette branche (sans forcer).",
|
||||
"repo.settings.protect_enable_force_push_all": "Activer les poussées forcées",
|
||||
"repo.settings.protect_enable_force_push_all_desc": "Toute personne pouvant pousser pourra forcer sur cette branche.",
|
||||
"repo.settings.protect_enable_force_push_all": "Activer la soumission forcée",
|
||||
"repo.settings.protect_enable_force_push_all_desc": "Toute personne pouvant soumettre pourra forcer sur cette branche.",
|
||||
"repo.settings.protect_enable_force_push_allowlist": "Soumission forcée sur autorisation uniquement",
|
||||
"repo.settings.protect_enable_force_push_allowlist_desc": "Seuls les utilisateurs ou équipes autorisés ayants un droit de pousser seront autorisés à pousser en force sur cette branche.",
|
||||
"repo.settings.protect_enable_force_push_allowlist_desc": "Seuls les utilisateurs ou équipes autorisés ayants un droit de soumission seront autorisés à soumettre en force sur cette branche.",
|
||||
"repo.settings.protect_enable_merge": "Activer la fusion",
|
||||
"repo.settings.protect_enable_merge_desc": "Toute personne ayant un accès en écriture sera autorisée à fusionner les demandes d'ajout dans cette branche.",
|
||||
"repo.settings.protect_whitelist_committers": "Soumissions sur autorisation uniquement",
|
||||
"repo.settings.protect_whitelist_committers_desc": "Seuls les utilisateurs ou les équipes autorisés pourront pousser sur cette branche (sans forcer).",
|
||||
"repo.settings.protect_whitelist_committers_desc": "Seuls les utilisateurs ou les équipes autorisés pourront soumettre sur cette branche (sans forcer).",
|
||||
"repo.settings.protect_whitelist_deploy_keys": "Clés de déploiement pouvant écrire autorisées à pousser.",
|
||||
"repo.settings.protect_whitelist_users": "Utilisateurs autorisés à pousser :",
|
||||
"repo.settings.protect_whitelist_teams": "Équipes autorisées à pousser :",
|
||||
"repo.settings.protect_force_push_allowlist_users": "Utilisateurs autorisés à pousser en force :",
|
||||
"repo.settings.protect_force_push_allowlist_teams": "Équipes autorisées à pousser en force :",
|
||||
"repo.settings.protect_force_push_allowlist_deploy_keys": "Clés de déploiement pouvant pousser autorisées à pousser en force.",
|
||||
"repo.settings.protect_force_push_allowlist_users": "Utilisateurs autorisés à soumettre en force :",
|
||||
"repo.settings.protect_force_push_allowlist_teams": "Équipes autorisées à soumettre en force :",
|
||||
"repo.settings.protect_force_push_allowlist_deploy_keys": "Inclure aussi les clés de déploiement autorisées à soumettre.",
|
||||
"repo.settings.protect_merge_whitelist_committers": "Fusion sur autorisation uniquement",
|
||||
"repo.settings.protect_merge_whitelist_committers_desc": "N’autoriser que les utilisateurs et les équipes listés à appliquer les demandes de fusion sur cette branche.",
|
||||
"repo.settings.protect_merge_whitelist_users": "Utilisateurs autorisés à fusionner :",
|
||||
"repo.settings.protect_merge_whitelist_teams": "Équipes autorisées à fusionner :",
|
||||
"repo.settings.protect_bypass_allowlist": "Contourner la protection de la branche",
|
||||
"repo.settings.protect_enable_bypass_allowlist": "Autoriser des utilisateurs et des équipes à contourner les restrictions de branche",
|
||||
"repo.settings.protect_enable_bypass_allowlist_desc": "Les utilisateurs ou équipes autorisés peuvent fusionner ou pousser des changements nonobstant les règles d’approbations, de vérifications de statut et les protections fichiers.",
|
||||
"repo.settings.protect_enable_bypass_allowlist_desc": "Les utilisateurs ou équipes autorisés peuvent fusionner ou soumettre des changements nonobstant les règles d’approbations, de vérifications des signaux et les protections de fichiers.",
|
||||
"repo.settings.protect_bypass_allowlist_users": "Liste d’utilisateurs autorisés à contourner les protections :",
|
||||
"repo.settings.protect_bypass_allowlist_teams": "Liste d’équipes autorisées à contourner les protections :",
|
||||
"repo.settings.protect_check_status_contexts": "Activer le Contrôle Qualité",
|
||||
"repo.settings.protect_status_check_patterns": "Motifs de vérification des statuts :",
|
||||
"repo.settings.protect_status_check_patterns_desc": "Entrez des motifs pour spécifier quelles vérifications doivent réussir avant que des branches puissent être fusionnées. Un motif par ligne. Un motif ne peut être vide.",
|
||||
"repo.settings.protect_check_status_contexts_desc": "Exiger le status « succès » avant de fusionner. Quand activée, une branche protégée ne peux accepter que des soumissions ou des fusions ayant le status « succès ». Lorsqu'il n’y a pas de contexte, la dernière révision fait foi.",
|
||||
"repo.settings.protect_check_status_contexts_list": "Contrôles qualité trouvés au cours de la semaine dernière pour ce dépôt",
|
||||
"repo.settings.protect_check_status_contexts": "Activer les signaux",
|
||||
"repo.settings.protect_status_check_patterns": "Motifs de signal :",
|
||||
"repo.settings.protect_status_check_patterns_desc": "Entrez des motifs pour spécifier quelles signaux doivent réussir avant que des branches puissent être fusionnées. Un motif par ligne. Un motif ne peut être vide.",
|
||||
"repo.settings.protect_check_status_contexts_desc": "Exiger la réussite des signaux avant de fusionner. Quand activé, une branche protégée ne peux accepter que des soumissions ou des fusions ayant le status « succès ». Lorsqu'il n’y a pas de contexte, la dernière révision fait foi.",
|
||||
"repo.settings.protect_check_status_contexts_list": "Signaux trouvés au cours de la semaine passée pour ce dépôt",
|
||||
"repo.settings.protect_status_check_matched": "Correspondant",
|
||||
"repo.settings.protect_invalid_status_check_pattern": "Motif de vérification des statuts incorrect : « %s ».",
|
||||
"repo.settings.protect_no_valid_status_check_patterns": "Aucun motif de vérification des statuts valide.",
|
||||
"repo.settings.protect_invalid_status_check_pattern": "Motif de signal invalide : « %s ».",
|
||||
"repo.settings.protect_no_valid_status_check_patterns": "Aucun motif de signaux valide.",
|
||||
"repo.settings.protect_required_approvals": "Minimum d'approbations requis :",
|
||||
"repo.settings.protect_required_approvals_desc": "Permet de fusionner les demandes d’ajout lorsque suffisamment d’évaluation sont positives.",
|
||||
"repo.settings.protect_approvals_whitelist_enabled": "Restreindre les approbations sur autorisation uniquement",
|
||||
@@ -2453,15 +2456,15 @@
|
||||
"repo.settings.remove_protected_branch_success": "La règle de protection de branche \"%s\" a été retirée.",
|
||||
"repo.settings.remove_protected_branch_failed": "Impossible de retirer la règle de protection de branche \"%s\".",
|
||||
"repo.settings.protected_branch_deletion": "Désactiver la protection de branche",
|
||||
"repo.settings.protected_branch_deletion_desc": "Désactiver la protection de branche permet aux utilisateurs ayant accès en écriture de pousser des modifications sur la branche. Continuer ?",
|
||||
"repo.settings.protected_branch_deletion_desc": "Désactiver la protection de branche permet aux utilisateurs ayant accès en écriture de soumettre des modifications sur la branche. Continuer ?",
|
||||
"repo.settings.block_rejected_reviews": "Bloquer la fusion en cas d’évaluations négatives",
|
||||
"repo.settings.block_rejected_reviews_desc": "La fusion ne sera pas possible lorsque des modifications sont demandées par les évaluateurs officiels, même s'il y a suffisamment d’approbations.",
|
||||
"repo.settings.block_on_official_review_requests": "Bloquer la fusion en cas de demande d’évaluation officielle",
|
||||
"repo.settings.block_on_official_review_requests_desc": "La fusion ne sera pas possible tant qu’elle aura des demandes d’évaluations officielles, même s'il y a suffisamment d’approbations.",
|
||||
"repo.settings.block_outdated_branch": "Bloquer la fusion si la demande d'ajout est obsolète",
|
||||
"repo.settings.block_outdated_branch_desc": "La fusion ne sera pas possible lorsque la branche principale est derrière la branche de base.",
|
||||
"repo.settings.block_admin_merge_override": "Les administrateurs doivent respecter les règles de protection des branches",
|
||||
"repo.settings.block_admin_merge_override_desc": "Les administrateurs sont également soumis aux règles de protection des branches. En activant la liste d’autorisation de contournement, les utilisateurs et équipes qui y figurent peuvent toujours contourner ces règles.",
|
||||
"repo.settings.block_admin_merge_override": "Inclure les administrateurs dans l’application de la règle",
|
||||
"repo.settings.block_admin_merge_override_desc": "Les administrateurs sont également soumis aux règles de protection des branches. En revanche, les utilisateurs et équipes qui figurent sur la liste dérogatoire y sont exemptés.",
|
||||
"repo.settings.default_branch_desc": "Sélectionnez une branche par défaut pour les révisions.",
|
||||
"repo.settings.default_target_branch_desc": "Les demandes d’ajout peuvent utiliser une branche cible différente, telle que définie dans la section Demandes d’ajouts des Paramètres avancés du dépôt.",
|
||||
"repo.settings.merge_style_desc": "Styles de fusion",
|
||||
@@ -2596,7 +2599,9 @@
|
||||
"repo.diff.review.reject": "Demander des changements",
|
||||
"repo.diff.review.self_approve": "Les auteurs d’une demande d’ajout ne peuvent pas approuver leur propre demande d’ajout",
|
||||
"repo.diff.committed_by": "révisé par",
|
||||
"repo.diff.coauthored_by": "coécrit par",
|
||||
"repo.commits.avatar_stack_and": "et",
|
||||
"repo.commits.avatar_stack_people": "%d personne(s)",
|
||||
"repo.diff.protected": "Protégé",
|
||||
"repo.diff.image.side_by_side": "Côte à côte",
|
||||
"repo.diff.image.swipe": "Glisser",
|
||||
@@ -2716,7 +2721,7 @@
|
||||
"repo.error.csv.too_large": "Impossible de visualiser le fichier car il est trop volumineux.",
|
||||
"repo.error.csv.unexpected": "Impossible de visualiser ce fichier car il contient un caractère inattendu ligne %d, colonne %d.",
|
||||
"repo.error.csv.invalid_field_count": "Impossible de visualiser ce fichier car il contient un nombre de champs incorrect à la ligne %d.",
|
||||
"repo.error.broken_git_hook": "Les crochets Git de ce dépôt semblent cassés. Veuillez suivre la <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation</a> pour les corriger, puis pousser des révisions pour actualiser le statut.",
|
||||
"repo.error.broken_git_hook": "Les crochets Git de ce dépôt semblent cassés. Veuillez suivre la <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation</a> pour les corriger, puis soummettre des révisions pour actualiser le statut.",
|
||||
"graphs.component_loading": "Chargement de %s…",
|
||||
"graphs.component_loading_failed": "Impossible de charger %s.",
|
||||
"graphs.component_loading_info": "Ça prend son temps…",
|
||||
@@ -2724,6 +2729,7 @@
|
||||
"graphs.code_frequency.what": "fréquence du code",
|
||||
"graphs.contributors.what": "contributions",
|
||||
"graphs.recent_commits.what": "révisions récentes",
|
||||
"graphs.chart_zoom_hint": "Glisser : zoom, Maj + Glisser : pano, Double-clic : recentrer",
|
||||
"org.org_name_holder": "Nom de l'organisation",
|
||||
"org.org_full_name_holder": "Nom complet de l'organisation",
|
||||
"org.org_name_helper": "Le nom de l'organisation doit être court et mémorable.",
|
||||
@@ -2745,8 +2751,8 @@
|
||||
"org.team_desc_helper": "Décrire le but ou le rôle de l’équipe.",
|
||||
"org.team_access_desc": "Accès au dépôt",
|
||||
"org.team_permission_desc": "Autorisation",
|
||||
"org.team_unit_desc": "Permettre l’accès aux Sections du dépôt",
|
||||
"org.team_unit_disabled": "(Désactivé)",
|
||||
"org.team_unit_desc": "Permettre l’accès aux sections du dépôt",
|
||||
"org.team_unit_disabled": "(Désactivée)",
|
||||
"org.form.name_been_taken": "Le nom d’organisation « %s » a déjà été utilisé.",
|
||||
"org.form.name_reserved": "Le nom d'organisation \"%s\" est réservé.",
|
||||
"org.form.name_pattern_not_allowed": "Le motif « %s » n'est pas autorisé dans un nom d'organisation.",
|
||||
@@ -2814,15 +2820,15 @@
|
||||
"org.teams.can_create_org_repo": "Créer des dépôts",
|
||||
"org.teams.can_create_org_repo_helper": "Les membres peuvent créer de nouveaux dépôts dans l'organisation. Le créateur obtiendra l'accès administrateur au nouveau dépôt.",
|
||||
"org.teams.none_access": "Aucun accès",
|
||||
"org.teams.none_access_helper": "Les membres ne peuvent voir ou faire quoi que ce soit sur cette partie. Sans effet pour les dépôts publics.",
|
||||
"org.teams.none_access_helper": "Les membres ne peuvent ni consulter ni participer à cette section. Ne s’applique pas pour les dépôts publics.",
|
||||
"org.teams.general_access": "Accès général",
|
||||
"org.teams.general_access_helper": "Les permissions des membres seront déterminées par la table des permissions ci-dessous.",
|
||||
"org.teams.read_access": "Lecture",
|
||||
"org.teams.read_access_helper": "Les membres peuvent voir et cloner les dépôts de l'équipe.",
|
||||
"org.teams.read_access_helper": "Les membres peuvent voir et cloner les dépôts de l’équipe.",
|
||||
"org.teams.write_access": "Écriture",
|
||||
"org.teams.write_access_helper": "Les membres peuvent voir et pousser dans les dépôts de l'équipe.",
|
||||
"org.teams.write_access_helper": "Les membres peuvent consulter et soumettre sur les dépôts de l’équipe.",
|
||||
"org.teams.admin_access": "Accès Administrateur",
|
||||
"org.teams.admin_access_helper": "Les membres peuvent tirer et pousser des modifications vers les dépôts de l'équipe, et y ajouter des collaborateurs.",
|
||||
"org.teams.admin_access_helper": "Les membres peuvent extraire et soumettre les dépôts de l’équipe et y ajouter des collaborateurs.",
|
||||
"org.teams.no_desc": "Aucune description",
|
||||
"org.teams.settings": "Paramètres",
|
||||
"org.teams.owners_permission_desc": "Les propriétaires ont un accès complet à <strong>tous les dépôts</strong> et disposent <strong> d'un accès administrateur</strong> de l'organisation.",
|
||||
@@ -2839,8 +2845,8 @@
|
||||
"org.teams.delete_team_desc": "Supprimer une équipe supprime l'accès aux dépôts à ses membres. Continuer ?",
|
||||
"org.teams.delete_team_success": "L’équipe a été supprimée.",
|
||||
"org.teams.read_permission_desc": "Cette équipe permet l'accès en <strong>lecture</strong> : les membres peuvent voir et dupliquer ses dépôts.",
|
||||
"org.teams.write_permission_desc": "Cette équipe permet l'accès en <strong>écriture</strong> : les membres peuvent participer à ses dépôts.",
|
||||
"org.teams.admin_permission_desc": "Cette équipe permet l'accès <strong>administrateur</strong> : les membres peuvent voir, participer et ajouter des collaborateurs à ses dépôts.",
|
||||
"org.teams.write_permission_desc": "Cette équipe accorde un accès en <strong>écriture</strong> : les membres peuvent participer à ses dépôts.",
|
||||
"org.teams.admin_permission_desc": "Cette équipe accorde un accès <strong>administrateur</strong> : les membres peuvent consulter, participer et ajouter des collaborateurs à ses dépôts.",
|
||||
"org.teams.create_repo_permission_desc": "De plus, cette équipe accorde la permission <strong>Créer un dépôt</strong> : les membres peuvent créer de nouveaux dépôts dans l'organisation.",
|
||||
"org.teams.repositories": "Dépôts de l'Équipe",
|
||||
"org.teams.remove_all_repos_title": "Supprimer tous les dépôts de l'équipe",
|
||||
@@ -2857,8 +2863,8 @@
|
||||
"org.teams.all_repositories": "Tous les dépôts",
|
||||
"org.teams.all_repositories_helper": "L'équipe a accès à tous les dépôts. Sélectionner ceci <strong>ajoutera tous les dépôts existants</strong> à l'équipe.",
|
||||
"org.teams.all_repositories_read_permission_desc": "Cette équipe accorde l'accès <strong>en lecture</strong> à <strong>tous les dépôts</strong> : les membres peuvent voir et cloner les dépôts.",
|
||||
"org.teams.all_repositories_write_permission_desc": "Cette équipe accorde l'accès <strong>en écriture</strong> à <strong>tous les dépôts</strong> : les membres peuvent lire et écrire dans les dépôts.",
|
||||
"org.teams.all_repositories_admin_permission_desc": "Cette équipe accorde l'accès <strong>administrateur</strong> à <strong>tous les dépôts</strong> : les membres peuvent lire, écrire dans et ajouter des collaborateurs aux dépôts.",
|
||||
"org.teams.all_repositories_write_permission_desc": "Cette équipe accorde un accès <strong>en écriture</strong> à <strong>tous les dépôts</strong> : les membres peuvent participer aux dépôts.",
|
||||
"org.teams.all_repositories_admin_permission_desc": "Cette équipe accorde un accès <strong>administrateur</strong> à <strong>tous les dépôts</strong> : les membres peuvent consulter, participer et ajouter des collaborateurs aux dépôts.",
|
||||
"org.teams.visibility": "Visibilité",
|
||||
"org.teams.visibility_private": "Privé",
|
||||
"org.teams.visibility_private_helper": "Visible uniquement aux membres de l’équipe et aux propriétaires de l’organisation.",
|
||||
@@ -3008,7 +3014,7 @@
|
||||
"admin.dashboard.gc_lfs": "Purger les métaobjets LFS",
|
||||
"admin.dashboard.stop_zombie_tasks": "Arrêter les tâches zombies",
|
||||
"admin.dashboard.stop_endless_tasks": "Arrêter les tâches interminables",
|
||||
"admin.dashboard.cancel_abandoned_jobs": "Annuler les actions des tâches abandonnés",
|
||||
"admin.dashboard.cancel_abandoned_jobs": "Annuler les actions des missions abandonnées",
|
||||
"admin.dashboard.start_schedule_tasks": "Démarrer les tâches planifiées",
|
||||
"admin.dashboard.sync_branch.started": "Début de la synchronisation des branches",
|
||||
"admin.dashboard.sync_tag.started": "Synchronisation des étiquettes",
|
||||
@@ -3445,7 +3451,7 @@
|
||||
"action.merge_pull_request": "a fusionné la demande d’ajout <a href=\"%[1]s\">%[3]s#%[2]s</a>",
|
||||
"action.auto_merge_pull_request": "a fusionné automatiquement la demande d’ajout <a href=\"%[1]s\">%[3]s#%[2]s</a>",
|
||||
"action.transfer_repo": "a transféré le dépôt <code>%s</code> vers <a href=\"%s\">%s</a>",
|
||||
"action.push_tag": "a poussé l’étiquette <a href=\"%[2]s\">%[3]s</a> de <a href=\"%[1]s\">%[4]s</a>",
|
||||
"action.push_tag": "a soumis l’étiquette <a href=\"%[2]s\">%[3]s</a> de <a href=\"%[1]s\">%[4]s</a>",
|
||||
"action.delete_tag": "a supprimé l’étiquette %[2]s de <a href=\"%[1]s\">%[3]s</a>",
|
||||
"action.delete_branch": "a supprimé la branche %[2]s de <a href=\"%[1]s\">%[3]s</a>",
|
||||
"action.compare_branch": "Comparer",
|
||||
@@ -3505,9 +3511,9 @@
|
||||
"gpg.error.failed_retrieval_gpg_keys": "Impossible de récupérer la clé liée au compte de l'auteur",
|
||||
"gpg.error.probable_bad_signature": "AVERTISSEMENT ! Bien qu’il y ait une clé avec cet ID dans la base de données, elle ne vérifie pas cette révision ! Cette révision est SUSPECTE.",
|
||||
"gpg.error.probable_bad_default_signature": "AVERTISSEMENT ! Bien que la clé par défaut ait cet ID, elle ne vérifie pas cette révision ! Cette révision est SUSPECTE.",
|
||||
"units.unit": "Ressource",
|
||||
"units.error.no_unit_allowed_repo": "Vous n'êtes pas autorisé à accéder à n'importe quelle section de ce dépôt.",
|
||||
"units.error.unit_not_allowed": "Vous n'êtes pas autorisé à accéder à cette section du dépôt.",
|
||||
"units.unit": "Section",
|
||||
"units.error.no_unit_allowed_repo": "Vous n’êtes pas autorisé à accéder à quelconque section de ce dépôt.",
|
||||
"units.error.unit_not_allowed": "Vous n’êtes pas autorisé à accéder à cette section du dépôt.",
|
||||
"packages.title": "Paquets",
|
||||
"packages.desc": "Gérer les paquets du dépôt.",
|
||||
"packages.empty": "Il n'y pas de paquet pour le moment.",
|
||||
@@ -3721,64 +3727,66 @@
|
||||
"actions.status.cancelling": "Annulation",
|
||||
"actions.status.skipped": "Ignoré",
|
||||
"actions.status.blocked": "Bloqué",
|
||||
"actions.runners": "Exécuteurs",
|
||||
"actions.runners.runner_manage_panel": "Gestion des exécuteurs",
|
||||
"actions.runners.new": "Créer un nouvel exécuteur",
|
||||
"actions.runners.new_notice": "Comment démarrer un exécuteur",
|
||||
"actions.runners": "Opérateurs",
|
||||
"actions.runners.runner_manage_panel": "Gestion des opérateurs",
|
||||
"actions.runners.new": "Créer un nouvel opérateur",
|
||||
"actions.runners.new_notice": "Comment démarrer un opérateur",
|
||||
"actions.runners.status": "Statut",
|
||||
"actions.runners.id": "ID",
|
||||
"actions.runners.name": "Nom",
|
||||
"actions.runners.owner_type": "Type",
|
||||
"actions.runners.availability": "Disponibilité",
|
||||
"actions.runners.availability": "Activation",
|
||||
"actions.runners.description": "Description",
|
||||
"actions.runners.labels": "Labels",
|
||||
"actions.runners.labels": "Libellés",
|
||||
"actions.runners.last_online": "Dernière fois en ligne",
|
||||
"actions.runners.runner_title": "Exécuteur",
|
||||
"actions.runners.task_list": "Tâches récentes sur cet exécuteur",
|
||||
"actions.runners.runner_title": "Opérateur",
|
||||
"actions.runners.task_list": "Tâches récentes de cet opérateur",
|
||||
"actions.runners.task_list.no_tasks": "Il n'y a pas de tâche ici.",
|
||||
"actions.runners.task_list.run": "Exécuter",
|
||||
"actions.runners.task_list.run": "Exécution",
|
||||
"actions.runners.task_list.status": "Statut",
|
||||
"actions.runners.task_list.repository": "Dépôt",
|
||||
"actions.runners.task_list.commit": "Révision",
|
||||
"actions.runners.task_list.done_at": "Fait à",
|
||||
"actions.runners.edit_runner": "Éditer l'Exécuteur",
|
||||
"actions.runners.edit_runner": "Éditer l’opérateur",
|
||||
"actions.runners.update_runner": "Appliquer les modifications",
|
||||
"actions.runners.update_runner_success": "Exécuteur mis à jour avec succès",
|
||||
"actions.runners.update_runner_failed": "Impossible d'actualiser l'Exécuteur",
|
||||
"actions.runners.enable_runner": "Activer cet exécuteur",
|
||||
"actions.runners.enable_runner_success": "Exécuteur activé avec succès",
|
||||
"actions.runners.enable_runner_failed": "Impossible d’activer l’exécuteur",
|
||||
"actions.runners.disable_runner": "Désactiver cet exécuteur",
|
||||
"actions.runners.disable_runner_success": "Exécuteur désactivé avec succès",
|
||||
"actions.runners.disable_runner_failed": "Impossible de désactiver l’exécuteur",
|
||||
"actions.runners.delete_runner": "Supprimer cet exécuteur",
|
||||
"actions.runners.delete_runner_success": "Exécuteur supprimé avec succès",
|
||||
"actions.runners.delete_runner_failed": "Impossible de supprimer l'Exécuteur",
|
||||
"actions.runners.delete_runner_header": "Êtes-vous sûr de vouloir supprimer cet exécuteur ?",
|
||||
"actions.runners.delete_runner_notice": "Si une tâche est en cours sur cet exécuteur, elle sera terminée et marquée comme échouée. Cela risque d’interrompre le flux de travail.",
|
||||
"actions.runners.none": "Aucun exécuteur disponible",
|
||||
"actions.runners.update_runner_success": "Opérateur mis à jour avec succès",
|
||||
"actions.runners.update_runner_failed": "Impossible d'actualiser l’opérateur",
|
||||
"actions.runners.enable_runner": "Activer cet opérateur",
|
||||
"actions.runners.enable_runner_success": "Opérateur activé avec succès",
|
||||
"actions.runners.enable_runner_failed": "Impossible d’activer l’opérateur",
|
||||
"actions.runners.disable_runner": "Désactiver cet opérateur",
|
||||
"actions.runners.disable_runner_success": "Opérateur désactivé avec succès",
|
||||
"actions.runners.disable_runner_failed": "Impossible de désactiver l’opérateur",
|
||||
"actions.runners.delete_runner": "Supprimer cet opérateur",
|
||||
"actions.runners.delete_runner_success": "Opérateur supprimé avec succès",
|
||||
"actions.runners.delete_runner_failed": "Impossible de supprimer l’opérateur",
|
||||
"actions.runners.delete_runner_header": "Êtes-vous sûr de vouloir supprimer cet opérateur ?",
|
||||
"actions.runners.delete_runner_notice": "Si des tâches sont en cours de réalisation par cet opérateur, elles seront abandonnées et marquées en échec, pouvant également entrainer l’échec des procédures appelantes.",
|
||||
"actions.runners.none": "Aucun opérateur disponible",
|
||||
"actions.runners.status.unspecified": "Inconnu",
|
||||
"actions.runners.status.idle": "Inactif",
|
||||
"actions.runners.status.active": "Actif",
|
||||
"actions.runners.status.idle": "Disponible",
|
||||
"actions.runners.status.active": "Occupé",
|
||||
"actions.runners.status.offline": "Hors-ligne",
|
||||
"actions.runners.version": "Version",
|
||||
"actions.runners.reset_registration_token": "Réinitialiser le jeton d'enregistrement",
|
||||
"actions.runners.reset_registration_token": "Réinitialiser le jeton d’enregistrement",
|
||||
"actions.runners.reset_registration_token_confirm": "Voulez-vous révoquer le jeton actuel et en générer un nouveau ?",
|
||||
"actions.runners.reset_registration_token_success": "Le jeton d’inscription de l’exécuteur a été réinitialisé avec succès",
|
||||
"actions.runs.all_workflows": "Tous les flux de travail",
|
||||
"actions.runs.other_workflows": "Autres flux de travail",
|
||||
"actions.runs.other_workflows_tooltip": "Les flux de travail qui ont été exécutés dans ce dépôt mais qui n’existent pas dans la branche par défaut.",
|
||||
"actions.runs.workflow_run_count_1": "%d exécution du workflow",
|
||||
"actions.runs.workflow_run_count_n": "%d exécutions du workflow",
|
||||
"actions.runners.reset_registration_token_success": "Le jeton d’inscription de l’opérateur a été réinitialisé avec succès",
|
||||
"actions.runs.all_workflows": "Toutes les procédures",
|
||||
"actions.runs.other_workflows": "Autres procédures",
|
||||
"actions.runs.other_workflows_tooltip": "Les procédures qui ont été exécutées dans ce dépôt mais qui n’existent pas dans la branche par défaut.",
|
||||
"actions.runs.workflow_run_count_1": "%d exécution de procédure",
|
||||
"actions.runs.workflow_run_count_n": "%d exécutions de procédure",
|
||||
"actions.runs.commit": "Révision",
|
||||
"actions.runs.run_details": "Détails de l’exécution",
|
||||
"actions.runs.workflow_file": "Fichier de flux de travail",
|
||||
"actions.runs.workflow_file": "Déclaration de la procédure",
|
||||
"actions.runs.workflow_file_no_permission": "Pas de permission pour voir la procédure",
|
||||
"actions.runs.scheduled": "Planifié",
|
||||
"actions.runs.pushed_by": "soumis par",
|
||||
"actions.runs.invalid_workflow_helper": "La configuration du flux de travail est invalide. Veuillez vérifier votre fichier %s.",
|
||||
"actions.runs.no_matching_online_runner_helper": "Aucun exécuteur en ligne correspondant au libellé %s",
|
||||
"actions.runs.no_job_without_needs": "Le flux de travail doit contenir au moins une tâche sans dépendance.",
|
||||
"actions.runs.no_job": "Le flux de travail doit contenir au moins une tâche",
|
||||
"actions.runs.invalid_workflow_helper": "La déclaration de la procédure est invalide. Veuillez vérifier le fichier « %s ».",
|
||||
"actions.runs.no_matching_online_runner_helper": "Aucun opérateur disponible correspondant au libellé « %s »",
|
||||
"actions.runs.no_job_without_needs": "La procédure doit contenir au moins une mission sans dépendance.",
|
||||
"actions.runs.no_job": "La procédure doit contenir au moins une mission.",
|
||||
"actions.runs.invalid_reusable_workflow_uses": "Clause \"uses\" invalide dans la procédure : %s",
|
||||
"actions.runs.actor": "Acteur",
|
||||
"actions.runs.status": "Statut",
|
||||
"actions.runs.actors_no_select": "Tous les acteurs",
|
||||
@@ -3786,45 +3794,82 @@
|
||||
"actions.runs.branch": "Branche",
|
||||
"actions.runs.branches_no_select": "Toutes les branches",
|
||||
"actions.runs.no_results": "Aucun résultat correspondant.",
|
||||
"actions.runs.no_workflows": "Il n'y a pas encore de workflows.",
|
||||
"actions.runs.no_workflows.quick_start": "Vous découvrez les Actions Gitea ? Consultez <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">le didacticiel</a>.",
|
||||
"actions.runs.no_workflows.documentation": "Pour plus d’informations sur les actions Gitea, voir <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">la documentation</a>.",
|
||||
"actions.runs.no_runs": "Le flux de travail n'a pas encore d'exécution.",
|
||||
"actions.runs.no_workflows": "Il n’y a pas de procédure ici.",
|
||||
"actions.runs.no_workflows.quick_start": "Vous découvrez les Actions Gitea ? Consultez <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">le didacticiel</a>.",
|
||||
"actions.runs.no_workflows.documentation": "Pour plus d’informations sur les Actions Gitea, voir <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">la documentation</a>.",
|
||||
"actions.runs.no_runs": "Cette procédure n’a pas encore été exécutée.",
|
||||
"actions.runs.empty_commit_message": "(message de révision vide)",
|
||||
"actions.runs.expire_log_message": "Les journaux ont été supprimés car ils étaient trop anciens.",
|
||||
"actions.runs.delete": "Supprimer cette exécution",
|
||||
"actions.runs.cancel": "Annuler l’exécution du flux",
|
||||
"actions.runs.delete": "Effacer l’execution de cette procédure",
|
||||
"actions.runs.cancel": "Annuler l’exécution de cette procédure",
|
||||
"actions.runs.delete.description": "Êtes-vous sûr de vouloir supprimer définitivement cette exécution ? Cette action ne peut pas être annulée.",
|
||||
"actions.runs.not_done": "Cette exécution du flux de travail n’est pas terminée.",
|
||||
"actions.runs.view_workflow_file": "Voir le fichier du flux de travail",
|
||||
"actions.runs.not_done": "Cette procédure n’est pas terminée.",
|
||||
"actions.runs.view_workflow_file": "Voir la déclaration de la procédure",
|
||||
"actions.runs.summary": "Résumé",
|
||||
"actions.runs.all_jobs": "Toutes les tâches",
|
||||
"actions.runs.all_jobs": "Toutes les missions",
|
||||
"actions.runs.job_summaries": "Résumé des missions",
|
||||
"actions.runs.expand_caller_jobs": "Afficher les missions de cette procédure réutilisable",
|
||||
"actions.runs.collapse_caller_jobs": "Masquer les missions de cette procédure réutilisable",
|
||||
"actions.runs.attempt": "Tentative",
|
||||
"actions.runs.latest": "Dernière",
|
||||
"actions.runs.latest_attempt": "Dernière tentative",
|
||||
"actions.runs.triggered_via": "Déclenché via %s",
|
||||
"actions.runs.rerun_triggered": "Relance enclenchée",
|
||||
"actions.runs.back_to_pull_request": "Retour à la demande d’ajout",
|
||||
"actions.runs.back_to_workflow": "Retour à la procédure",
|
||||
"actions.runs.total_duration": "Durée totale :",
|
||||
"actions.runs.workflow_dependencies": "Dépendances de la procédure",
|
||||
"actions.runs.graph_jobs_count_1": "%d mission",
|
||||
"actions.runs.graph_jobs_count_n": "%d missions",
|
||||
"actions.runs.graph_dependencies_count_1": "%d dépendance",
|
||||
"actions.runs.graph_dependencies_count_n": "%d dépendances",
|
||||
"actions.runs.graph_success_rate": "%s succès",
|
||||
"actions.runs.graph_zoom_in": "Zoomer (Ctrl/⌘ + défilement)",
|
||||
"actions.runs.graph_zoom_max": "Déjà zoomé à 100%",
|
||||
"actions.runs.graph_zoom_max": "Déjà à 100%",
|
||||
"actions.runs.graph_zoom_out": "Dézoomer (Ctrl/⌘ + défilement)",
|
||||
"actions.workflow.disable": "Désactiver le flux de travail",
|
||||
"actions.workflow.disable_success": "Le flux de travail « %s » a bien été désactivé.",
|
||||
"actions.workflow.enable": "Activer le flux de travail",
|
||||
"actions.workflow.enable_success": "Le flux de travail « %s » a bien été activé.",
|
||||
"actions.workflow.disabled": "Le flux de travail est désactivé.",
|
||||
"actions.runs.graph_reset_view": "Rétablir",
|
||||
"actions.workflow.disable": "Désactiver la procédure",
|
||||
"actions.workflow.disable_success": "La procédure « %s » a bien été désactivée.",
|
||||
"actions.workflow.enable": "Activer la procédure",
|
||||
"actions.workflow.enable_success": "La procédure « %s » a bien été activée.",
|
||||
"actions.workflow.disabled": "La procédure est désactivée.",
|
||||
"actions.workflow.scope_owner": "Propriétaire",
|
||||
"actions.workflow.scope_global": "Global",
|
||||
"actions.workflow.required": "Requis",
|
||||
"actions.workflow.run": "Exécuter le flux de travail",
|
||||
"actions.workflow.not_found": "Flux de travail « %s » introuvable.",
|
||||
"actions.workflow.run_success": "Le flux de travail « %s » s’est correctement exécuté.",
|
||||
"actions.workflow.from_ref": "Utiliser le flux de travail depuis",
|
||||
"actions.workflow.has_workflow_dispatch": "Ce flux de travail a un déclencheur d’événement workflow_dispatch.",
|
||||
"actions.workflow.has_no_workflow_dispatch": "Le flux de travail %s n’a pas de déclencheur d’événement workflow_dispatch.",
|
||||
"actions.need_approval_desc": "Besoin d’approbation pour exécuter des flux de travail pour une demande d’ajout de bifurcation.",
|
||||
"actions.approve_all_success": "Tous les flux de travail ont été acceptés.",
|
||||
"actions.workflow.scoped_required_cannot_disable": "Cette procédure transversale est requise.",
|
||||
"actions.scoped_workflows": "Procédures transversales",
|
||||
"actions.scoped_workflows.desc_org": "Enrôlez un dépôt afin de rendre ses procédures accessibles à votre organisation. Toutes les procédures de la branche principale de ce dépôt seront ainsi exécutées dans chaque dépôt de cette organisation, comme si elles y avaient été créées.",
|
||||
"actions.scoped_workflows.desc_user": "Enrôlez un dépôt afin de rendre ses procédures accessibles à votre compte. Toutes les procédures de la branche principale de ce dépôt seront ainsi exécutées dans chaque dépôt que vous possédez, comme si elles y avaient été créées.",
|
||||
"actions.scoped_workflows.desc_global": "Enrôlez un dépôt afin de rendre ses procédures accessibles à l’ensemble du serveur. Toutes les procédures de la branche principale de ce dépôt seront ainsi exécutées dans chaque dépôt, comme si elles y avaient été créées. Sur un serveur volumineux, ces procédures peuvent lourdement solliciter les ressources du système.",
|
||||
"actions.scoped_workflows.add_help": "Pour rendre des procédures transversales, soumettez leurs déclarations dans le dossier <code>%s</code> sur la branche par défaut de ce dépôt, puis enrôlez celui-ci ci-dessous.",
|
||||
"actions.scoped_workflows.security_note": "Parce qu’une procédure transversale opère sur d’autres dépôts que le sien, ses extrants sont journalisés sur ces dépôts et sont donc consultable par leurs utilisateurs. Ainsi, une procédure issue d’un dépôt privé peut-être reconstruit à partir des journaux qu'elle produit. Exposer une procédure transversale peut donc compromètre la confidentialité de son dépôt hôte. Si une procédure transversale référence une autre procédure issue d’un dépôt privé, assurez-vous que les dépôts affectés puissent également s'y référer, sans quoi ces procédures échoueront.",
|
||||
"actions.scoped_workflows.source.add": "Enrôler un dépôt",
|
||||
"actions.scoped_workflows.source.add_success": "Dépôt enrôlé.",
|
||||
"actions.scoped_workflows.source.remove_success": "Dépôt retiré.",
|
||||
"actions.scoped_workflows.source.not_found": "Dépôt introuvable.",
|
||||
"actions.scoped_workflows.required.update_success": "Procédure requise mise à jour.",
|
||||
"actions.scoped_workflows.required.label": "Marquer les procédures comme requises (elles ne pourront être désactivés depuis un dépôt) :",
|
||||
"actions.scoped_workflows.required.patterns": "Motifs de signal requis",
|
||||
"actions.scoped_workflows.required.patterns_aria": "Motifs de signal requis pour « %s »",
|
||||
"actions.scoped_workflows.required.patterns_note": "est uniquement appliqué lorsque la procédure est requise",
|
||||
"actions.scoped_workflows.required.patterns_hint": "Marquez la procédure comme requise pour configurer ses motifs de signal.",
|
||||
"actions.scoped_workflows.required.patterns_help": "Un motif de signal par ligne. Une demande d’ajout concernée peut être fusionnée à condition qu’au moins un signal par motif ait réussi. Cela est imposé pour toutes les branches affectées ayant des règles de protections (même désactivées), mais pas les branches sans protections.",
|
||||
"actions.scoped_workflows.required.patterns_empty": "Chaque procédure requise nécessite au moins un motif de signal.",
|
||||
"actions.scoped_workflows.required.missing_file": "Le fichier n’est plus dans le dépôt enrôlé.",
|
||||
"actions.scoped_workflows.required.expected_contexts": "Signaux attendus (un signal qui correspond au motif est marqué)",
|
||||
"actions.scoped_workflows.required.no_status_contexts": "Comme cette procédure ne publie aucun signal, la rentre obligatoire empêchera toutes demandes d’ajouts concernées d’être fusionnées. Préférez laisser cette procédure facultative.",
|
||||
"actions.scoped_workflows.no_files": "Aucune procédure transversale n'a été trouvé dans la branche par défaut.",
|
||||
"actions.workflow.run": "Réaliser la procédure",
|
||||
"actions.workflow.create_status_badge": "Créer un badge d’état",
|
||||
"actions.workflow.status_badge": "Badge d’état",
|
||||
"actions.workflow.status_badge_url": "URL du badge",
|
||||
"actions.workflow.not_found": "La procédure « %s » est introuvable.",
|
||||
"actions.workflow.run_success": "La procédure « %s » est accomplie.",
|
||||
"actions.workflow.from_ref": "Utiliser la procédure depuis",
|
||||
"actions.workflow.has_workflow_dispatch": "Cette procédure dispose d’un déclencheur workflow_dispatch.",
|
||||
"actions.workflow.has_no_workflow_dispatch": "La procédure « %s » n’a pas de déclencheur workflow_dispatch.",
|
||||
"actions.need_approval_desc": "Une approbation est nécessaire pour exécuter les procédures d‘une demande d’ajout de bifurcation.",
|
||||
"actions.approve_all_success": "Toutes les procédures ont été approuvées.",
|
||||
"actions.variables": "Variables",
|
||||
"actions.variables.management": "Gestion des variables",
|
||||
"actions.variables.creation": "Ajouter une variable",
|
||||
@@ -3845,7 +3890,7 @@
|
||||
"actions.general": "Général",
|
||||
"actions.general.enable_actions": "Activer les actions",
|
||||
"actions.general.collaborative_owners_management": "Gestion des collaborateurs",
|
||||
"actions.general.collaborative_owners_management_help": "Un collaborateur est un utilisateur ou une organisation dont le dépôt privé peut accéder aux actions et flux de travail de ce dépôt.",
|
||||
"actions.general.collaborative_owners_management_help": "Un collaborateur est un utilisateur ou une organisation dont le dépôt privé peut accéder aux actions et procédures de ce dépôt.",
|
||||
"actions.general.add_collaborative_owner": "Ajouter un collaborateur",
|
||||
"actions.general.collaborative_owner_not_exist": "Le collaborateur n’existe pas.",
|
||||
"actions.general.remove_collaborative_owner": "Supprimer le collaborateur",
|
||||
@@ -3863,21 +3908,21 @@
|
||||
"git.filemode.symbolic_link": "Lien symbolique",
|
||||
"git.filemode.submodule": "Sous-module",
|
||||
"org.repos.none": "Aucun dépôt.",
|
||||
"actions.general.permissions": "Permissions du jeton des actions",
|
||||
"actions.general.token_permissions.mode": "Permissions par défaut du jeton",
|
||||
"actions.general.token_permissions.mode.desc": "Une tâche d’Actions utilisera les permissions par défaut si aucune n’est déclarée dans le fichier du flux de travail.",
|
||||
"actions.general.token_permissions.mode.permissive": "Permissif",
|
||||
"actions.general.token_permissions.mode.permissive.desc": "Permissions en lecture et écriture sur le dépôt de la tâche.",
|
||||
"actions.general.token_permissions.mode.restricted": "Restreint",
|
||||
"actions.general.token_permissions.mode.restricted.desc": "Permissions en lecture seule pour le contenu (code, publications) sur le dépôt de la tâche.",
|
||||
"actions.general.token_permissions.override_owner": "Écraser la configuration faite par le propriétaire",
|
||||
"actions.general.token_permissions.override_owner_desc": "Si actif, ce dépôt utilisera sa propre configuration pour les actions au lieu de respecter celle du propriétaire (utilisateur ou organisation).",
|
||||
"actions.general.token_permissions.maximum": "Permissions maximales du jeton",
|
||||
"actions.general.token_permissions.maximum.description": "Les permissions effectives de la tâche des actions seront limitées par les permissions maximales.",
|
||||
"actions.general.token_permissions.fork_pr_note": "Si une tâche est démarrée par une demande de fusion depuis une bifurcation, ses permissions effectives ne dépasseront pas les permissions en lecture-seule.",
|
||||
"actions.general.token_permissions.customize_max_permissions": "Personnaliser les permissions maximales",
|
||||
"actions.general.permissions": "Permissions intégrées au jeton d’Actions",
|
||||
"actions.general.token_permissions.mode": "Régime de restriction par défaut",
|
||||
"actions.general.token_permissions.mode.desc": "Lorsqu’une procédure ne contient pas de restriction, elle est alors restreinte au régime par défaut du dépôt.",
|
||||
"actions.general.token_permissions.mode.permissive": "Régime permissif",
|
||||
"actions.general.token_permissions.mode.permissive.desc": "Les missions peuvent consulter et modifier ce dépôt.",
|
||||
"actions.general.token_permissions.mode.restricted": "Régime limitant",
|
||||
"actions.general.token_permissions.mode.restricted.desc": "Les missions ne peuvent que consulter le contenu (code et publications) de ce dépôt.",
|
||||
"actions.general.token_permissions.override_owner": "Outrepasser la configuration du propriétaire",
|
||||
"actions.general.token_permissions.override_owner_desc": "Si actif, ce dépôt donnera ses propres restrictions à la place de celles du propriétaire (utilisateur ou organisation).",
|
||||
"actions.general.token_permissions.maximum": "Restrictions affinées",
|
||||
"actions.general.token_permissions.maximum.description": "Pour protéger plus finement le dépôt, les restrictions appliquées aux missions peuvent être définies pour chaque section individuellement.",
|
||||
"actions.general.token_permissions.fork_pr_note": "Lorsqu’une mission est initiée par une demande d’ajout depuis une bifurcation, ses restrictions effectives ne peuvent dépasser la lecture seule.",
|
||||
"actions.general.token_permissions.customize_max_permissions": "Affiner les restrictions",
|
||||
"actions.general.cross_repo": "Accès inter-dépôt",
|
||||
"actions.general.cross_repo_desc": "Permet aux dépôts sélectionnés d’être visible en lecture-seule par tous les dépôts de ce propriétaire à l’aide de GITEA_TOKEN lors de l’exécution des tâches d’actions.",
|
||||
"actions.general.cross_repo_desc": "Permet aux dépôts de ce propriétaire de consulter (en lecture seule) les dépôts listés ci-dessous pendant l’exécution des missions d’Actions (nécessite un jeton GITEA_TOKEN).",
|
||||
"actions.general.cross_repo_selected": "Dépôts sélectionnés",
|
||||
"actions.general.cross_repo_target_repos": "Dépôts cibles",
|
||||
"actions.general.cross_repo_add": "Ajouter un dépôt cible"
|
||||
|
||||
@@ -3779,6 +3779,7 @@
|
||||
"actions.runs.commit": "Tiomantas",
|
||||
"actions.runs.run_details": "Sonraí Rith",
|
||||
"actions.runs.workflow_file": "Comhad sreabhadh oibre",
|
||||
"actions.runs.workflow_file_no_permission": "Gan cead chun an comhad sreafa oibre a fheiceáil",
|
||||
"actions.runs.scheduled": "Sceidealaithe",
|
||||
"actions.runs.pushed_by": "bhrú ag",
|
||||
"actions.runs.invalid_workflow_helper": "Tá comhad cumraíochta sreabhadh oibre nebhailí. Seiceáil do chomhad cumraithe le do thoil: %s",
|
||||
@@ -3835,7 +3836,33 @@
|
||||
"actions.workflow.scope_owner": "Úinéir",
|
||||
"actions.workflow.scope_global": "Domhanda",
|
||||
"actions.workflow.required": "Riachtanach",
|
||||
"actions.workflow.scoped_required_cannot_disable": "Tá an sreabhadh oibre raonaithe seo riachtanach agus ní féidir é a dhíchumasú.",
|
||||
"actions.scoped_workflows": "Sreafaí Oibre Raonaithe",
|
||||
"actions.scoped_workflows.desc_org": "Cláraigh stórtha mar fhoinsí sreabhadh oibre raonta. Ritheann comhaid sreabhadh oibre faoi eolairí sreabhadh oibre raonta brainse réamhshocraithe stórtha foinse ar gach stórtha den eagraíocht seo, i gcomhthéacs an stórtha sin féin.",
|
||||
"actions.scoped_workflows.desc_user": "Cláraigh stórtha mar fhoinsí sreabhadh oibre raonta. Ritheann comhaid sreabhadh oibre faoi eolairí sreabhadh oibre raonta brainse réamhshocraithe stórtha foinse ar gach stór atá i do sheilbh, i gcomhthéacs an stórtha sin féin.",
|
||||
"actions.scoped_workflows.desc_global": "Cláraigh stórtha mar fhoinsí sreabhadh oibre raonta. Ritheann comhaid sreabhadh oibre faoi eolairí sreabhadh oibre raonta brainse réamhshocraithe stórtha foinse ar gach stór ar an gcás seo, i gcomhthéacs an stórtha sin féin. Ós rud é go ndéantar foinsí ar leibhéal an chás a mheas ar imeachtaí gach stórtha, is féidir le clárú na gcomhad sin forchostais a chur leis ar chásanna móra.",
|
||||
"actions.scoped_workflows.add_help": "Chun sreafaí oibre raonta a sholáthar ó stór, cuir na comhaid sreafa oibre faoi <code>%s</code> ar a bhrainse réamhshocraithe, agus ansin cláraigh an stór mar fhoinse thíos.",
|
||||
"actions.scoped_workflows.security_note": "Déantar ábhar sreafa oibre stórais foinse a fhorghníomhú i ngach stórais lena mbaineann sé, agus scríobhtar a scripteanna céime agus a n-aschur chuig logaí Gníomhartha an stórais sin agus is féidir le duine ar bith ar féidir leo Gníomhartha an stórais ídigh a fheiceáil iad a léamh. Dá bhrí sin, nochtar loighic a sreafa oibre trí na logaí sin nuair a chláraítear stórais phríobháidigh mar fhoinse. Ní chláraítear ach stórais a bhféadfadh a n-ábhar sreafa oibre a bheith roinnte le gach stórais ídigh. Má thagraíonn sreabhadh oibre raonaithe do shreabhadh oibre in-athúsáidte ó stórais phríobháidigh, déan cinnte gur féidir le gach stórais ídigh é a léamh, nó teipfidh ar an sreabhadh oibre ansin.",
|
||||
"actions.scoped_workflows.source.add": "Cuir stór foinse leis",
|
||||
"actions.scoped_workflows.source.add_success": "Stór foinse curtha leis.",
|
||||
"actions.scoped_workflows.source.remove_success": "Baineadh an stór foinse.",
|
||||
"actions.scoped_workflows.source.not_found": "Níor aimsíodh an stórlann.",
|
||||
"actions.scoped_workflows.required.update_success": "Sreafaí oibre riachtanacha nuashonraithe.",
|
||||
"actions.scoped_workflows.required.label": "Marcáil sreafaí oibre mar riachtanacha (ní féidir le stórtha sreabhadh oibre riachtanach a dhíchumasú):",
|
||||
"actions.scoped_workflows.required.patterns": "Patrúin seiceála stádais riachtanacha",
|
||||
"actions.scoped_workflows.required.patterns_aria": "Patrúin seiceála stádais riachtanacha do %s",
|
||||
"actions.scoped_workflows.required.patterns_note": "i bhfeidhm ach amháin nuair a bhíonn an sreabhadh oibre ag teastáil",
|
||||
"actions.scoped_workflows.required.patterns_hint": "Marcáil an sreabhadh oibre mar is gá chun a phatrúin seiceála stádais a chumrú.",
|
||||
"actions.scoped_workflows.required.patterns_help": "Patrún seiceála stádais amháin (glob) in aghaidh an líne. Ní féidir iarratas tarraingthe íditheach a chumasc ach amháin nuair a bheidh stádas a mheaitseálann gach patrún rite. Cuirtear é seo i bhfeidhm ar aon bhrainse sprice a bhfuil riail chosanta aige, fiú ceann a bhfuil a sheiceálacha stádais féin díchumasaithe; ní dhéantar geataíocht ar bhrainse sprice gan aon riail chosanta.",
|
||||
"actions.scoped_workflows.required.patterns_empty": "Teastaíonn patrún seiceála stádais amháin ar a laghad ó gach sreabhadh oibre riachtanach.",
|
||||
"actions.scoped_workflows.required.missing_file": "níl an comhad sa bhunleagan a thuilleadh",
|
||||
"actions.scoped_workflows.required.expected_contexts": "Seiceálacha stádais ionchais (marcáiltear seic a mheaitseálann patrún)",
|
||||
"actions.scoped_workflows.required.no_status_contexts": "Ní phostálann an sreabhadh oibre seo aon seiceálacha stádais, mar sin má mharcálann tú é mar riachtanas, chuirfeadh sé bac ar gach iarratas tarraingthe atá á úsáid ó chumasc. Díthiceáil Riachtanach.",
|
||||
"actions.scoped_workflows.no_files": "Ní bhfuarthas aon chomhaid sreabha oibre raonaithe ar an mbrainse réamhshocraithe.",
|
||||
"actions.workflow.run": "Rith Sreabhadh Oibre",
|
||||
"actions.workflow.create_status_badge": "Cruthaigh suaitheantas stádais",
|
||||
"actions.workflow.status_badge": "Suaitheantas Stádais",
|
||||
"actions.workflow.status_badge_url": "URL suaitheantais",
|
||||
"actions.workflow.not_found": "Níor aimsíodh sreabhadh oibre '%s'.",
|
||||
"actions.workflow.run_success": "Ritheann sreabhadh oibre '%s' go rathúil.",
|
||||
"actions.workflow.from_ref": "Úsáid sreabhadh oibre ó",
|
||||
|
||||
@@ -2872,6 +2872,7 @@
|
||||
"org.teams.visibility_limited_helper": "この組織のすべてのメンバーに表示されます。",
|
||||
"org.teams.visibility_public": "公開",
|
||||
"org.teams.visibility_public_helper": "サインインしているすべてのユーザーに表示されます。",
|
||||
"org.teams.owners_visibility_fixed": "Ownersチームの公開範囲は変更できません。",
|
||||
"org.teams.invite.title": "あなたは組織 <strong>%[2]s</strong> 内のチーム <strong>%[1]s</strong> への参加に招待されました。",
|
||||
"org.teams.invite.by": "%s からの招待",
|
||||
"org.teams.invite.description": "下のボタンをクリックしてチームに参加してください。",
|
||||
@@ -3778,6 +3779,7 @@
|
||||
"actions.runs.commit": "コミット",
|
||||
"actions.runs.run_details": "実行の詳細",
|
||||
"actions.runs.workflow_file": "ワークフローのファイル",
|
||||
"actions.runs.workflow_file_no_permission": "ワークフローファイルを表示する権限がありません",
|
||||
"actions.runs.scheduled": "スケジュール済み",
|
||||
"actions.runs.pushed_by": "pushed by",
|
||||
"actions.runs.invalid_workflow_helper": "ワークフロー設定ファイルは無効です。あなたの設定ファイルを確認してください: %s",
|
||||
@@ -3834,7 +3836,33 @@
|
||||
"actions.workflow.scope_owner": "オーナー",
|
||||
"actions.workflow.scope_global": "グローバル",
|
||||
"actions.workflow.required": "必須",
|
||||
"actions.workflow.scoped_required_cannot_disable": "このスコープ付きワークフローは必須で、無効にできません。",
|
||||
"actions.scoped_workflows": "スコープ付きワークフロー",
|
||||
"actions.scoped_workflows.desc_org": "スコープ付きワークフローのソースとなるリポジトリを登録します。 ソースリポジトリのデフォルトブランチ内で、スコープ付きワークフロー用ディレクトリにワークフローファイルを置くと、組織内のすべてのリポジトリで、それぞれのリポジトリ自身のコンテキストで実行されます。",
|
||||
"actions.scoped_workflows.desc_user": "スコープ付きワークフローのソースとなるリポジトリを登録します。 ソースリポジトリのデフォルトブランチ内で、スコープ付きワークフロー用ディレクトリにワークフローファイルを置くと、あなたが所有するすべてのリポジトリで、それぞれのリポジトリ自身のコンテキストで実行されます。",
|
||||
"actions.scoped_workflows.desc_global": "スコープ付きワークフローのソースとなるリポジトリを登録します。 ソースリポジトリのデフォルトブランチ内で、スコープ付きワークフロー用ディレクトリにワークフローファイルを置くと、このインスタンスのすべてのリポジトリで、それぞれのリポジトリ自身のコンテキストで実行されます。 インスタンスレベルのソースはあらゆるリポジトリでイベントが発生するたびに評価されるため、大規模インスタンスで登録するとオーバーヘッドが増加する可能性があります。",
|
||||
"actions.scoped_workflows.add_help": "スコープ付きワークフローをリポジトリから提供するには、デフォルトブランチの <code>%s</code> にワークフローファイルをコミットし、以下でそのリポジトリをソースとして登録します。",
|
||||
"actions.scoped_workflows.security_note": "ソースリポジトリのワークフローの内容は、適用対象となるすべてのリポジトリ内で実行されます。 ステップごとのスクリプトとその出力は、実行したリポジトリのActionsログに書き込まれ、そのリポジトリのActionsを閲覧できるユーザーであれば誰でも読むことができます。 プライベートリポジトリをソースとして登録しても、そのワークフローのロジックはログを通して開示されることになります。 すべての適用先リポジトリとワークフローの内容を共有しても問題ないリポジトリだけ登録してください。 スコープ付きワークフローが、プライベートリポジトリの再利用可能ワークフローを参照している場合は、すべての適用先リポジトリがそのワークフローを読み取れるようにしてください。 そうでない場合、そこでのワークフローは失敗します。",
|
||||
"actions.scoped_workflows.source.add": "ソースリポジトリを追加",
|
||||
"actions.scoped_workflows.source.add_success": "ソースリポジトリを追加しました。",
|
||||
"actions.scoped_workflows.source.remove_success": "ソースリポジトリを削除しました。",
|
||||
"actions.scoped_workflows.source.not_found": "リポジトリが見つかりません。",
|
||||
"actions.scoped_workflows.required.update_success": "必須ワークフローを更新しました。",
|
||||
"actions.scoped_workflows.required.label": "ワークフローを必須としてマークする (必須ワークフローはリポジトリから無効にすることはできません):",
|
||||
"actions.scoped_workflows.required.patterns": "必須ステータスチェックパターン",
|
||||
"actions.scoped_workflows.required.patterns_aria": "%s の必須ステータスチェックパターン",
|
||||
"actions.scoped_workflows.required.patterns_note": "ワークフローが必須の場合にだけ適用されます",
|
||||
"actions.scoped_workflows.required.patterns_hint": "ステータスチェックパターンを設定するには、ワークフローを必須とマークします。",
|
||||
"actions.scoped_workflows.required.patterns_help": "ステータスチェックのパターン(glob)を1行につき1つずつ記入します。 対象となるプルリクエストは、ステータスがすべてのパターンに一致して初めてマージ可能になります。 これは保護ルールを持つターゲットブランチであれば、そのステータスチェックが無効になっていたとしても適用されます。 一方、保護ルールを持たないターゲットブランチは制限されません。",
|
||||
"actions.scoped_workflows.required.patterns_empty": "必須ワークフローには、少なくともひとつのステータスチェックパターンが必要です。",
|
||||
"actions.scoped_workflows.required.missing_file": "ファイルがもうソースにありません",
|
||||
"actions.scoped_workflows.required.expected_contexts": "想定されるステータスチェック (パターンに一致するチェックがマークされています)",
|
||||
"actions.scoped_workflows.required.no_status_contexts": "このワークフローのステータスチェックが送られてきていないため、必須ワークフローにすると、適用されるプルリクエストのマージをすべてブロックしてしまいます。 必須を解除してください。",
|
||||
"actions.scoped_workflows.no_files": "デフォルトブランチに、スコープ付きワークフローのファイルが見つかりません。",
|
||||
"actions.workflow.run": "ワークフローを実行",
|
||||
"actions.workflow.create_status_badge": "ステータスバッジを作成する",
|
||||
"actions.workflow.status_badge": "ステータスバッジ",
|
||||
"actions.workflow.status_badge_url": "バッジURL",
|
||||
"actions.workflow.not_found": "ワークフロー '%s' が見つかりません。",
|
||||
"actions.workflow.run_success": "ワークフロー '%s' は正常に実行されました。",
|
||||
"actions.workflow.from_ref": "使用するワークフローの取得元",
|
||||
|
||||
@@ -1584,7 +1584,7 @@
|
||||
"repo.issues.label_archived_filter": "아카이빙된 레이블 표시",
|
||||
"repo.issues.label_archive_tooltip": "아카아빙된 레이블은 레이블로 검색할 때 기본적으로 제안 목록에서 제외됩니다.",
|
||||
"repo.issues.label_exclusive_desc": "레이블명을 <code>스코프/항목</code>으로 지정하여 다른 <code>스코프/</code> 레이블과 상호 배타적으로 만드세요.",
|
||||
"repo.issues.label_exclusive_warning": "이슈 또는 풀 리퀘스트의 레이블을 편집할 때 충돌하는 스코프 지정 레이블은 모두 제거됩니다.",
|
||||
"repo.issues.label_exclusive_warning": "이슈 또는 풀 리퀘스트의 레이블을 편집할 때 충돌하는 범위지정 레이블은 모두 제거됩니다.",
|
||||
"repo.issues.label_exclusive_order": "정렬 순서",
|
||||
"repo.issues.label_exclusive_order_tooltip": "같은 스코프 내의 독점 레이블은 이 숫자 순서에 따라 정렬됩니다.",
|
||||
"repo.issues.label_count": "레이블 %d개",
|
||||
@@ -3820,7 +3820,12 @@
|
||||
"actions.workflow.scope_owner": "소유자",
|
||||
"actions.workflow.scope_global": "글로벌",
|
||||
"actions.workflow.required": "필수 항목",
|
||||
"actions.workflow.scoped_required_cannot_disable": "범위지정 워크플로우가 요구되며 비활성화할 수 없습니다.",
|
||||
"actions.scoped_workflows": "범위지정 워크플로우",
|
||||
"actions.workflow.run": "워크플로 실행",
|
||||
"actions.workflow.create_status_badge": "상태 배지 생성",
|
||||
"actions.workflow.status_badge": "상태 배지",
|
||||
"actions.workflow.status_badge_url": "배지 URL",
|
||||
"actions.workflow.not_found": "워크플로 '%s'를 찾을 수 없습니다.",
|
||||
"actions.workflow.run_success": "워크플로 '%s'가 성공적으로 실행되었습니다.",
|
||||
"actions.workflow.from_ref": "다음에서 워크플로 사용",
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
"search.fuzzy_tooltip": "Incluir também os resultados que estejam próximos do termo de pesquisa",
|
||||
"search.words": "Palavras",
|
||||
"search.words_tooltip": "Incluir apenas os resultados que correspondam às palavras do termo de pesquisa",
|
||||
"search.regexp": "Expressão regular",
|
||||
"search.regexp_tooltip": "Incluir apenas os resultados que correspondam ao termo de pesquisa com expressões regulares",
|
||||
"search.exact": "Fiel",
|
||||
"search.exact_tooltip": "Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa",
|
||||
@@ -1002,6 +1003,7 @@
|
||||
"repo.multiple_licenses": "Múltiplas licenças",
|
||||
"repo.object_format": "Formato dos elementos",
|
||||
"repo.object_format_helper": "Formato dos elementos do repositório. Não poderá ser alterado mais tarde. SHA1 é o mais compatível.",
|
||||
"repo.readme": "README",
|
||||
"repo.readme_helper": "Escolha um modelo de ficheiro README.",
|
||||
"repo.readme_helper_desc": "Este é o sítio onde pode escrever uma descrição completa do seu trabalho.",
|
||||
"repo.auto_init": "Inicializar repositório (adiciona `.gitignore`, `LICENSE` e `README.md`)",
|
||||
@@ -1889,6 +1891,7 @@
|
||||
"repo.pulls.closed_at": "fechou este pedido de integração <a id=\"%[1]s\" href=\"#%[1]s\">%[2]s</a>",
|
||||
"repo.pulls.reopened_at": "reabriu este pedido de integração <a id=\"%[1]s\" href=\"#%[1]s\">%[2]s</a>",
|
||||
"repo.pulls.cmd_instruction_hint": "Ver instruções para a linha de comandos",
|
||||
"repo.pulls.cmd_instruction_checkout_title": "Checkout",
|
||||
"repo.pulls.cmd_instruction_checkout_desc": "A partir do seu repositório, crie um novo ramo e teste nele as modificações.",
|
||||
"repo.pulls.cmd_instruction_merge_title": "Integrar",
|
||||
"repo.pulls.cmd_instruction_merge_desc": "Integrar as modificações e enviar para o Gitea.",
|
||||
@@ -3776,6 +3779,7 @@
|
||||
"actions.runs.commit": "Cometimento",
|
||||
"actions.runs.run_details": "Detalhes da execução",
|
||||
"actions.runs.workflow_file": "Ficheiro de sequência de trabalho",
|
||||
"actions.runs.workflow_file_no_permission": "Sem permissão para ver o ficheiro da sequência de trabalho",
|
||||
"actions.runs.scheduled": "Agendadas",
|
||||
"actions.runs.pushed_by": "enviado por",
|
||||
"actions.runs.invalid_workflow_helper": "O ficheiro de configuração da sequência de trabalho é inválido. Verifique o seu ficheiro de configuração: %s",
|
||||
@@ -3832,6 +3836,29 @@
|
||||
"actions.workflow.scope_owner": "Proprietário(a)",
|
||||
"actions.workflow.scope_global": "Global",
|
||||
"actions.workflow.required": "Obrigatório",
|
||||
"actions.workflow.scoped_required_cannot_disable": "Esta sequência de trabalho de âmbito específico é obrigatória e não pode ser desabilitada.",
|
||||
"actions.scoped_workflows": "Sequências de trabalho de âmbito específico",
|
||||
"actions.scoped_workflows.desc_org": "Registe repositórios como fontes de sequências de trabalho de âmbito específico. Os ficheiros das sequências de trabalho dentro de pastas das sequências de trabalho de âmbito específico do ramo principal de um repositório fonte são executados em todos os repositórios desta organização, no próprio contexto desse repositório.",
|
||||
"actions.scoped_workflows.desc_user": "Registe repositórios como fontes de sequências de trabalho de âmbito específico. Os ficheiros das sequências de trabalho dentro de pastas das sequências de trabalho de âmbito específico do ramo principal de um repositório fonte são executados em todos os seus repositórios, no próprio contexto desse repositório.",
|
||||
"actions.scoped_workflows.desc_global": "Registe repositórios como fontes de sequências de trabalho de âmbito específico. Os ficheiros das sequências de trabalho dentro de pastas das sequências de trabalho de âmbito específico do ramo principal de um repositório fonte são executados em todos os repositórios desta instância, no próprio contexto desse repositório. Uma vez que as fontes ao nível da instância são avaliadas em todos os eventos do repositório, registá-las poderá acrescentar uma sobrecarga em instâncias grandes.",
|
||||
"actions.scoped_workflows.add_help": "Para fornecer sequências de trabalho de âmbito específico a partir de um repositório, cometa os ficheiros da sequência de trabalho sob <code>%s</code> no seu ramo principal e depois registe o repositório como uma fonte abaixo.",
|
||||
"actions.scoped_workflows.security_note": "O conteúdo da sequência de trabalho de um repositório de origem é executado em todos os repositórios aos quais se aplica e os seus scripts de etapas, bem como os seus resultados, são escritos nos registos das operações desse repositório e podem ser lidos por qualquer pessoa que tenha permissão para ver as operações do repositório consumidor. Portanto, registar um repositório privado como uma fonte divulga a lógica da sequência de trabalho através desses registos. Registe apenas repositórios cujo conteúdo da sequência de trabalho possa ser partilhado com todos os repositórios consumidores. Se uma sequência de trabalho de âmbito específico fizer referência a uma sequência de trabalho reutilizável de um repositório privado, certifique-se que todos os repositórios consumidores a podem ler, caso contrário a sequência de trabalho irá falhar aí.",
|
||||
"actions.scoped_workflows.source.add": "Adicionar repositório de origem",
|
||||
"actions.scoped_workflows.source.add_success": "Repositório de origem adicionado.",
|
||||
"actions.scoped_workflows.source.remove_success": "Repositório de origem removido.",
|
||||
"actions.scoped_workflows.source.not_found": "Repositório não encontrado.",
|
||||
"actions.scoped_workflows.required.update_success": "As sequências de trabalho obrigatórias foram refrescadas.",
|
||||
"actions.scoped_workflows.required.label": "Marcar sequências de trabalho como sendo obrigatórias (uma sequência de trabalho obrigatória não pode ser desabilitada pelos repositórios):",
|
||||
"actions.scoped_workflows.required.patterns": "Padrões de verificação de estado obrigatórios",
|
||||
"actions.scoped_workflows.required.patterns_aria": "Padrões de verificação de estado obrigatórios para %s",
|
||||
"actions.scoped_workflows.required.patterns_note": "aplicada apenas enquanto a sequência de trabalho for obrigatória",
|
||||
"actions.scoped_workflows.required.patterns_hint": "Marque a sequência de trabalho como sendo obrigatória para configurar os seus padrões de verificação de estado.",
|
||||
"actions.scoped_workflows.required.patterns_help": "Um padrão de verificação de estado (glob) por linha. Um pedido de integração consumidor só pode ser executado depois de ter sido aprovado um estado que corresponda a todos os padrões. Esta regra é aplicada a qualquer ramo de destino que tenha uma regra de salvaguarda, mesmo que as suas próprias verificações de estado estejam desabilitadas; um ramo de destino sem regra de salvaguarda não está sujeito a restrições.",
|
||||
"actions.scoped_workflows.required.patterns_empty": "Cada sequência de trabalho obrigatória precisa de pelo menos um padrão de verificação de estado.",
|
||||
"actions.scoped_workflows.required.missing_file": "o ficheiro já não está na origem",
|
||||
"actions.scoped_workflows.required.expected_contexts": "Verificações de estado esperadas (está marcada uma verificação que corresponde a um padrão)",
|
||||
"actions.scoped_workflows.required.no_status_contexts": "Esta sequência de trabalho não faz verificações de estado; por isso, marcá-la como obrigatória impediria a execução de todos os pedidos de integração que a utilizam. Desmarque a opção «Obrigatória».",
|
||||
"actions.scoped_workflows.no_files": "Não foram encontrados quaisquer ficheiros de sequência de trabalho de âmbito específico no ramo principal.",
|
||||
"actions.workflow.run": "Executar sequência de trabalho",
|
||||
"actions.workflow.create_status_badge": "Criar distintivo de estado",
|
||||
"actions.workflow.status_badge": "Distintivo de estado",
|
||||
|
||||
@@ -3779,6 +3779,7 @@
|
||||
"actions.runs.commit": "提交",
|
||||
"actions.runs.run_details": "运行详情",
|
||||
"actions.runs.workflow_file": "工作流文件",
|
||||
"actions.runs.workflow_file_no_permission": "没有权限查看工作流文件",
|
||||
"actions.runs.scheduled": "已计划的",
|
||||
"actions.runs.pushed_by": "推送者",
|
||||
"actions.runs.invalid_workflow_helper": "工作流配置文件无效。请检查您的配置文件:%s",
|
||||
@@ -3835,7 +3836,33 @@
|
||||
"actions.workflow.scope_owner": "拥有者",
|
||||
"actions.workflow.scope_global": "全局",
|
||||
"actions.workflow.required": "必须",
|
||||
"actions.workflow.scoped_required_cannot_disable": "这个作用域工作流是必需的,不能被禁用。",
|
||||
"actions.scoped_workflows": "作用域工作流",
|
||||
"actions.scoped_workflows.desc_org": "将仓库注册为作用域工作流的源仓库。源仓库默认分支的「作用域工作流目录」中的工作流文件会在此组织中的每个仓库上运行,并使用对应仓库自身的上下文。",
|
||||
"actions.scoped_workflows.desc_user": "将仓库注册为作用域工作流的源仓库。源仓库默认分支的「作用域工作流目录」中的工作流文件会在您拥有的每个仓库上运行,并使用对应仓库自身的上下文。",
|
||||
"actions.scoped_workflows.desc_global": "将仓库注册为作用域工作流的源仓库。源仓库默认分支的「作用域工作流目录」中的工作流文件会在这个实例的每个仓库上运行,并使用对应仓库自身的上下文。由于实例级注册会针对每个仓库的事件进行检测,这在大型实例上可能会带来额外开销。",
|
||||
"actions.scoped_workflows.add_help": "若要从仓库中提供作用域工作流,请在默认分支上提交工作流文件至 <code>%s</code> 然后将仓库注册为下面的源仓库。",
|
||||
"actions.scoped_workflows.security_note": "一个源仓库的工作流会在它应用到的每个仓库上执行,它的 step 脚本和输出会被写到相关仓库的工作流日志里,并且能被任何有权查看这个仓库工作流的用户读取。因此,将一个私有仓库注册为源仓库会通过这些日志暴露工作流的逻辑。请仅注册那些工作流内容可以与所有相关仓库共享的仓库。如果一个作用域工作流从一个私有仓库引用了可复用工作流,请确保每个相关仓库都能读取它,否则该工作流会在这些仓库中运行失败。",
|
||||
"actions.scoped_workflows.source.add": "添加源仓库",
|
||||
"actions.scoped_workflows.source.add_success": "源仓库已添加。",
|
||||
"actions.scoped_workflows.source.remove_success": "源仓库已删除。",
|
||||
"actions.scoped_workflows.source.not_found": "未找到仓库。",
|
||||
"actions.scoped_workflows.required.update_success": "「必需」工作流已更新。",
|
||||
"actions.scoped_workflows.required.label": "把工作流标记为必需(「必需」工作流不能被仓库禁用):",
|
||||
"actions.scoped_workflows.required.patterns": "「必需」状态检查表达式",
|
||||
"actions.scoped_workflows.required.patterns_aria": "%s 的「必需」状态检查表达式",
|
||||
"actions.scoped_workflows.required.patterns_note": "仅在工作流为「必需」时执行",
|
||||
"actions.scoped_workflows.required.patterns_hint": "标记工作流为「必需」以配置它的状态检查表达式。",
|
||||
"actions.scoped_workflows.required.patterns_help": "每行一个状态检查表达式(glob)。消费了这个工作流的合并请求只能在每条表达式都通过后才能合并。这仅适用于目标分支被分支保护规则保护的情况,即使分支保护规则的状态检查被禁用。没有被分支保护规则保护的目标分支不会被限制。\n每行填写一个状态检查表达式(glob)。只有当每个表达式都有一个匹配且已通过的状态检查时,相关的合并请求才能被合并。这仅适用于目标分支被分支保护规则保护的情况,即使分支保护规则的状态检查被禁用。没有被分支保护规则保护的目标分支不会被限制。",
|
||||
"actions.scoped_workflows.required.patterns_empty": "每个「必需」工作流都需要至少一个状态检查表达式。",
|
||||
"actions.scoped_workflows.required.missing_file": "文件不存在于源仓库",
|
||||
"actions.scoped_workflows.required.expected_contexts": "预期状态检查(匹配表达式的状态检查会被标记)",
|
||||
"actions.scoped_workflows.required.no_status_contexts": "此工作流不会报告任何状态检查,将其标记为「必需」后所有相关的合并请求都将无法合并。请不要将其标记为「必需」。",
|
||||
"actions.scoped_workflows.no_files": "默认分支上未找到任何作用域工作流文件。",
|
||||
"actions.workflow.run": "运行工作流",
|
||||
"actions.workflow.create_status_badge": "创建状态徽章",
|
||||
"actions.workflow.status_badge": "状态徽章",
|
||||
"actions.workflow.status_badge_url": "徽章 URL",
|
||||
"actions.workflow.not_found": "未找到工作流「%s」。",
|
||||
"actions.workflow.run_success": "工作流「%s」已成功运行。",
|
||||
"actions.workflow.from_ref": "使用工作流从",
|
||||
|
||||
+25
-29
@@ -14,21 +14,21 @@
|
||||
"@codemirror/commands": "6.10.4",
|
||||
"@codemirror/lang-json": "6.0.2",
|
||||
"@codemirror/lang-markdown": "6.5.0",
|
||||
"@codemirror/language": "6.12.3",
|
||||
"@codemirror/language": "6.12.4",
|
||||
"@codemirror/language-data": "6.5.2",
|
||||
"@codemirror/legacy-modes": "6.5.3",
|
||||
"@codemirror/lint": "6.9.7",
|
||||
"@codemirror/search": "6.7.1",
|
||||
"@codemirror/state": "6.7.0",
|
||||
"@codemirror/view": "6.43.2",
|
||||
"@codemirror/view": "6.43.4",
|
||||
"@deltablot/dropzone": "7.4.3",
|
||||
"@github/markdown-toolbar-element": "2.2.3",
|
||||
"@github/paste-markdown": "1.5.3",
|
||||
"@github/text-expander-element": "2.9.4",
|
||||
"@lezer/highlight": "1.2.3",
|
||||
"@mcaptcha/vanilla-glue": "0.1.0-rc2",
|
||||
"@mermaid-js/layout-elk": "0.2.1",
|
||||
"@primer/octicons": "19.28.1",
|
||||
"@mermaid-js/layout-elk": "0.2.2",
|
||||
"@primer/octicons": "19.29.1",
|
||||
"@replit/codemirror-indentation-markers": "6.5.3",
|
||||
"@replit/codemirror-lang-nix": "6.0.1",
|
||||
"@replit/codemirror-lang-svelte": "6.0.0",
|
||||
@@ -36,11 +36,11 @@
|
||||
"@resvg/resvg-wasm": "2.6.2",
|
||||
"@vitejs/plugin-vue": "6.0.7",
|
||||
"ansi_up": "6.0.6",
|
||||
"asciinema-player": "3.16.0",
|
||||
"asciinema-player": "3.17.0",
|
||||
"chart.js": "4.5.1",
|
||||
"chartjs-adapter-dayjs-4": "1.0.4",
|
||||
"chartjs-plugin-zoom": "2.2.0",
|
||||
"clippie": "4.2.0",
|
||||
"clippie": "4.2.1",
|
||||
"codemirror-lang-elixir": "4.0.1",
|
||||
"colord": "2.9.3",
|
||||
"compare-versions": "6.1.1",
|
||||
@@ -50,13 +50,13 @@
|
||||
"esbuild": "0.28.1",
|
||||
"idiomorph": "0.7.4",
|
||||
"jquery": "4.0.0",
|
||||
"js-yaml": "4.2.0",
|
||||
"js-yaml": "5.2.1",
|
||||
"katex": "0.17.0",
|
||||
"mermaid": "11.15.0",
|
||||
"mermaid": "11.16.0",
|
||||
"online-3d-viewer": "0.18.0",
|
||||
"pdfobject": "2.3.1",
|
||||
"perfect-debounce": "2.1.0",
|
||||
"postcss": "8.5.15",
|
||||
"postcss": "8.5.16",
|
||||
"rolldown-license-plugin": "3.0.9",
|
||||
"sortablejs": "1.15.7",
|
||||
"swagger-ui-dist": "5.32.8",
|
||||
@@ -67,40 +67,36 @@
|
||||
"tributejs": "5.1.3",
|
||||
"uint8-to-base64": "0.2.1",
|
||||
"vanilla-colorful": "0.7.2",
|
||||
"vite": "8.1.0",
|
||||
"vite-string-plugin": "2.0.4",
|
||||
"vue": "3.5.38",
|
||||
"vue-bar-graph": "2.2.0",
|
||||
"vite": "8.1.3",
|
||||
"vite-string-plugin": "2.0.5",
|
||||
"vue": "3.5.39",
|
||||
"vue-chartjs": "5.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "4.7.2",
|
||||
"@eslint/json": "2.0.0",
|
||||
"@eslint/json": "2.0.1",
|
||||
"@playwright/test": "1.61.1",
|
||||
"@stylistic/eslint-plugin": "5.10.0",
|
||||
"@stylistic/stylelint-plugin": "5.2.0",
|
||||
"@stylistic/stylelint-plugin": "5.2.1",
|
||||
"@types/codemirror": "5.60.17",
|
||||
"@types/jquery": "4.0.1",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/katex": "0.16.8",
|
||||
"@types/node": "25.9.4",
|
||||
"@types/node": "26.1.0",
|
||||
"@types/pdfobject": "2.2.5",
|
||||
"@types/sortablejs": "1.15.9",
|
||||
"@types/swagger-ui-dist": "3.30.6",
|
||||
"@types/throttle-debounce": "5.0.2",
|
||||
"@types/toastify-js": "1.12.4",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.1",
|
||||
"@vitejs/plugin-vue": "6.0.7",
|
||||
"@vitest/eslint-plugin": "1.6.20",
|
||||
"eslint": "10.5.0",
|
||||
"eslint": "10.6.0",
|
||||
"eslint-import-resolver-typescript": "4.4.5",
|
||||
"eslint-plugin-array-func": "5.1.1",
|
||||
"eslint-plugin-de-morgan": "2.1.2",
|
||||
"eslint-plugin-import-x": "4.17.0",
|
||||
"eslint-plugin-import-x": "4.17.1",
|
||||
"eslint-plugin-playwright": "2.10.4",
|
||||
"eslint-plugin-regexp": "3.1.0",
|
||||
"eslint-plugin-regexp": "3.1.1",
|
||||
"eslint-plugin-sonarjs": "4.1.0",
|
||||
"eslint-plugin-unicorn": "68.0.0",
|
||||
"eslint-plugin-unicorn": "70.0.0",
|
||||
"eslint-plugin-vue": "10.9.2",
|
||||
"eslint-plugin-vue-scoped-css": "3.1.1",
|
||||
"eslint-plugin-wc": "3.1.0",
|
||||
@@ -110,17 +106,17 @@
|
||||
"markdownlint-cli": "0.49.0",
|
||||
"material-icon-theme": "5.36.1",
|
||||
"postcss-html": "1.8.1",
|
||||
"spectral-cli-bundle": "1.0.8",
|
||||
"stylelint": "17.13.0",
|
||||
"spectral-cli-bundle": "1.0.11",
|
||||
"stylelint": "17.14.0",
|
||||
"stylelint-config-recommended": "18.0.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "3.0.0",
|
||||
"stylelint-declaration-strict-value": "1.11.1",
|
||||
"stylelint-value-no-unknown-custom-properties": "6.1.1",
|
||||
"svgo": "4.0.1",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "8.62.0",
|
||||
"updates": "17.18.0",
|
||||
"typescript-eslint": "8.62.1",
|
||||
"updates": "17.18.2",
|
||||
"vitest": "4.1.9",
|
||||
"vue-tsc": "3.3.5"
|
||||
"vue-tsc": "3.3.6"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+458
-496
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-graph-stacked-area" width="16" height="16" aria-hidden="true"><path d="M10.548 11.513a.775.775 0 0 1-1.096 0L7 9.06 2.56 13.5H14.5V7.56ZM14.72 1.22a.75.75 0 1 1 1.06 1.06l-5.232 5.233a.775.775 0 0 1-1.096 0L7 5.06l-5.72 5.72A.75.75 0 1 1 .22 9.72l6.232-6.233.059-.052a.775.775 0 0 1 1.037.052L10 5.94ZM16 14.225a.776.776 0 0 1-.775.775H.81a.775.775 0 0 1-.548-1.323l6.19-6.19.058-.052a.775.775 0 0 1 1.037.052L10 9.94l4.677-4.676.096-.082A.775.775 0 0 1 16 5.81Z"/></svg>
|
||||
|
After Width: | Height: | Size: 550 B |
+1
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-project-check" width="16" height="16" aria-hidden="true"><path d="M14.25 0C15.216 0 16 .784 16 1.75v6.498a.75.75 0 1 1-1.5.002V6.5h-8v8h.75a.75.75 0 1 1-.001 1.5H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0ZM1.5 14.25c0 .138.112.25.25.25H5v-8H1.5ZM1.75 1.5a.25.25 0 0 0-.25.25V5H5V1.5Zm4.75 0V5h8V1.75a.25.25 0 0 0-.25-.25Z"/><path d="M15.963 11.737a.75.75 0 0 1-.202.524l-3.5 3.5a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l1.47 1.47 2.97-2.97a.75.75 0 0 1 1.261.536"/></svg>
|
||||
|
After Width: | Height: | Size: 568 B |
+1
-1
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-repo-forked-locked" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M9 10.167V9a3 3 0 1 1 6 0v1.167c.591.281 1 .884 1 1.583v2.5A1.75 1.75 0 0 1 14.25 16h-4.5A1.75 1.75 0 0 1 8 14.25v-2.5c0-.699.409-1.302 1-1.583M13.5 10h-3V9a1.5 1.5 0 0 1 3 0z"/><path fill-rule="evenodd" d="M3.25 1a2.247 2.247 0 0 1 1.938 3.388 2.25 2.25 0 0 1-1.189.982v.881a.746.746 0 0 0 .75.748h3.095c-.052.428-.008.986.051 1.499H7.75v1.748c-.315.418-.5.938-.5 1.502v.294a1 1 0 0 0-.256-.041.75.75 0 0 0-.53.22.79.79 0 0 0-.22.547.746.746 0 0 0 .75.748.8.8 0 0 0 .25-.042v.796q.002.376.107.72-.177.03-.357.031a2.247 2.247 0 0 1-1.938-3.388c.268-.456.69-.805 1.19-.983V8.5h-2.5a2.23 2.23 0 0 1-1.586-.66 2.25 2.25 0 0 1-.66-1.591v-.881A2.255 2.255 0 0 1 .03 2.867c.088-.523.36-.998.768-1.34C1.205 1.187 1.72 1 2.25 1zm0 1.499a.75.75 0 0 0-.53.22.8.8 0 0 0-.22.547.75.75 0 0 0 1.5 0 .8.8 0 0 0-.22-.547.75.75 0 0 0-.53-.22m7.5.75a2.247 2.247 0 0 1 1.938 3.388 2.25 2.25 0 0 1-1.189.982V8.96c0 .414.336.75.75.75h.5a.75.75 0 0 1 0 1.5h-.5a3 3 0 0 1-3-3v.54a.746.746 0 0 1 .75-.748h1.01V5a1.5 1.5 0 0 1 1.5-1.5zm0 1.499a.75.75 0 0 0-.53.22.8.8 0 0 0-.22.547.75.75 0 0 0 1.5 0 .8.8 0 0 0-.22-.547.75.75 0 0 0-.53-.22"/><path d="M8.63 7.749a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-repo-forked-locked" width="16" height="16" aria-hidden="true"><path d="M9 10.167V9a3 3 0 1 1 6 0v1.168c.591.281 1 .884 1 1.582v2.5A1.75 1.75 0 0 1 14.25 16h-4.5A1.75 1.75 0 0 1 8 14.249v-2.5c0-.698.409-1.3 1-1.582M3.25 1A2.251 2.251 0 0 1 4 5.371v.878c0 .414.336.75.75.75h3.096c-.051.429.008.987.067 1.5H7.75v1.749a2.5 2.5 0 0 0-.5 1.501v.295a.75.75 0 1 0 0 1.409v.796q.002.376.107.72a2.25 2.25 0 0 1-1.106-4.343V8.5h-1.5a2.25 2.25 0 0 1-2.25-2.25v-.878A2.25 2.25 0 0 1 3.25 1m7.5 0a2.25 2.25 0 0 1 1.94 3.388c-.222.379-.55.68-.94.874h-.003a2.22 2.22 0 0 1-1.82.079l-.003-.001A2.248 2.248 0 0 1 10.75 1m2.75 9V9a1.5 1.5 0 0 0-3 0v1ZM3.25 2.499a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5m7.5 0a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5"/><path d="M8.63 7.749a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 872 B |
+1
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-view-files" width="16" height="16" aria-hidden="true"><path d="M1.75 10a.75.75 0 0 1 .75.75v2.5c0 .138.112.25.25.25h2.5a.75.75 0 1 1 0 1.5h-2.5A1.75 1.75 0 0 1 1 13.25v-2.5a.75.75 0 0 1 .75-.75m12.5 0a.75.75 0 0 1 .75.75v2.5A1.75 1.75 0 0 1 13.25 15h-2.5a.75.75 0 1 1 0-1.5h2.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 .75-.75m-6 0a.75.75 0 1 1 0 1.5h-3a.75.75 0 1 1 0-1.5Zm3-2.5a.75.75 0 1 1 0 1.5h-6a.75.75 0 0 1 0-1.5Zm-1-2.5a.75.75 0 1 1 0 1.5h-5a.75.75 0 0 1 0-1.5Zm-5-4a.75.75 0 0 1 0 1.5h-2.5a.25.25 0 0 0-.25.25v2.5a.75.75 0 0 1-1.5 0v-2.5C1 1.784 1.784 1 2.75 1Zm8 0c.966 0 1.75.784 1.75 1.75v2.5a.75.75 0 1 1-1.5 0v-2.5a.25.25 0 0 0-.25-.25h-2.5a.75.75 0 1 1 0-1.5Z"/></svg>
|
||||
|
After Width: | Height: | Size: 761 B |
+1
-1
@@ -5,7 +5,7 @@ requires-python = ">=3.10"
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"djlint==1.39.4",
|
||||
"djlint==1.40.1",
|
||||
"yamllint==1.38.0",
|
||||
"zizmor==1.26.1",
|
||||
]
|
||||
|
||||
+14
-3
@@ -73,7 +73,11 @@
|
||||
"postUpdateOptions": ["gomodUpdateImportPaths"],
|
||||
"postUpgradeTasks": {
|
||||
"commands": ["make tidy"],
|
||||
"fileFilters": ["go.mod", "go.sum", "assets/go-licenses.json"],
|
||||
"fileFilters": [
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"assets/go-licenses.json",
|
||||
],
|
||||
"executionMode": "branch",
|
||||
},
|
||||
},
|
||||
@@ -95,8 +99,15 @@
|
||||
"matchManagers": ["npm"],
|
||||
"postUpdateOptions": ["pnpmDedupe"],
|
||||
"postUpgradeTasks": {
|
||||
"commands": ["make svg"],
|
||||
"fileFilters": ["package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml", "public/assets/img/svg/**", "options/fileicon/**"],
|
||||
"commands": ["make svg", "make generate-codemirror-languages"],
|
||||
"fileFilters": [
|
||||
"package.json",
|
||||
"pnpm-lock.yaml",
|
||||
"pnpm-workspace.yaml",
|
||||
"public/assets/img/svg/**",
|
||||
"options/fileicon/**",
|
||||
"assets/codemirror-languages.json",
|
||||
],
|
||||
"executionMode": "branch",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -73,9 +73,7 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
|
||||
}
|
||||
|
||||
if publicOnly {
|
||||
// a public-only token must not reach limited-visibility owners either,
|
||||
// matching how orgs/users are enforced elsewhere in this file
|
||||
if ctx.Package != nil && !ctx.Package.Owner.Visibility.IsPublic() {
|
||||
if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
|
||||
ctx.HTTPError(http.StatusForbidden, "reqToken", "token scope is limited to public packages")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -333,18 +333,11 @@ func ListPackageTags(ctx *context.Context) {
|
||||
func AddPackageTag(ctx *context.Context) {
|
||||
packageName := packageNameFromParams(ctx)
|
||||
|
||||
// the dist-tag body is only a quoted version string; bound it to avoid an unbounded
|
||||
// read that could exhaust memory
|
||||
const maxDistTagBodySize = 4 * 1024
|
||||
body, err := io.ReadAll(io.LimitReader(ctx.Req.Body, maxDistTagBodySize+1))
|
||||
body, err := io.ReadAll(ctx.Req.Body)
|
||||
if err != nil {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if len(body) > maxDistTagBodySize {
|
||||
apiError(ctx, http.StatusRequestEntityTooLarge, errors.New("request body too large"))
|
||||
return
|
||||
}
|
||||
version := strings.Trim(string(body), "\"") // is as "version" in the body
|
||||
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName, version)
|
||||
|
||||
@@ -291,9 +291,7 @@ func checkTokenPublicOnly() func(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
case auth_model.AccessTokenScopeCategoryPackage:
|
||||
// a public-only token must not reach limited-visibility owners either,
|
||||
// matching the org/user public-only enforcement above
|
||||
if ctx.Package != nil && !ctx.Package.Owner.Visibility.IsPublic() {
|
||||
if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public packages")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@ import (
|
||||
"gitea.com/gitea/runner/act/model"
|
||||
)
|
||||
|
||||
// ListActionsSecrets list an repo's actions secrets
|
||||
// ListActionsSecrets list a repo's actions secrets
|
||||
func (Action) ListActionsSecrets(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/actions/secrets repository repoListActionsSecrets
|
||||
// ---
|
||||
// summary: List an repo's actions secrets
|
||||
// summary: List a repo's actions secrets
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
repo_service "gitea.dev/services/repository"
|
||||
)
|
||||
|
||||
// UpdateVatar updates the Avatar of an Repo
|
||||
// UpdateAvatar updates the Avatar of a Repo
|
||||
func UpdateAvatar(ctx *context.APIContext) {
|
||||
// swagger:operation POST /repos/{owner}/{repo}/avatar repository repoUpdateAvatar
|
||||
// ---
|
||||
@@ -56,7 +56,7 @@ func UpdateAvatar(ctx *context.APIContext) {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// UpdateAvatar deletes the Avatar of an Repo
|
||||
// DeleteAvatar deletes the Avatar of a Repo
|
||||
func DeleteAvatar(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/avatar repository repoDeleteAvatar
|
||||
// ---
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"gitea.dev/services/context"
|
||||
)
|
||||
|
||||
// GetGitAllRefs get ref or an list all the refs of a repository
|
||||
// GetGitAllRefs get ref or a list of all the refs of a repository
|
||||
func GetGitAllRefs(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/git/refs repository repoListAllGitRefs
|
||||
// ---
|
||||
|
||||
@@ -178,12 +178,13 @@ func DeleteIssueLabel(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
// the label must belong to this repo (or its owning org); otherwise a foreign label ID
|
||||
// is rejected the same way as a nonexistent one, closing a cross-repo enumeration oracle
|
||||
labelID := ctx.PathParamInt64("id")
|
||||
label, err := issues_model.GetLabelInRepoOrOrgByID(ctx, ctx.Repo.Repository.ID, ctx.Repo.Owner.ID, ctx.Repo.Owner.IsOrganization(), labelID)
|
||||
label, err := issues_model.GetLabelByID(ctx, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
ctx.APIErrorAuto(err)
|
||||
if issues_model.IsErrLabelNotExist(err) {
|
||||
ctx.APIError(http.StatusUnprocessableEntity, err.Error())
|
||||
} else {
|
||||
ctx.APIErrorInternal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -221,8 +221,7 @@ func AddTime(ctx *context.APIContext) {
|
||||
// allow only RepoAdmin, Admin and User to add time
|
||||
user, err = user_model.GetUserByName(ctx, form.User)
|
||||
if err != nil {
|
||||
ctx.APIErrorAuto(err)
|
||||
return
|
||||
ctx.APIErrorInternal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,11 @@ func GetLabel(ctx *context.APIContext) {
|
||||
l, err = issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, intID)
|
||||
}
|
||||
if err != nil {
|
||||
ctx.APIErrorAuto(err)
|
||||
if issues_model.IsErrRepoLabelNotExist(err) {
|
||||
ctx.APIErrorNotFound()
|
||||
} else {
|
||||
ctx.APIErrorInternal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -210,7 +214,11 @@ func EditLabel(ctx *context.APIContext) {
|
||||
form := web.GetForm(ctx).(*api.EditLabelOption)
|
||||
l, err := issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
ctx.APIErrorAuto(err)
|
||||
if issues_model.IsErrRepoLabelNotExist(err) {
|
||||
ctx.APIErrorNotFound()
|
||||
} else {
|
||||
ctx.APIErrorInternal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1251,13 +1251,6 @@ func UpdatePullRequest(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
// a public-only token must not update (push into) a private head repo,
|
||||
// even when the base repo named in the route is public
|
||||
if !ctx.TokenCanAccessRepo(pr.HeadRepo) {
|
||||
ctx.APIErrorNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
// keep API back-compat: when no style is given, default to "merge" rather than the repo's DefaultUpdateStyle,
|
||||
// so existing API clients keep getting a merge update.
|
||||
rebase := repo_model.UpdateStyle(ctx.FormString("style", string(repo_model.UpdateStyleMerge))) == repo_model.UpdateStyleRebase
|
||||
|
||||
@@ -21,21 +21,8 @@ import (
|
||||
"gitea.dev/routers/api/v1/utils"
|
||||
"gitea.dev/services/context"
|
||||
"gitea.dev/services/convert"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// actionsOwnerAccessibleRepoIDsSubQuery returns the sub-query restricting an owner-scoped actions
|
||||
// listing to the repos whose actions the caller can read, or nil when no restriction applies. A bare
|
||||
// org member must not be able to enumerate runs/jobs of repos they have no access to. A site admin may
|
||||
// skip the access filter, but a public-only token must stay confined to public repos even for an admin.
|
||||
func actionsOwnerAccessibleRepoIDsSubQuery(ctx *context.APIContext, ownerID int64) *builder.Builder {
|
||||
if ownerID > 0 && (ctx.Doer == nil || !ctx.Doer.IsAdmin || ctx.PublicOnly) {
|
||||
return repo_model.FindUserActionsAccessibleOwnerRepoIDsSubQuery(ownerID, ctx.Doer, ctx.PublicOnly)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListJobs lists jobs for api route validated ownerID and repoID
|
||||
// ownerID == 0 and repoID == 0 means all jobs
|
||||
// ownerID == 0 and repoID != 0 means all jobs for the given repo
|
||||
@@ -73,8 +60,6 @@ func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64, runAttemptI
|
||||
opts.Statuses = append(opts.Statuses, values...)
|
||||
}
|
||||
|
||||
opts.AccessibleRepoIDsSubQuery = actionsOwnerAccessibleRepoIDsSubQuery(ctx, opts.OwnerID)
|
||||
|
||||
jobs, total, err := db.FindAndCount[actions_model.ActionRunJob](ctx, opts)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
@@ -196,8 +181,6 @@ func ListRuns(ctx *context.APIContext, ownerID, repoID int64, workflowID string)
|
||||
}
|
||||
excludePullRequests := ctx.FormBool("exclude_pull_requests")
|
||||
|
||||
opts.AccessibleRepoIDsSubQuery = actionsOwnerAccessibleRepoIDsSubQuery(ctx, opts.OwnerID)
|
||||
|
||||
runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, opts)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user