mirror of
https://codeberg.org/forgejo-aneksajo/forgejo-aneksajo.git
synced 2025-07-23 14:00:07 +02:00
Compare commits
98 commits
977627fe6e
...
f4b18c3579
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f4b18c3579 | ||
![]() |
945d0749f7 | ||
![]() |
b07bae88b3 | ||
![]() |
594a4bd48b | ||
![]() |
c8b724dd62 | ||
![]() |
e354c700ed | ||
![]() |
8a513e8bed | ||
![]() |
4e2a8f5e34 | ||
![]() |
18f1f2c64b | ||
![]() |
1c5730451c | ||
![]() |
102e684080 | ||
![]() |
9c5ad9774d | ||
![]() |
ee0e43cc98 | ||
![]() |
3f819eebc9 | ||
![]() |
48af1b08b4 | ||
![]() |
fa0b12e0d4 | ||
![]() |
fc65179afa | ||
![]() |
0897c0d270 | ||
![]() |
ff6a40b012 | ||
![]() |
226b232b3e | ||
![]() |
4d2afbdc93 | ||
![]() |
09d9263982 | ||
![]() |
f9eb02710d | ||
![]() |
23cf3f1035 | ||
![]() |
15efbcc98f | ||
![]() |
09eb07041b | ||
![]() |
e6ee8228d7 | ||
![]() |
9ac39f03ee | ||
![]() |
0ac363a8d5 | ||
![]() |
702eaaeacb | ||
![]() |
4e8bf3f2cb | ||
![]() |
f9a4d6c290 | ||
![]() |
0b09604f54 | ||
![]() |
25032cca85 | ||
![]() |
3f72184695 | ||
![]() |
d1e3bba0bc | ||
![]() |
50fac66ddd | ||
![]() |
5c320e2555 | ||
![]() |
110c5aa238 | ||
![]() |
fcb90c66c2 | ||
![]() |
4e749c76cc | ||
![]() |
ebc819acec | ||
![]() |
92f78eb7a2 | ||
![]() |
ed56990af1 | ||
![]() |
842800449c | ||
![]() |
5c8e7cb40a | ||
![]() |
99608ef6cc | ||
![]() |
1beeb5da2a | ||
![]() |
d8a94c23c5 | ||
![]() |
262e8bf493 | ||
![]() |
c72fd88d35 | ||
![]() |
16484c72ec | ||
![]() |
006d9c060e | ||
![]() |
f067db8f8e | ||
![]() |
d3c6ab538e | ||
![]() |
eed84d72b6 | ||
![]() |
ff4f2bcf07 | ||
![]() |
dc0d4fb3ad | ||
![]() |
673eccf51f | ||
![]() |
ed87ecd17f | ||
![]() |
e9f0e96a27 | ||
![]() |
55df95b1be | ||
![]() |
aa8fa7f7e9 | ||
![]() |
a821eb9e0f | ||
![]() |
bdef19f62b | ||
![]() |
f13147a019 | ||
![]() |
1ca805933f | ||
![]() |
da267ab00e | ||
![]() |
fe55ddcdaf | ||
![]() |
738ec94b8f | ||
![]() |
a4cb898335 | ||
![]() |
4a30f59e6e | ||
![]() |
723fa1c966 | ||
![]() |
661028623c | ||
![]() |
5a54ce0fbc | ||
![]() |
5bb61cf6c2 | ||
![]() |
5d7953def4 | ||
![]() |
5816106de5 | ||
![]() |
c5601e9399 | ||
![]() |
1d15e243e4 | ||
![]() |
97220d1ce9 | ||
![]() |
44a5cd3b7a | ||
![]() |
6a9fb3dbbc | ||
![]() |
fe07c90636 | ||
![]() |
4215476cee | ||
![]() |
e837350319 | ||
![]() |
bc6c0b610b | ||
![]() |
b067d0df6e | ||
![]() |
c5bfe77873 | ||
![]() |
b2241c3939 | ||
![]() |
a4396782b5 | ||
![]() |
55cff9cfb4 | ||
![]() |
5ffe4e54e1 | ||
![]() |
9147665e2c | ||
![]() |
5395eea338 | ||
![]() |
973bc33a5f | ||
![]() |
722ea4179c | ||
![]() |
9ebdc09939 |
209 changed files with 5860 additions and 2229 deletions
|
@ -13,6 +13,8 @@ runs:
|
|||
run: |
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
echo "deb http://deb.debian.org/debian/ ${RELEASE} main" > "/etc/apt/sources.list.d/${RELEASE}.list"
|
||||
wget -O- http://neuro.debian.net/lists/bookworm.de-fzj.libre | tee /etc/apt/sources.list.d/neurodebian.sources.list
|
||||
apt-key adv --recv-keys --keyserver hkps://keyserver.ubuntu.com 0xA5D32F012649A5A9
|
||||
env:
|
||||
RELEASE: ${{inputs.release}}
|
||||
- name: install packages
|
||||
|
@ -24,6 +26,7 @@ runs:
|
|||
- name: remove temporary package list to prevent using it in other steps
|
||||
run: |
|
||||
rm "/etc/apt/sources.list.d/${RELEASE}.list"
|
||||
rm "/etc/apt/sources.list.d/neurodebian.sources.list"
|
||||
apt-get update -qq
|
||||
env:
|
||||
RELEASE: ${{inputs.release}}
|
||||
|
|
41
.forgejo/workflows/build-oci-image.yml
Normal file
41
.forgejo/workflows/build-oci-image.yml
Normal file
|
@ -0,0 +1,41 @@
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'forgejo'
|
||||
tags:
|
||||
- '*-git-annex*'
|
||||
|
||||
jobs:
|
||||
build-oci-image:
|
||||
runs-on: docker
|
||||
strategy:
|
||||
matrix:
|
||||
type: ["rootful", "rootless"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # fetch the full history so that the Forgejo version is determined properly
|
||||
- name: Determine registry and username
|
||||
id: determine-registry-and-username
|
||||
run: |
|
||||
echo "registry=${GITHUB_SERVER_URL#https://}" >> "$GITHUB_OUTPUT"
|
||||
echo "username=${GITHUB_REPOSITORY%/*}" >> "$GITHUB_OUTPUT"
|
||||
- name: Install Docker
|
||||
run: curl -fsSL https://get.docker.com | sh
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ steps.determine-registry-and-username.outputs.registry }}
|
||||
username: ${{ steps.determine-registry-and-username.outputs.username }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ${{ (matrix.type == 'rootful' && 'Dockerfile') || (matrix.type == 'rootless' && 'Dockerfile.rootless') }}
|
||||
push: true
|
||||
tags: ${{ steps.determine-registry-and-username.outputs.registry }}/${{ github.repository }}:${{ github.ref_name }}${{ (matrix.type == 'rootful' && ' ') || (matrix.type == 'rootless' && '-rootless') }}
|
|
@ -2,6 +2,8 @@
|
|||
#
|
||||
# See also https://forgejo.org/docs/next/contributor/release/#stable-release-process
|
||||
#
|
||||
# TOKEN_NEXT_DIGEST is a token with write repository access to https://invisible.forgejo.org/infrastructure/next-digest issued by https://invisible.forgejo.org/forgejo-next-digest
|
||||
#
|
||||
# https://codeberg.org/forgejo-experimental/forgejo
|
||||
#
|
||||
# Copies a release from codeberg.org/forgejo-integration to codeberg.org/forgejo-experimental
|
||||
|
@ -14,7 +16,7 @@
|
|||
# vars.DOER: forgejo-experimental-ci
|
||||
# secrets.TOKEN: <generated from codeberg.org/forgejo-experimental-ci>
|
||||
#
|
||||
# http://private.forgejo.org/forgejo/forgejo
|
||||
# http://invisible.forgejo.org/forgejo/forgejo
|
||||
#
|
||||
# Copies & sign a release from codeberg.org/forgejo-integration to codeberg.org/forgejo
|
||||
#
|
||||
|
@ -80,7 +82,7 @@ jobs:
|
|||
- name: upgrade v*.next.forgejo.org
|
||||
uses: https://data.forgejo.org/infrastructure/next-digest@v1.1.0
|
||||
with:
|
||||
url: https://placeholder:${{ secrets.TOKEN_NEXT_DIGEST }}@code.forgejo.org/infrastructure/next-digest
|
||||
url: https://placeholder:${{ secrets.TOKEN_NEXT_DIGEST }}@invisible.forgejo.org/infrastructure/next-digest
|
||||
ref_name: '${{ github.ref_name }}'
|
||||
image: 'codeberg.org/forgejo-experimental/forgejo'
|
||||
tag_suffix: '-rootless'
|
||||
|
|
|
@ -10,7 +10,6 @@ on:
|
|||
|
||||
jobs:
|
||||
backend-checks:
|
||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
||||
runs-on: docker
|
||||
container:
|
||||
image: 'data.forgejo.org/oci/node:22-bookworm'
|
||||
|
@ -27,7 +26,6 @@ jobs:
|
|||
- run: su forgejo -c 'make --always-make -j$(nproc) lint-backend tidy-check swagger-check lint-swagger fmt-check swagger-validate' # ensure the "go-licenses" make target runs
|
||||
- uses: ./.forgejo/workflows-composite/build-backend
|
||||
frontend-checks:
|
||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
||||
runs-on: docker
|
||||
container:
|
||||
image: 'data.forgejo.org/oci/node:22-bookworm'
|
||||
|
@ -176,7 +174,6 @@ jobs:
|
|||
TAGS: bindata
|
||||
TEST_REDIS_SERVER: cacher:${{ matrix.cacher.port }}
|
||||
test-mysql:
|
||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
||||
runs-on: docker
|
||||
needs: [backend-checks, frontend-checks]
|
||||
container:
|
||||
|
@ -199,15 +196,13 @@ jobs:
|
|||
- name: install dependencies & git >= 2.42
|
||||
uses: ./.forgejo/workflows-composite/apt-install-from
|
||||
with:
|
||||
packages: git git-lfs
|
||||
packages: git git-annex-standalone git-lfs
|
||||
- uses: ./.forgejo/workflows-composite/build-backend
|
||||
- run: |
|
||||
su forgejo -c 'make test-mysql-migration test-mysql'
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
USE_REPO_TEST_DIR: 1
|
||||
test-pgsql:
|
||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
||||
runs-on: docker
|
||||
needs: [backend-checks, frontend-checks]
|
||||
container:
|
||||
|
@ -236,17 +231,15 @@ jobs:
|
|||
- name: install dependencies & git >= 2.42
|
||||
uses: ./.forgejo/workflows-composite/apt-install-from
|
||||
with:
|
||||
packages: git git-lfs
|
||||
packages: git git-annex-standalone git-lfs
|
||||
- uses: ./.forgejo/workflows-composite/build-backend
|
||||
- run: |
|
||||
su forgejo -c 'make test-pgsql-migration test-pgsql'
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
RACE_ENABLED: true
|
||||
USE_REPO_TEST_DIR: 1
|
||||
TEST_LDAP: 1
|
||||
test-sqlite:
|
||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
||||
runs-on: docker
|
||||
needs: [backend-checks, frontend-checks]
|
||||
container:
|
||||
|
@ -258,25 +251,21 @@ jobs:
|
|||
- name: install dependencies & git >= 2.42
|
||||
uses: ./.forgejo/workflows-composite/apt-install-from
|
||||
with:
|
||||
packages: git git-lfs
|
||||
packages: git git-annex-standalone git-lfs
|
||||
- uses: ./.forgejo/workflows-composite/build-backend
|
||||
- run: |
|
||||
su forgejo -c 'make test-sqlite-migration test-sqlite'
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
TAGS: sqlite sqlite_unlock_notify
|
||||
RACE_ENABLED: true
|
||||
TEST_TAGS: sqlite sqlite_unlock_notify
|
||||
USE_REPO_TEST_DIR: 1
|
||||
security-check:
|
||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
||||
runs-on: docker
|
||||
needs:
|
||||
- test-sqlite
|
||||
- test-pgsql
|
||||
- test-mysql
|
||||
- test-remote-cacher
|
||||
- test-unit
|
||||
container:
|
||||
image: 'data.forgejo.org/oci/node:22-bookworm'
|
||||
options: --tmpfs /tmp:exec,noatime
|
||||
|
|
|
@ -78,6 +78,7 @@ RUN apk --no-cache add \
|
|||
sqlite \
|
||||
su-exec \
|
||||
gnupg \
|
||||
git-annex \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN addgroup \
|
||||
|
|
|
@ -73,6 +73,7 @@ RUN apk --no-cache add \
|
|||
curl \
|
||||
gnupg \
|
||||
openssh-client \
|
||||
git-annex \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN addgroup \
|
||||
|
|
4
Makefile
4
Makefile
|
@ -8,7 +8,7 @@ self := $(location)
|
|||
@tmpdir=`mktemp --tmpdir -d` ; \
|
||||
echo Using temporary directory $$tmpdir for test repositories ; \
|
||||
USE_REPO_TEST_DIR= $(MAKE) -f $(self) --no-print-directory REPO_TEST_DIR=$$tmpdir/ $@ ; \
|
||||
STATUS=$$? ; rm -r "$$tmpdir" ; exit $$STATUS
|
||||
STATUS=$$? ; chmod -R +w "$$tmpdir" && rm -r "$$tmpdir" ; exit $$STATUS
|
||||
|
||||
else
|
||||
|
||||
|
@ -92,7 +92,7 @@ else
|
|||
FORGEJO_VERSION_API ?= $(GITEA_VERSION)+${GITEA_COMPATIBILITY}
|
||||
else
|
||||
# drop the "g" prefix prepended by git describe to the commit hash
|
||||
FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//' | sed 's/\-g/-/')+${GITEA_COMPATIBILITY}
|
||||
FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//' | sed 's/\-g/-/2')+${GITEA_COMPATIBILITY}
|
||||
endif
|
||||
endif
|
||||
FORGEJO_VERSION_MAJOR=$(shell echo $(FORGEJO_VERSION) | sed -e 's/\..*//')
|
||||
|
|
|
@ -1,27 +1,33 @@
|
|||
<svg viewBox="0 0 212 212" xmlns="http://www.w3.org/2000/svg">
|
||||
<style type="text/css">
|
||||
circle {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-width: 15;
|
||||
}
|
||||
path {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-width: 25;
|
||||
}
|
||||
.orange {
|
||||
stroke:#ff6600;
|
||||
}
|
||||
.red {
|
||||
stroke:#d40000;
|
||||
}
|
||||
</style>
|
||||
<g transform="translate(6,6)">
|
||||
<path d="M58 168 v-98 a50 50 0 0 1 50-50 h20" class="orange" />
|
||||
<path d="M58 168 v-30 a50 50 0 0 1 50-50 h20" class="red" />
|
||||
<circle cx="142" cy="20" r="18" class="orange" />
|
||||
<circle cx="142" cy="88" r="18" class="red" />
|
||||
<circle cx="58" cy="180" r="18" class="red" />
|
||||
<!--
|
||||
This logo was created by Michael Hanke <mih@ngln.eu> from
|
||||
the original Forgejo logo by Caesar Schinas and the git-annex
|
||||
logo by Henrik Nyh <http://henrik.nyh.se/>, Joey Hess <id@joeyh.name>,
|
||||
John Lawrence, and Yann Büchau <nobodyinperson at posteo de>.
|
||||
|
||||
It is licensed under the Creative Commons Attribution-ShareAlike 4.0
|
||||
International (CC BY-SA 4.0) license.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="212" height="212"
|
||||
viewBox="0 0 56.092 56.092" xmlns:v="https://vecta.io/nano">
|
||||
<g transform="matrix(1.003855 0 0 1.003855 -155.52693 -24.929635)"
|
||||
fill="none">
|
||||
<g stroke-width="6.615">
|
||||
<path d="M168.804 70.908V44.979a13.229 13.229 0 0 1 13.229-13.229h5.292"
|
||||
stroke="#f60" />
|
||||
<path d="M168.804 70.908v-7.937a13.229 13.229 0 0 1 13.229-13.229h5.292"
|
||||
stroke="#d40000" />
|
||||
</g>
|
||||
<g stroke-width="3.969">
|
||||
<circle cx="191.029" cy="31.75" r="4.762" stroke="#f60" />
|
||||
<g stroke="#d40000">
|
||||
<circle cx="191.029" cy="49.742" r="4.762" />
|
||||
<circle cx="168.804" cy="74.083" r="4.762" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="#777">
|
||||
<path d="M34.648 56.182c-2.089-.269-4.238-2.244-4.538-4.561-.288-1.937.128-4.198 1.729-5.476.702-.479 1.658-1.015 2.5-.951v2.757c-1.003.112-1.975 1.252-1.954 2.296.025 1.359.626 2.607 1.933 3.014 1.446.487 3.102.348 4.44-.461 1.106-.862 1.208-2.466.63-3.687-.26-.672-.821-1.165-1.582-1.163v2.392H35.66v-5.149h6.5v1.852l-1.78.016c1.661.947 1.832 2.991 1.747 4.409.06 2.436-2.347 4.422-4.734 4.688-.864.065-1.594.068-2.743.023zm-4.644-12.011l-.014-2.702h12.185l-.001 2.715-12.17-.013zm4.645-3.651v-2.892h-4.724v-2.516h4.724v-3.245h2.826v3.245h4.702v2.516h-4.702l-.047 2.886-2.779.006z" />
|
||||
<path d="M22.23 24.801l-2.819 3.94h1.565a9.01 9.01 0 0 0 .103 1.283l2.488-.377c-.044-.296-.071-.599-.075-.906h1.556zm1.531 5.732l-2.405.724a8.9 8.9 0 0 0 .447 1.18l2.281-1.055a6.36 6.36 0 0 1-.323-.848zm.753 1.622L22.4 33.518a8.99 8.99 0 0 0 3.949 3.365l1.006-2.305a6.45 6.45 0 0 1-2.839-2.421zm3.675 2.715l-.679 2.425a8.94 8.94 0 0 0 1.18.244l.348-2.492a6.52 6.52 0 0 1-.848-.178zm21.673-10.069l-2.819 3.94h1.565c-.003.308-.03.61-.075.906l2.488.377a9.01 9.01 0 0 0 .103-1.283h1.556zm-1.523 5.732a6.36 6.36 0 0 1-.323.848l2.281 1.055c.176-.379.325-.774.447-1.18zm-.753 1.622a6.45 6.45 0 0 1-2.839 2.421l1.006 2.305a8.99 8.99 0 0 0 3.949-3.365zm-3.675 2.715a6.52 6.52 0 0 1-.848.178l.348 2.492a8.94 8.94 0 0 0 1.18-.244z"
|
||||
fill-rule="evenodd" />
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 2.4 KiB |
9
assets/go-licenses.json
generated
9
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
|
@ -1,27 +1,33 @@
|
|||
<svg viewBox="0 0 212 212" xmlns="http://www.w3.org/2000/svg">
|
||||
<style type="text/css">
|
||||
circle {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-width: 15;
|
||||
}
|
||||
path {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-width: 25;
|
||||
}
|
||||
.orange {
|
||||
stroke:#ff6600;
|
||||
}
|
||||
.red {
|
||||
stroke:#d40000;
|
||||
}
|
||||
</style>
|
||||
<g transform="translate(6,6)">
|
||||
<path d="M58 168 v-98 a50 50 0 0 1 50-50 h20" class="orange" />
|
||||
<path d="M58 168 v-30 a50 50 0 0 1 50-50 h20" class="red" />
|
||||
<circle cx="142" cy="20" r="18" class="orange" />
|
||||
<circle cx="142" cy="88" r="18" class="red" />
|
||||
<circle cx="58" cy="180" r="18" class="red" />
|
||||
<!--
|
||||
This logo was created by Michael Hanke <mih@ngln.eu> from
|
||||
the original Forgejo logo by Caesar Schinas and the git-annex
|
||||
logo by Henrik Nyh <http://henrik.nyh.se/>, Joey Hess <id@joeyh.name>,
|
||||
John Lawrence, and Yann Büchau <nobodyinperson at posteo de>.
|
||||
|
||||
It is licensed under the Creative Commons Attribution-ShareAlike 4.0
|
||||
International (CC BY-SA 4.0) license.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="212" height="212"
|
||||
viewBox="0 0 56.092 56.092" xmlns:v="https://vecta.io/nano">
|
||||
<g transform="matrix(1.003855 0 0 1.003855 -155.52693 -24.929635)"
|
||||
fill="none">
|
||||
<g stroke-width="6.615">
|
||||
<path d="M168.804 70.908V44.979a13.229 13.229 0 0 1 13.229-13.229h5.292"
|
||||
stroke="#f60" />
|
||||
<path d="M168.804 70.908v-7.937a13.229 13.229 0 0 1 13.229-13.229h5.292"
|
||||
stroke="#d40000" />
|
||||
</g>
|
||||
<g stroke-width="3.969">
|
||||
<circle cx="191.029" cy="31.75" r="4.762" stroke="#f60" />
|
||||
<g stroke="#d40000">
|
||||
<circle cx="191.029" cy="49.742" r="4.762" />
|
||||
<circle cx="168.804" cy="74.083" r="4.762" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="#777">
|
||||
<path d="M34.648 56.182c-2.089-.269-4.238-2.244-4.538-4.561-.288-1.937.128-4.198 1.729-5.476.702-.479 1.658-1.015 2.5-.951v2.757c-1.003.112-1.975 1.252-1.954 2.296.025 1.359.626 2.607 1.933 3.014 1.446.487 3.102.348 4.44-.461 1.106-.862 1.208-2.466.63-3.687-.26-.672-.821-1.165-1.582-1.163v2.392H35.66v-5.149h6.5v1.852l-1.78.016c1.661.947 1.832 2.991 1.747 4.409.06 2.436-2.347 4.422-4.734 4.688-.864.065-1.594.068-2.743.023zm-4.644-12.011l-.014-2.702h12.185l-.001 2.715-12.17-.013zm4.645-3.651v-2.892h-4.724v-2.516h4.724v-3.245h2.826v3.245h4.702v2.516h-4.702l-.047 2.886-2.779.006z" />
|
||||
<path d="M22.23 24.801l-2.819 3.94h1.565a9.01 9.01 0 0 0 .103 1.283l2.488-.377c-.044-.296-.071-.599-.075-.906h1.556zm1.531 5.732l-2.405.724a8.9 8.9 0 0 0 .447 1.18l2.281-1.055a6.36 6.36 0 0 1-.323-.848zm.753 1.622L22.4 33.518a8.99 8.99 0 0 0 3.949 3.365l1.006-2.305a6.45 6.45 0 0 1-2.839-2.421zm3.675 2.715l-.679 2.425a8.94 8.94 0 0 0 1.18.244l.348-2.492a6.52 6.52 0 0 1-.848-.178zm21.673-10.069l-2.819 3.94h1.565c-.003.308-.03.61-.075.906l2.488.377a9.01 9.01 0 0 0 .103-1.283h1.556zm-1.523 5.732a6.36 6.36 0 0 1-.323.848l2.281 1.055c.176-.379.325-.774.447-1.18zm-.753 1.622a6.45 6.45 0 0 1-2.839 2.421l1.006 2.305a8.99 8.99 0 0 0 3.949-3.365zm-3.675 2.715a6.52 6.52 0 0 1-.848.178l.348 2.492a8.94 8.94 0 0 0 1.18-.244z"
|
||||
fill-rule="evenodd" />
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 2.4 KiB |
76
cmd/serv.go
76
cmd/serv.go
|
@ -38,6 +38,7 @@ import (
|
|||
|
||||
const (
|
||||
lfsAuthenticateVerb = "git-lfs-authenticate"
|
||||
gitAnnexShellVerb = "git-annex-shell"
|
||||
)
|
||||
|
||||
// CmdServ represents the available serv sub-command.
|
||||
|
@ -79,6 +80,7 @@ var (
|
|||
"git-upload-archive": perm.AccessModeRead,
|
||||
"git-receive-pack": perm.AccessModeWrite,
|
||||
lfsAuthenticateVerb: perm.AccessModeNone,
|
||||
gitAnnexShellVerb: perm.AccessModeRead, // annex permissions are enforced by GIT_ANNEX_SHELL_READONLY, rather than the Gitea API, but read permissions are required at a minimum
|
||||
}
|
||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
)
|
||||
|
@ -212,6 +214,28 @@ func runServ(c *cli.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
if verb == gitAnnexShellVerb {
|
||||
if !setting.Annex.Enabled {
|
||||
return fail(ctx, "Unknown git command", "git-annex request over SSH denied, git-annex support is disabled")
|
||||
}
|
||||
|
||||
if len(words) < 3 {
|
||||
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
|
||||
}
|
||||
|
||||
// git-annex always puts the repo in words[2], unlike most other
|
||||
// git subcommands; and it sometimes names repos like /~/, as if
|
||||
// $HOME should get expanded while also being rooted. e.g.:
|
||||
// git-annex-shell 'configlist' '/~/user/repo'
|
||||
// git-annex-shell 'sendkey' '/user/repo 'key'
|
||||
repoPath = words[2]
|
||||
repoPath = strings.TrimPrefix(repoPath, "/")
|
||||
repoPath = strings.TrimPrefix(repoPath, "~/")
|
||||
}
|
||||
|
||||
// prevent directory traversal attacks
|
||||
repoPath = filepath.Clean("/" + repoPath)[1:]
|
||||
|
||||
rr := strings.SplitN(repoPath, "/", 2)
|
||||
if len(rr) != 2 {
|
||||
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
|
||||
|
@ -225,6 +249,18 @@ func runServ(c *cli.Context) error {
|
|||
// so that username and reponame are not affected.
|
||||
repoPath = strings.ToLower(strings.TrimSpace(repoPath))
|
||||
|
||||
// put the sanitized repoPath back into the argument list for later
|
||||
if verb == gitAnnexShellVerb {
|
||||
// git-annex-shell demands an absolute path
|
||||
absRepoPath, err := filepath.Abs(filepath.Join(setting.RepoRootPath, repoPath))
|
||||
if err != nil {
|
||||
return fail(ctx, "Error locating repoPath", "%v", err)
|
||||
}
|
||||
words[2] = absRepoPath
|
||||
} else {
|
||||
words[1] = repoPath
|
||||
}
|
||||
|
||||
if alphaDashDotPattern.MatchString(reponame) {
|
||||
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
|
||||
}
|
||||
|
@ -303,21 +339,45 @@ func runServ(c *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var gitcmd *exec.Cmd
|
||||
gitBinPath := filepath.Dir(git.GitExecutable) // e.g. /usr/bin
|
||||
gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack
|
||||
if _, err := os.Stat(gitBinVerb); err != nil {
|
||||
gitBinVerb, err := exec.LookPath(verb)
|
||||
if err != nil {
|
||||
// if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git
|
||||
// ps: Windows only has "git.exe" in the bin path, so Windows always uses this way
|
||||
// ps: git-annex-shell and other extensions may not necessarily be in gitBinPath,
|
||||
// but '{gitBinPath}/git annex-shell' should be able to find them on $PATH.
|
||||
verbFields := strings.SplitN(verb, "-", 2)
|
||||
if len(verbFields) == 2 {
|
||||
// use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ...
|
||||
gitcmd = exec.CommandContext(ctx, git.GitExecutable, verbFields[1], repoPath)
|
||||
gitBinVerb = git.GitExecutable
|
||||
words = append([]string{verbFields[1]}, words...)
|
||||
}
|
||||
}
|
||||
if gitcmd == nil {
|
||||
// by default, use the verb (it has been checked above by allowedCommands)
|
||||
gitcmd = exec.CommandContext(ctx, gitBinVerb, repoPath)
|
||||
|
||||
// by default, use the verb (it has been checked above by allowedCommands)
|
||||
gitcmd := exec.CommandContext(ctx, gitBinVerb, words[1:]...)
|
||||
|
||||
if verb == gitAnnexShellVerb {
|
||||
// This doesn't get its own isolated section like LFS does, because LFS
|
||||
// is handled by internal Gitea routines, but git-annex has to be shelled out
|
||||
// to like other git subcommands, so we need to build up gitcmd.
|
||||
|
||||
// TODO: does this work on Windows?
|
||||
gitcmd.Env = append(gitcmd.Env,
|
||||
// "If set, disallows running git-shell to handle unknown commands."
|
||||
// - git-annex-shell(1)
|
||||
"GIT_ANNEX_SHELL_LIMITED=True",
|
||||
// "If set, git-annex-shell will refuse to run commands
|
||||
// that do not operate on the specified directory."
|
||||
// - git-annex-shell(1)
|
||||
fmt.Sprintf("GIT_ANNEX_SHELL_DIRECTORY=%s", words[2]),
|
||||
)
|
||||
if results.UserMode < perm.AccessModeWrite {
|
||||
// "If set, disallows any action that could modify the git-annex repository."
|
||||
// - git-annex-shell(1)
|
||||
// We set this when the backend API has told us that we don't have write permission to this repo.
|
||||
log.Debug("Setting GIT_ANNEX_SHELL_READONLY=True")
|
||||
gitcmd.Env = append(gitcmd.Env, "GIT_ANNEX_SHELL_READONLY=True")
|
||||
}
|
||||
}
|
||||
|
||||
process.SetSysProcAttribute(gitcmd)
|
||||
|
|
11
cmd/web.go
11
cmd/web.go
|
@ -9,6 +9,7 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -258,6 +259,12 @@ func runWeb(ctx *cli.Context) error {
|
|||
createPIDFile(ctx.String("pid"))
|
||||
}
|
||||
|
||||
if setting.Annex.Enabled {
|
||||
if _, err := exec.LookPath("git-annex"); err != nil {
|
||||
log.Fatal("You have enabled git-annex support but git-annex is not installed. Please make sure that Forgejo's PATH contains the git-annex executable.")
|
||||
}
|
||||
}
|
||||
|
||||
if !setting.InstallLock {
|
||||
if err := serveInstall(ctx); err != nil {
|
||||
return err
|
||||
|
@ -322,6 +329,10 @@ func listen(m http.Handler, handleRedirector bool) error {
|
|||
log.Info("LFS server enabled")
|
||||
}
|
||||
|
||||
if setting.Annex.Enabled {
|
||||
log.Info("git-annex enabled")
|
||||
}
|
||||
|
||||
var err error
|
||||
switch setting.Protocol {
|
||||
case setting.HTTP:
|
||||
|
|
|
@ -2678,6 +2678,17 @@ LEVEL = Info
|
|||
;; Limit the number of concurrent upload/download operations within a batch
|
||||
;BATCH_OPERATION_CONCURRENCY = 8
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[annex]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; Whether git-annex is enabled; defaults to false
|
||||
;ENABLED = false
|
||||
;; Whether to disable p2phttp support; default is the same as repository.DISABLE_HTTP_GIT
|
||||
;DISABLE_P2PHTTP = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; settings for packages, will override storage setting
|
||||
|
|
82
go.mod
82
go.mod
|
@ -2,7 +2,7 @@ module forgejo.org
|
|||
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.1
|
||||
toolchain go1.24.3
|
||||
|
||||
require (
|
||||
code.forgejo.org/f3/gof3/v3 v3.10.6
|
||||
|
@ -25,7 +25,7 @@ require (
|
|||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
|
||||
github.com/alecthomas/chroma/v2 v2.15.0
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/blevesearch/bleve/v2 v2.4.4
|
||||
github.com/blevesearch/bleve/v2 v2.5.2
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.8
|
||||
github.com/caddyserver/certmagic v0.22.2
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
|
@ -48,7 +48,6 @@ require (
|
|||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/go-openapi/spec v0.20.14
|
||||
github.com/go-sql-driver/mysql v1.9.1
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.14.0
|
||||
github.com/go-webauthn/webauthn v0.12.2
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
|
@ -72,12 +71,12 @@ require (
|
|||
github.com/lib/pq v1.10.9
|
||||
github.com/markbates/goth v1.80.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-sqlite3 v1.14.24
|
||||
github.com/mattn/go-sqlite3 v1.14.28
|
||||
github.com/meilisearch/meilisearch-go v0.31.0
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/minio/minio-go/v7 v7.0.88
|
||||
github.com/msteinert/pam/v2 v2.0.0
|
||||
github.com/msteinert/pam/v2 v2.1.0
|
||||
github.com/nektos/act v0.2.52
|
||||
github.com/niklasfasching/go-org v1.7.0
|
||||
github.com/olivere/elastic/v7 v7.0.32
|
||||
|
@ -118,53 +117,42 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.19.1 // indirect
|
||||
cloud.google.com/go v0.116.0 // indirect
|
||||
cloud.google.com/go/auth v0.9.9 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.2.1 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.1 // indirect
|
||||
cloud.google.com/go/monitoring v1.21.1 // indirect
|
||||
cloud.google.com/go/spanner v1.73.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.9.3 // indirect
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.1.12 // indirect
|
||||
github.com/blevesearch/geo v0.1.20 // indirect
|
||||
github.com/blevesearch/go-faiss v1.0.24 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.22.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.2.8 // indirect
|
||||
github.com/blevesearch/geo v0.2.3 // indirect
|
||||
github.com/blevesearch/go-faiss v1.0.25 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
|
||||
github.com/blevesearch/segment v0.9.1 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
||||
github.com/blevesearch/vellum v1.0.10 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.16 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b // indirect
|
||||
github.com/blevesearch/vellum v1.1.0 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.8 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
|
@ -173,11 +161,8 @@ require (
|
|||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
|
@ -185,15 +170,12 @@ require (
|
|||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.22.7 // indirect
|
||||
github.com/go-webauthn/x v0.1.19 // indirect
|
||||
github.com/go-webauthn/x v0.1.20 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
|
@ -201,10 +183,6 @@ require (
|
|||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/go-tpm v0.9.3 // indirect
|
||||
github.com/google/s2a-go v0.1.8 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||
github.com/googleapis/go-sql-spanner v1.7.4 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
|
@ -235,13 +213,13 @@ require (
|
|||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rhysd/actionlint v1.6.27 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
||||
|
@ -252,18 +230,9 @@ require (
|
|||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
github.com/zeebo/assert v1.3.0 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.3.9 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||
go.etcd.io/bbolt v1.4.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
|
@ -271,11 +240,6 @@ require (
|
|||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/time v0.10.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
google.golang.org/api v0.203.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
package auth_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
auth_model "forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -275,13 +273,7 @@ func TestBuiltinApplicationsClientIDs(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOrphanedOAuth2Applications(t *testing.T) {
|
||||
defer unittest.OverrideFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
|
||||
Base: setting.AppWorkPath,
|
||||
Dirs: []string{"models/auth/TestOrphanedOAuth2Applications/"},
|
||||
},
|
||||
)()
|
||||
defer unittest.OverrideFixtures("models/auth/TestOrphanedOAuth2Applications")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
count, err := auth_model.CountOrphanedOAuth2Applications(db.DefaultContext)
|
||||
|
|
21
models/auth/two_factor.go
Normal file
21
models/auth/two_factor.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// HasTwoFactorByUID returns true if the user has TOTP or WebAuthn enabled for
|
||||
// their account.
|
||||
func HasTwoFactorByUID(ctx context.Context, userID int64) (bool, error) {
|
||||
hasTOTP, err := HasTOTPByUID(ctx, userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if hasTOTP {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return HasWebAuthnRegistrationsByUID(ctx, userID)
|
||||
}
|
34
models/auth/two_factor_test.go
Normal file
34
models/auth/two_factor_test.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
package auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHasTwoFactorByUID(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
t.Run("No twofactor", func(t *testing.T) {
|
||||
ok, err := HasTwoFactorByUID(t.Context(), 2)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, ok)
|
||||
})
|
||||
|
||||
t.Run("WebAuthn credential", func(t *testing.T) {
|
||||
ok, err := HasTwoFactorByUID(t.Context(), 32)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
t.Run("TOTP", func(t *testing.T) {
|
||||
ok, err := HasTwoFactorByUID(t.Context(), 24)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, ok)
|
||||
})
|
||||
}
|
|
@ -139,9 +139,9 @@ func GetTwoFactorByUID(ctx context.Context, uid int64) (*TwoFactor, error) {
|
|||
return twofa, nil
|
||||
}
|
||||
|
||||
// HasTwoFactorByUID returns the two-factor authentication token associated with
|
||||
// the user, if any.
|
||||
func HasTwoFactorByUID(ctx context.Context, uid int64) (bool, error) {
|
||||
// HasTOTPByUID returns the TOTP authentication token associated with
|
||||
// the user, if the user has TOTP enabled for their account.
|
||||
func HasTOTPByUID(ctx context.Context, uid int64) (bool, error) {
|
||||
return db.GetEngine(ctx).Where("uid=?", uid).Exist(&TwoFactor{})
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,9 @@ func (l *XORMLogBridge) Warn(v ...any) {
|
|||
|
||||
// Warnf show warning log
|
||||
func (l *XORMLogBridge) Warnf(format string, v ...any) {
|
||||
if format == "Table %s Column %s db default is %s, struct default is %s" || format == "Table %s Column %s db nullable is %v, struct nullable is %v" {
|
||||
return
|
||||
}
|
||||
l.Log(stackLevel, log.WARN, format, v...)
|
||||
}
|
||||
|
||||
|
|
17
models/fixtures/TestGetUsedForUser/action_artifact.yaml
Normal file
17
models/fixtures/TestGetUsedForUser/action_artifact.yaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
-
|
||||
id: 1001
|
||||
run_id: 792
|
||||
runner_id: 1
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
storage_path: "27/5/1730330775594233150.chunk"
|
||||
file_size: 693147180559
|
||||
file_compressed_size: 693147180559
|
||||
content_encoding: "application/zip"
|
||||
artifact_path: "big-file.zip"
|
||||
artifact_name: "big-file"
|
||||
status: 4
|
||||
created_unix: 1730330775
|
||||
updated_unix: 1730330775
|
||||
expired_unix: 1738106775
|
|
@ -65,6 +65,7 @@
|
|||
merge_base: 985f0301dba5e7b34be866819cd15ad3d8f508ee
|
||||
has_merged: false
|
||||
allow_maintainer_edit: true
|
||||
commits_behind: 1
|
||||
|
||||
-
|
||||
id: 6
|
||||
|
|
|
@ -132,6 +132,7 @@
|
|||
owner_name: org3
|
||||
lower_name: repo5
|
||||
name: repo5
|
||||
default_branch: master
|
||||
num_watches: 0
|
||||
num_stars: 0
|
||||
num_forks: 0
|
||||
|
|
|
@ -5,7 +5,6 @@ package git
|
|||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
|
@ -18,13 +17,7 @@ import (
|
|||
)
|
||||
|
||||
func TestIterateRepositoryIDsWithLFSMetaObjects(t *testing.T) {
|
||||
defer unittest.OverrideFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
|
||||
Base: setting.AppWorkPath,
|
||||
Dirs: []string{"models/git/TestIterateRepositoryIDsWithLFSMetaObjects/"},
|
||||
},
|
||||
)()
|
||||
defer unittest.OverrideFixtures("models/git/TestIterateRepositoryIDsWithLFSMetaObjects")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
type repocount struct {
|
||||
|
|
|
@ -649,8 +649,11 @@ func (c *Comment) LoadAssigneeUserAndTeam(ctx context.Context) error {
|
|||
|
||||
if c.Issue.Repo.Owner.IsOrganization() {
|
||||
c.AssigneeTeam, err = organization.GetTeamByID(ctx, c.AssigneeTeamID)
|
||||
if err != nil && !organization.IsErrTeamNotExist(err) {
|
||||
return err
|
||||
if err != nil {
|
||||
if !organization.IsErrTeamNotExist(err) {
|
||||
return err
|
||||
}
|
||||
c.AssigneeTeam = organization.NewGhostTeam()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
)
|
||||
|
||||
func TestPrivateIssueProjects(t *testing.T) {
|
||||
defer tests.AddFixtures("models/fixtures/PrivateIssueProjects/")()
|
||||
defer unittest.OverrideFixtures("models/fixtures/PrivateIssueProjects")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
@ -119,7 +119,7 @@ func TestPrivateIssueProjects(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPrivateRepoProjects(t *testing.T) {
|
||||
defer tests.AddFixtures("models/fixtures/TestPrivateRepoProjects/")()
|
||||
defer unittest.OverrideFixtures("models/fixtures/TestPrivateRepoProjects")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"forgejo.org/models/unittest"
|
||||
user_model "forgejo.org/models/user"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -161,7 +160,7 @@ func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetUnmergedPullRequestsByHeadInfoMax(t *testing.T) {
|
||||
defer tests.AddFixtures("models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/")()
|
||||
defer unittest.OverrideFixtures("models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repoID := int64(1)
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
package issues_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
issues_model "forgejo.org/models/issues"
|
||||
"forgejo.org/models/unittest"
|
||||
user_model "forgejo.org/models/user"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/timeutil"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -81,13 +79,7 @@ func TestCreateOrStopIssueStopwatch(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetUIDsAndStopwatch(t *testing.T) {
|
||||
defer unittest.OverrideFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
|
||||
Base: setting.AppWorkPath,
|
||||
Dirs: []string{"models/issues/TestGetUIDsAndStopwatch/"},
|
||||
},
|
||||
)()
|
||||
defer unittest.OverrideFixtures("models/issues/TestGetUIDsAndStopwatch")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
uidStopwatches, err := issues_model.GetUIDsAndStopwatch(db.DefaultContext)
|
||||
|
|
5
models/organization/TestFindOrgs/org_user.yml
Normal file
5
models/organization/TestFindOrgs/org_user.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
-
|
||||
id: 1000
|
||||
uid: 4
|
||||
org_id: 22
|
||||
is_public: true
|
|
@ -26,6 +26,7 @@ type SearchOrganizationsOptions struct {
|
|||
type FindOrgOptions struct {
|
||||
db.ListOptions
|
||||
UserID int64
|
||||
IncludeLimited bool
|
||||
IncludePrivate bool
|
||||
}
|
||||
|
||||
|
@ -43,7 +44,11 @@ func (opts FindOrgOptions) ToConds() builder.Cond {
|
|||
cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
|
||||
}
|
||||
if !opts.IncludePrivate {
|
||||
cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
|
||||
if !opts.IncludeLimited {
|
||||
cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
|
||||
} else {
|
||||
cond = cond.And(builder.In("`user`.visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
|
||||
}
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ func TestCountOrganizations(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFindOrgs(t *testing.T) {
|
||||
defer unittest.OverrideFixtures("models/organization/TestFindOrgs")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
|
||||
|
@ -34,8 +35,14 @@ func TestFindOrgs(t *testing.T) {
|
|||
IncludePrivate: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
if assert.Len(t, orgs, 1) {
|
||||
assert.EqualValues(t, 3, orgs[0].ID)
|
||||
if assert.Len(t, orgs, 2) {
|
||||
if orgs[0].ID == 22 {
|
||||
assert.EqualValues(t, 22, orgs[0].ID)
|
||||
assert.EqualValues(t, 3, orgs[1].ID)
|
||||
} else {
|
||||
assert.EqualValues(t, 3, orgs[0].ID)
|
||||
assert.EqualValues(t, 22, orgs[1].ID)
|
||||
}
|
||||
}
|
||||
|
||||
orgs, err = db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
|
||||
|
@ -50,6 +57,14 @@ func TestFindOrgs(t *testing.T) {
|
|||
IncludePrivate: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 2, total)
|
||||
|
||||
total, err = db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
|
||||
UserID: 4,
|
||||
IncludePrivate: false,
|
||||
IncludeLimited: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 1, total)
|
||||
}
|
||||
|
||||
|
|
|
@ -292,3 +292,11 @@ func FixInconsistentOwnerTeams(ctx context.Context) (int64, error) {
|
|||
|
||||
return int64(len(teamIDs)), nil
|
||||
}
|
||||
|
||||
func NewGhostTeam() *Team {
|
||||
return &Team{
|
||||
ID: -1,
|
||||
Name: "Ghost team",
|
||||
LowerName: "ghost team",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
package organization_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/organization"
|
||||
"forgejo.org/models/perm"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -189,13 +187,7 @@ func TestHasTeamRepo(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInconsistentOwnerTeam(t *testing.T) {
|
||||
defer unittest.OverrideFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
|
||||
Base: setting.AppWorkPath,
|
||||
Dirs: []string{"models/organization/TestInconsistentOwnerTeam/"},
|
||||
},
|
||||
)()
|
||||
defer unittest.OverrideFixtures("models/organization/TestInconsistentOwnerTeam")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &organization.TeamUnit{ID: 1000, TeamID: 1000, AccessMode: perm.AccessModeNone})
|
||||
|
|
|
@ -4,11 +4,9 @@
|
|||
package packages
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/setting"
|
||||
|
||||
_ "forgejo.org/models"
|
||||
_ "forgejo.org/models/actions"
|
||||
|
@ -16,16 +14,6 @@ import (
|
|||
_ "forgejo.org/models/forgefed"
|
||||
)
|
||||
|
||||
func AddFixtures(dirs ...string) func() {
|
||||
return unittest.OverrideFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
|
||||
Base: setting.AppWorkPath,
|
||||
Dirs: dirs,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
func TestPackagesGetOrInsertBlob(t *testing.T) {
|
||||
defer AddFixtures("models/fixtures/TestPackagesGetOrInsertBlob/")()
|
||||
defer unittest.OverrideFixtures("models/fixtures/TestPackagesGetOrInsertBlob")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
blake2bIsSet := unittest.AssertExistsAndLoadBean(t, &PackageBlob{ID: 1})
|
||||
|
|
19
models/quota/main_test.go
Normal file
19
models/quota/main_test.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package quota
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/unittest"
|
||||
|
||||
_ "forgejo.org/models"
|
||||
_ "forgejo.org/models/actions"
|
||||
_ "forgejo.org/models/activities"
|
||||
_ "forgejo.org/models/forgefed"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m)
|
||||
}
|
|
@ -131,7 +131,8 @@ func createQueryFor(ctx context.Context, userID int64, q string) db.Engine {
|
|||
case "artifacts":
|
||||
session = session.
|
||||
Table("action_artifact").
|
||||
Join("INNER", "`repository`", "`action_artifact`.repo_id = `repository`.id")
|
||||
Join("INNER", "`repository`", "`action_artifact`.repo_id = `repository`.id").
|
||||
Where("`action_artifact`.status != ?", action_model.ArtifactStatusExpired)
|
||||
case "packages":
|
||||
session = session.
|
||||
Table("package_version").
|
||||
|
|
23
models/quota/used_test.go
Normal file
23
models/quota/used_test.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package quota
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetUsedForUser(t *testing.T) {
|
||||
defer unittest.OverrideFixtures("models/fixtures/TestGetUsedForUser/")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
used, err := GetUsedForUser(t.Context(), 5)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, 4096, used.Size.Assets.Artifacts)
|
||||
}
|
|
@ -4,7 +4,6 @@
|
|||
package repo_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -14,7 +13,6 @@ import (
|
|||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/models/user"
|
||||
"forgejo.org/modules/optional"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -410,13 +408,7 @@ func TestSearchRepositoryByTopicName(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSearchRepositoryIDsByCondition(t *testing.T) {
|
||||
defer unittest.OverrideFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
|
||||
Base: setting.AppWorkPath,
|
||||
Dirs: []string{"models/repo/TestSearchRepositoryIDsByCondition/"},
|
||||
},
|
||||
)()
|
||||
defer unittest.OverrideFixtures("models/repo/TestSearchRepositoryIDsByCondition")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
// Sanity check of the database
|
||||
limitedUser := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 33, Visibility: structs.VisibleTypeLimited})
|
||||
|
|
198
models/unittest/fixture_loader.go
Normal file
198
models/unittest/fixture_loader.go
Normal file
|
@ -0,0 +1,198 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package unittest
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json" //nolint:depguard
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type insertSQL struct {
|
||||
statement string
|
||||
values []any
|
||||
}
|
||||
|
||||
type fixtureFile struct {
|
||||
name string
|
||||
insertSQLs []insertSQL
|
||||
}
|
||||
|
||||
type loader struct {
|
||||
db *sql.DB
|
||||
dialect string
|
||||
|
||||
fixtureFiles []*fixtureFile
|
||||
}
|
||||
|
||||
func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string) (*loader, error) {
|
||||
l := &loader{
|
||||
db: db,
|
||||
dialect: dialect,
|
||||
fixtureFiles: []*fixtureFile{},
|
||||
}
|
||||
|
||||
// Load fixtures
|
||||
for _, fixturePath := range fixturePaths {
|
||||
stat, err := os.Stat(fixturePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If fixture path is a directory, then read read the files of the directory
|
||||
// and use those as fixture files.
|
||||
if stat.IsDir() {
|
||||
files, err := os.ReadDir(fixturePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range files {
|
||||
if !file.IsDir() {
|
||||
fixtureFile, err := l.buildFixtureFile(filepath.Join(fixturePath, file.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.fixtureFiles = append(l.fixtureFiles, fixtureFile)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fixtureFile, err := l.buildFixtureFile(fixturePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.fixtureFiles = append(l.fixtureFiles, fixtureFile)
|
||||
}
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// quoteKeyword returns the quoted string of keyword.
|
||||
func (l *loader) quoteKeyword(keyword string) string {
|
||||
switch l.dialect {
|
||||
case "sqlite3":
|
||||
return `"` + keyword + `"`
|
||||
case "mysql":
|
||||
return "`" + keyword + "`"
|
||||
case "postgres":
|
||||
parts := strings.Split(keyword, ".")
|
||||
for i, p := range parts {
|
||||
parts[i] = `"` + p + `"`
|
||||
}
|
||||
return strings.Join(parts, ".")
|
||||
default:
|
||||
return "invalid"
|
||||
}
|
||||
}
|
||||
|
||||
// placeholder returns the placeholder string.
|
||||
func (l *loader) placeholder(index int) string {
|
||||
if l.dialect == "postgres" {
|
||||
return fmt.Sprintf("$%d", index)
|
||||
}
|
||||
return "?"
|
||||
}
|
||||
|
||||
func (l *loader) buildFixtureFile(fixturePath string) (*fixtureFile, error) {
|
||||
f, err := os.Open(fixturePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var records []map[string]any
|
||||
if err := yaml.NewDecoder(f).Decode(&records); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fixture := &fixtureFile{
|
||||
name: filepath.Base(strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))),
|
||||
insertSQLs: []insertSQL{},
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
columns := []string{}
|
||||
sqlValues := []string{}
|
||||
values := []any{}
|
||||
i := 1
|
||||
|
||||
for key, value := range record {
|
||||
columns = append(columns, l.quoteKeyword(key))
|
||||
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
// Try to decode hex.
|
||||
if strings.HasPrefix(v, "0x") {
|
||||
value, err = hex.DecodeString(strings.TrimPrefix(v, "0x"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case []any:
|
||||
// Decode array.
|
||||
var bytes []byte
|
||||
bytes, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value = string(bytes)
|
||||
}
|
||||
|
||||
values = append(values, value)
|
||||
|
||||
sqlValues = append(sqlValues, l.placeholder(i))
|
||||
i++
|
||||
}
|
||||
|
||||
// Construct the insert SQL.
|
||||
fixture.insertSQLs = append(fixture.insertSQLs, insertSQL{
|
||||
statement: fmt.Sprintf(
|
||||
"INSERT INTO %s (%s) VALUES (%s)",
|
||||
l.quoteKeyword(fixture.name),
|
||||
strings.Join(columns, ", "),
|
||||
strings.Join(sqlValues, ", "),
|
||||
),
|
||||
values: values,
|
||||
})
|
||||
}
|
||||
|
||||
return fixture, nil
|
||||
}
|
||||
|
||||
func (l *loader) Load() error {
|
||||
// Start transaction.
|
||||
tx, err := l.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = tx.Rollback()
|
||||
}()
|
||||
|
||||
// Clean the table and re-insert the fixtures.
|
||||
tableDeleted := map[string]struct{}{}
|
||||
for _, fixture := range l.fixtureFiles {
|
||||
if _, ok := tableDeleted[fixture.name]; !ok {
|
||||
if _, err := tx.Exec(fmt.Sprintf("DELETE FROM %s", l.quoteKeyword(fixture.name))); err != nil {
|
||||
return fmt.Errorf("cannot delete table %s: %w", fixture.name, err)
|
||||
}
|
||||
tableDeleted[fixture.name] = struct{}{}
|
||||
}
|
||||
|
||||
for _, insertSQL := range fixture.insertSQLs {
|
||||
if _, err := tx.Exec(insertSQL.statement, insertSQL.values...); err != nil {
|
||||
return fmt.Errorf("cannot insert %q with values %q: %w", insertSQL.statement, insertSQL.values, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
|
@ -6,7 +6,6 @@ package unittest
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
|
@ -14,12 +13,11 @@ import (
|
|||
"forgejo.org/modules/auth/password/hash"
|
||||
"forgejo.org/modules/setting"
|
||||
|
||||
"github.com/go-testfixtures/testfixtures/v3"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
var fixturesLoader *testfixtures.Loader
|
||||
var fixturesLoader *loader
|
||||
|
||||
// GetXORMEngine gets the XORM engine
|
||||
func GetXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) {
|
||||
|
@ -29,11 +27,18 @@ func GetXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) {
|
|||
return db.DefaultContext.(*db.Context).Engine().(*xorm.Engine)
|
||||
}
|
||||
|
||||
func OverrideFixtures(opts FixturesOptions, engine ...*xorm.Engine) func() {
|
||||
func OverrideFixtures(dir string) func() {
|
||||
old := fixturesLoader
|
||||
if err := InitFixtures(opts, engine...); err != nil {
|
||||
|
||||
opts := FixturesOptions{
|
||||
Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"),
|
||||
Base: setting.AppWorkPath,
|
||||
Dirs: []string{dir},
|
||||
}
|
||||
if err := InitFixtures(opts); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return func() {
|
||||
fixturesLoader = old
|
||||
}
|
||||
|
@ -42,19 +47,19 @@ func OverrideFixtures(opts FixturesOptions, engine ...*xorm.Engine) func() {
|
|||
// InitFixtures initialize test fixtures for a test database
|
||||
func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
|
||||
e := GetXORMEngine(engine...)
|
||||
var fixtureOptionFiles func(*testfixtures.Loader) error
|
||||
fixturePaths := []string{}
|
||||
if opts.Dir != "" {
|
||||
fixtureOptionFiles = testfixtures.Directory(opts.Dir)
|
||||
fixturePaths = append(fixturePaths, opts.Dir)
|
||||
} else {
|
||||
fixtureOptionFiles = testfixtures.Files(opts.Files...)
|
||||
fixturePaths = append(fixturePaths, opts.Files...)
|
||||
}
|
||||
var fixtureOptionDirs []func(*testfixtures.Loader) error
|
||||
if opts.Dirs != nil {
|
||||
for _, dir := range opts.Dirs {
|
||||
fixtureOptionDirs = append(fixtureOptionDirs, testfixtures.Directory(filepath.Join(opts.Base, dir)))
|
||||
fixturePaths = append(fixturePaths, filepath.Join(opts.Base, dir))
|
||||
}
|
||||
}
|
||||
dialect := "unknown"
|
||||
|
||||
var dialect string
|
||||
switch e.Dialect().URI().DBType {
|
||||
case schemas.POSTGRES:
|
||||
dialect = "postgres"
|
||||
|
@ -63,22 +68,10 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
|
|||
case schemas.SQLITE:
|
||||
dialect = "sqlite3"
|
||||
default:
|
||||
fmt.Println("Unsupported RDBMS for integration tests")
|
||||
os.Exit(1)
|
||||
}
|
||||
loaderOptions := []func(loader *testfixtures.Loader) error{
|
||||
testfixtures.Database(e.DB().DB),
|
||||
testfixtures.Dialect(dialect),
|
||||
testfixtures.DangerousSkipTestDatabaseCheck(),
|
||||
fixtureOptionFiles,
|
||||
}
|
||||
loaderOptions = append(loaderOptions, fixtureOptionDirs...)
|
||||
|
||||
if e.Dialect().URI().DBType == schemas.POSTGRES {
|
||||
loaderOptions = append(loaderOptions, testfixtures.SkipResetSequences())
|
||||
panic("Unsupported RDBMS for test")
|
||||
}
|
||||
|
||||
fixturesLoader, err = testfixtures.New(loaderOptions...)
|
||||
fixturesLoader, err = newFixtureLoader(e.DB().DB, dialect, fixturePaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
_ "forgejo.org/models"
|
||||
_ "forgejo.org/models/actions"
|
||||
_ "forgejo.org/models/activities"
|
||||
_ "forgejo.org/models/forgefed"
|
||||
_ "forgejo.org/models/user"
|
||||
)
|
||||
|
||||
|
|
|
@ -576,7 +576,7 @@ func GetUserSalt() (string, error) {
|
|||
// Note: The set of characters here can safely expand without a breaking change,
|
||||
// but characters removed from this set can cause user account linking to break
|
||||
var (
|
||||
customCharsReplacement = strings.NewReplacer("Æ", "AE")
|
||||
customCharsReplacement = strings.NewReplacer("Æ", "AE", "ß", "ss")
|
||||
removeCharsRE = regexp.MustCompile(`['´\x60]`)
|
||||
removeDiacriticsTransform = transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
|
||||
replaceCharsHyphenRE = regexp.MustCompile(`[\s~+]`)
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"forgejo.org/modules/timeutil"
|
||||
"forgejo.org/modules/util"
|
||||
"forgejo.org/modules/validation"
|
||||
"forgejo.org/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -73,7 +72,7 @@ func TestGetUserFromMap(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetUserByName(t *testing.T) {
|
||||
defer tests.AddFixtures("models/user/fixtures/")()
|
||||
defer unittest.OverrideFixtures("models/user/fixtures")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
{
|
||||
|
@ -120,7 +119,7 @@ func TestCanCreateOrganization(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetAllUsers(t *testing.T) {
|
||||
defer tests.AddFixtures("models/user/fixtures/")()
|
||||
defer unittest.OverrideFixtures("models/user/fixtures")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
users, err := user_model.GetAllUsers(db.DefaultContext)
|
||||
|
@ -145,7 +144,7 @@ func TestAPActorID(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSearchUsers(t *testing.T) {
|
||||
defer tests.AddFixtures("models/user/fixtures/")()
|
||||
defer unittest.OverrideFixtures("models/user/fixtures")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
testSuccess := func(opts *user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
|
||||
users, _, err := user_model.SearchUsers(db.DefaultContext, opts)
|
||||
|
@ -634,6 +633,7 @@ func Test_NormalizeUserFromEmail(t *testing.T) {
|
|||
{"test", "test", true},
|
||||
{"Sinéad.O'Connor", "Sinead.OConnor", true},
|
||||
{"Æsir", "AEsir", true},
|
||||
{"Flußpferd", "Flusspferd", true},
|
||||
// \u00e9\u0065\u0301
|
||||
{"éé", "ee", true},
|
||||
{"Awareness Hub", "Awareness-Hub", true},
|
||||
|
|
243
modules/annex/annex.go
Normal file
243
modules/annex/annex.go
Normal file
|
@ -0,0 +1,243 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Unlike modules/lfs, which operates mainly on git.Blobs, this operates on git.TreeEntrys.
|
||||
// The motivation for this is that TreeEntrys have an easy pointer to the on-disk repo path,
|
||||
// while blobs do not (in fact, if building with TAGS=gogit, blobs might exist only in a mock
|
||||
// filesystem, living only in process RAM). We must have the on-disk path to do anything
|
||||
// useful with git-annex because all of its interesting data is on-disk under .git/annex/.
|
||||
|
||||
package annex
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"forgejo.org/modules/git"
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/typesniffer"
|
||||
|
||||
"gopkg.in/ini.v1" //nolint:depguard // This import is forbidden in favor of using the setting module, but we need ini parsing for something other than Forgejo settings
|
||||
)
|
||||
|
||||
// ErrBlobIsNotAnnexed occurs if a blob does not contain a valid annex key
|
||||
var ErrBlobIsNotAnnexed = errors.New("not a git-annex pointer")
|
||||
|
||||
func PrivateInit(ctx context.Context, repoPath string) error {
|
||||
if _, _, err := git.NewCommand(ctx, "config", "annex.private", "true").RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, _, err := git.NewCommand(ctx, "annex", "init").RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LookupKey(blob *git.Blob) (string, error) {
|
||||
stdout, _, err := git.NewCommand(git.DefaultContext, "annex", "lookupkey", "--ref").AddDynamicArguments(blob.ID.String()).RunStdString(&git.RunOpts{Dir: blob.Repo().Path})
|
||||
if err != nil {
|
||||
return "", ErrBlobIsNotAnnexed
|
||||
}
|
||||
key := strings.TrimSpace(stdout)
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// LookupKeyBatch runs git annex lookupkey --batch --ref
|
||||
func LookupKeyBatch(ctx context.Context, shasToBatchReader *io.PipeReader, lookupKeyBatchWriter *io.PipeWriter, wg *sync.WaitGroup, repoPath string) {
|
||||
defer wg.Done()
|
||||
defer shasToBatchReader.Close()
|
||||
defer lookupKeyBatchWriter.Close()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
var errbuf strings.Builder
|
||||
if err := git.NewCommand(ctx, "annex", "lookupkey", "--batch", "--ref").Run(&git.RunOpts{
|
||||
Dir: repoPath,
|
||||
Stdout: lookupKeyBatchWriter,
|
||||
Stdin: shasToBatchReader,
|
||||
Stderr: stderr,
|
||||
}); err != nil {
|
||||
_ = lookupKeyBatchWriter.CloseWithError(fmt.Errorf("git annex lookupkey --batch --ref [%s]: %w - %s", repoPath, err, errbuf.String()))
|
||||
}
|
||||
}
|
||||
|
||||
// CopyFromToBatch runs git -c annex.hardlink=true annex copy --batch-keys --from <remote> --to <remote>
|
||||
func CopyFromToBatch(ctx context.Context, from, to string, keysToCopyReader *io.PipeReader, wg *sync.WaitGroup, repoPath string) {
|
||||
defer wg.Done()
|
||||
defer keysToCopyReader.Close()
|
||||
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
var errbuf strings.Builder
|
||||
if err := git.NewCommand(ctx, "-c", "annex.hardlink=true", "annex", "copy", "--batch-keys", "--from").AddDynamicArguments(from).AddArguments("--to").AddDynamicArguments(to).Run(&git.RunOpts{
|
||||
Dir: repoPath,
|
||||
Stdout: stdout,
|
||||
Stdin: keysToCopyReader,
|
||||
Stderr: stderr,
|
||||
}); err != nil {
|
||||
_ = keysToCopyReader.CloseWithError(fmt.Errorf("git annex copy --batch-keys --from <remote> --to <remote> [%s]: %w - %s", repoPath, err, errbuf.String()))
|
||||
}
|
||||
}
|
||||
|
||||
func ContentLocationFromKey(repoPath, key string) (string, error) {
|
||||
contentLocation, _, err := git.NewCommandContextNoGlobals(git.DefaultContext, "annex", "contentlocation").AddDynamicArguments(key).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("in %s: %s does not seem to be a valid annexed file: %w", repoPath, key, err)
|
||||
}
|
||||
contentLocation = strings.TrimSpace(contentLocation)
|
||||
contentLocation = path.Clean("/" + contentLocation)[1:] // prevent directory traversals
|
||||
contentLocation = path.Join(repoPath, contentLocation)
|
||||
|
||||
return contentLocation, nil
|
||||
}
|
||||
|
||||
// return the absolute path of the content pointed to by the annex pointer stored in the git object
|
||||
// errors if the content is not found in this repo
|
||||
func ContentLocation(blob *git.Blob) (string, error) {
|
||||
key, err := LookupKey(blob)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ContentLocationFromKey(blob.Repo().Path, key)
|
||||
}
|
||||
|
||||
// returns a stream open to the annex content
|
||||
func Content(blob *git.Blob) (*os.File, error) {
|
||||
contentLocation, err := ContentLocation(blob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return os.Open(contentLocation)
|
||||
}
|
||||
|
||||
// whether the object appears to be a valid annex pointer
|
||||
// does *not* verify if the content is actually in this repo;
|
||||
// for that, use ContentLocation()
|
||||
func IsAnnexed(blob *git.Blob) (bool, error) {
|
||||
if !setting.Annex.Enabled {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// LookupKey is written to only return well-formed keys
|
||||
// so the test is just to see if it errors
|
||||
_, err := LookupKey(blob)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrBlobIsNotAnnexed) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// PathIsAnnexRepo determines if repoPath is a git-annex enabled repository
|
||||
func PathIsAnnexRepo(repoPath string) bool {
|
||||
_, _, err := git.NewCommand(git.DefaultContext, "config", "annex.uuid").RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsAnnexRepo determines if repo is a git-annex enabled repository
|
||||
func IsAnnexRepo(repo *git.Repository) bool {
|
||||
_, _, err := git.NewCommand(repo.Ctx, "config", "annex.uuid").RunStdString(&git.RunOpts{Dir: repo.Path})
|
||||
return err == nil
|
||||
}
|
||||
|
||||
var uuid2repoPathCache = make(map[string]string)
|
||||
|
||||
func Init() error {
|
||||
if !setting.Annex.Enabled {
|
||||
return nil
|
||||
}
|
||||
if !setting.Annex.DisableP2PHTTP {
|
||||
log.Info("Populating the git-annex UUID cache with existing repositories")
|
||||
start := time.Now()
|
||||
if err := updateUUID2RepoPathCache(); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Populating the git-annex UUID cache took %v", time.Since(start))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateUUID2RepoPathCache() error {
|
||||
configFiles, err := filepath.Glob(filepath.Join(setting.RepoRootPath, "*", "*", "config"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, configFile := range configFiles {
|
||||
repoPath := strings.TrimSuffix(configFile, "/config")
|
||||
config, err := ini.Load(configFile)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
repoUUID := config.Section("annex").Key("uuid").Value()
|
||||
if repoUUID != "" {
|
||||
uuid2repoPathCache[repoUUID] = repoPath
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func repoPathFromUUIDCache(uuid string) (string, error) {
|
||||
if repoPath, ok := uuid2repoPathCache[uuid]; ok {
|
||||
return repoPath, nil
|
||||
}
|
||||
// If the cache didn't contain an entry for the UUID then update the cache and try again
|
||||
if err := updateUUID2RepoPathCache(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if repoPath, ok := uuid2repoPathCache[uuid]; ok {
|
||||
return repoPath, nil
|
||||
}
|
||||
return "", fmt.Errorf("no repository known for UUID '%s'", uuid)
|
||||
}
|
||||
|
||||
func checkValidity(uuid, repoPath string) (bool, error) {
|
||||
stdout, _, err := git.NewCommand(git.DefaultContext, "config", "annex.uuid").RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
repoUUID := strings.TrimSpace(stdout)
|
||||
return uuid == repoUUID, nil
|
||||
}
|
||||
|
||||
func UUID2RepoPath(uuid string) (string, error) {
|
||||
// Get the current cache entry for the UUID
|
||||
repoPath, err := repoPathFromUUIDCache(uuid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Check if it is still up-to-date
|
||||
valid, err := checkValidity(uuid, repoPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !valid {
|
||||
// If it isn't, remove the cache entry and try again
|
||||
delete(uuid2repoPathCache, uuid)
|
||||
return UUID2RepoPath(uuid)
|
||||
}
|
||||
// Otherwise just return the cached entry
|
||||
return repoPath, nil
|
||||
}
|
||||
|
||||
// GuessContentType guesses the content type of the annexed blob.
|
||||
func GuessContentType(blob *git.Blob) (typesniffer.SniffedType, error) {
|
||||
r, err := Content(blob)
|
||||
if err != nil {
|
||||
return typesniffer.SniffedType{}, err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
return typesniffer.DetectContentTypeFromReader(r)
|
||||
}
|
|
@ -16,6 +16,7 @@ import (
|
|||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"forgejo.org/modules/annex"
|
||||
"forgejo.org/modules/git"
|
||||
"forgejo.org/modules/log"
|
||||
|
||||
|
@ -101,6 +102,12 @@ func Int64sToStrings(ints []int64) []string {
|
|||
|
||||
// EntryIcon returns the octicon class for displaying files/directories
|
||||
func EntryIcon(entry *git.TreeEntry) string {
|
||||
isAnnexed, _ := annex.IsAnnexed(entry.Blob())
|
||||
if isAnnexed {
|
||||
// Show git-annex files as binary files to differentiate them from non-annexed files
|
||||
// TODO: find a more suitable icon, maybe something related to git-annex
|
||||
return "file-binary"
|
||||
}
|
||||
switch {
|
||||
case entry.IsLink():
|
||||
te, _, err := entry.FollowLink()
|
||||
|
|
|
@ -126,6 +126,10 @@ func (b *blobReader) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (b *Blob) Repo() *Repository {
|
||||
return b.repo
|
||||
}
|
||||
|
||||
// Name returns name of the tree entry this blob object was created from (or empty string)
|
||||
func (b *Blob) Name() string {
|
||||
return b.name
|
||||
|
|
|
@ -447,12 +447,13 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS
|
|||
}
|
||||
|
||||
// AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests
|
||||
// It also re-enables git-credential(1), which is used to test git-annex's HTTP support
|
||||
func AllowLFSFiltersArgs() TrustedCmdArgs {
|
||||
// Now here we should explicitly allow lfs filters to run
|
||||
filteredLFSGlobalArgs := make(TrustedCmdArgs, len(globalCommandArgs))
|
||||
j := 0
|
||||
for _, arg := range globalCommandArgs {
|
||||
if strings.Contains(string(arg), "lfs") {
|
||||
if strings.Contains(string(arg), "lfs") || strings.Contains(string(arg), "credential") {
|
||||
j--
|
||||
} else {
|
||||
filteredLFSGlobalArgs[j] = arg
|
||||
|
|
|
@ -85,6 +85,8 @@ readLoop:
|
|||
_, _ = payloadSB.Write(line)
|
||||
case "encoding":
|
||||
_, _ = payloadSB.Write(line)
|
||||
case "change-id": // jj-vcs specific header.
|
||||
_, _ = payloadSB.Write(line)
|
||||
case "gpgsig":
|
||||
fallthrough
|
||||
case "gpgsig-sha256": // FIXME: no intertop, so only 1 exists at present.
|
||||
|
|
|
@ -189,6 +189,55 @@ ISO-8859-1`, commitFromReader.Signature.Payload)
|
|||
assert.EqualValues(t, commitFromReader, commitFromReader2)
|
||||
}
|
||||
|
||||
func TestCommitWithChangeIDFromReader(t *testing.T) {
|
||||
commitString := `e66911914414b0daa85d4a428c8d607b9b249a2c commit 611
|
||||
tree efd3cbedfc360ce9f60e5f92d51221be5afb4bf0
|
||||
author Nicole Patricia Mazzuca <nicole@strega-nil.co> 1746965490 +0200
|
||||
committer Nicole Patricia Mazzuca <nicole@strega-nil.co> 1746965630 +0200
|
||||
change-id psyxzzozmuvvwrwnpqpvmtwntqsnwzpu
|
||||
gpgsig -----BEGIN PGP SIGNATURE-----
|
||||
` + " " + `
|
||||
iHUEABYKAB0WIQT/T2ISZ7rMF2EbKVdDm0tNAL/2MgUCaCCUfgAKCRBDm0tNAL/2
|
||||
Mmu/AQC0OWWHsSlfDKIArdALjDLgd00OQVbP+6iYVE9e+rorFwEA5qYVAXD60EHB
|
||||
+7UVcfwZ2jKajkk3q01VyT/CDo3LLQE=
|
||||
=yq2Y
|
||||
-----END PGP SIGNATURE-----
|
||||
|
||||
views: first commit!
|
||||
|
||||
includes a basic month view, and prints a nice view of an imaginary
|
||||
January where the year starts on a Monday :)`
|
||||
|
||||
sha := &Sha1Hash{0xe6, 0x69, 0x11, 0x91, 0x44, 0x14, 0xb0, 0xda, 0xa8, 0x5d, 0x4a, 0x42, 0x8c, 0x8d, 0x60, 0x7b, 0x9b, 0x24, 0x9a, 0x2c}
|
||||
gitRepo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare"))
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, gitRepo)
|
||||
defer gitRepo.Close()
|
||||
|
||||
commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, commitFromReader)
|
||||
assert.EqualValues(t, sha, commitFromReader.ID)
|
||||
assert.Equal(t, `-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iHUEABYKAB0WIQT/T2ISZ7rMF2EbKVdDm0tNAL/2MgUCaCCUfgAKCRBDm0tNAL/2
|
||||
Mmu/AQC0OWWHsSlfDKIArdALjDLgd00OQVbP+6iYVE9e+rorFwEA5qYVAXD60EHB
|
||||
+7UVcfwZ2jKajkk3q01VyT/CDo3LLQE=
|
||||
=yq2Y
|
||||
-----END PGP SIGNATURE-----
|
||||
`, commitFromReader.Signature.Signature)
|
||||
assert.Equal(t, `tree efd3cbedfc360ce9f60e5f92d51221be5afb4bf0
|
||||
author Nicole Patricia Mazzuca <nicole@strega-nil.co> 1746965490 +0200
|
||||
committer Nicole Patricia Mazzuca <nicole@strega-nil.co> 1746965630 +0200
|
||||
change-id psyxzzozmuvvwrwnpqpvmtwntqsnwzpu
|
||||
|
||||
views: first commit!
|
||||
|
||||
includes a basic month view, and prints a nice view of an imaginary
|
||||
January where the year starts on a Monday :)`, commitFromReader.Signature.Payload)
|
||||
assert.Equal(t, "Nicole Patricia Mazzuca <nicole@strega-nil.co>", commitFromReader.Author.String())
|
||||
}
|
||||
|
||||
func TestHasPreviousCommit(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
||||
|
||||
|
|
|
@ -106,3 +106,36 @@ func BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader *io.PipeReader, s
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BlobsLessThanOrEqual32KiBFromCatFileBatchCheck reads a pipeline from cat-file --batch-check and returns the blobs <=32KiB in size
|
||||
func BlobsLessThanOrEqual32KiBFromCatFileBatchCheck(catFileCheckReader *io.PipeReader, shasToBatchWriter *io.PipeWriter, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
defer catFileCheckReader.Close()
|
||||
scanner := bufio.NewScanner(catFileCheckReader)
|
||||
defer func() {
|
||||
_ = shasToBatchWriter.CloseWithError(scanner.Err())
|
||||
}()
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
fields := strings.Split(line, " ")
|
||||
if len(fields) < 3 || fields[1] != "blob" {
|
||||
continue
|
||||
}
|
||||
size, _ := strconv.Atoi(fields[2])
|
||||
if size > 32*1024 {
|
||||
continue
|
||||
}
|
||||
toWrite := []byte(fields[0] + "\n")
|
||||
for len(toWrite) > 0 {
|
||||
n, err := shasToBatchWriter.Write(toWrite)
|
||||
if err != nil {
|
||||
_ = catFileCheckReader.CloseWithError(err)
|
||||
break
|
||||
}
|
||||
toWrite = toWrite[n:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,7 +204,13 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
if !isTrue(isGenerated) && enry.IsGenerated(f.Name(), content) {
|
||||
|
||||
// We consider three cases:
|
||||
// 1. linguist-generated=true, then we ignore the file.
|
||||
// 2. linguist-generated=false, we don't ignore the file.
|
||||
// 3. linguist-generated is not set, then `enry.IsGenerated` determines if the file is generated.
|
||||
if isTrue(isGenerated) || !isFalse(isGenerated) && enry.IsGenerated(f.Name(), content) {
|
||||
log.Trace("Ignore %q for language stats, because it is generated", f.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,15 @@ func TestRepository_GetLanguageStats(t *testing.T) {
|
|||
"Python": 134,
|
||||
"Java": 112,
|
||||
}, stats)
|
||||
|
||||
stats, err = gitRepo.GetLanguageStats("95d3505f2db273e40be79f84416051ae85e9ea0d")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, map[string]int64{
|
||||
"Cobra": 67,
|
||||
"Python": 67,
|
||||
"Java": 112,
|
||||
}, stats)
|
||||
}
|
||||
|
||||
func TestMergeLanguageStats(t *testing.T) {
|
||||
|
|
Binary file not shown.
|
@ -1,2 +0,0 @@
|
|||
0000000000000000000000000000000000000000 8fee858da5796dfb37704761701bb8e800ad9ef3 Andrew Thornton <art27@cantab.net> 1632140318 +0100 commit (initial): Add some test files for GetLanguageStats
|
||||
8fee858da5796dfb37704761701bb8e800ad9ef3 341fca5b5ea3de596dc483e54c2db28633cd2f97 oliverpool <git@olivier.pfad.fr> 1711278775 +0100 push
|
|
@ -1,2 +0,0 @@
|
|||
0000000000000000000000000000000000000000 8fee858da5796dfb37704761701bb8e800ad9ef3 Andrew Thornton <art27@cantab.net> 1632140318 +0100 commit (initial): Add some test files for GetLanguageStats
|
||||
8fee858da5796dfb37704761701bb8e800ad9ef3 341fca5b5ea3de596dc483e54c2db28633cd2f97 oliverpool <git@olivier.pfad.fr> 1711278775 +0100 push
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2
modules/git/tests/repos/language_stats_repo/packed-refs
Normal file
2
modules/git/tests/repos/language_stats_repo/packed-refs
Normal file
|
@ -0,0 +1,2 @@
|
|||
# pack-refs with: peeled fully-peeled sorted
|
||||
95d3505f2db273e40be79f84416051ae85e9ea0d refs/heads/master
|
|
@ -1 +0,0 @@
|
|||
341fca5b5ea3de596dc483e54c2db28633cd2f97
|
|
@ -259,11 +259,11 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
|
|||
if opts.Mode == internal.CodeSearchModeUnion {
|
||||
query := bleve.NewDisjunctionQuery()
|
||||
for _, field := range strings.Fields(opts.Keyword) {
|
||||
query.AddQuery(inner_bleve.MatchPhraseQuery(field, "Content", repoIndexerAnalyzer, 0))
|
||||
query.AddQuery(inner_bleve.MatchPhraseQuery(field, "Content", repoIndexerAnalyzer, false))
|
||||
}
|
||||
keywordQuery = query
|
||||
} else {
|
||||
keywordQuery = inner_bleve.MatchPhraseQuery(opts.Keyword, "Content", repoIndexerAnalyzer, 0)
|
||||
keywordQuery = inner_bleve.MatchPhraseQuery(opts.Keyword, "Content", repoIndexerAnalyzer, false)
|
||||
}
|
||||
|
||||
if len(opts.RepoIDs) > 0 {
|
||||
|
|
|
@ -65,5 +65,7 @@ func TokenizerConstructor(config map[string]any, cache *registry.Cache) (analysi
|
|||
}
|
||||
|
||||
func init() {
|
||||
registry.RegisterTokenizer(Name, TokenizerConstructor)
|
||||
if err := registry.RegisterTokenizer(Name, TokenizerConstructor); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,11 +29,11 @@ func MatchQuery(matchTerm, field, analyzer string, fuzziness int) *query.MatchQu
|
|||
}
|
||||
|
||||
// MatchPhraseQuery generates a match phrase query for the given phrase, field and analyzer
|
||||
func MatchPhraseQuery(matchPhrase, field, analyzer string, fuzziness int) *query.MatchPhraseQuery {
|
||||
func MatchPhraseQuery(matchPhrase, field, analyzer string, autoFuzzy bool) *query.MatchPhraseQuery {
|
||||
q := bleve.NewMatchPhraseQuery(matchPhrase)
|
||||
q.FieldVal = field
|
||||
q.Analyzer = analyzer
|
||||
q.Fuzziness = fuzziness
|
||||
q.SetAutoFuzziness(autoFuzzy)
|
||||
return q
|
||||
}
|
||||
|
||||
|
|
|
@ -162,15 +162,10 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
}
|
||||
q := bleve.NewBooleanQuery()
|
||||
for _, token := range tokens {
|
||||
fuzziness := 0
|
||||
if token.Fuzzy {
|
||||
// TODO: replace with "auto" after bleve update
|
||||
fuzziness = min(len(token.Term)/4, 2)
|
||||
}
|
||||
innerQ := bleve.NewDisjunctionQuery(
|
||||
inner_bleve.MatchPhraseQuery(token.Term, "title", issueIndexerAnalyzer, fuzziness),
|
||||
inner_bleve.MatchPhraseQuery(token.Term, "content", issueIndexerAnalyzer, fuzziness),
|
||||
inner_bleve.MatchPhraseQuery(token.Term, "comments", issueIndexerAnalyzer, fuzziness))
|
||||
inner_bleve.MatchPhraseQuery(token.Term, "title", issueIndexerAnalyzer, token.Fuzzy),
|
||||
inner_bleve.MatchPhraseQuery(token.Term, "content", issueIndexerAnalyzer, token.Fuzzy),
|
||||
inner_bleve.MatchPhraseQuery(token.Term, "comments", issueIndexerAnalyzer, token.Fuzzy))
|
||||
|
||||
switch token.Kind {
|
||||
case internal.BoolOptMust:
|
||||
|
|
25
modules/markup/external/external.go
vendored
25
modules/markup/external/external.go
vendored
|
@ -11,6 +11,7 @@ import (
|
|||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"forgejo.org/modules/annex"
|
||||
"forgejo.org/modules/graceful"
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/markup"
|
||||
|
@ -82,8 +83,22 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
|
|||
commands = strings.Fields(command)
|
||||
args = commands[1:]
|
||||
)
|
||||
|
||||
if p.IsInputFile {
|
||||
isAnnexed, _ := annex.IsAnnexed(ctx.Blob)
|
||||
// if a renderer wants to read a file, and we have annexed content, we can
|
||||
// provide the annex key file location directly to the renderer. git-annex
|
||||
// takes care of having that location be read-only, so no critical
|
||||
// protection layer is needed. Moreover, the file readily exists, and
|
||||
// expensive temporary files can be avoided, also allowing an operator
|
||||
// to raise MAX_DISPLAY_FILE_SIZE without much negative impact.
|
||||
if p.IsInputFile && isAnnexed {
|
||||
// look for annexed content, will be empty, if there is none
|
||||
annexContentLocation, _ := annex.ContentLocation(ctx.Blob)
|
||||
// we call the renderer, even if there is no annex content present.
|
||||
// showing the pointer file content is not much use, and a topical
|
||||
// renderer might be able to produce something useful from the
|
||||
// filename alone (present in ENV)
|
||||
args = append(args, annexContentLocation)
|
||||
} else if p.IsInputFile {
|
||||
// write to temp file
|
||||
f, err := os.CreateTemp("", "gitea_input")
|
||||
if err != nil {
|
||||
|
@ -126,6 +141,12 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
|
|||
os.Environ(),
|
||||
"GITEA_PREFIX_SRC="+ctx.Links.SrcLink(),
|
||||
"GITEA_PREFIX_RAW="+ctx.Links.RawLink(),
|
||||
// also communicate the relative path of the to-be-rendered item.
|
||||
// this enables the renderer to make use of the original file name
|
||||
// and path, e.g., to make rendering or dtype-detection decisions
|
||||
// that go beyond the originally matched extension. Even if the
|
||||
// content is directly streamed to STDIN
|
||||
"GITEA_RELATIVE_PATH="+ctx.RelativePath,
|
||||
)
|
||||
if !p.IsInputFile {
|
||||
cmd.Stdin = input
|
||||
|
|
|
@ -55,7 +55,7 @@ var (
|
|||
shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`)
|
||||
|
||||
// anyHashPattern splits url containing SHA into parts
|
||||
anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(/[-+~_%.a-zA-Z0-9/]+)?(\?[-+~_%\.a-zA-Z0-9=&]+)?(#[-+~_%.a-zA-Z0-9]+)?`)
|
||||
anyHashPattern = regexp.MustCompile(`https?://(?:(?:\S+/){3,4}(?:commit|tree|blob)/)([0-9a-f]{7,64})(/[-+~_%.a-zA-Z0-9/]+)?(\?[-+~_%\.a-zA-Z0-9=&]+)?(#[-+~_%.a-zA-Z0-9]+)?`)
|
||||
|
||||
// comparePattern matches "http://domain/org/repo/compare/COMMIT1...COMMIT2#hash"
|
||||
comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`)
|
||||
|
|
|
@ -469,6 +469,10 @@ func TestRegExp_anySHA1Pattern(t *testing.T) {
|
|||
for k, v := range testCases {
|
||||
assert.Equal(t, anyHashPattern.FindStringSubmatch(k)[1:], v)
|
||||
}
|
||||
|
||||
for _, v := range []string{"https://codeberg.org/forgejo/forgejo/attachments/774421a1-b0ae-4501-8fba-983874b76811"} {
|
||||
assert.False(t, anyHashPattern.MatchString(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegExp_shortLinkPattern(t *testing.T) {
|
||||
|
|
|
@ -67,14 +67,18 @@ type Header struct {
|
|||
|
||||
// RenderContext represents a render context
|
||||
type RenderContext struct {
|
||||
Ctx context.Context
|
||||
RelativePath string // relative path from tree root of the branch
|
||||
Type string
|
||||
IsWiki bool
|
||||
Links Links
|
||||
Metas map[string]string
|
||||
DefaultLink string
|
||||
GitRepo *git.Repository
|
||||
Ctx context.Context
|
||||
RelativePath string // relative path from tree root of the branch
|
||||
Type string
|
||||
IsWiki bool
|
||||
Links Links
|
||||
Metas map[string]string
|
||||
DefaultLink string
|
||||
GitRepo *git.Repository
|
||||
// reporting the target blob that is to-be-rendered enables
|
||||
// deeper inspection in the handler for external renderer
|
||||
// (i.e., more targeted handling of annexed files)
|
||||
Blob *git.Blob
|
||||
ShaExistCache map[string]bool
|
||||
cancelFn func()
|
||||
SidebarTocNode ast.Node
|
||||
|
|
|
@ -40,6 +40,7 @@ type ServCommandResults struct {
|
|||
UserName string
|
||||
UserEmail string
|
||||
UserID int64
|
||||
UserMode perm.AccessMode
|
||||
OwnerName string
|
||||
RepoName string
|
||||
RepoID int64
|
||||
|
|
|
@ -460,7 +460,8 @@ func findAllIssueReferencesBytes(content []byte, links []string) []*rawReference
|
|||
}
|
||||
parts := strings.Split(u.EscapedPath(), "/")
|
||||
// /user/repo/issues/3
|
||||
if len(parts) != 5 || parts[0] != "" {
|
||||
// /user/repo/pulls/7/files/...
|
||||
if len(parts) < 5 || parts[0] != "" {
|
||||
continue
|
||||
}
|
||||
var sep string
|
||||
|
|
|
@ -132,6 +132,30 @@ func TestFindAllIssueReferences(t *testing.T) {
|
|||
{203, "user4", "repo5", "203", true, XRefActionNone, nil, nil, ""},
|
||||
},
|
||||
},
|
||||
{
|
||||
"This http://gitea.com:3000/user4/repo5/pulls/202#x yes.",
|
||||
[]testResult{
|
||||
{202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
|
||||
},
|
||||
},
|
||||
{
|
||||
"This http://gitea.com:3000/user4/repo5/pulls/202/commits yes.",
|
||||
[]testResult{
|
||||
{202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
|
||||
},
|
||||
},
|
||||
{
|
||||
"This http://gitea.com:3000/user4/repo5/pulls/202/files yes.",
|
||||
[]testResult{
|
||||
{202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
|
||||
},
|
||||
},
|
||||
{
|
||||
"This http://gitea.com:3000/user4/repo5/pulls/202/files#diff- yes.",
|
||||
[]testResult{
|
||||
{202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""},
|
||||
},
|
||||
},
|
||||
{
|
||||
"This http://GiTeA.COM:3000/user4/repo6/pulls/205 yes.",
|
||||
[]testResult{
|
||||
|
|
25
modules/setting/annex.go
Normal file
25
modules/setting/annex.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"forgejo.org/modules/log"
|
||||
)
|
||||
|
||||
// Annex represents the configuration for git-annex
|
||||
var Annex = struct {
|
||||
Enabled bool `ini:"ENABLED"`
|
||||
DisableP2PHTTP bool `ini:"DISABLE_P2PHTTP"`
|
||||
}{}
|
||||
|
||||
func loadAnnexFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("annex")
|
||||
if err := sec.MapTo(&Annex); err != nil {
|
||||
log.Fatal("Failed to map Annex settings: %v", err)
|
||||
}
|
||||
if !sec.HasKey("DISABLE_P2PHTTP") {
|
||||
// If DisableP2PHTTP is not explicitly set then use DisableHTTPGit as its default
|
||||
Annex.DisableP2PHTTP = Repository.DisableHTTPGit
|
||||
}
|
||||
}
|
|
@ -148,6 +148,7 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error {
|
|||
loadCamoFrom(cfg)
|
||||
loadI18nFrom(cfg)
|
||||
loadGitFrom(cfg)
|
||||
loadAnnexFrom(cfg)
|
||||
loadMirrorFrom(cfg)
|
||||
loadMarkupFrom(cfg)
|
||||
loadQuotaFrom(cfg)
|
||||
|
|
|
@ -53,7 +53,8 @@ type CreateHookOption struct {
|
|||
BranchFilter string `json:"branch_filter" binding:"GlobPattern"`
|
||||
AuthorizationHeader string `json:"authorization_header"`
|
||||
// default: false
|
||||
Active bool `json:"active"`
|
||||
Active bool `json:"active"`
|
||||
IsSystemWebhook bool `json:"is_system_webhook"`
|
||||
}
|
||||
|
||||
// EditHookOption options when modify one hook
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package i18n
|
||||
|
@ -239,6 +240,7 @@ func (l *locale) TrString(trKey string, trArgs ...any) string {
|
|||
if defaultLang, ok := l.store.localeMap[l.store.defaultLang]; ok {
|
||||
if msg := defaultLang.LookupNewStyleMessage(trKey); msg != "" {
|
||||
format = msg
|
||||
found = true
|
||||
} else if foundIndex {
|
||||
// Third fallback: old-style default language
|
||||
if msg, ok := defaultLang.idxToMsgMap[idx]; ok {
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
@ -32,10 +34,48 @@ func Remove(name string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// RemoveAll removes the named file or (empty) directory with at most 5 attempts.
|
||||
// MakeWritable recursively makes the named directory writable.
|
||||
func MakeWritable(name string) error {
|
||||
return filepath.WalkDir(name, func(path string, d fs.DirEntry, err error) error {
|
||||
// NB: this is called WalkDir but it works on a single file too
|
||||
if err == nil {
|
||||
info, err := d.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Don't try chmod'ing symlinks (will fail with broken symlinks)
|
||||
if info.Mode()&os.ModeSymlink != os.ModeSymlink {
|
||||
// 0200 == u+w, in octal unix permission notation
|
||||
err = os.Chmod(path, info.Mode()|0o200)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveAll removes the named file or directory with at most 5 attempts.
|
||||
func RemoveAll(name string) error {
|
||||
var err error
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
// Do chmod -R +w to help ensure the removal succeeds.
|
||||
// In particular, in the git-annex case, this handles
|
||||
// https://git-annex.branchable.com/internals/lockdown/ :
|
||||
//
|
||||
// > (The only bad consequence of this is that rm -rf .git
|
||||
// > doesn't work unless you first run chmod -R +w .git)
|
||||
|
||||
err = MakeWritable(name)
|
||||
if err != nil {
|
||||
// try again
|
||||
<-time.After(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
|
||||
err = os.RemoveAll(name)
|
||||
if err == nil {
|
||||
break
|
||||
|
|
|
@ -90,7 +90,7 @@ func logPrinter(logger log.Logger) func(trigger Event, record *requestRecord) {
|
|||
status = v.WrittenStatus()
|
||||
}
|
||||
logf := logger.Info
|
||||
if strings.HasPrefix(req.RequestURI, "/assets/") {
|
||||
if strings.HasPrefix(req.RequestURI, "/assets/") || req.RequestURI == "/api/actions/runner.v1.RunnerService/FetchTask" || req.RequestURI == "/api/actions/runner.v1.RunnerService/UpdateLog" {
|
||||
logf = logger.Trace
|
||||
}
|
||||
message := completedMessage
|
||||
|
|
|
@ -939,7 +939,7 @@ access_token_deletion=Odstranit přístupový token
|
|||
access_token_deletion_cancel_action=Zrušit
|
||||
access_token_deletion_confirm_action=Smazat
|
||||
access_token_deletion_desc=Smazání tokenu zruší přístup k vašemu účtu pro aplikace, které jej používají. Tuto akci nelze vrátit. Pokračovat?
|
||||
delete_token_success=Token byl odstraněn. Aplikace, které jej používají již nemají přístup k vašemu účtu.
|
||||
delete_token_success=Token byl odstraněn. Aplikace, které jej používají, již nemají přístup k vašemu účtu.
|
||||
repo_and_org_access=Přístup k repozitářům a organizacím
|
||||
permissions_public_only=Pouze veřejné
|
||||
permissions_access_all=Vše (veřejné, soukromé a omezené)
|
||||
|
@ -1349,6 +1349,7 @@ view_git_blame=Zobrazit git blame
|
|||
video_not_supported_in_browser=Váš prohlížeč nepodporuje značku HTML5 „video“.
|
||||
audio_not_supported_in_browser=Váš prohlížeč nepodporuje značku HTML5 „audio“.
|
||||
stored_lfs=Uloženo pomocí Git LFS
|
||||
stored_annex=Uloženo pomocí Git Annex
|
||||
symbolic_link=Symbolický odkaz
|
||||
executable_file=Spustitelný soubor
|
||||
vendored = Vendorováno
|
||||
|
@ -1374,6 +1375,7 @@ editor.upload_file=Nahrát soubor
|
|||
editor.edit_file=Upravit soubor
|
||||
editor.preview_changes=Náhled změn
|
||||
editor.cannot_edit_lfs_files=Soubory LFS nemohou být upravovány přes webové rozhraní.
|
||||
editor.cannot_edit_annex_files=Annex soubory nemohou být upravovány přes webové rozhraní.
|
||||
editor.cannot_edit_non_text_files=Binární soubory nemohou být upravovány přes webové rozhraní.
|
||||
editor.edit_this_file=Upravit soubor
|
||||
editor.this_file_locked=Soubor je uzamčen
|
||||
|
|
|
@ -229,7 +229,7 @@ app_desc=Ein einfacher, selbst gehosteter Git-Service
|
|||
install=Einfach zu installieren
|
||||
install_desc=Starte einfach <a target="_blank" rel="noopener noreferrer" href="%[1]s">die Anwendung</a> für deine Plattform oder nutze <a target="_blank" rel="noopener noreferrer" href="%[2]s">Docker</a>. Es existieren auch <a target="_blank" rel="noopener noreferrer" href="%[3]s">paketierte Versionen</a>.
|
||||
platform=Plattformübergreifend
|
||||
platform_desc=Forgejo läuft auf freien Betriebssystemen wie Linux und FreeBSD, sowie auf verschiedenen CPU-Architekturen. Wähle das System, das du magst!
|
||||
platform_desc=Forgejo läuft auf freien Betriebssystemen wie Linux und FreeBSD sowie auf verschiedenen CPU-Architekturen. Wähle das System, das du magst!
|
||||
lightweight=Leichtgewichtig
|
||||
lightweight_desc=Forgejo hat minimale Systemanforderungen und kann selbst auf einem günstigen und stromsparenden Raspberry Pi betrieben werden!
|
||||
license=Quelloffen
|
||||
|
@ -339,7 +339,7 @@ default_enable_timetracking=Zeiterfassung standardmäßig aktivieren
|
|||
default_enable_timetracking.description=Zeiterfassung standardmäßig für neue Repositorys aktivieren.
|
||||
no_reply_address=Versteckte E-Mail-Domain
|
||||
no_reply_address_helper=Domain-Name für Benutzer mit einer versteckten Emailadresse. Zum Beispiel wird der Benutzername „Joe“ in Git als „joe@noreply.example.org“ protokolliert, wenn die versteckte E-Mail-Domain „noreply.example.org“ festgelegt ist.
|
||||
password_algorithm=Passwort Hashing Algorithmus
|
||||
password_algorithm=Passwort-Hashing-Algorithmus
|
||||
invalid_password_algorithm=Ungültiger Passwort-Hash-Algorithmus
|
||||
password_algorithm_helper=Lege einen Passwort-Hashing-Algorithmus fest. Algorithmen haben unterschiedliche Anforderungen und Stärken. Der argon2-Algorithmus ist ziemlich sicher, aber er verbraucht viel Speicher und kann für kleine Systeme ungeeignet sein.
|
||||
enable_update_checker=Aktualisierungsprüfung aktivieren
|
||||
|
@ -347,11 +347,11 @@ env_config_keys=Umgebungskonfiguration
|
|||
env_config_keys_prompt=Die folgenden Umgebungsvariablen werden auch auf Ihre Konfigurationsdatei angewendet:
|
||||
allow_dots_in_usernames = Erlaubt Benutzern die Verwendung von Punkten in ihren Benutzernamen. Hat keine Auswirkungen auf bestehende Konten.
|
||||
enable_update_checker_helper_forgejo = Prüft regelmäßig auf neue Forgejo-Versionen, indem ein DNS-TXT-Eintrag unter release.forgejo.org überprüft wird.
|
||||
smtp_from_invalid = Die „Sende E-Mail Als“-Adresse ist ungültig
|
||||
smtp_from_invalid = Die „Sende E-Mail als“-Adresse ist ungültig
|
||||
config_location_hint = Diese Konfigurationsoptionen werden gespeichert in:
|
||||
allow_only_external_registration = Registrierung nur mittels externer Dienste zulassen
|
||||
app_slogan = Instanz-Slogan
|
||||
app_slogan_helper = Instanz-Slogan hier eingeben. Leer lassen zum deaktivieren.
|
||||
app_slogan_helper = Instanz-Slogan hier eingeben. Leer lassen zum Deaktivieren.
|
||||
|
||||
[home]
|
||||
uname_holder=Benutzername oder E-Mail-Adresse
|
||||
|
@ -418,7 +418,7 @@ forgot_password_title=Passwort vergessen
|
|||
forgot_password=Passwort vergessen?
|
||||
sign_up_now=Noch kein Konto? Jetzt registrieren.
|
||||
sign_up_successful=Konto wurde erfolgreich erstellt. Willkommen!
|
||||
confirmation_mail_sent_prompt=Eine neue Bestätigungs-E-Mail wurde an <b>%s</b> gesendet. Um den Registrierungsprozess abzuschließen, überprüf bitte deinen Posteingang und folg dem angegebenen Link innerhalb von: %s. Falls die E-Mail inkorrekt sein sollte, kannst du dich einloggen und anfragen, eine weitere Bestätigungs-E-Mail an eine andere Adresse zu senden.
|
||||
confirmation_mail_sent_prompt=Eine neue Bestätigungs-E-Mail wurde an <b>%s</b> gesendet. Um den Registrierungsprozess abzuschließen, überprüfe bitte deinen Posteingang und folge dem angegebenen Link innerhalb von: %s. Falls die E-Mail inkorrekt sein sollte, kannst du dich einloggen und anfragen, eine weitere Bestätigungs-E-Mail an eine andere Adresse zu senden.
|
||||
must_change_password=Aktualisiere dein Passwort
|
||||
allow_password_change=Verlange vom Benutzer das Passwort zu ändern (empfohlen)
|
||||
reset_password_mail_sent_prompt=Eine Bestätigungs-E-Mail wurde an <b>%s</b> gesendet. Um den Kontowiederherstellungsprozess abzuschließen, überprüfe bitte deinen Posteingang und folge dem angegebenen Link innerhalb von %s.
|
||||
|
@ -451,7 +451,7 @@ oauth_signup_tab=Neues Konto registrieren
|
|||
oauth_signup_title=Neues Konto fertigstellen
|
||||
oauth_signup_submit=Konto vervollständigen
|
||||
oauth_signin_tab=Mit einem existierenden Konto verbinden
|
||||
oauth_signin_title=Anmelden um verbundenes Konto zu autorisieren
|
||||
oauth_signin_title=Anmelden, um verbundenes Konto zu autorisieren
|
||||
oauth_signin_submit=Konto verbinden
|
||||
oauth.signin.error=Beim Verarbeiten der Autorisierungsanfrage ist ein Fehler aufgetreten. Wenn dieser Fehler weiterhin besteht, wende dich bitte an deinen Administrator.
|
||||
oauth.signin.error.access_denied=Die Autorisierungsanfrage wurde abgelehnt.
|
||||
|
@ -562,7 +562,7 @@ password_change.subject = Dein Passwort wurde geändert
|
|||
password_change.text_1 = Das Passwort für deinen Account wurde soeben geändert.
|
||||
primary_mail_change.subject = Deine primäre E-Mail-Adresse wurde geändert
|
||||
totp_disabled.subject = TOTP wurde deaktiviert
|
||||
totp_disabled.text_1 = TOTP (Time-based one-time password [Zeitbasiertes Einmalpasswort]) wurde auf deinem Account soeben deaktiviert.
|
||||
totp_disabled.text_1 = TOTP (Time-based one-time password [zeitbasiertes Einmalpasswort]) wurde auf deinem Account soeben deaktiviert.
|
||||
totp_disabled.no_2fa = Es sind keine anderen 2FA-Methoden mehr konfiguriert, was bedeutet, dass es nicht mehr nötig ist, sich in deinen Account mit 2FA einzuloggen.
|
||||
removed_security_key.subject = Ein Sicherheitsschlüssel wurde entfernt
|
||||
removed_security_key.no_2fa = Es sind keine anderen 2FA-Methoden mehr konfiguriert, was bedeutet, dass es nicht mehr nötig ist, sich in deinen Account mit 2FA einzuloggen.
|
||||
|
@ -572,7 +572,7 @@ reset_password.text_1 = Das Passwort für deinen Account wurde soeben geändert.
|
|||
primary_mail_change.text_1 = Die primäre E-Mail-Adresse deines Account wurde soeben zu %[1]s geändert. Das bedeutet, dass diese E-Mail-Adresse keine E-Mail-Benachrichtigungen für deinen Account erhalten wird.
|
||||
account_security_caution.text_2 = Wenn du das nicht warst, wurde dein Account kompromittiert. Bitte kontaktiere die Admins dieser Webseite.
|
||||
totp_enrolled.subject = Du hast TOTP als 2FA-Methode aktiviert
|
||||
totp_enrolled.text_1.has_webauthn = Du hast gerade eben TOTP für deinen Account aktiviert. Das bedeutet, dass du in Zukunft für alle Logins in deinen Account TOTP als 2FA-Methode benutzen könntest, oder einen deiner Sicherheitsschlüssel.
|
||||
totp_enrolled.text_1.has_webauthn = Du hast gerade eben TOTP für deinen Account aktiviert. Das bedeutet, dass du in Zukunft für alle Logins in deinen Account TOTP als 2FA-Methode oder einen deiner Sicherheitsschlüssel benutzen könntest.
|
||||
totp_enrolled.text_1.no_webauthn = Du hast gerade eben TOTP für deinen Account aktiviert. Das bedeutet, dass du in Zukunft für alle Logins in deinen Account TOTP als 2FA-Methode benutzen musst.
|
||||
|
||||
[modal]
|
||||
|
@ -661,8 +661,8 @@ organization_leave_success=Du hast die Organisation %s erfolgreich verlassen.
|
|||
invalid_ssh_key=Dein SSH-Key kann nicht überprüft werden: %s
|
||||
invalid_gpg_key=Dein GPG-Key kann nicht überprüft werden: %s
|
||||
invalid_ssh_principal=Ungültige Identität: %s
|
||||
must_use_public_key=Der von dir bereitgestellte Key ist ein privater Key. Bitte lade deinen privaten Key nirgendwo hoch. Verwende stattdessen deinen öffentlichen Key.
|
||||
unable_verify_ssh_key=Der SSH-Key kann nicht verifiziert werden, überprüfe ihn auf Fehler.
|
||||
must_use_public_key=Der von dir bereitgestellte Schlüssel ist ein privater. Bitte lade deinen privaten Schlüssel nirgendwo hoch, sondern verwende stattdessen deinen öffentlichen.
|
||||
unable_verify_ssh_key=Der SSH-Schlüssel kann nicht verifiziert werden, überprüfe ihn auf Fehler.
|
||||
auth_failed=Authentifizierung fehlgeschlagen: %v
|
||||
|
||||
still_own_repo=Dein Konto besitzt ein oder mehrere Repositorys. Diese müssen erst gelöscht oder übertragen werden.
|
||||
|
@ -700,7 +700,7 @@ watched=Beobachtete Repositorys
|
|||
code=Quelltext
|
||||
projects=Projekte
|
||||
overview=Übersicht
|
||||
following_few=%d Folge ich
|
||||
following_few=%d folge ich
|
||||
follow=Folgen
|
||||
unfollow=Nicht mehr folgen
|
||||
user_bio=Biografie
|
||||
|
@ -722,7 +722,7 @@ follow_blocked_user = Du kannst diesen Benutzer nicht folgen, weil du ihn blocki
|
|||
block_user.detail_3 = Ihr werdet nicht mehr in der Lage sein, euch gegenseitig als Repository-Mitarbeiter hinzuzufügen.
|
||||
unblock = Nicht mehr blockieren
|
||||
followers_one = %d Follower
|
||||
following_one = %d Folge ich
|
||||
following_one = %d folge ich
|
||||
followers.title.few = Follower
|
||||
following.title.one = Folgt
|
||||
following.title.few = Folgt
|
||||
|
@ -774,7 +774,7 @@ cancel=Abbrechen
|
|||
language=Sprache
|
||||
ui=Theme
|
||||
hidden_comment_types=Ausgeblendete Kommentartypen
|
||||
hidden_comment_types_description=Die hier markierten Kommentartypen werden nicht innerhalb der Issue-Seiten angezeigt. Die Markierung von „Label“ zum Beispiel entfernt alle Kommentare der Form „<Benutzer> hat <Label> hinzugefügt/entfernt“.
|
||||
hidden_comment_types_description=Die hier markierten Kommentartypen werden innerhalb der Issue-Seiten nicht angezeigt. Beispielsweise entfernt das Ankreuzen von „Label“ alle Kommentare der Form „<Benutzer> hat <Label> hinzugefügt/entfernt“.
|
||||
hidden_comment_types.ref_tooltip=Kommentare, in denen dieses Issue von einem anderen Issue/Commit/… referenziert wurde
|
||||
hidden_comment_types.issue_ref_tooltip=Kommentare, bei denen der Benutzer den Branch/Tag des Issues ändert
|
||||
comment_type_group_reference=Verweis auf Mitglieder
|
||||
|
@ -853,9 +853,9 @@ manage_ssh_keys=SSH-Schlüssel verwalten
|
|||
manage_ssh_principals=SSH-Zertifikats-Principals verwalten
|
||||
manage_gpg_keys=GPG-Schlüssel verwalten
|
||||
add_key=Schlüssel hinzufügen
|
||||
ssh_desc=Diese öffentlichen SSH-Keys sind mit deinem Account verbunden. Der dazugehörigen privaten SSH-Keys geben dir vollen Zugriff auf deine Repositorys. Verifizierte SSH-Key können verwendet werden, um SSH-signierte Git-Commits zu signieren.
|
||||
ssh_desc=Diese öffentlichen SSH-Schlüssel sind mit deinem Account verbunden. Der dazugehörigen privaten SSH-Schlüssel geben dir vollen Zugriff auf deine Repositorys. Verifizierte SSH-Schlüssel können verwendet werden, um SSH-signierte Git-Commits zu signieren.
|
||||
principal_desc=Diese SSH-Zertifikat-Principals sind mit deinem Konto verknüpft und erlauben den vollen Zugriff auf deine Repositorys.
|
||||
gpg_desc=Diese öffentlichen GPG-Keys sind mit deinem Account verbunden und werden benutzt um deine Commits zu verifizieren. Halte die dazugehörigen privaten GPG-Keys geheim, da diese deine Commits signieren.
|
||||
gpg_desc=Diese öffentlichen GPG-Schlüssel sind mit deinem Account verbunden und werden benutzt, um deine Commits zu verifizieren. Halte die dazugehörigen privaten GPG-Schlüssel geheim, da diese deine Commits signieren.
|
||||
ssh_helper=<strong>Brauchst du Hilfe?</strong> Sieh dir die Anleitung zum <a href="%s">Erzeugen deiner eigenen SSH-Schlüssel</a> an oder zum Lösen <a href="%s">häufiger Probleme</a>, denen du bei der Arbeit mit SSH begegnen kannst.
|
||||
gpg_helper=<strong>Brauchst du Hilfe?</strong> Sieh dir die Anleitung <a href="%s">über GPG</a> an.
|
||||
add_new_key=SSH-Schlüssel hinzufügen
|
||||
|
@ -896,7 +896,7 @@ key_id=Schlüssel-ID
|
|||
key_name=Schlüsselname
|
||||
key_content=Inhalt
|
||||
principal_content=Inhalt
|
||||
add_key_success=Der SSH-Key „%s“ wurde hinzugefügt.
|
||||
add_key_success=Der SSH-Schlüssel „%s“ wurde hinzugefügt.
|
||||
add_gpg_key_success=Der GPG-Schlüssel „%s“ wurde hinzugefügt.
|
||||
add_principal_success=Die SSH-Zertifikatsidentität „%s“ wurde hinzugefügt.
|
||||
delete_key=Entfernen
|
||||
|
@ -968,8 +968,8 @@ oauth2_confidential_client=Vertraulicher Client. Für Anwendungen aktivieren, di
|
|||
oauth2_redirect_uris=URIs für die Weiterleitung. Bitte verwende eine neue Zeile für jede URI.
|
||||
save_application=Speichern
|
||||
oauth2_client_id=Client-ID
|
||||
oauth2_client_secret=Client-Secret
|
||||
oauth2_regenerate_secret=Secret neu generieren
|
||||
oauth2_client_secret=Client-Geheimnis
|
||||
oauth2_regenerate_secret=Geheimnis neu generieren
|
||||
oauth2_regenerate_secret_hint=Secret verloren?
|
||||
oauth2_client_secret_hint=Das Secret wird nach dem Verlassen oder Aktualisieren dieser Seite nicht mehr angezeigt. Bitte stelle sicher, dass du es gespeichert hast.
|
||||
oauth2_application_edit=Bearbeiten
|
||||
|
@ -984,7 +984,7 @@ revoke_oauth2_grant=Autorisierung widerrufen
|
|||
revoke_oauth2_grant_description=Wenn du die Autorisierung widerrufst, kann die Anwendung nicht mehr auf deine Daten zugreifen. Bist du dir sicher?
|
||||
revoke_oauth2_grant_success=Zugriff erfolgreich widerrufen.
|
||||
|
||||
twofa_desc=Um dein Konto gegen Passwortdiebstahl zu schützen, kannst du eine Smartphone oder ein anderes Gerät verwenden, um Time-Based One-Time Passwords („TOTP“) zu erhalten.
|
||||
twofa_desc=Um dein Konto gegen Passwortdiebstahl zu schützen, kannst du eine Smartphone oder ein anderes Gerät verwenden, um zeitbasierte Einmalpasswörter („TOTP“) zu erhalten.
|
||||
twofa_is_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung <strong>eingeschaltet</strong>.
|
||||
twofa_not_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung momentan nicht eingeschaltet.
|
||||
twofa_disable=Zwei-Faktor-Authentifizierung deaktivieren
|
||||
|
@ -1028,7 +1028,7 @@ confirm_delete_account=Löschen bestätigen
|
|||
delete_account_title=Benutzerkonto löschen
|
||||
delete_account_desc=Bist du sicher, dass du diesen Account dauerhaft löschen möchtest?
|
||||
|
||||
email_notifications.enable=E-Mail Benachrichtigungen aktivieren
|
||||
email_notifications.enable=E-Mail-Benachrichtigungen aktivieren
|
||||
email_notifications.onmention=Nur E-Mail bei Erwähnung
|
||||
email_notifications.disable=E-Mail-Benachrichtigungen deaktivieren
|
||||
email_notifications.submit=E-Mail-Einstellungen festlegen
|
||||
|
@ -1045,7 +1045,7 @@ user_block_success = Dieser Benutzer wurde erfolgreich blockiert.
|
|||
twofa_recovery_tip = Falls du dein Gerät verlierst, wirst du in der Lage sein, einen einmalig verwendbaren Wiederherstellungsschlüssel zu verwenden, um den auf dein Konto wiederherzustellen.
|
||||
webauthn_alternative_tip = Du möchtest vielleicht eine zusätzliche Authentifizierungsmethode einrichten.
|
||||
blocked_users_none = Keine Benutzer blockiert.
|
||||
webauthn_key_loss_warning = Falls du deine Security-Keys verlierst, wirst du Zugang zu deinem Konto verlieren.
|
||||
webauthn_key_loss_warning = Falls du deine Sicherheitsschlüssel verlierst, wirst du Zugang zu deinem Konto verlieren.
|
||||
user_unblock_success = Die Blockierung dieses Benutzers wurde erfolgreich zurückgenommen.
|
||||
blocked_users = Blockierte Benutzer
|
||||
blocked_since = Blockiert seit %s
|
||||
|
@ -1054,7 +1054,7 @@ hints = Hinweise
|
|||
additional_repo_units_hint = Aktivierung zusätzlicher Repository-Einheiten vorschlagen
|
||||
update_hints = Hinweise aktualisieren
|
||||
update_hints_success = Hinweise wurden aktualisiert.
|
||||
additional_repo_units_hint_description = Einen „Mehr aktivieren“-Hinweis für Repositories, welche nicht alle verfügbaren Einheiten aktiviert haben, anzeigen.
|
||||
additional_repo_units_hint_description = Einen „Mehr aktivieren“-Hinweis für Repositorys, welche nicht alle verfügbaren Einheiten aktiviert haben, anzeigen.
|
||||
pronouns = Pronomen
|
||||
pronouns_custom = Eigene
|
||||
pronouns_unspecified = Nicht spezifiziert
|
||||
|
@ -1064,10 +1064,10 @@ language.localization_project = Hilf uns, Forgejo in deine Sprache zu übersetze
|
|||
language.description = Diese Sprache wird in deinem Konto gespeichert und standardmäßig nach dem Anmelden benutzt.
|
||||
user_block_yourself = Du kannst dich nicht selbst blockieren.
|
||||
pronouns_custom_label = Individuelle Pronomen
|
||||
change_username_redirect_prompt.with_cooldown.one = Der alte Benutzername ist nach einer Schutzzeit von einem Tag wieder für alle Verfügbar. Du kannst den alten Benutzername während dieser Schutzzeit erneut beanspruchen.
|
||||
change_username_redirect_prompt.with_cooldown.few = Der alte Benutzername ist nach einer Schutzzeit von %[1]d Tagen wieder für alle Verfügbar. Du kannst den alten Benutzername während dieser Schutzzeit erneut beanspruchen.
|
||||
change_username_redirect_prompt.with_cooldown.one = Der alte Benutzername ist nach einer Schutzzeit von einem Tag wieder für alle verfügbar. Du kannst den alten Benutzername während dieser Schutzzeit erneut beanspruchen.
|
||||
change_username_redirect_prompt.with_cooldown.few = Der alte Benutzername ist nach einer Schutzzeit von %[1]d Tagen wieder für alle verfügbar. Du kannst den alten Benutzername während dieser Schutzzeit erneut beanspruchen.
|
||||
keep_pronouns_private = Pronomen nur angemeldeten Nutzern anzeigen
|
||||
keep_pronouns_private.description = Dies verbirgt deine Pronomen von Besuchern die nicht angemeldet sind.
|
||||
keep_pronouns_private.description = Dies verbirgt deine Pronomen von Besuchern, die nicht angemeldet sind.
|
||||
quota.sizes.assets.artifacts = Artefakte
|
||||
quota.applies_to_user = Die folgenden Quota-Regeln greifen für deinen Account
|
||||
quota.sizes.assets.attachments.issues = Issue-Anhänge
|
||||
|
@ -1088,9 +1088,9 @@ quota.sizes.assets.all = Assets
|
|||
quota.sizes.assets.attachments.all = Anhänge
|
||||
quota.sizes.assets.packages.all = Pakete
|
||||
quota.sizes.wiki = Wiki
|
||||
regenerate_token_success = Der Token wurde regeneriert. Anwendungen die ihn benutzen haben nicht länger Zugriff auf deinen Account und müssen mit dem neuen Token aktualisiert werden.
|
||||
regenerate_token_success = Der Token wurde regeneriert. Anwendungen, die ihn benutzen, haben nicht länger Zugriff auf deinen Account und müssen mit dem neuen Token aktualisiert werden.
|
||||
access_token_regeneration = Zugangstoken regenerieren
|
||||
access_token_regeneration_desc = Einen Token zu regenerieren wird den Zugriff auf deinen Account von Anwendungen die ihn nutzen zurückziehen. Dies kann nicht rückgängig gemacht werden. Fortsetzen?
|
||||
access_token_regeneration_desc = Einen Token zu regenerieren, wird den Zugriff auf deinen Account von Anwendungen, die ihn nutzen, zurückziehen. Dies kann nicht rückgängig gemacht werden. Fortsetzen?
|
||||
regenerate_token = Regenerieren
|
||||
|
||||
[repo]
|
||||
|
@ -1133,7 +1133,7 @@ issue_labels=Labels
|
|||
issue_labels_helper=Wähle eine Label-Sammlung
|
||||
license=Lizenz
|
||||
license_helper=Wähle eine Lizenz
|
||||
license_helper_desc=Eine Lizenz regelt, was andere mit deinem Code tun (oder nicht tun) können. Unsicher, welches für dein Projekt die Richtige ist? Siehe <a target="_blank" rel="noopener noreferrer" href="%s">Choose a license</a>.
|
||||
license_helper_desc=Eine Lizenz regelt, was andere mit deinem Code tun (oder nicht tun) können. Unsicher, welche für dein Projekt die richtige ist? Siehe <a target="_blank" rel="noopener noreferrer" href="%s">Choose a license</a>.
|
||||
readme=README
|
||||
readme_helper=Wähle eine README-Vorlage
|
||||
readme_helper_desc=Hier kannst du eine komplette Beschreibung für dein Projekt schreiben.
|
||||
|
@ -1189,9 +1189,9 @@ tree_path_not_found_commit=Pfad %[1]s existiert nicht in Commit%[2]s
|
|||
tree_path_not_found_branch=Pfad %[1]s existiert nicht in Branch %[2]s
|
||||
tree_path_not_found_tag=Pfad %[1]s existiert nicht in Tag %[2]s
|
||||
|
||||
transfer.accept=Übertragung Akzeptieren
|
||||
transfer.accept=Übertragung akzeptieren
|
||||
transfer.accept_desc=Übertragung nach „%s“
|
||||
transfer.reject=Übertragung Ablehnen
|
||||
transfer.reject=Übertragung ablehnen
|
||||
transfer.reject_desc=Übertragung nach„%s“ abbrechen
|
||||
transfer.no_permission_to_accept=Du hast keine Berechtigung, diesen Transfer anzunehmen.
|
||||
transfer.no_permission_to_reject=Du hast keine Berechtigung, diesen Transfer abzulehnen.
|
||||
|
@ -1241,7 +1241,7 @@ migrate_items_pullrequests=Pull-Requests
|
|||
migrate_items_merge_requests=Merge-Requests
|
||||
migrate_items_releases=Releases
|
||||
migrate_repo=Repository migrieren
|
||||
migrate.clone_address=Migrations- / Klon-URL
|
||||
migrate.clone_address=Migrations-/Klon-URL
|
||||
migrate.clone_address_desc=Die HTTP(S)- oder „git clone“-URL eines bereits existierenden Repositorys
|
||||
migrate.github_token_desc=Du kannst hier ein oder mehrere Token durch Komma getrennt eintippen, um die Migration aufgrund der GitHub-API-Ratenlimitierung zu beschleunigen. WARNUNG: Der Missbrauch dieser Funktion kann gegen die Richtlinien des Diensteanbieters verstoßen und zur Kontosperrung führen.
|
||||
migrate.clone_local_path=oder ein lokaler Serverpfad
|
||||
|
@ -1259,7 +1259,7 @@ migrate.migrating_failed=Migrieren von <b>%s</b> fehlgeschlagen.
|
|||
migrate.migrating_failed.error=Migration fehlgeschlagen: %s
|
||||
migrate.migrating_failed_no_addr=Migration fehlgeschlagen.
|
||||
migrate.github.description=Daten von github.com oder GitHub-Enterprise-Server migrieren.
|
||||
migrate.git.description=Ein Repository von einem beliebigen Git-Service klonen.
|
||||
migrate.git.description=Ein Repository von einem beliebigen Git-Dienst klonen.
|
||||
migrate.gitlab.description=Daten von gitlab.com oder anderen GitLab-Instanzen migrieren.
|
||||
migrate.gitea.description=Daten von gitea.com oder anderen Gitea-Instanzen migrieren.
|
||||
migrate.gogs.description=Daten von notabug.org oder anderen Gogs-Instanzen migrieren.
|
||||
|
@ -1349,6 +1349,8 @@ view_git_blame=„git blame“ ansehen
|
|||
video_not_supported_in_browser=Dein Browser unterstützt das HTML5-„video“-Tag nicht.
|
||||
audio_not_supported_in_browser=Dein Browser unterstützt das HTML5-„audio“-Tag nicht.
|
||||
stored_lfs=Gespeichert mit Git LFS
|
||||
stored_annex=Gespeichert mit Git Annex
|
||||
stored_annex_not_present = hier nicht vorhanden, versuche git annex whereis
|
||||
symbolic_link=Softlink
|
||||
executable_file=Ausführbare Datei
|
||||
commit_graph=Commit-Graph
|
||||
|
@ -1372,6 +1374,7 @@ editor.upload_file=Datei hochladen
|
|||
editor.edit_file=Datei bearbeiten
|
||||
editor.preview_changes=Vorschau der Änderungen
|
||||
editor.cannot_edit_lfs_files=LFS-Dateien können im Webinterface nicht bearbeitet werden.
|
||||
editor.cannot_edit_annex_files=Annex-Dateien können im Webinterface nicht bearbeitet werden.
|
||||
editor.cannot_edit_non_text_files=Binärdateien können nicht im Webinterface bearbeitet werden.
|
||||
editor.edit_this_file=Datei bearbeiten
|
||||
editor.this_file_locked=Datei ist gesperrt
|
||||
|
@ -1419,8 +1422,8 @@ editor.commit_empty_file_text=Die Datei, die du commiten willst, ist leer. Fortf
|
|||
editor.no_changes_to_show=Keine Änderungen vorhanden.
|
||||
editor.fail_to_update_file=Fehler beim Aktualisieren/Erstellen der Datei „%s“.
|
||||
editor.fail_to_update_file_summary=Fehlermeldung:
|
||||
editor.push_rejected_no_message=Die Änderung wurde vom Server ohne Nachricht abgelehnt. Bitte überprüfe die Git Hooks.
|
||||
editor.push_rejected=Die Änderung wurde vom Server abgelehnt. Bitte überprüfe die Git Hooks.
|
||||
editor.push_rejected_no_message=Die Änderung wurde vom Server ohne Nachricht abgelehnt. Bitte überprüfe die Git-Hooks.
|
||||
editor.push_rejected=Die Änderung wurde vom Server abgelehnt. Bitte überprüfe die Git-Hooks.
|
||||
editor.push_rejected_summary=Vollständige Ablehnungsmeldung:
|
||||
editor.add_subdir=Verzeichnis erstellen …
|
||||
editor.unable_to_upload_files=Fehler beim Hochladen der Dateien nach „%s“. Fehler: %v
|
||||
|
@ -1450,7 +1453,7 @@ commits.signed_by=Signiert von
|
|||
commits.signed_by_untrusted_user=Signiert von nicht vertrauenswürdigen Benutzern
|
||||
commits.signed_by_untrusted_user_unmatched=Signiert von nicht vertrauenswürdigen Benutzern, der nicht mit dem Committer übereinstimmt
|
||||
commits.gpg_key_id=GPG-Schlüssel-ID
|
||||
commits.ssh_key_fingerprint=SSH-Key-Fingerabdruck
|
||||
commits.ssh_key_fingerprint=SSH-Schlüssel-Fingerabdruck
|
||||
commits.view_path=An diesem Punkt im Verlauf anzeigen
|
||||
|
||||
commit.operations=Operationen
|
||||
|
@ -1497,7 +1500,7 @@ projects.column.new_title=Name
|
|||
projects.column.new_submit=Spalte erstellen
|
||||
projects.column.new=Neue Spalte
|
||||
projects.column.set_default=Als Standard verwenden
|
||||
projects.column.set_default_desc=Diese Spalte als Standard für nicht kategorisierte Issues und Pull Requests festlegen
|
||||
projects.column.set_default_desc=Diese Spalte als Standard für nicht kategorisierte Issues und Pull-Requests festlegen
|
||||
projects.column.unset_default=Standard entfernen
|
||||
projects.column.unset_default_desc=Diese Spalte nicht als Standard verwenden
|
||||
projects.column.delete=Spalte löschen
|
||||
|
@ -2016,7 +2019,7 @@ milestones.filter_sort.most_complete=Vollständigste
|
|||
milestones.filter_sort.most_issues=Meiste Issues
|
||||
milestones.filter_sort.least_issues=Wenigste Issues
|
||||
|
||||
signing.will_sign=Dieser Commit wird mit dem Key „%s“ signiert werden.
|
||||
signing.will_sign=Dieser Commit wird mit dem Schlüssel „%s“ signiert werden.
|
||||
signing.wont_sign.error=Es gab einen Fehler bei der Prüfung, ob der Commit signiert werden kann.
|
||||
signing.wont_sign.nokey=Diese Instanz hat keinen Schlüssel, um diesen Commit zu signieren.
|
||||
signing.wont_sign.never=Commits werden nie signiert.
|
||||
|
@ -2213,7 +2216,7 @@ settings.pulls.allow_rebase_update=Update von Pull-Request-Branches per Rebase e
|
|||
settings.pulls.default_delete_branch_after_merge=Standardmäßig bei Pull-Requests den Branch nach dem Zusammenführen löschen
|
||||
settings.pulls.default_allow_edits_from_maintainers=Änderungen von Maintainern standardmäßig erlauben
|
||||
settings.releases_desc=Repository-Releases aktivieren
|
||||
settings.packages_desc=Repository Packages Registry aktivieren
|
||||
settings.packages_desc=Repository-Paket-Registry aktivieren
|
||||
settings.projects_desc=Repository-Projekte aktivieren
|
||||
settings.actions_desc=Aktiviere integrierte CI/CD-Pipelines mit Forgejo-Actions
|
||||
settings.admin_settings=Administratoreinstellungen
|
||||
|
@ -2788,7 +2791,7 @@ pulls.made_using_agit = AGit
|
|||
settings.confirmation_string = Bestätigungsstring
|
||||
pulls.agit_explanation = Mittels AGit-Workflow erstellt. AGit erlaubt Mitwirkenden, Änderungen mittels „git push“ vorzuschlagen, ohne einen Fork oder neuen Branch zu erstellen.
|
||||
activity.navbar.recent_commits = Neueste Commits
|
||||
activity.navbar.code_frequency = Code-Frequenz
|
||||
activity.navbar.code_frequency = Code-Häufigkeit
|
||||
file_follow = Symlink folgen
|
||||
error.broken_git_hook = Die Git-Hooks des Repositorys scheinen kaputt zu sein. Bitte folge der <a target="_blank" rel="noreferrer" href="%s">Dokumentation</a> um sie zu reparieren, dann pushe einige Commits um den Status zu aktualisieren.
|
||||
pulls.merged_title_desc_one = hat %[1]d Commit von <code>%[2]s</code> nach <code>%[3]s</code> %[4]s zusammengeführt
|
||||
|
@ -2894,7 +2897,7 @@ pulls.sign_in_require = <a href="%s">Anmelden</a>, um einen neuen Pull-Request z
|
|||
new_from_template = Eine Vorlage benutzen
|
||||
new_from_template_description = Du kannst eine existierende Repository-Vorlage auf dieser Instanz benutzen und ihre Einstellungen anwenden.
|
||||
auto_init_description = Die Git-Historie mit einer README-Datei und optional einer Lizenz- und .gitignore-Datei starten.
|
||||
issues.context.menu = Kommentar Menü
|
||||
issues.context.menu = Kommentarmenü
|
||||
issues.reaction.add = Reaktion hinzufügen
|
||||
issues.reaction.alt_many = %[1]s und %[2]d mehr reagierten %[3]s.
|
||||
issues.reaction.alt_few = %[1]s reagierten %[2]s.
|
||||
|
@ -2919,7 +2922,7 @@ component_failed_to_load = Ein unerwarteter Fehler ist aufgetreten.
|
|||
component_loading = Lade %s …
|
||||
contributors.what = Beiträge
|
||||
recent_commits.what = neueste Commits
|
||||
code_frequency.what = Code-Frequenz
|
||||
code_frequency.what = Code-Häufigkeit
|
||||
|
||||
|
||||
[org]
|
||||
|
|
|
@ -207,6 +207,10 @@ table_modal.label.rows = Σειρές
|
|||
table_modal.label.columns = Στήλες
|
||||
buttons.new_table.tooltip = Προσθήκη πίνακα
|
||||
|
||||
link_modal.header = Προσθήκη υπερσυνδέσμου
|
||||
link_modal.url = URL
|
||||
link_modal.description = Περιγραφή
|
||||
|
||||
[filter]
|
||||
string.asc=A - Z
|
||||
string.desc=Z - A
|
||||
|
@ -1321,6 +1325,7 @@ view_git_blame=Προβολή git blame
|
|||
video_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 «video».
|
||||
audio_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 «audio».
|
||||
stored_lfs=Αποθηκεύτηκε με το Git LFS
|
||||
stored_annex=Αποθηκεύτηκε με το Git Annex
|
||||
symbolic_link=Symbolic link
|
||||
executable_file=Εκτελέσιμο αρχείο
|
||||
commit_graph=Γράφημα commit
|
||||
|
@ -1344,6 +1349,7 @@ editor.upload_file=Ανέβασμα αρχείου
|
|||
editor.edit_file=Επεξεργασία αρχείου
|
||||
editor.preview_changes=Προεπισκόπηση αλλαγών
|
||||
editor.cannot_edit_lfs_files=Τα αρχεία LFS δεν μπορούν να επεξεργαστούν στη διεπαφή web.
|
||||
editor.cannot_edit_annex_files=Τα αρχεία Annex δεν μπορούν να επεξεργαστούν στη διεπαφή web.
|
||||
editor.cannot_edit_non_text_files=Τα δυαδικά αρχεία δεν μπορούν να επεξεργαστούν στη διεπαφή web.
|
||||
editor.edit_this_file=Επεξεργασία αρχείου
|
||||
editor.this_file_locked=Το αρχείο είναι κλειδωμένο
|
||||
|
|
|
@ -1367,6 +1367,8 @@ view_git_blame = View git blame
|
|||
video_not_supported_in_browser = Your browser does not support the HTML5 "video" tag.
|
||||
audio_not_supported_in_browser = Your browser does not support the HTML5 "audio" tag.
|
||||
stored_lfs = Stored with Git LFS
|
||||
stored_annex = Stored with Git Annex
|
||||
stored_annex_not_present = not present here, try using git annex whereis
|
||||
symbolic_link = Symbolic link
|
||||
executable_file = Executable file
|
||||
vendored = Vendored
|
||||
|
@ -1394,6 +1396,7 @@ editor.upload_file = Upload file
|
|||
editor.edit_file = Edit file
|
||||
editor.preview_changes = Preview changes
|
||||
editor.cannot_edit_lfs_files = LFS files cannot be edited in the web interface.
|
||||
editor.cannot_edit_annex_files = Annex files cannot be edited in the web interface.
|
||||
editor.cannot_edit_non_text_files = Binary files cannot be edited in the web interface.
|
||||
editor.edit_this_file = Edit file
|
||||
editor.this_file_locked = File is locked
|
||||
|
|
|
@ -20,7 +20,7 @@ notifications=Notificaciones
|
|||
active_stopwatch=Rastreador de tiempo activo
|
||||
create_new=Crear…
|
||||
user_profile_and_more=Perfil y configuración…
|
||||
signed_in_as=Identificado como
|
||||
signed_in_as=Conectado como
|
||||
enable_javascript=Este sitio web requiere JavaScript.
|
||||
toc=Tabla de contenidos
|
||||
licenses=Licencias
|
||||
|
@ -38,12 +38,12 @@ passcode=Código de acceso
|
|||
|
||||
webauthn_insert_key=Introduzca su clave de seguridad
|
||||
webauthn_sign_in=Presione el botón en su clave de seguridad. Si su clave de seguridad no tiene ningún botón, vuelva a insertarla.
|
||||
webauthn_press_button=Por favor, presione el botón de su llave de seguridad…
|
||||
webauthn_press_button=Por favor, presione el botón en su clave de seguridad…
|
||||
webauthn_use_twofa=Utilice un código de doble factor desde su teléfono móvil
|
||||
webauthn_error=No se pudo leer su llave de seguridad.
|
||||
webauthn_unsupported_browser=Su navegador no soporta actualmente WebAuthn.
|
||||
webauthn_unsupported_browser=Actualmente su navegador no soporta WebAuthn.
|
||||
webauthn_error_unknown=Ha ocurrido un error desconocido. Por favor, inténtelo de nuevo.
|
||||
webauthn_error_insecure=`WebAuthn sólo soporta conexiones seguras. Para probar sobre HTTP, puede utilizar el origen "localhost" o "127.0.0.1"`
|
||||
webauthn_error_insecure=WebAuthn sólo soporta conexiones seguras. Para probar sobre HTTP, puede utilizar el origen "localhost" o "127.0.0.1"
|
||||
webauthn_error_unable_to_process=El servidor no pudo procesar su solicitud.
|
||||
webauthn_error_duplicated=La clave de seguridad no está permitida para esta solicitud. Por favor, asegúrese de que la clave no está ya registrada.
|
||||
webauthn_error_empty=Debe establecer un nombre para esta clave.
|
||||
|
@ -72,7 +72,7 @@ all=Todos
|
|||
sources=Propios
|
||||
mirrors=Réplica
|
||||
collaborative=Colaborativo
|
||||
forks=Forks
|
||||
forks=Bifurcaciones
|
||||
|
||||
activities=Actividades
|
||||
pull_requests=Solicitudes de incorporación de cambios
|
||||
|
@ -301,7 +301,7 @@ offline_mode.description=Deshabilitar redes de distribución de contenido de ter
|
|||
disable_gravatar=Desactivar Gravatar
|
||||
disable_gravatar.description=Desactivar el Gravatar y otros fuentes de avatares de terceros. Se utilizará un avatar por defecto a menos que un usuario suba un avatar localmente.
|
||||
federated_avatar_lookup=Habilitar avatares federados
|
||||
federated_avatar_lookup.description=Buscar de avatares con Libravatar.
|
||||
federated_avatar_lookup.description=Busca avatares con Libravatar.
|
||||
disable_registration=Deshabilitar auto-registro
|
||||
disable_registration.description=Sólo los administradores de la instancia podrán crear nuevas cuentas. Es muy recomendable mantener deshabilitado el registro a menos que pretenda alojar una instancia pública para todo el mundo y esté preparado para lidiar con grandes cantidades de cuentas de spam.
|
||||
allow_only_external_registration.description=Los usuarios sólo podrán crear nuevas cuentas utilizando servicios externos configurados.
|
||||
|
@ -669,7 +669,7 @@ still_own_packages=Tu cuenta posee uno o más paquetes, elimínalos primero.
|
|||
org_still_own_repo=Esta organización todavía posee uno o más repositorios, elimínalos o transfiérelos primero.
|
||||
org_still_own_packages=Esta organización todavía posee uno o más paquetes, elimínalos primero.
|
||||
|
||||
target_branch_not_exist=La rama de destino no existe
|
||||
target_branch_not_exist=La rama de destino no existe.
|
||||
admin_cannot_delete_self = No puedes eliminarte a ti mismo cuando eres un admin (administrador). Por favor, elimina primero tus privilegios de administrador.
|
||||
username_error_no_dots = ` solo puede contener carácteres alfanuméricos ("0-9","a-z","A-Z"), guiones ("-"), y guiones bajos ("_"). No puede empezar o terminar con carácteres no alfanuméricos y también están prohibidos los carácteres no alfanuméricos consecutivos.`
|
||||
unsupported_login_type = No se admite el tipo de inicio de sesión para eliminar la cuenta.
|
||||
|
@ -844,7 +844,7 @@ add_email_success=La nueva dirección de correo electrónico ha sido añadida.
|
|||
email_preference_set_success=La preferencia de correo electrónico se ha establecido correctamente.
|
||||
add_openid_success=La nueva dirección OpenID ha sido añadida.
|
||||
keep_email_private=Ocultar dirección de correo electrónico
|
||||
keep_email_private_popup=Esto ocultará tu dirección de correo electrónico de tu perfil. Ya no será la dirección predeterminada para los confirmaciones realizadas a través de la interfaz web, como las subidas y ediciones de archivos, y no se utilizará para las confirmaciones de fusión. En su lugar, se utilizará una dirección especial %s para asociar las confirmaciones a tu cuenta. Ten en cuenta que cambiar esta opción no afectará a las confirmaciones existentes.
|
||||
keep_email_private_popup=Su dirección de correo electrónico no se mostrará en su perfil y no será la predeterminada para las confirmaciones realizadas a través de la interfaz web, como las subidas de archivos, las ediciones y las confirmaciones de fusión. En su lugar, se utilizará una dirección especial %s para vincular las confirmaciones a tu cuenta. Esta opción no afectará a las confirmaciones existentes.
|
||||
openid_desc=OpenID le permite delegar la autenticación a un proveedor externo.
|
||||
|
||||
manage_ssh_keys=Gestionar claves SSH
|
||||
|
@ -997,7 +997,7 @@ scan_this_image=Escanee esta imagen con su aplicación de autenticación:
|
|||
or_enter_secret=O introduzca el secreto: %s
|
||||
then_enter_passcode=E introduzca el código de acceso mostrado en la aplicación:
|
||||
passcode_invalid=El código de acceso es incorrecto. Vuelva a intentarlo.
|
||||
twofa_enrolled=Su cuenta ha sido inscrita en la autenticación de doble factor. ¡Guarde su código de respaldo (%s) en un lugar seguro, ya que sólo se muestra una vez!
|
||||
twofa_enrolled=Su cuenta ha sido inscrita en la autenticación de doble factor. Guarde su código de respaldo (%s) en un lugar seguro, ya que no se mostrará nuevamente.
|
||||
twofa_failed_get_secret=No se pudo obtener el secreto.
|
||||
|
||||
webauthn_desc=Las claves de seguridad son dispositivos hardware que contienen claves criptográficas. Pueden ser usados para la autenticación de doble factor. Las claves de seguridad deben soportar el estándar <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>.
|
||||
|
@ -1084,6 +1084,12 @@ quota.rule.exceeded = Excedido
|
|||
quota.rule.no_limit = Ilimitado
|
||||
quota.sizes.assets.all = Activos
|
||||
|
||||
access_token_regeneration = Regenerar token de acceso
|
||||
quota.sizes.git.lfs = Git LFS
|
||||
quota.sizes.assets.attachments.issues = Archivos adjuntos de incidencia
|
||||
|
||||
keep_pronouns_private.description = Esto ocultará sus pronombres a los visitantes que no hayan iniciado sesión.
|
||||
|
||||
[repo]
|
||||
owner=Propietario
|
||||
owner_helper=Algunas organizaciones pueden no aparecer en el menú desplegable debido a un límite máximo de recuento de repositorios.
|
||||
|
@ -1103,8 +1109,8 @@ clone_helper=¿Necesita ayuda para clonar? Visite <a target="_blank" rel="noopen
|
|||
fork_repo=Hacer una bifurcación del repositorio
|
||||
fork_from=Crear una bifurcación desde
|
||||
already_forked=Ya ha forkeado %s
|
||||
fork_to_different_account=Forkear a una cuenta diferente
|
||||
fork_visibility_helper=La visibilidad de un repositorio del cual se ha hecho fork no puede ser cambiada.
|
||||
fork_to_different_account=Bifurcar a una cuenta diferente
|
||||
fork_visibility_helper=No se puede cambiar la visibilidad de un repositorio bifurcado.
|
||||
fork_branch=Rama a clonar en la bifurcación
|
||||
all_branches=Todas las ramas
|
||||
fork_no_valid_owners=Este repositorio no puede ser bifurcado porque no hay propietarios válidos.
|
||||
|
@ -1158,7 +1164,7 @@ mirror_password_help=Cambie el nombre de usario para eliminar una contraseña al
|
|||
watchers=Seguidores
|
||||
stargazers=Fans
|
||||
stars_remove_warning=Esto eliminará todas las estrellas de este repositorio.
|
||||
forks=Forks
|
||||
forks=Bifurcaciones
|
||||
reactions_more=y %d más
|
||||
unit_disabled=El administrador del sitio ha deshabilitado esta sección del repositorio.
|
||||
language_other=Otros
|
||||
|
@ -1228,8 +1234,8 @@ migrate_items_wiki=Wiki
|
|||
migrate_items_milestones=Hitos
|
||||
migrate_items_labels=Etiquetas
|
||||
migrate_items_issues=Incidencias
|
||||
migrate_items_pullrequests=Pull requests
|
||||
migrate_items_merge_requests=Merge requests
|
||||
migrate_items_pullrequests=Solicitudes de extracción
|
||||
migrate_items_merge_requests=Solicitudes de fusión
|
||||
migrate_items_releases=Lanzamientos
|
||||
migrate_repo=Migrar repositorio
|
||||
migrate.clone_address=Migrar / Clonar desde URL
|
||||
|
@ -1268,17 +1274,17 @@ migrate.cancel_migrating_title=Cancelar la migración
|
|||
migrate.cancel_migrating_confirm=¿Quiere cancelar esta migración?
|
||||
|
||||
mirror_from=réplica de
|
||||
forked_from=forkeado de
|
||||
forked_from=bifurcado de
|
||||
generated_from=generado desde
|
||||
fork_from_self=No puede hacer fork a un repositorio que ya es suyo.
|
||||
fork_guest_user=Regístrate para forkear este repositorio.
|
||||
fork_from_self=No puede bifurcar un repositorio que ya es suyo.
|
||||
fork_guest_user=Iniciar sesión para bifurcar este repositorio.
|
||||
watch_guest_user=Iniciar sesión para seguir este repositorio.
|
||||
star_guest_user=Iniciar sesión para destacar este repositorio.
|
||||
unwatch=Dejar de seguir
|
||||
watch=Seguir
|
||||
unstar=Eliminar de favoritos
|
||||
star=Destacar
|
||||
fork=Fork
|
||||
fork=Bifurcar
|
||||
download_archive=Descargar repositorio
|
||||
more_operations=Más operaciones
|
||||
|
||||
|
@ -1340,6 +1346,7 @@ view_git_blame=Ver Git blame
|
|||
video_not_supported_in_browser=Su navegador no soporta el tag "video" de HTML5.
|
||||
audio_not_supported_in_browser=Su navegador no soporta el tag "audio" de HTML5.
|
||||
stored_lfs=Almacenados con Git LFS
|
||||
stored_annex=Almacenados con Git Annex
|
||||
symbolic_link=Enlace simbólico
|
||||
executable_file=Archivo ejecutable
|
||||
commit_graph=Gráfico de commits
|
||||
|
@ -1363,6 +1370,7 @@ editor.upload_file=Subir archivo
|
|||
editor.edit_file=Editar archivo
|
||||
editor.preview_changes=Vista previa de los cambios
|
||||
editor.cannot_edit_lfs_files=Los archivos LFS no se pueden editar en la interfaz web.
|
||||
editor.cannot_edit_annex_files=Los archivos Annex no se pueden editar en la interfaz web.
|
||||
editor.cannot_edit_non_text_files=Los archivos binarios no se pueden editar en la interfaz web.
|
||||
editor.edit_this_file=Editar archivo
|
||||
editor.this_file_locked=El archivo está bloqueado
|
||||
|
@ -1518,7 +1526,7 @@ issues.new.no_projects=Ningún proyecto
|
|||
issues.new.open_projects=Proyectos abiertos
|
||||
issues.new.closed_projects=Proyectos cerrados
|
||||
issues.new.no_items=No hay elementos
|
||||
issues.new.milestone=Milestone
|
||||
issues.new.milestone=Hito
|
||||
issues.new.no_milestone=Sin hito
|
||||
issues.new.clear_milestone=Limpiar Milestone
|
||||
issues.new.open_milestone=Hitos abiertos
|
||||
|
@ -1566,12 +1574,12 @@ issues.change_title_at=`cambió el título de <b><strike>%s</strike></b> a <b>%s
|
|||
issues.change_ref_at=`cambió referencia de <b><strike>%s</strike></b> a <b>%s</b> %s`
|
||||
issues.remove_ref_at=`eliminó la referencia <b>%s</b> %s`
|
||||
issues.add_ref_at=`añadió la referencia <b>%s</b> %s`
|
||||
issues.delete_branch_at=`rama eliminada <b>%s</b> %s`
|
||||
issues.delete_branch_at=`eliminó la rama <b>%s</b> %s`
|
||||
issues.filter_label=Etiqueta
|
||||
issues.filter_label_exclude=`Usa <code>alt</code> + <code>clic/enter</code> para excluir etiquetas`
|
||||
issues.filter_label_no_select=Todas las etiquetas
|
||||
issues.filter_label_select_no_label=Sin etiqueta
|
||||
issues.filter_milestone=Milestone
|
||||
issues.filter_milestone=Hito
|
||||
issues.filter_milestone_all=Todos los hitos
|
||||
issues.filter_milestone_none=Sin hitos
|
||||
issues.filter_milestone_open=Abrir hitos
|
||||
|
@ -1602,8 +1610,8 @@ issues.filter_sort.nearduedate=Fecha de vencimiento más cercana
|
|||
issues.filter_sort.farduedate=Fecha de vencimiento más lejana
|
||||
issues.filter_sort.moststars=Mas estrellas
|
||||
issues.filter_sort.feweststars=Menor número de estrellas
|
||||
issues.filter_sort.mostforks=La mayoría de forks
|
||||
issues.filter_sort.fewestforks=Menor número de forks
|
||||
issues.filter_sort.mostforks=La mayoría de bifurcaciones
|
||||
issues.filter_sort.fewestforks=Menor número de bifurcaciones
|
||||
issues.keyword_search_unavailable=La búsqueda por palabra clave no está disponible actualmente. Por favor, contacte con el administrador de su sitio.
|
||||
issues.action_open=Abrir
|
||||
issues.action_close=Cerrar
|
||||
|
@ -1627,7 +1635,7 @@ issues.closed_title=Cerrada
|
|||
issues.draft_title=Borrador
|
||||
issues.num_comments_1=%d comentario
|
||||
issues.num_comments=%d comentarios
|
||||
issues.commented_at=`comentado <a href="#%s">%s</a>`
|
||||
issues.commented_at=`comentó <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=¿Seguro que deseas eliminar este comentario?
|
||||
issues.context.copy_link=Copiar enlace
|
||||
issues.context.quote_reply=Citar respuesta
|
||||
|
@ -1637,7 +1645,7 @@ issues.context.delete=Eliminar
|
|||
issues.no_content=No se ha proporcionado una descripción.
|
||||
issues.close=Cerrar incidencia
|
||||
issues.comment_pull_merged_at=commit fusionado %[1]s en %[2]s %[3]s
|
||||
issues.comment_manually_pull_merged_at=commit manualmente fusionado %[1]s en %[2]s %[3]s
|
||||
issues.comment_manually_pull_merged_at=commit %[1]s manualmente fusionado en %[2]s %[3]s
|
||||
issues.close_comment_issue=Cerrar con comentario
|
||||
issues.reopen_issue=Reabrir
|
||||
issues.reopen_comment_issue=Reabrir con comentario
|
||||
|
@ -1662,7 +1670,7 @@ issues.role.collaborator=Colaborador
|
|||
issues.role.collaborator_helper=Este usuario ha sido invitado a colaborar en el repositorio.
|
||||
issues.role.first_time_contributor=Contribuyente por primera vez
|
||||
issues.role.first_time_contributor_helper=Esta es la primera contribución de este usuario al repositorio.
|
||||
issues.role.contributor=Colaborador
|
||||
issues.role.contributor=Contribuidor
|
||||
issues.role.contributor_helper=Este usuario ha realizado commit previamente en este repositorio.
|
||||
issues.re_request_review=Solicitar revisión de nuevo
|
||||
issues.is_stale=Ha habido cambios en este PR desde esta revisión
|
||||
|
@ -1753,7 +1761,7 @@ issues.error_modifying_due_date=Fallo al modificar la fecha de vencimiento.
|
|||
issues.error_removing_due_date=Fallo al eliminar la fecha de vencimiento.
|
||||
issues.push_commit_1=añadió %d commit %s
|
||||
issues.push_commits_n=añadió %d commits %s
|
||||
issues.force_push_codes=`hizo push forzado %[1]s de <a class="%[7]s" href="%[3]s"><code>%[2]s</code></a> a <a class="%[7]s" href="%[5]s"><code>%[4]s</code></a> %[6]s`
|
||||
issues.force_push_codes=`empujó forzosamente %[1]s de <a class="%[7]s" href="%[3]s"><code>%[2]s</code></a> a <a class="%[7]s" href="%[5]s"><code>%[4]s</code></a> %[6]s`
|
||||
issues.force_push_compare=Comparar
|
||||
issues.due_date_form=aaaa-mm-dd
|
||||
issues.due_date_form_add=Añadir fecha de vencimiento
|
||||
|
@ -1872,7 +1880,7 @@ pulls.title_desc_few=quiere fusionar %[1]d commits de <code>%[2]s</code> en <cod
|
|||
pulls.merged_title_desc_few=fusionó %[1]d commits de <code>%[2]s</code> en <code>%[3]s</code> %[4]s
|
||||
pulls.change_target_branch_at=`cambió la rama objetivo de <b>%s</b> a <b>%s</b> %s`
|
||||
pulls.tab_conversation=Conversación
|
||||
pulls.tab_commits=Commits
|
||||
pulls.tab_commits=Confirmaciones
|
||||
pulls.tab_files=Archivos modificados
|
||||
pulls.reopen_to_merge=Vuelva a abrir este Pull Request para realizar una fusión.
|
||||
pulls.cant_reopen_deleted_branch=Este pull request no se puede reabrir porque la rama fue eliminada.
|
||||
|
@ -1973,7 +1981,7 @@ pulls.auto_merge_canceled_schedule_comment=`canceló la fusión automática de e
|
|||
pulls.delete.title=¿Borrar este pull request?
|
||||
pulls.delete.text=¿Realmente quieres eliminar esta pull request? (Esto eliminará permanentemente todo el contenido. Considera cerrarlo si simplemente deseas archivarlo)
|
||||
|
||||
pulls.recently_pushed_new_branches=Has realizado push en la rama <strong>%[1]s</strong> %[2]s
|
||||
pulls.recently_pushed_new_branches=Empujaste en la rama <a href="%[3]s"><strong>%[1]s</strong></a> %[2]s
|
||||
|
||||
pull.deleted_branch=(eliminado):%s
|
||||
|
||||
|
@ -1984,7 +1992,7 @@ milestones.no_due_date=Sin fecha límite
|
|||
milestones.open=Abrir
|
||||
milestones.close=Cerrar
|
||||
milestones.new_subheader=Los hitos pueden ayudarle a organizar los problemas y monitorizar su progreso.
|
||||
milestones.completeness=%d%% Completado
|
||||
milestones.completeness=<strong>%d%%</strong> Completado
|
||||
milestones.create=Crear hito
|
||||
milestones.title=Título
|
||||
milestones.desc=Descripción
|
||||
|
@ -2025,7 +2033,7 @@ ext_wiki=Wiki externa
|
|||
ext_wiki.desc=Enlace a una wiki externa.
|
||||
|
||||
wiki=Wiki
|
||||
wiki.welcome=¡Bienvenidos a la Wiki!
|
||||
wiki.welcome=Bienvenido a la Wiki.
|
||||
wiki.welcome_desc=Esta wiki le permite escribir y compartir documentación con otros colaboradores.
|
||||
wiki.desc=Escriba y comparta documentación con colaboradores.
|
||||
wiki.create_first_page=Crear la primera página
|
||||
|
@ -2102,7 +2110,7 @@ activity.git_stats_author_n=%d autores
|
|||
activity.git_stats_pushed_1=ha hecho push
|
||||
activity.git_stats_pushed_n=han hecho push
|
||||
activity.git_stats_commit_1=%d commit
|
||||
activity.git_stats_commit_n=%d commits
|
||||
activity.git_stats_commit_n=%d confirmaciones
|
||||
activity.git_stats_push_to_branch=a %s y
|
||||
activity.git_stats_push_to_all_branches=en todas las ramas.
|
||||
activity.git_stats_on_default_branch=En %s,
|
||||
|
@ -2252,7 +2260,7 @@ settings.trust_model.default.desc=Utilice el modelo de confianza de repositorio
|
|||
settings.trust_model.collaborator=Colaborador
|
||||
settings.trust_model.collaborator.long=Colaborador: Confiar en firmas de colaboradores
|
||||
settings.trust_model.collaborator.desc=Las firmas válidas de los colaboradores de este repositorio serán marcadas como "confiables" - (coincidan o no con el committer). De lo contrario, las firmas válidas serán marcadas como "no confiables" si la firma coincide con el committer y "no coincidente" si no lo es.
|
||||
settings.trust_model.committer=Committer
|
||||
settings.trust_model.committer=Confirmador
|
||||
settings.trust_model.committer.long=Committer: Firmas de confianza que coinciden con los committers (Esto coincide con GitHub y obligará a Forgejo a firmar los commits a tener a Forgejo como el committer)
|
||||
settings.trust_model.committer.desc=Las firmas válidas sólo se marcarán como "confiables" si coinciden con el committer, de lo contrario se marcarán como "no confiable". Esto obliga a Forgejo a ser el committer en commits firmados con el commit real marcado como Co-autorizado por: y Co-commited por: en el tráiler. La clave de Forgejo por defecto debe coincidir con un usuario en la base de datos.
|
||||
settings.trust_model.collaboratorcommitter=Colaborador+Comitter
|
||||
|
@ -2334,7 +2342,7 @@ settings.event_create=Crear
|
|||
settings.event_create_desc=Rama o etiqueta creada.
|
||||
settings.event_delete=Eliminar
|
||||
settings.event_delete_desc=Rama o etiqueta eliminada.
|
||||
settings.event_fork=Fork
|
||||
settings.event_fork=Bifurcación
|
||||
settings.event_fork_desc=Repositorio forkeado.
|
||||
settings.event_wiki=Wiki
|
||||
settings.event_wiki_desc=Página de la Wiki creada, renombrada, editada o eliminada.
|
||||
|
@ -2522,7 +2530,7 @@ settings.archive.branchsettings_unavailable=Los ajustes de rama no están dispon
|
|||
settings.archive.tagsettings_unavailable=Los ajustes de las etiquetas no están disponibles si el repositorio está archivado.
|
||||
settings.unarchive.button=Desarchivar repositorio
|
||||
settings.unarchive.header=Desarchivar este repositorio
|
||||
settings.unarchive.text=La desarchivación del repositorio restablecerá su capacidad de recibir confirmaciones y subidos, así como nuevas incidencias y solicitudes de incorporación de cambios.
|
||||
settings.unarchive.text=La desarchivación del repositorio restablecerá su capacidad de recibir confirmaciones y empujes, así como nuevas incidencias y solicitudes de incorporación de cambios.
|
||||
settings.unarchive.success=El repositorio se ha desarchivado correctamente.
|
||||
settings.unarchive.error=Ocurrió un error mientras se trataba de des-archivar el repositorio. Revisa el registro para más detalles.
|
||||
settings.update_avatar_success=El avatar del repositorio ha sido actualizado.
|
||||
|
@ -2645,7 +2653,7 @@ release.cancel=Cancelar
|
|||
release.publish=Publicar lanzamiento
|
||||
release.save_draft=Guardar borrador
|
||||
release.edit_release=Actualizar Lanzamiento
|
||||
release.delete_release=Eliminar Lanzamiento
|
||||
release.delete_release=Eliminar lanzamiento
|
||||
release.delete_tag=Eliminar tag
|
||||
release.deletion=Eliminar lanzamiento
|
||||
release.deletion_desc=Eliminar un lanzamiento sólo lo elimina de Forgejo. No afectará la etiqueta Git, el contenido de su repositorio o su historial. ¿Continuar?
|
||||
|
@ -2671,7 +2679,7 @@ branch.delete_html=Eliminar rama
|
|||
branch.delete_desc=Eliminar una rama es permanente. Aunque la rama eliminada puede continuar existiendo durante un corto tiempo antes de que sea eliminada, en la mayoría de los casos NO PUEDE deshacerse. ¿Continuar?
|
||||
branch.deletion_success=La rama "%s" ha sido eliminada.
|
||||
branch.deletion_failed=Error al eliminar la rama "%s".
|
||||
branch.delete_branch_has_new_commits=La rama "%s" no se puede eliminar porque se han añadido nuevos commits después de la fusión.
|
||||
branch.delete_branch_has_new_commits=La rama "%s" no se puede eliminar porque se han añadido nuevas confirmaciones después de la fusión.
|
||||
branch.create_branch=Crear rama %s
|
||||
branch.create_from=`de "%s"`
|
||||
branch.create_success=La rama "%s" ha sido creada.
|
||||
|
@ -2874,6 +2882,11 @@ issues.filter_no_results = No hay resultados
|
|||
|
||||
release.type_attachment = Archivo adjunto
|
||||
|
||||
issues.reaction.alt_few = %[1]s reaccionado con %[2]s.
|
||||
settings.event_pull_request_enforcement = Aplicación
|
||||
settings.sourcehut_builds.visibility = Visibilidad de trabajo
|
||||
settings.ignore_stale_approvals = Ignorar las aprobaciones obsoletas
|
||||
|
||||
[graphs]
|
||||
component_loading = Cargando %s…
|
||||
component_loading_failed = No se pudo cargar %s
|
||||
|
@ -2940,11 +2953,11 @@ settings.hooks_desc=Añadir webhooks que serán ejecutados para <strong>todos lo
|
|||
|
||||
settings.labels_desc=Añadir etiquetas que pueden ser utilizadas en problemas para <strong>todos los repositorios</strong> bajo esta organización.
|
||||
|
||||
members.membership_visibility=Visibilidad de Membresía:
|
||||
members.membership_visibility=Visibilidad de membresía:
|
||||
members.public=Público
|
||||
members.public_helper=hacer oculto
|
||||
members.public_helper=Hacer oculto
|
||||
members.private=Oculto
|
||||
members.private_helper=hacer público
|
||||
members.private_helper=Hacer público
|
||||
members.member_role=Rol del miembro:
|
||||
members.owner=Propietario
|
||||
members.member=Miembro
|
||||
|
@ -2953,7 +2966,7 @@ members.remove.detail=¿Destituir a %[1]s de %[2]s?
|
|||
members.leave=Abandonar
|
||||
members.leave.detail=¿Irse de %s?
|
||||
members.invite_desc=Añadir un miembro nuevo a %s:
|
||||
members.invite_now=Invitar
|
||||
members.invite_now=Invitar ahora
|
||||
|
||||
teams.join=Unirse
|
||||
teams.leave=Abandonar
|
||||
|
@ -2962,7 +2975,7 @@ teams.can_create_org_repo=Crear repositorios
|
|||
teams.can_create_org_repo_helper=Los miembros pueden crear nuevos repositorios en la organización. El creador obtendrá acceso al administrador del nuevo repositorio.
|
||||
teams.none_access=Sin acceso
|
||||
teams.none_access_helper=Los miembros no pueden ver o hacer ninguna otra acción en esta unidad.
|
||||
teams.general_access=Acceso general
|
||||
teams.general_access=Acceso personalizado
|
||||
teams.general_access_helper=Los permisos de los miembros se decidirán por debajo de la tabla de permisos.
|
||||
teams.read_access=Leer
|
||||
teams.read_access_helper=Los miembros pueden ver y clonar los repositorios del equipo.
|
||||
|
@ -3552,6 +3565,10 @@ emails.delete_desc = ¿Estás seguro que quieres eliminar esta dirección de cor
|
|||
monitor.duration = Duración (es)
|
||||
|
||||
|
||||
self_check = Autocomprobación
|
||||
dashboard.sync_tag.started = Sincronización de etiquetas iniciada
|
||||
config.app_slogan = Eslogan de la instancia
|
||||
|
||||
[action]
|
||||
create_repo=creó el repositorio <a href="%s">%s</a>
|
||||
rename_repo=repositorio renombrado de <code>%[1]s</code> a <a href="%[2]s">%[3]s</a>
|
||||
|
@ -3814,6 +3831,13 @@ alt.repository.multiple_groups = Este paquete está disponible en múltiples gru
|
|||
arch.version.description = Descripción
|
||||
arch.version.provides = Proveedores
|
||||
|
||||
arch.version.optdepends = Dependencias opcionales
|
||||
arch.version.makedepends = Construir dependencias
|
||||
arch.version.checkdepends = Comprobar dependencias
|
||||
npm.dependencies.bundle = Empaquetar dependencias
|
||||
|
||||
arch.pacman.helper.gpg = Añade el certificado de confianza para pacman:
|
||||
|
||||
[secrets]
|
||||
secrets=Secretos
|
||||
description=Los secretos pasarán a ciertas acciones y no se podrán leer de otro modo.
|
||||
|
@ -3860,7 +3884,7 @@ runners.task_list.no_tasks=Todavía no hay tarea.
|
|||
runners.task_list.run=Ejecutar
|
||||
runners.task_list.status=Estado
|
||||
runners.task_list.repository=Repositorio
|
||||
runners.task_list.commit=Commit
|
||||
runners.task_list.commit=Confirmación
|
||||
runners.task_list.done_at=Hecho en
|
||||
runners.edit_runner=Editar nodo
|
||||
runners.update_runner=Actualizar cambios
|
||||
|
@ -3920,6 +3944,13 @@ runs.expire_log_message = Los registros han sido eliminados porque eran demasiad
|
|||
|
||||
runs.workflow = Flujo de trabajo
|
||||
|
||||
workflow.dispatch.use_from = Usar el flujo de trabajo de
|
||||
workflow.dispatch.run = Correr flujo de trabajo
|
||||
|
||||
runs.no_workflows = Aún no hay flujos de trabajo.
|
||||
workflow.dispatch.success = La ejecución del flujo de trabajo se ha solicitado correctamente.
|
||||
workflow.dispatch.invalid_input_type = Tipo de entrada inválida "%s".
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Proyecto individual
|
||||
type-2.display_name=Proyecto de repositorio
|
||||
|
@ -3961,7 +3992,7 @@ exact = Exacto
|
|||
exact_tooltip = Incluir sólo los resultados que corresponden al término de búsqueda exacto
|
||||
issue_kind = Buscar incidencias…
|
||||
fuzzy = Difusa
|
||||
runner_kind = Buscar ejecutores…
|
||||
runner_kind = Buscar corredores…
|
||||
regexp_tooltip = Interpretar los términos de búsqueda como una expresión regular
|
||||
regexp = Expresión Regular
|
||||
|
||||
|
|
|
@ -1036,6 +1036,7 @@ file_copy_permalink=پرمالینک را کپی کنید
|
|||
video_not_supported_in_browser=مرورگر شما از تگ video که در HTML5 تعریف شده است، پشتیبانی نمی کند.
|
||||
audio_not_supported_in_browser=مرورگر شما از تگ audio که در HTML5 تعریف شده است، پشتیبانی نمی کند.
|
||||
stored_lfs=ذخیره شده با GIT LFS
|
||||
stored_annex=ذخیره شده با GIT Annex
|
||||
symbolic_link=پیوند نمادین
|
||||
commit_graph=نمودار کامیت
|
||||
commit_graph.select=انتخاب برنچها
|
||||
|
@ -1053,6 +1054,7 @@ editor.upload_file=بارگذاری پرونده
|
|||
editor.edit_file=ویرایش پرونده
|
||||
editor.preview_changes=پیش نمایش تغییرات
|
||||
editor.cannot_edit_lfs_files=پرونده های LFS در صحفه وب قابل تغییر نیست.
|
||||
editor.cannot_edit_annex_files=پرونده های Annex در صحفه وب قابل تغییر نیست.
|
||||
editor.cannot_edit_non_text_files=پروندههای دودویی در صفحه وب قابل تغییر نیست.
|
||||
editor.edit_this_file=ویرایش پرونده
|
||||
editor.this_file_locked=پرونده قفل شده است
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
home=Etusivu
|
||||
dashboard=Kojelauta
|
||||
explore=Tutki
|
||||
help=Apua
|
||||
help=Ohje
|
||||
logo=Logo
|
||||
sign_in=Kirjaudu sisään
|
||||
sign_in_or=tai
|
||||
|
@ -41,7 +41,7 @@ webauthn_error=Turva-avainta ei voitu lukea.
|
|||
webauthn_unsupported_browser=Selaimesi ei tällä hetkellä tue WebAuthnia.
|
||||
webauthn_error_unknown=Tuntematon virhe. Yritä uudelleen.
|
||||
webauthn_error_insecure=`WebAuthn tukee vain suojattuja yhteyksiä. Testaukseen HTTP:n yli, voit käyttää osoitetta "localhost" tai "127.0.0.1"`
|
||||
webauthn_error_unable_to_process=Palvelin ei pystynyt toteuttamaan kutsua.
|
||||
webauthn_error_unable_to_process=Palvelin ei pystynyt käsittelemään pyyntöä.
|
||||
webauthn_error_duplicated=Turva-avainta ei ole sallittu tässä pyynnössä. Varmista, ettei avainta ole jo rekisteröity.
|
||||
webauthn_error_empty=Sinun täytyy asettaa nimi tälle avaimelle.
|
||||
webauthn_error_timeout=Aikakatkaisu saavutettu ennenkuin avaintasi on voitu lukea. Lataa tämä sivu uudelleen ja yritä uudelleen.
|
||||
|
@ -98,7 +98,7 @@ preview=Esikatselu
|
|||
loading=Ladataan…
|
||||
|
||||
error=Virhe
|
||||
error404=Sivu, jota yrität nähdä, joko <strong>ei löydy</strong>, <strong>on poistettu</strong> tai <strong>et ole oikeutettu</strong> katsomaan sitä.
|
||||
error404=Sivu, jota yrität nähdä, joko <strong>ei löydy</strong>, <strong>on poistettu</strong> tai <strong>et ole oikeutettu</strong> katsomaan sitä.
|
||||
|
||||
never=Ei koskaan
|
||||
|
||||
|
@ -216,7 +216,7 @@ string.asc = A - Ö
|
|||
string.desc = Ö - A
|
||||
|
||||
[error]
|
||||
occurred=Virhe tapahtui
|
||||
occurred=Tapahtui virhe
|
||||
missing_csrf=Virheellinen pyyntö: CSRF-tunnusta ei ole olemassa
|
||||
invalid_csrf=Virheellinen pyyntö: Virheellinen CSRF-tunniste
|
||||
not_found=Kohdetta ei löytynyt.
|
||||
|
@ -250,7 +250,7 @@ db_schema=Skeema
|
|||
ssl_mode=SSL
|
||||
path=Polku
|
||||
sqlite_helper=SQLite3-tietokannan tiedostopolku.<br>Syötä absoluuttinen polku, jos ajat Forgejoa palveluna.
|
||||
reinstall_error=Yrität asentaa olemassa olevaan Forgejo tietokantaan
|
||||
reinstall_error=Yrität asentaa olemassa olevaan Forgejo-tietokantaan
|
||||
reinstall_confirm_message=Asentaminen uudelleen olemassa olevalla Forgejo-tietokannalla voi aiheuttaa useita ongelmia. Useimmissa tapauksissa sinun pitäisi käyttää olemassa olevia "app.ini" asetuksia Forgejon käyttöön. Jos tiedät mitä teet, vahvista seuraavat seikat:
|
||||
reinstall_confirm_check_1=Tiedot, jotka on salattu SECRET_KEY:llä app.ini:ssä saatetaan menettää: käyttäjät eivät ehkä voi kirjautua sisään 2FA/OTP:lla ja peilit eivät välttämättä toimi oikein. Ruksaamalla tämän vahvistat, että nykyinen app.ini -tiedosto sisältää oikean SECRET_KEY:n.
|
||||
reinstall_confirm_check_2=Repot ja asetukset saattaa olla tarpeen uudelleensynkronoida. Valitsemalla tämän vahvistat, että uudelleensynkronoit repojen koukut ja authorized_keys -tiedoston manuaalisesti. Varmistat, että repon ja peilin asetukset ovat oikeat.
|
||||
|
@ -451,7 +451,7 @@ email_domain_blacklisted=Et voi rekisteröityä sähköpostiosoittellasi.
|
|||
authorize_application=Valtuuta sovellus
|
||||
authorize_redirect_notice=Sinut uudelleen ohjataan osoitteeseen %s jos valtuutat tämän sovelluksen.
|
||||
authorize_application_created_by=Tämän sovelluksen on luonnut %s.
|
||||
authorize_application_description=Jos myönnät valtuuden, sovellus voi käyttää kaikkia tilitietojasi ja kirjoittaa niihin, mukaan lukien yksityiset repot ja organisaatiot.
|
||||
authorize_application_description=Jos myönnät valtuuden, sovellus voi käyttää kaikkia tilitietojasi ja kirjoittaa niihin, mukaan lukien yksityiset repositoriot ja organisaatiot.
|
||||
authorize_title=Valtuutatko "%s" pääsemään tilillesi?
|
||||
authorization_failed=Käyttöoikeuden varmistus epäonnistui
|
||||
sspi_auth_failed=SSPI todennus epäonnistui
|
||||
|
@ -550,6 +550,12 @@ totp_disabled.no_2fa = Muita kaksivaiheisen tunnistautumisen menetelmiä ei ole
|
|||
|
||||
totp_enrolled.subject = Olet aktivoinut TOTP:in kaksivaiheisen todennuksen menetelmäksi
|
||||
|
||||
removed_security_key.no_2fa = Yhtään kaksivaiheisen tunnistautumisen menetelmää ei ole määritelty, joten tilillesi ei enää tarvitse kirjautua kaksivaiheisella tunnistautumisella.
|
||||
totp_enrolled.text_1.no_webauthn = Otit TOTP:n käyttöön tilillesi. Tämä tarkoittaa, että kirjauduttaessa sisään tilillesi sinun täytyy käyttää TOTP menetelmää kaksivaiheisena tunnistautumisena.
|
||||
totp_enrolled.text_1.has_webauthn = Otit TOTP:n käyttöön tilillesi. Tämä tarkoittaa, että kirjauduttaessa sisään tilillesi voit käyttää TOTP menetelmää kaksivaiheisena tunnistautumisena tai mitä tahansa turva-avaimiasi.
|
||||
repo.collaborator.added.subject = %s lisäsi sinut %s yhteistyökumppaniksi
|
||||
team_invite.text_3 = Huomaa: Tämä kutsu on tarkoitettu %[1]s:lle. Jos et odottanut tätä kutsua, voit jättää tämän sähköpostin huomiotta.
|
||||
|
||||
[modal]
|
||||
yes=Kyllä
|
||||
no=Ei
|
||||
|
@ -567,7 +573,7 @@ SSHTitle=SSH avain nimi
|
|||
HttpsUrl=HTTPS-osoite
|
||||
TeamName=Tiimin nimi
|
||||
AuthName=Luvan nimi
|
||||
AdminEmail=Ylläpito sähköposti
|
||||
AdminEmail=Ylläpitäjän sähköposti
|
||||
|
||||
NewBranchName=Uuden haaran nimi
|
||||
CommitSummary=Commitin yhteenveto
|
||||
|
@ -694,7 +700,7 @@ webauthn=Kaksivaiheinen todennus (Turva-avaimet)
|
|||
public_profile=Julkinen profiili
|
||||
password_username_disabled=Ei-paikalliset käyttäjät eivät voi muuttaa käyttäjätunnustaan. Ole hyvä ja ota yhteyttä sivuston ylläpitäjään saadaksesi lisätietoa.
|
||||
full_name=Koko nimi
|
||||
website=Nettisivut
|
||||
website=Verkkosivusto
|
||||
location=Sijainti
|
||||
update_theme=Vaihda teema
|
||||
update_profile=Päivitä profiili
|
||||
|
@ -830,7 +836,7 @@ access_token_deletion_cancel_action=Peruuta
|
|||
access_token_deletion_confirm_action=Poista
|
||||
permission_read=Luettu
|
||||
|
||||
edit_oauth2_application=Muokkaa OAuth2 sovellusta
|
||||
edit_oauth2_application=Muokkaa OAuth2-sovellusta
|
||||
remove_oauth2_application=Poista OAuth2-sovellus
|
||||
remove_oauth2_application_success=Sovellus on poistettu.
|
||||
create_oauth2_application=Luo uusi OAuth2-sovellus
|
||||
|
@ -854,7 +860,7 @@ twofa_enrolled=Tiliisi on otettu käyttöön kaksivaiheinen vahvistus. Ota palau
|
|||
webauthn_nickname=Nimimerkki
|
||||
|
||||
manage_account_links=Yhdistetyt tilit
|
||||
manage_account_links_desc=Nämä ulkoiset tilit on linkitetty Forgejo tiliisi.
|
||||
manage_account_links_desc=Nämä ulkoiset tilit on linkitetty Forgejo-tiliisi.
|
||||
link_account=Yhdistä tili
|
||||
remove_account_link=Poista yhdistetty tili
|
||||
remove_account_link_desc=Linkitetyn tilin poistaminen peruuttaa pääsyn Forgejo-tiliisi linkitetyn tili kautta. Jatketaanko?
|
||||
|
@ -1042,7 +1048,7 @@ migrate.github_token_desc=Voit laittaa yhden tai useamman pääsymerkin pilkulla
|
|||
migrate.permission_denied=Sinun ei sallita tuovan paikallisia repoja.
|
||||
migrate.failed=Siirto epäonnistui: %v
|
||||
migrate.migrate_items_options=Pääsymerkki vaaditaan lisäkohteiden siirtämiseen
|
||||
migrate.migrating=Tuodaan kohteesta <b>%s</b> ...
|
||||
migrate.migrating=Tuodaan kohteesta <b>%s</b> …
|
||||
migrate.migrating_failed=Tuonti kohteesta <b>%s</b> epäonnistui.
|
||||
migrate.migrating_git=Tuodaan Git-tietoja
|
||||
|
||||
|
@ -1063,7 +1069,7 @@ code.desc=Pääsy lähdekoodiin, tiedostoihin, committeihin ja haaroihin.
|
|||
branch=Haara
|
||||
tree=Puu
|
||||
filter_branch_and_tag=Suodata haara tai tagi
|
||||
branches=Branchit
|
||||
branches=Haarat
|
||||
tags=Tagit
|
||||
issues=Ongelmat
|
||||
pulls=Vetopyynnöt
|
||||
|
@ -1088,7 +1094,7 @@ blame=Selitys
|
|||
download_file=Lataa tiedosto
|
||||
normal_view=Normaali näkymä
|
||||
line=rivi
|
||||
lines=rivejä
|
||||
lines=riviä
|
||||
|
||||
editor.new_file=Uusi tiedosto
|
||||
editor.upload_file=Lähetä tiedosto
|
||||
|
@ -1195,7 +1201,7 @@ issues.filter_type.all_issues=Kaikki ongelmat
|
|||
issues.filter_type.assigned_to_you=Osoitettu sinulle
|
||||
issues.filter_type.created_by_you=Ilmoittamasi
|
||||
issues.filter_type.mentioning_you=Jotka mainitsee sinut
|
||||
issues.filter_type.review_requested=Arvostelua pyydetty
|
||||
issues.filter_type.review_requested=Katselmointi pyydetty
|
||||
issues.filter_sort=Lajittele
|
||||
issues.filter_sort.latest=Uusin
|
||||
issues.filter_sort.oldest=Vanhin
|
||||
|
@ -1576,7 +1582,7 @@ settings.lfs_locks=Lukot
|
|||
settings.lfs_invalid_locking_path=Virheellinen polku: %s
|
||||
settings.lfs_invalid_lock_directory=Hakemistoa ei voida lukita: %s
|
||||
settings.lfs_lock_already_exists=Lukitus on jo olemassa: %s
|
||||
settings.lfs_lock_path=Lukittavan tiedostopolku...
|
||||
settings.lfs_lock_path=Lukittavan tiedostopolku…
|
||||
settings.lfs_locks_no_locks=Ei lukkoja
|
||||
settings.lfs_lock_file_no_exist=Lukittua tiedostoa ei ole olemassa oletushaarassa
|
||||
settings.lfs_force_unlock=Pakota lukituksen avaus
|
||||
|
@ -1610,8 +1616,8 @@ diff.comment.add_single_comment=Lisää yksittäinen kommentti
|
|||
diff.comment.add_review_comment=Lisää kommentti
|
||||
diff.comment.start_review=Aloita tarkistus
|
||||
diff.comment.reply=Vastaa
|
||||
diff.review.header=Lähetä arvio
|
||||
diff.review.placeholder=Tarkistuksen kommentti
|
||||
diff.review.header=Lähetä katselmointi
|
||||
diff.review.placeholder=Katselmoinnin kommentti
|
||||
diff.review.comment=Kommentoi
|
||||
diff.review.approve=Hyväksy
|
||||
diff.review.reject=Pyydä muutoksia
|
||||
|
@ -2204,7 +2210,7 @@ create_org=Luo organisaatio
|
|||
repo_updated=Päivitetty %s
|
||||
members=Jäsenet
|
||||
teams=Tiimit
|
||||
lower_members=jäsenet
|
||||
lower_members=jäsentä
|
||||
lower_repositories=repot
|
||||
create_new_team=Uusi tiimi
|
||||
create_team=Luo tiimi
|
||||
|
@ -2316,7 +2322,7 @@ config=Asetukset
|
|||
notices=Järjestelmän ilmoitukset
|
||||
monitor=Valvonta
|
||||
first_page=Ensimmäinen
|
||||
last_page=Viimeisin
|
||||
last_page=Viimeinen
|
||||
total=Yhteensä: %d
|
||||
|
||||
dashboard.statistic=Yhteenveto
|
||||
|
@ -2667,7 +2673,7 @@ auths.enable_ldap_groups = Käytä LDAP-ryhmiä
|
|||
auths.login_source_exist = Todennuslähde "%s" on jo olemassa.
|
||||
|
||||
[action]
|
||||
create_repo=luotu repo <a href="%s">%s</a>
|
||||
create_repo=loi repon <a href="%s">%s</a>
|
||||
rename_repo=uudelleennimetty repo <code>%[1]s</code> nimelle <a href="%[2]s">%[3]s</a>
|
||||
transfer_repo=siirretty repo <code>%s</code> kohteeseen <a href="%s">%s</a>
|
||||
push_tag=työnsi tagin <a href="%[2]s">%[3]s</a> kohteeseen <a href="%[1]s">%[4]s</a>
|
||||
|
|
|
@ -1351,6 +1351,7 @@ view_git_blame=Voir Git blame
|
|||
video_not_supported_in_browser=Votre navigateur ne supporte pas la balise « vidéo » HTML5.
|
||||
audio_not_supported_in_browser=Votre navigateur ne supporte pas la balise « audio » HTML5.
|
||||
stored_lfs=Stocké avec Git LFS
|
||||
stored_annex=Stocké avec Git Annex
|
||||
symbolic_link=Lien symbolique
|
||||
executable_file=Fichier exécutable
|
||||
vendored = Vendored
|
||||
|
@ -1376,6 +1377,7 @@ editor.upload_file=Téléverser un fichier
|
|||
editor.edit_file=Modifier le fichier
|
||||
editor.preview_changes=Aperçu des modifications
|
||||
editor.cannot_edit_lfs_files=Les fichiers LFS ne peuvent pas être modifiés dans l'interface web.
|
||||
editor.cannot_edit_annex_files=Les fichiers Annex ne peuvent pas être modifiés dans l'interface web.
|
||||
editor.cannot_edit_non_text_files=Les fichiers binaires ne peuvent pas être édités dans l'interface web.
|
||||
editor.edit_this_file=Modifier le fichier
|
||||
editor.this_file_locked=Le fichier est verrouillé
|
||||
|
@ -4080,4 +4082,4 @@ issues.write = <b>Écrire :</b> Fermer des tickets et gérer les métadonnées t
|
|||
pulls.read = <b>Lire :</b> Lire et créer des demandes de tirage.
|
||||
|
||||
[translation_meta]
|
||||
test = Ceci est une chaîne de test. Elle n'est pas affichée dans l'interface de Forgejo mais est utilisée à des fins de test. N'hésitez pas à entrer 'ok' pour gagner du temps (ou un fait amusant de votre choix) pour atteindre ce doux 100 % de complétion :)
|
||||
test = Ceci est une chaîne de test. Elle n'est pas affichée dans l'interface de Forgejo mais est utilisée à des fins de test. N'hésitez pas à entrer 'ok' pour gagner du temps (ou un fait amusant de votre choix) pour atteindre ce doux 100 % de complétion. :-)
|
||||
|
|
|
@ -787,6 +787,7 @@ file_too_large=Ez a fájl túl nagy ahhoz, hogy megjelenítsük.
|
|||
video_not_supported_in_browser=A böngésző nem támogatja a HTML5 video tag-et.
|
||||
audio_not_supported_in_browser=A böngésző nem támogatja a HTML5 audio tag-et.
|
||||
stored_lfs=Git LFS-el eltárolva
|
||||
stored_annex=Git Annex-el eltárolva
|
||||
symbolic_link=Szimbolikus hivatkozás
|
||||
commit_graph=Commit gráf
|
||||
commit_graph.hide_pr_refs=Pull request-ek elrejtése
|
||||
|
@ -799,6 +800,7 @@ editor.upload_file=Fájl feltöltése
|
|||
editor.edit_file=Fájl szerkesztése
|
||||
editor.preview_changes=Változások előnézete
|
||||
editor.cannot_edit_lfs_files=LFS fájlok nem szerkeszthetőek a webes felületen.
|
||||
editor.cannot_edit_annex_files=Annex fájlok nem szerkeszthetőek a webes felületen.
|
||||
editor.cannot_edit_non_text_files=Bináris fájlok nem szerkeszthetőek a webes felületen.
|
||||
editor.edit_this_file=Fájl szerkesztése
|
||||
editor.this_file_locked=Zárolt állomány
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue