1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2025-09-15 18:56:59 +00:00

Merge branch 'forgejo' into port-32327

This commit is contained in:
Maxim Slipenko 2025-06-26 15:00:17 +02:00
commit 2ce4db6822
266 changed files with 6145 additions and 2215 deletions

View file

@ -13,6 +13,13 @@ forgejo.org/models
IsErrSHANotFound
IsErrMergeDivergingFastForwardOnly
forgejo.org/models/activities
GetActivityByID
NewFederatedUserActivity
CreateUserActivity
GetFollowingFeeds
FederatedUserActivity.loadActor
forgejo.org/models/auth
WebAuthnCredentials
@ -54,9 +61,17 @@ forgejo.org/models/user
IsErrExternalLoginUserAlreadyExist
IsErrExternalLoginUserNotExist
NewFederatedUser
NewFederatedUserFollower
IsErrUserSettingIsNotExist
GetUserAllSettings
DeleteUserSetting
GetFederatedUser
GetFederatedUserByUserID
UpdateFederatedUser
GetFollowersForUser
AddFollower
RemoveFollower
IsFollowingAp
forgejo.org/modules/activitypub
NewContext

View file

@ -37,13 +37,9 @@ coverage.all
coverage/
cpu.out
/modules/migration/bindata.go
/modules/migration/bindata.go.hash
/modules/options/bindata.go
/modules/options/bindata.go.hash
/modules/public/bindata.go
/modules/public/bindata.go.hash
/modules/templates/bindata.go
/modules/templates/bindata.go.hash
*.db

View file

@ -6,7 +6,7 @@ body:
- type: markdown
attributes:
value: |
**NOTE: If your issue is a security concern, please email <security@forgejo.org> (GPG: `A4676E79`) instead of opening a public issue.**
**NOTE: If your issue is a security concern, please email <security@forgejo.org> ([security.txt](https://forgejo.org/.well-known/security.txt)) instead of opening a public issue.**
- type: markdown
attributes:
value: |

View file

@ -6,7 +6,7 @@ body:
- type: markdown
attributes:
value: |
**NOTE: If your issue is a security concern, please email <security@forgejo.org> (GPG: `A4676E79`) instead of opening a public issue.**
**NOTE: If your issue is a security concern, please email <security@forgejo.org> ([security.txt](https://forgejo.org/.well-known/security.txt)) instead of opening a public issue.**
- type: markdown
attributes:
value: |

View file

@ -1,4 +1,4 @@
FROM data.forgejo.org/oci/alpine:3.21
FROM data.forgejo.org/oci/alpine:3.22
ARG RELEASE_VERSION=unkown
LABEL maintainer="contact@forgejo.org" \
org.opencontainers.image.version="${RELEASE_VERSION}"

View file

@ -164,7 +164,7 @@ jobs:
- name: build container & release
if: ${{ secrets.TOKEN != '' }}
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5
with:
forgejo: "${{ env.GITHUB_SERVER_URL }}"
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
@ -183,7 +183,7 @@ jobs:
- name: build rootless container
if: ${{ secrets.TOKEN != '' }}
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5
with:
forgejo: "${{ env.GITHUB_SERVER_URL }}"
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"

View file

@ -44,7 +44,7 @@ jobs:
- uses: https://data.forgejo.org/actions/checkout@v4
- name: copy & sign
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.4
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.5
with:
from-forgejo: ${{ vars.FORGEJO }}
to-forgejo: ${{ vars.FORGEJO }}

View file

@ -28,7 +28,7 @@ jobs:
runs-on: docker
container:
image: data.forgejo.org/renovate/renovate:40.48.4
image: data.forgejo.org/renovate/renovate:41.1.4
steps:
- name: Load renovate repo cache

View file

@ -91,6 +91,7 @@ jobs:
RACE_ENABLED: 'true'
TAGS: bindata
TEST_ELASTICSEARCH_URL: http://elasticsearch:9200
TEST_MINIO_ENDPOINT: minio:9000
test-e2e:
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
runs-on: docker
@ -114,6 +115,11 @@ jobs:
run: |
su forgejo -c 'make deps-frontend frontend'
- uses: ./.forgejo/workflows-composite/build-backend
- name: Decide to run all tests
id: run-all
if: contains(github.event.pull_request.labels.*.name, 'run-all-playwright-tests') || contains(github.event.pull_request.title, 'playwright')
run: |
echo "all=1" >> "$GITHUB_OUTPUT"
- name: Get changed files
id: changed-files
uses: https://data.forgejo.org/tj-actions/changed-files@v46
@ -126,6 +132,7 @@ jobs:
USE_REPO_TEST_DIR: 1
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
CHANGED_FILES: ${{steps.changed-files.outputs.all_changed_files}}
RUN_ALL: ${{steps.run-all.all}}
- name: Upload test artifacts on failure
if: failure()
uses: https://data.forgejo.org/forgejo/upload-artifact@v4

View file

@ -10,6 +10,7 @@
# Javascript and CSS code.
web_src/.* @beowulf @gusted
web_src/css/.* @0ko
# HTML templates used by the backend.
templates/.* @beowulf @gusted

View file

@ -1,6 +1,6 @@
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.21 AS build-env
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.22 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct}
@ -33,10 +33,10 @@ RUN apk --no-cache add build-base git nodejs npm
COPY . ${GOPATH}/src/forgejo.org
WORKDIR ${GOPATH}/src/forgejo.org
RUN make clean
RUN make clean-no-bindata
RUN make frontend
RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini
RUN LDFLAGS="-buildid=" make RELEASE_VERSION=$RELEASE_VERSION GOFLAGS="-trimpath" go-check generate-backend static-executable && xx-verify gitea
RUN LDFLAGS="-buildid=" make FORGEJO_GENERATE_SKIP_HASH=true RELEASE_VERSION=$RELEASE_VERSION GOFLAGS="-trimpath" go-check generate-backend static-executable && xx-verify gitea
# Copy local files
COPY docker/root /tmp/local
@ -51,7 +51,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
/go/src/forgejo.org/environment-to-ini
RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete
FROM data.forgejo.org/oci/alpine:3.21
FROM data.forgejo.org/oci/alpine:3.22
ARG RELEASE_VERSION
LABEL maintainer="contact@forgejo.org" \
org.opencontainers.image.authors="Forgejo" \

View file

@ -1,6 +1,6 @@
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.21 AS build-env
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.22 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct}
@ -33,10 +33,10 @@ RUN apk --no-cache add build-base git nodejs npm
COPY . ${GOPATH}/src/forgejo.org
WORKDIR ${GOPATH}/src/forgejo.org
RUN make clean
RUN make clean-no-bindata
RUN make frontend
RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini
RUN make RELEASE_VERSION=$RELEASE_VERSION go-check generate-backend static-executable && xx-verify gitea
RUN make FORGEJO_GENERATE_SKIP_HASH=true RELEASE_VERSION=$RELEASE_VERSION go-check generate-backend static-executable && xx-verify gitea
# Copy local files
COPY docker/rootless /tmp/local
@ -49,7 +49,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
/go/src/forgejo.org/environment-to-ini
RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete
FROM data.forgejo.org/oci/alpine:3.21
FROM data.forgejo.org/oci/alpine:3.22
ARG RELEASE_VERSION
LABEL maintainer="contact@forgejo.org" \
org.opencontainers.image.authors="Forgejo" \

View file

@ -47,8 +47,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go
RENOVATE_NPM_PACKAGE ?= renovate@40.48.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
RENOVATE_NPM_PACKAGE ?= renovate@41.1.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...
@ -129,7 +128,7 @@ WEBPACK_CONFIGS := webpack.config.js tailwind.config.js
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
BINDATA_DEST := modules/migration/bindata.go modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
@ -222,7 +221,6 @@ help:
@echo " - lint-go lint go files"
@echo " - lint-go-fix lint go files and fix issues"
@echo " - lint-go-vet lint go files with vet"
@echo " - lint-go-gopls lint go files with gopls"
@echo " - lint-js lint js files"
@echo " - lint-js-fix lint js files and fix issues"
@echo " - lint-css lint css files"
@ -325,8 +323,12 @@ clean-all: clean
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
.PHONY: clean
clean:
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
clean: clean-no-bindata
rm -rf $(BINDATA_DEST) $(BINDATA_HASH)
.PHONY: clean-no-bindata
clean-no-bindata:
rm -rf $(EXECUTABLE) $(DIST) \
integrations*.test \
e2e*.test \
tests/integration/gitea-integration-* \
@ -483,11 +485,6 @@ lint-go-vet:
@echo "Running go vet..."
@$(GO) vet ./...
.PHONY: lint-go-gopls
lint-go-gopls:
@echo "Running gopls check..."
@GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA)
.PHONY: lint-editorconfig
lint-editorconfig:
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates .forgejo/workflows
@ -928,7 +925,6 @@ deps-tools:
$(GO) install $(GO_LICENSES_PACKAGE)
$(GO) install $(GOVULNCHECK_PACKAGE)
$(GO) install $(GOMOCK_PACKAGE)
$(GO) install $(GOPLS_PACKAGE)
node_modules: package-lock.json
npm install --no-save

File diff suppressed because one or more lines are too long

View file

@ -22,12 +22,19 @@ import (
"github.com/klauspost/compress/zstd"
)
func needsUpdate(dir, filename string) (bool, []byte) {
needRegen := false
func fileExists(filename string) bool {
_, err := os.Stat(filename)
if err != nil {
needRegen = true
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
panic(err)
}
func needsUpdate(dir, filename string) (bool, []byte) {
needRegen := !fileExists(filename)
oldHash, err := os.ReadFile(filename + ".hash")
if err != nil {
@ -73,10 +80,14 @@ func main() {
useGlobalModTime, _ = strconv.ParseBool(os.Args[4])
}
update, newHash := needsUpdate(dir, filename)
if os.Getenv("FORGEJO_GENERATE_SKIP_HASH") == "true" && fileExists(filename) {
fmt.Printf("bindata %s already exists and FORGEJO_GENERATE_SKIP_HASH=true\n", packageName)
return
}
update, newHash := needsUpdate(dir, filename)
if !update {
fmt.Printf("bindata for %s already up-to-date\n", packageName)
fmt.Printf("bindata %s already exists and the checksum is a match\n", packageName)
return
}

View file

@ -37,6 +37,7 @@ func TestLocalizationPolicy(t *testing.T) {
assert.Empty(t, checkLocaleContent([]byte("teams.specific_repositories_helper = Members will only have access to repositories explicitly added to the team. Selecting this <strong>will not</strong> automatically remove repositories already added with <i>All repositories</i>.")))
assert.Empty(t, checkLocaleContent([]byte("sqlite_helper = File path for the SQLite3 database.<br>Enter an absolute path if you run Forgejo as a service.")))
assert.Empty(t, checkLocaleContent([]byte("hi_user_x = Hi <b>%s</b>,")))
assert.Empty(t, checkLocaleContent([]byte("key = Press <kbd>Shift</kbd>")))
assert.Equal(t, []string{"error404: The page you are trying to reach either <strong\x1b[31m title='aaa'\x1b[0m>does not exist</strong> or <strong>you are not authorized</strong> to view it."}, checkLocaleContent([]byte("error404 = The page you are trying to reach either <strong title='aaa'>does not exist</strong> or <strong>you are not authorized</strong> to view it.")))
})

View file

@ -1,45 +1,46 @@
Environment To Ini
==================
Multiple docker users have requested that the Gitea docker is changed
to permit arbitrary configuration via environment variables.
This tool allows defining Forgejo's entire configuration via environment
variables, mostly geared towards usage in Docker.
Gitea needs to use an ini file for configuration because the running
environment that starts the docker may not be the same as that used
by the hooks. An ini file also gives a good default and means that
users do not have to completely provide a full environment.
Forgejo needs to use an INI file for configuration because the running
environment that starts the container may not be the same as the one used
by the hooks. An INI file also gives a good default and means that
users do not have to provide the entire set of environment variables.
With those caveats above, this command provides a generic way of
converting suitably structured environment variables into any ini
value.
To use the command is very simple just run it and the default gitea
app.ini will be rewritten to take account of the variables provided,
however there are various options to give slightly different
behavior and these can be interrogated with the `-h` option.
When run, `environment-to-ini` will write the config files based on the
environment variables provided.
Check with the `-h` flag for several options to alter this behaviour.
The environment variables should be of the form:
Environment variables of the form "FORGEJO__SECTION_NAME__KEY_NAME"
will be mapped to the ini section "[section_name]" and the key
"KEY_NAME" with the value as provided.
GITEA__SECTION_NAME__KEY_NAME
Note, SECTION_NAME in the notation above is case-insensitive.
Environment variables of the form "FORGEJO__SECTION_NAME__KEY_NAME__FILE"
will be mapped to the ini section "[section_name]" and the key
"KEY_NAME" with the value loaded from the specified file.
Environment variables are usually restricted to a reduced character
set "0-9A-Z_" - in order to allow the setting of sections with
characters outside of that set, they should be escaped as following:
"_0X2E_" for "." and "_0X2D_" for "-". The entire section and key names
can be escaped as a UTF8 byte string if necessary. E.g. to configure:
"_0X2E_" for ".". The entire section and key names can be escaped as
a UTF8 byte string if necessary. E.g. to configure:
"""
...
[log.console]
COLORIZE=false
STDERR=true
...
"""
"""
...
[log.console]
COLORIZE=false
STDERR=true
...
"""
You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false"
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
You would set the environment variables: "FORGEJO__LOG_0x2E_CONSOLE__COLORIZE=false"
and "FORGEJO__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
on the configuration cheat sheet.
To build locally, run:

View file

@ -1,5 +1,5 @@
import eslintCommunityEslintPluginEslintComments from '@eslint-community/eslint-plugin-eslint-comments';
import stylisticEslintPluginJs from '@stylistic/eslint-plugin-js';
import stylisticEslintPlugin from '@stylistic/eslint-plugin';
import vitest from '@vitest/eslint-plugin';
import arrayFunc from 'eslint-plugin-array-func';
import eslintPluginImportX from 'eslint-plugin-import-x';
@ -26,7 +26,7 @@ export default tseslint.config(
{
plugins: {
'@eslint-community/eslint-comments': eslintCommunityEslintPluginEslintComments,
'@stylistic/js': stylisticEslintPluginJs,
'@stylistic': stylisticEslintPlugin,
'@vitest': vitest,
'array-func': arrayFunc,
'no-jquery': noJquery,
@ -69,62 +69,62 @@ export default tseslint.config(
'@eslint-community/eslint-comments/no-unused-enable': [2],
'@eslint-community/eslint-comments/no-use': [0],
'@eslint-community/eslint-comments/require-description': [0],
'@stylistic/js/array-bracket-newline': [0],
'@stylistic/js/array-bracket-spacing': [2, 'never'],
'@stylistic/js/array-element-newline': [0],
'@stylistic/js/arrow-parens': [2, 'always'],
'@stylistic/array-bracket-newline': [0],
'@stylistic/array-bracket-spacing': [2, 'never'],
'@stylistic/array-element-newline': [0],
'@stylistic/arrow-parens': [2, 'always'],
'@stylistic/js/arrow-spacing': [2, {
'@stylistic/arrow-spacing': [2, {
before: true,
after: true,
}],
'@stylistic/js/block-spacing': [0],
'@stylistic/block-spacing': [0],
'@stylistic/js/brace-style': [2, '1tbs', {
'@stylistic/brace-style': [2, '1tbs', {
allowSingleLine: true,
}],
'@stylistic/js/comma-dangle': [2, 'always-multiline'],
'@stylistic/comma-dangle': [2, 'always-multiline'],
'@stylistic/js/comma-spacing': [2, {
'@stylistic/comma-spacing': [2, {
before: false,
after: true,
}],
'@stylistic/js/comma-style': [2, 'last'],
'@stylistic/js/computed-property-spacing': [2, 'never'],
'@stylistic/js/dot-location': [2, 'property'],
'@stylistic/js/eol-last': [2],
'@stylistic/js/function-call-spacing': [2, 'never'],
'@stylistic/js/function-call-argument-newline': [0],
'@stylistic/js/function-paren-newline': [0],
'@stylistic/js/generator-star-spacing': [0],
'@stylistic/js/implicit-arrow-linebreak': [0],
'@stylistic/comma-style': [2, 'last'],
'@stylistic/computed-property-spacing': [2, 'never'],
'@stylistic/dot-location': [2, 'property'],
'@stylistic/eol-last': [2],
'@stylistic/function-call-spacing': [2, 'never'],
'@stylistic/function-call-argument-newline': [0],
'@stylistic/function-paren-newline': [0],
'@stylistic/generator-star-spacing': [0],
'@stylistic/implicit-arrow-linebreak': [0],
'@stylistic/js/indent': [2, 2, {
'@stylistic/indent': [2, 2, {
ignoreComments: true,
SwitchCase: 1,
}],
'@stylistic/js/key-spacing': [2],
'@stylistic/js/keyword-spacing': [2],
'@stylistic/js/linebreak-style': [2, 'unix'],
'@stylistic/js/lines-around-comment': [0],
'@stylistic/js/lines-between-class-members': [0],
'@stylistic/js/max-len': [0],
'@stylistic/js/max-statements-per-line': [0],
'@stylistic/js/multiline-ternary': [0],
'@stylistic/js/new-parens': [2],
'@stylistic/js/newline-per-chained-call': [0],
'@stylistic/js/no-confusing-arrow': [0],
'@stylistic/js/no-extra-parens': [0],
'@stylistic/js/no-extra-semi': [2],
'@stylistic/js/no-floating-decimal': [0],
'@stylistic/js/no-mixed-operators': [0],
'@stylistic/js/no-mixed-spaces-and-tabs': [2],
'@stylistic/key-spacing': [2],
'@stylistic/keyword-spacing': [2],
'@stylistic/linebreak-style': [2, 'unix'],
'@stylistic/lines-around-comment': [0],
'@stylistic/lines-between-class-members': [0],
'@stylistic/max-len': [0],
'@stylistic/max-statements-per-line': [0],
'@stylistic/multiline-ternary': [0],
'@stylistic/new-parens': [2],
'@stylistic/newline-per-chained-call': [0],
'@stylistic/no-confusing-arrow': [0],
'@stylistic/no-extra-parens': [0],
'@stylistic/no-extra-semi': [2],
'@stylistic/no-floating-decimal': [0],
'@stylistic/no-mixed-operators': [0],
'@stylistic/no-mixed-spaces-and-tabs': [2],
'@stylistic/js/no-multi-spaces': [2, {
'@stylistic/no-multi-spaces': [2, {
ignoreEOLComments: true,
exceptions: {
@ -132,60 +132,60 @@ export default tseslint.config(
},
}],
'@stylistic/js/no-multiple-empty-lines': [2, {
'@stylistic/no-multiple-empty-lines': [2, {
max: 1,
maxEOF: 0,
maxBOF: 0,
}],
'@stylistic/js/no-tabs': [2],
'@stylistic/js/no-trailing-spaces': [2],
'@stylistic/js/no-whitespace-before-property': [2],
'@stylistic/js/nonblock-statement-body-position': [2],
'@stylistic/js/object-curly-newline': [0],
'@stylistic/js/object-curly-spacing': [2, 'never'],
'@stylistic/js/object-property-newline': [0],
'@stylistic/js/one-var-declaration-per-line': [0],
'@stylistic/js/operator-linebreak': [2, 'after'],
'@stylistic/js/padded-blocks': [2, 'never'],
'@stylistic/js/padding-line-between-statements': [0],
'@stylistic/js/quote-props': [0],
'@stylistic/no-tabs': [2],
'@stylistic/no-trailing-spaces': [2],
'@stylistic/no-whitespace-before-property': [2],
'@stylistic/nonblock-statement-body-position': [2],
'@stylistic/object-curly-newline': [0],
'@stylistic/object-curly-spacing': [2, 'never'],
'@stylistic/object-property-newline': [0],
'@stylistic/one-var-declaration-per-line': [0],
'@stylistic/operator-linebreak': [2, 'after'],
'@stylistic/padded-blocks': [2, 'never'],
'@stylistic/padding-line-between-statements': [0],
'@stylistic/quote-props': [0],
'@stylistic/js/quotes': [2, 'single', {
'@stylistic/quotes': [2, 'single', {
avoidEscape: true,
allowTemplateLiterals: true,
}],
'@stylistic/js/rest-spread-spacing': [2, 'never'],
'@stylistic/rest-spread-spacing': [2, 'never'],
'@stylistic/js/semi': [2, 'always', {
'@stylistic/semi': [2, 'always', {
omitLastInOneLineBlock: true,
}],
'@stylistic/js/semi-spacing': [2, {
'@stylistic/semi-spacing': [2, {
before: false,
after: true,
}],
'@stylistic/js/semi-style': [2, 'last'],
'@stylistic/js/space-before-blocks': [2, 'always'],
'@stylistic/semi-style': [2, 'last'],
'@stylistic/space-before-blocks': [2, 'always'],
'@stylistic/js/space-before-function-paren': [2, {
'@stylistic/space-before-function-paren': [2, {
anonymous: 'ignore',
named: 'never',
asyncArrow: 'always',
}],
'@stylistic/js/space-in-parens': [2, 'never'],
'@stylistic/js/space-infix-ops': [2],
'@stylistic/js/space-unary-ops': [2],
'@stylistic/js/spaced-comment': [2, 'always'],
'@stylistic/js/switch-colon-spacing': [2],
'@stylistic/js/template-curly-spacing': [2, 'never'],
'@stylistic/js/template-tag-spacing': [2, 'never'],
'@stylistic/js/wrap-iife': [2, 'inside'],
'@stylistic/js/wrap-regex': [0],
'@stylistic/js/yield-star-spacing': [2, 'after'],
'@stylistic/space-in-parens': [2, 'never'],
'@stylistic/space-infix-ops': [2],
'@stylistic/space-unary-ops': [2],
'@stylistic/spaced-comment': [2, 'always'],
'@stylistic/switch-colon-spacing': [2],
'@stylistic/template-curly-spacing': [2, 'never'],
'@stylistic/template-tag-spacing': [2, 'never'],
'@stylistic/wrap-iife': [2, 'inside'],
'@stylistic/wrap-regex': [0],
'@stylistic/yield-star-spacing': [2, 'after'],
'accessor-pairs': [2],
'array-callback-return': [2, {

24
go.mod
View file

@ -41,14 +41,14 @@ require (
github.com/gliderlabs/ssh v0.3.8
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.2.1
github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.2
github.com/go-git/go-git/v5 v5.13.2
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-openapi/spec v0.21.0
github.com/go-sql-driver/mysql v1.9.2
github.com/go-sql-driver/mysql v1.9.3
github.com/go-webauthn/webauthn v0.13.0
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
@ -63,8 +63,8 @@ require (
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/huandu/xstrings v1.5.0
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
github.com/jhillyerd/enmime/v2 v2.1.0
github.com/inbucket/html2text v0.9.0
github.com/jhillyerd/enmime/v2 v2.2.0
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.18.0
@ -76,7 +76,7 @@ require (
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.93
github.com/minio/minio-go/v7 v7.0.94
github.com/msteinert/pam/v2 v2.1.0
github.com/nektos/act v0.2.52
github.com/niklasfasching/go-org v1.8.0
@ -92,7 +92,6 @@ require (
github.com/stretchr/testify v1.10.0
github.com/syndtr/goleveldb v1.0.0
github.com/ulikunitz/xz v0.5.12
github.com/urfave/cli/v2 v2.27.6
github.com/urfave/cli/v3 v3.3.3
github.com/valyala/fastjson v1.6.4
github.com/yohcop/openid-go v1.0.1
@ -152,7 +151,6 @@ require (
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.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
github.com/davidmz/go-pageant v1.0.2 // indirect
@ -160,7 +158,7 @@ require (
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fatih/color v1.18.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
@ -194,7 +192,7 @@ require (
github.com/libdns/libdns v1.0.0-beta.1 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/markbates/going v1.0.3 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mholt/acmez/v3 v3.1.2 // indirect
github.com/miekg/dns v1.1.63 // indirect
@ -207,7 +205,9 @@ require (
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/olekukonko/tablewriter v1.0.7 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
@ -221,7 +221,6 @@ require (
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/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
@ -229,7 +228,6 @@ require (
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/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.4.0 // indirect
@ -246,7 +244,7 @@ require (
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.26.0
replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.28.0
replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1

51
go.sum
View file

@ -4,8 +4,8 @@ code.forgejo.org/f3/gof3/v3 v3.11.0 h1:f/xToKwqTgxG6PYxvewywjDQyCcyHEEJ6sZqUitFs
code.forgejo.org/f3/gof3/v3 v3.11.0/go.mod h1:4FaRUNSQGBiD1M0DuB0yNv+Z2wMtlOeckgygHSSq4KQ=
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU=
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM=
code.forgejo.org/forgejo/act v1.26.0 h1:6mTmoaw7d/WpYiw/Pw6AaypxFdgJog5OFi/PMEgEbxs=
code.forgejo.org/forgejo/act v1.26.0/go.mod h1:HFDFrXPrqfM9aH2RCnMiBdo/3ThxDmZjp58InPjGOfo=
code.forgejo.org/forgejo/act v1.28.0 h1:96njNC7C1YNyjWq5OWvLZMF/nw0PMthzIA8Nwbnn7jo=
code.forgejo.org/forgejo/act v1.28.0/go.mod h1:dFuiwAmD5vyrzecysHB2kL/GM3wRpoVPl+WdbCTC8Bs=
code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE=
code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
code.forgejo.org/forgejo/go-rpmutils v1.0.0 h1:RZGGeKt70p/WaIEL97pyT6uiiEIoN8/aLmS5Z6WmX0M=
@ -152,8 +152,6 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
@ -194,8 +192,8 @@ github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTe
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
@ -215,8 +213,8 @@ github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5La
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
@ -247,8 +245,8 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
@ -343,12 +341,12 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA=
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/inbucket/html2text v0.9.0 h1:ULJmVcBEMAcmLE+/rN815KG1Fx6+a4HhbUxiDiN+qks=
github.com/inbucket/html2text v0.9.0/go.mod h1:QDaumzl+/OzlSVbNohhmg+yAy5pKjUjzCKW2BMvztKE=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jhillyerd/enmime/v2 v2.1.0 h1:c8Qwi5Xq5EdtMN6byQWoZ/8I2RMTo6OJ7Xay+s1oPO0=
github.com/jhillyerd/enmime/v2 v2.1.0/go.mod h1:EJ74dcRbBcqHSP2TBu08XRoy6y3Yx0cevwb1YkGMEmQ=
github.com/jhillyerd/enmime/v2 v2.2.0 h1:Pe35MB96eZK5Q0XjlvPftOgWypQpd1gcbfJKAt7rsB8=
github.com/jhillyerd/enmime/v2 v2.2.0/go.mod h1:SOBXlCemjhiV2DvHhAKnJiWrtJGS/Ffuw4Iy7NjBTaI=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@ -391,12 +389,10 @@ github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE
github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o=
github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8=
github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
@ -413,8 +409,8 @@ github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY
github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.93 h1:lAB4QJp8Nq3vDMOU0eKgMuyBiEGMNlXQ5Glc8qAxqSU=
github.com/minio/minio-go/v7 v7.0.93/go.mod h1:71t2CqDt3ThzESgZUlU1rBN54mksGGlkLcFgguDnnAc=
github.com/minio/minio-go/v7 v7.0.94 h1:1ZoksIKPyaSt64AVOyaQvhDOgVC3MfZsWM6mZXRUGtM=
github.com/minio/minio-go/v7 v7.0.94/go.mod h1:71t2CqDt3ThzESgZUlU1rBN54mksGGlkLcFgguDnnAc=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -438,8 +434,12 @@ github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWk
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw=
github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs=
github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E=
github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -497,8 +497,6 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
@ -533,8 +531,6 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/urfave/cli/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I=
github.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
@ -545,8 +541,6 @@ github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js=
@ -653,7 +647,6 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -21,7 +21,7 @@ import (
_ "forgejo.org/modules/markup/markdown"
_ "forgejo.org/modules/markup/orgmode"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
)
// these flags will be set by the build flags

View file

@ -55,6 +55,7 @@ type ActionRun struct {
PreviousDuration time.Duration
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"`
NotifyEmail bool
}
func init() {

View file

@ -442,6 +442,12 @@ func (a *Action) GetIssueContent(ctx context.Context) string {
return a.Issue.Content
}
func GetActivityByID(ctx context.Context, id int64) (*Action, error) {
var act Action
_, err := db.GetEngine(ctx).ID(id).Get(&act)
return &act, err
}
// GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct {
db.ListOptions
@ -467,11 +473,8 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
return nil, 0, err
}
sess := db.GetEngine(ctx).Where(cond).
Select("`action`.*"). // this line will avoid select other joined table's columns
Join("INNER", "repository", "`repository`.id = `action`.repo_id")
opts.SetDefaultValues()
sess := db.GetEngine(ctx).Where(cond)
sess = db.SetSessionPagination(sess, &opts)
actions := make([]*Action, 0, opts.PageSize)
@ -598,13 +601,14 @@ func DeleteOldActions(ctx context.Context, olderThan time.Duration) (err error)
}
// NotifyWatchers creates batch of actions for every watcher.
func NotifyWatchers(ctx context.Context, actions ...*Action) error {
func NotifyWatchers(ctx context.Context, actions ...*Action) ([]Action, error) {
var watchers []*repo_model.Watch
var repo *repo_model.Repository
var err error
var permCode []bool
var permIssue []bool
var permPR []bool
var out []Action
e := db.GetEngine(ctx)
@ -615,14 +619,14 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
// Add feeds for user self and all watchers.
watchers, err = repo_model.GetWatchers(ctx, act.RepoID)
if err != nil {
return fmt.Errorf("get watchers: %w", err)
return nil, fmt.Errorf("get watchers: %w", err)
}
// Be aware that optimizing this correctly into the `GetWatchers` SQL
// query is for most cases less performant than doing this.
blockedDoerUserIDs, err := user_model.ListBlockedByUsersID(ctx, act.ActUserID)
if err != nil {
return fmt.Errorf("user_model.ListBlockedByUsersID: %w", err)
return nil, fmt.Errorf("user_model.ListBlockedByUsersID: %w", err)
}
if len(blockedDoerUserIDs) > 0 {
@ -637,8 +641,9 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
// Add feed for actioner.
act.UserID = act.ActUserID
if _, err = e.Insert(act); err != nil {
return fmt.Errorf("insert new actioner: %w", err)
return nil, fmt.Errorf("insert new actioner: %w", err)
}
out = append(out, *act)
if repoChanged {
act.loadRepo(ctx)
@ -646,7 +651,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
// check repo owner exist.
if err := act.Repo.LoadOwner(ctx); err != nil {
return fmt.Errorf("can't get repo owner: %w", err)
return nil, fmt.Errorf("can't get repo owner: %w", err)
}
} else if act.Repo == nil {
act.Repo = repo
@ -657,7 +662,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
act.ID = 0
act.UserID = act.Repo.Owner.ID
if err = db.Insert(ctx, act); err != nil {
return fmt.Errorf("insert new actioner: %w", err)
return nil, fmt.Errorf("insert new actioner: %w", err)
}
}
@ -710,26 +715,29 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
}
if err = db.Insert(ctx, act); err != nil {
return fmt.Errorf("insert new action: %w", err)
return nil, fmt.Errorf("insert new action: %w", err)
}
}
}
return nil
return out, nil
}
// NotifyWatchersActions creates batch of actions for every watcher.
func NotifyWatchersActions(ctx context.Context, acts []*Action) error {
func NotifyWatchersActions(ctx context.Context, acts []*Action) ([]Action, error) {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
return nil, err
}
defer committer.Close()
var out []Action
for _, act := range acts {
if err := NotifyWatchers(ctx, act); err != nil {
return err
as, err := NotifyWatchers(ctx, act)
if err != nil {
return nil, err
}
out = append(out, as...)
}
return committer.Commit()
return out, committer.Commit()
}
// DeleteIssueActions delete all actions related with issueID

View file

@ -197,7 +197,8 @@ func TestNotifyWatchers(t *testing.T) {
RepoID: 1,
OpType: activities_model.ActionStarRepo,
}
require.NoError(t, activities_model.NotifyWatchers(db.DefaultContext, action))
_, err := activities_model.NotifyWatchers(db.DefaultContext, action)
require.NoError(t, err)
// One watchers are inactive, thus action is only created for user 8, 1, 4, 11
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
@ -226,24 +227,6 @@ func TestNotifyWatchers(t *testing.T) {
})
}
func TestGetFeedsCorrupted(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
ID: 8,
RepoID: 1700,
})
actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: true,
})
require.NoError(t, err)
assert.Empty(t, actions)
assert.Equal(t, int64(0), count)
}
func TestConsistencyUpdateAction(t *testing.T) {
if !setting.Database.Type.IsSQLite3() {
t.Skip("Test is only for SQLite database.")

View file

@ -0,0 +1,106 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activities
import (
"context"
"fmt"
"forgejo.org/models/db"
user_model "forgejo.org/models/user"
"forgejo.org/modules/json"
"forgejo.org/modules/log"
"forgejo.org/modules/timeutil"
"forgejo.org/modules/validation"
ap "github.com/go-ap/activitypub"
)
type FederatedUserActivity struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL"`
ActorID int64
ActorURI string
Actor *user_model.User `xorm:"-"` // transient
NoteContent string `xorm:"TEXT"`
NoteURL string `xorm:"VARCHAR(255)"`
OriginalNote string `xorm:"TEXT"`
Created timeutil.TimeStamp `xorm:"created"`
}
func init() {
db.RegisterModel(new(FederatedUserActivity))
}
func NewFederatedUserActivity(userID, actorID int64, actorURI, noteContent, noteURL string, originalNote ap.Activity) (FederatedUserActivity, error) {
jsonString, err := json.Marshal(originalNote)
if err != nil {
return FederatedUserActivity{}, err
}
result := FederatedUserActivity{
UserID: userID,
ActorID: actorID,
ActorURI: actorURI,
NoteContent: noteContent,
NoteURL: noteURL,
OriginalNote: string(jsonString),
}
if valid, err := validation.IsValid(result); !valid {
return FederatedUserActivity{}, err
}
return result, nil
}
func (federatedUserActivity FederatedUserActivity) Validate() []string {
var result []string
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.UserID, "UserID")...)
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.ActorID, "ActorID")...)
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.ActorURI, "ActorURI")...)
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.NoteContent, "NoteContent")...)
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.NoteURL, "NoteURL")...)
result = append(result, validation.ValidateNotEmpty(federatedUserActivity.OriginalNote, "OriginalNote")...)
return result
}
func CreateUserActivity(ctx context.Context, federatedUserActivity *FederatedUserActivity) error {
if valid, err := validation.IsValid(federatedUserActivity); !valid {
return err
}
_, err := db.GetEngine(ctx).Insert(federatedUserActivity)
return err
}
type GetFollowingFeedsOptions struct {
db.ListOptions
}
func GetFollowingFeeds(ctx context.Context, actorID int64, opts GetFollowingFeedsOptions) ([]*FederatedUserActivity, int64, error) {
log.Debug("user_id = %s", actorID)
sess := db.GetEngine(ctx).Where("user_id = ?", actorID)
opts.SetDefaultValues()
sess = db.SetSessionPagination(sess, &opts)
actions := make([]*FederatedUserActivity, 0, opts.PageSize)
count, err := sess.FindAndCount(&actions)
if err != nil {
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
}
for _, act := range actions {
if err := act.loadActor(ctx); err != nil {
return nil, 0, err
}
}
return actions, count, err
}
func (federatedUserActivity *FederatedUserActivity) loadActor(ctx context.Context) error {
log.Debug("for activity %s", federatedUserActivity)
actorUser, _, err := user_model.GetFederatedUserByUserID(ctx, federatedUserActivity.ActorID)
if err != nil {
return err
}
federatedUserActivity.Actor = actorUser
return nil
}

View file

@ -0,0 +1,24 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package activities
import (
"testing"
"forgejo.org/modules/validation"
)
func Test_FederatedUserActivityValidation(t *testing.T) {
sut := FederatedUserActivity{}
sut.UserID = 13
sut.ActorID = 33
sut.ActorURI = "33"
sut.NoteContent = "Any content!"
sut.NoteURL = "https://example.org/note/17"
sut.OriginalNote = "federatedUserActivityNote-17"
if res, _ := validation.IsValid(sut); !res {
t.Errorf("sut expected to be valid: %v\n", sut.Validate())
}
}

View file

@ -59,14 +59,6 @@
created_unix: 1603011540 # grouped with id:7
- id: 8
user_id: 1
op_type: 12 # close issue
act_user_id: 1
repo_id: 1700 # dangling intentional
is_private: false
created_unix: 1603011541
- id: 9
user_id: 34
op_type: 12 # close issue
act_user_id: 34

View file

@ -113,3 +113,344 @@
review_id: 22
assignee_id: 5
created_unix: 946684817
-
id: 13
type: 29 # push
poster_id: 2
issue_id: 19 # in repo_id 58
content: '{"is_force_push":false,"commit_ids":["4ca8bcaf27e28504df7bf996819665986b01c847","96cef4a7b72b3c208340ae6f0cf55a93e9077c93","c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2"]}'
created_unix: 1688672373
-
id: 14
type: 29 # push
poster_id: 2
issue_id: 19 # in repo_id 58
content: '{"is_force_push":false,"commit_ids":["23576dd018294e476c06e569b6b0f170d0558705"]}'
created_unix: 1688672374
-
id: 15
type: 29 # push
poster_id: 2
issue_id: 19 # in repo_id 58
content: '{"is_force_push":false,"commit_ids":["3e64625bd6eb5bcba69ac97de6c8f507402df861", "c704db5794097441aa2d9dd834d5b7e2f8f08108"]}'
created_unix: 1688672375
-
id: 16
type: 29 # push
poster_id: 2
issue_id: 19 # in repo_id 58
content: '{"is_force_push":false,"commit_ids":["811d46c7e518f4f180afb862c0db5cb8c80529ce", "747ddb3506a4fa04a7747808eb56ae16f9e933dc", "837d5c8125633d7d258f93b998e867eab0145520", "1978192d98bb1b65e11c2cf37da854fbf94bffd6"]}'
created_unix: 1688672376
-
id: 17
type: 29 # push
poster_id: 2
issue_id: 19 # in repo_id 58
content: '{"is_force_push":true,"commit_ids":["1978192d98bb1b65e11c2cf37da854fbf94bffd6", "9b93963cf6de4dc33f915bb67f192d099c301f43"]}'
created_unix: 1749734240
-
id: 2000
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 1
old_milestone_id: 0
created_unix: 946684820
-
id: 2001
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 2
old_milestone_id: 1
created_unix: 946684920
-
id: 2002
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 0
old_milestone_id: 2
created_unix: 946685020
-
id: 2003
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 10 # not exsting milestone
old_milestone_id: 0
created_unix: 946685080
-
id: 2010
type: 30 # project
poster_id: 1
issue_id: 1 # in repo_id 1
project_id: 1
old_project_id: 0
created_unix: 946685120
-
id: 2011
type: 30 # project
poster_id: 1
issue_id: 1 # in repo_id 1
project_id: 2
old_project_id: 1
created_unix: 946685220
-
id: 2012
type: 30 # project
poster_id: 1
issue_id: 1 # in repo_id 1
project_id: 0
old_project_id: 2
created_unix: 946685320
-
id: 2013
type: 30 # project
poster_id: 1
issue_id: 1 # in repo_id 1
project_id: 10 # not existing project
old_project_id: 0
created_unix: 946685420
-
id: 2020
type: 7 # label
poster_id: 1
issue_id: 1 # in repo_id 1
label_id: 1
content: 1 # add label
created_unix: 946685520
-
id: 2021
type: 7 # label
poster_id: 1
issue_id: 1
label_id: 2
content: 1 # add label
created_unix: 946685620
-
id: 2022
type: 7 # label
poster_id: 2
issue_id: 1 # in repo_id 1
label_id: 1
content: 0 # remove label
created_unix: 946685720
-
id: 2023
type: 7 # label
poster_id: 1
issue_id: 1 # in repo_id 1
label_id: 1
content: 1 # add label
created_unix: 946685720
-
id: 2024
type: 7 # label
poster_id: 1
issue_id: 1 # in repo_id 1
label_id: 2
content: 0 # remove label
created_unix: 946685720
-
id: 2025
type: 7 # label
poster_id: 2
issue_id: 1 # in repo_id 1
label_id: 2
content: 1 # add label
created_unix: 946685820
-
id: 2026
type: 7 # label
poster_id: 1
issue_id: 1 # in repo_id 1
label_id: 1
content: 0 # remove label
created_unix: 946685920
-
id: 2027
type: 7 # label
poster_id: 1
issue_id: 1 # in repo_id 1
label_id: 2
content: 0 # remove label
created_unix: 946685920
- id: 2040
type: 9 # assignee
poster_id: 1
issue_id: 1 # in repo_id 1
assignee_id: 1 # self
removed_assignee: false # add assignee
created_unix: 946688020
- id: 2041
type: 9 # assignee
poster_id: 2
issue_id: 1 # in repo_id 1
assignee_id: 1
removed_assignee: true # remove assignee
created_unix: 946688120
- id: 2042
type: 9 # assignee
poster_id: 1
issue_id: 1 # in repo_id 1
assignee_id: 2
removed_assignee: false # add assignee
created_unix: 946688220
- id: 2043
type: 9 # assignee
poster_id: 2
issue_id: 1 # in repo_id 1
assignee_id: 2 # self
removed_assignee: true # remove assignee
created_unix: 946688320
- id: 2050
type: 23 # lock
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946688420
- id: 2051
type: 24 # unlock
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946688520
- id: 2052
type: 23 # lock
poster_id: 1
issue_id: 1 # in repo_id 1
content: "Too heated"
created_unix: 946688620
- id: 2053
type: 24 # unlock
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946688720
- id: 2060
type: 36 # pin
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946688820
- id: 2061
type: 37 # unpin
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946688920
- id: 2070
type: 2 # close
poster_id: 1
issue_id: 1 # in repo_id 1
created_unix: 946689020
- id: 2071
type: 1 # reopen
poster_id: 2
issue_id: 1 # in repo_id 1
created_unix: 946689220
- id: 2072
type: 2 # close
poster_id: 1
issue_id: 2 # pull in repo_id 1
created_unix: 946689320
- id: 2073
type: 1 # reopen
poster_id: 2
issue_id: 2 # pull in repo_id 1
created_unix: 946689420
- id: 2080
type: 3 # issue reference
poster_id: 1
issue_id: 1 # issue in repo_id 1
ref_repo_id: 1
ref_issue_id: 5 # issue in repo_id 1
created_unix: 946689500
- id: 2081
type: 3 # issue reference
poster_id: 1
issue_id: 1 # issue in repo_id 1
ref_repo_id: 1
ref_issue_id: 2 # pull in repo_id 1
created_unix: 946689600
- id: 2082
type: 3 # issue reference
poster_id: 1
issue_id: 1 # issue in repo_id 1
ref_repo_id: 32
ref_issue_id: 16 # issue in repo_id 32
created_unix: 946689700
- id: 2083
type: 3 # issue reference
poster_id: 1
issue_id: 1 # issue in repo_id 1
ref_repo_id: 10
ref_issue_id: 8 # pull in repo_id 10
created_unix: 946689800
- id: 2090
type: 6 # pull reference
poster_id: 1
issue_id: 2 # pull in repo_id 1
ref_repo_id: 1
ref_issue_id: 1 # issue in repo_id 1
created_unix: 946689900
- id: 2091
type: 6 # pull reference
poster_id: 1
issue_id: 2 # pull in repo_id 1
ref_repo_id: 1
ref_issue_id: 2 # pull in repo_id 1
created_unix: 946690000
- id: 2092
type: 6 # pull reference
poster_id: 1
issue_id: 2 # pull in repo_id 1
ref_repo_id: 32
ref_issue_id: 16 # issue in repo_id 32
created_unix: 946690050
- id: 2093
type: 6 # pull reference
poster_id: 1
issue_id: 2 # pull in repo_id 1
ref_repo_id: 10
ref_issue_id: 8 # pull in repo_id 10
created_unix: 946690100

View file

@ -0,0 +1 @@
[] # empty

View file

@ -6,6 +6,7 @@ package forgefed
import (
"database/sql"
"fmt"
"net/url"
"strings"
"time"
@ -17,9 +18,9 @@ import (
// swagger:model
type FederationHost struct {
ID int64 `xorm:"pk autoincr"`
HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"`
HostFqdn string `xorm:"host_fqdn UNIQUE(federation_host) INDEX VARCHAR(255) NOT NULL"`
HostPort uint16 `xorm:" UNIQUE(federation_host) INDEX NOT NULL DEFAULT 443"`
NodeInfo NodeInfo `xorm:"extends NOT NULL"`
HostPort uint16 `xorm:"NOT NULL DEFAULT 443"`
HostSchema string `xorm:"NOT NULL DEFAULT 'https'"`
LatestActivity time.Time `xorm:"NOT NULL"`
KeyID sql.NullString `xorm:"key_id UNIQUE"`
@ -42,6 +43,13 @@ func NewFederationHost(hostFqdn string, nodeInfo NodeInfo, port uint16, schema s
return result, nil
}
func (host FederationHost) AsURL() url.URL {
return url.URL{
Scheme: host.HostSchema,
Host: fmt.Sprintf("%v:%v", host.HostFqdn, host.HostPort),
}
}
// Validate collects error strings in a slice and returns this
func (host FederationHost) Validate() []string {
var result []string

View file

@ -17,12 +17,14 @@ type (
)
const (
ForgejoSourceType SoftwareNameType = "forgejo"
GiteaSourceType SoftwareNameType = "gitea"
ForgejoSourceType SoftwareNameType = "forgejo"
GiteaSourceType SoftwareNameType = "gitea"
MastodonSourceType SoftwareNameType = "mastodon"
GoToSocialSourceType SoftwareNameType = "gotosocial"
)
var KnownSourceTypes = []any{
ForgejoSourceType, GiteaSourceType,
ForgejoSourceType, GiteaSourceType, MastodonSourceType, GoToSocialSourceType,
}
// ------------------------------------------------ NodeInfoWellKnown ------------------------------------------------

View file

@ -103,6 +103,12 @@ var migrations = []*Migration{
NewMigration("Normalize repository.topics to empty slice instead of null", SetTopicsAsEmptySlice),
// v31 -> v32
NewMigration("Migrate maven package name concatenation", ChangeMavenArtifactConcatenation),
// v32 -> v33
NewMigration("Add federated user activity tables, update the `federated_user` table & add indexes", FederatedUserActivityMigration),
// v33 -> v34
NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun),
// v34 -> v35
NewMigration("Add index to `stopped` column in `action_run` table", AddIndexToActionRunStopped),
}
// GetCurrentDBVersion returns the current Forgejo database version.

View file

@ -0,0 +1,126 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package forgejo_migrations //nolint:revive
import (
"fmt"
"forgejo.org/modules/log"
"forgejo.org/modules/timeutil"
"xorm.io/xorm"
)
func dropOldFederationHostIndexes(x *xorm.Engine) {
// drop unique index on HostFqdn
type FederationHost struct {
ID int64 `xorm:"pk autoincr"`
HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"`
}
err := x.DropIndexes(FederationHost{})
if err != nil {
log.Warn("migration[33]: There was an issue: %v", err)
return
}
}
func addFederatedUserActivityTables(x *xorm.Engine) {
type FederatedUserActivity struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL INDEX user_id"`
ActorID int64
ActorURI string
NoteContent string `xorm:"TEXT"`
NoteURL string `xorm:"VARCHAR(255)"`
OriginalNote string `xorm:"TEXT"`
Created timeutil.TimeStamp `xorm:"created"`
}
// add unique index on HostFqdn+HostPort
type FederationHost struct {
ID int64 `xorm:"pk autoincr"`
HostFqdn string `xorm:"host_fqdn UNIQUE(federation_host) INDEX VARCHAR(255) NOT NULL"`
HostPort uint16 `xorm:"UNIQUE(federation_host) INDEX NOT NULL DEFAULT 443"`
}
type FederatedUserFollower struct {
ID int64 `xorm:"pk autoincr"`
FollowedUserID int64 `xorm:"NOT NULL unique(fuf_rel)"`
FollowingUserID int64 `xorm:"NOT NULL unique(fuf_rel)"`
}
// Add InboxPath to FederatedUser & add index fo UserID
type FederatedUser struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL INDEX user_id"`
InboxPath string
}
var err error
err = x.Sync(&FederationHost{})
if err != nil {
log.Warn("migration[33]: There was an issue: %v", err)
return
}
err = x.Sync(&FederatedUserActivity{})
if err != nil {
log.Warn("migration[33]: There was an issue: %v", err)
return
}
err = x.Sync(&FederatedUserFollower{})
if err != nil {
log.Warn("migration[33]: There was an issue: %v", err)
return
}
err = x.Sync(&FederatedUser{})
if err != nil {
log.Warn("migration[33]: There was an issue: %v", err)
return
}
// Migrate
sessMigration := x.NewSession()
defer sessMigration.Close()
if err := sessMigration.Begin(); err != nil {
log.Warn("migration[33]: There was an issue: %v", err)
return
}
federatedUsers := make([]*FederatedUser, 0)
err = sessMigration.OrderBy("id").Find(&federatedUsers)
if err != nil {
log.Warn("migration[33]: There was an issue: %v", err)
return
}
for _, federatedUser := range federatedUsers {
if federatedUser.InboxPath != "" {
log.Info("migration[33]: This user was already migrated: %v", federatedUser)
} else {
// Migrate User.InboxPath
sql := "UPDATE `federated_user` SET `inbox_path` = ? WHERE `id` = ?"
if _, err := sessMigration.Exec(sql, fmt.Sprintf("/api/v1/activitypub/user-id/%v/inbox", federatedUser.UserID), federatedUser.ID); err != nil {
log.Warn("migration[33]: There was an issue: %v", err)
return
}
}
}
err = sessMigration.Commit()
if err != nil {
log.Warn("migration[33]: There was an issue: %v", err)
return
}
}
func FederatedUserActivityMigration(x *xorm.Engine) error {
dropOldFederationHostIndexes(x)
addFederatedUserActivityTables(x)
return nil
}

View file

@ -0,0 +1,46 @@
// Copyright 2025 The Forgejo Authors.
// SPDX-License-Identifier: GPL-3.0-or-later
package forgejo_migrations //nolint:revive
import (
"testing"
"time"
migration_tests "forgejo.org/models/migrations/test"
"forgejo.org/modules/log"
ft "forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_FederatedUserActivityMigration(t *testing.T) {
lc, cl := ft.NewLogChecker(log.DEFAULT, log.WARN)
lc.Filter("migration[33]")
defer cl()
// intentionally conflicting definition
type FederatedUser struct {
ID int64 `xorm:"pk autoincr"`
UserID string
}
// Prepare TestEnv
x, deferable := migration_tests.PrepareTestEnv(t, 0,
new(FederatedUser),
)
sessTest := x.NewSession()
sessTest.Insert(FederatedUser{UserID: "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"})
sessTest.Commit()
defer deferable()
if x == nil || t.Failed() {
return
}
require.NoError(t, FederatedUserActivityMigration(x))
logFiltered, _ := lc.Check(5 * time.Second)
assert.NotEmpty(t, logFiltered)
}

View file

@ -0,0 +1,14 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package forgejo_migrations //nolint:revive
import "xorm.io/xorm"
func AddNotifyEmailToActionRun(x *xorm.Engine) error {
type ActionRun struct {
ID int64
NotifyEmail bool
}
return x.Sync(new(ActionRun))
}

View file

@ -0,0 +1,19 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package forgejo_migrations //nolint:revive
import (
"forgejo.org/modules/timeutil"
"xorm.io/xorm"
)
func AddIndexToActionRunStopped(x *xorm.Engine) error {
type ActionRun struct {
ID int64
Stopped timeutil.TimeStamp `xorm:"index"`
}
return x.Sync(&ActionRun{})
}

View file

@ -179,25 +179,6 @@ func (status *CommitStatus) LocaleString(lang translation.Locale) string {
return lang.TrString("repo.commitstatus." + status.State.String())
}
// HideActionsURL set `TargetURL` to an empty string if the status comes from Gitea Actions
func (status *CommitStatus) HideActionsURL(ctx context.Context) {
if status.RepoID == 0 {
return
}
if status.Repo == nil {
if err := status.loadRepository(ctx); err != nil {
log.Error("loadRepository: %v", err)
return
}
}
prefix := fmt.Sprintf("%s/actions", status.Repo.Link())
if strings.HasPrefix(status.TargetURL, prefix) {
status.TargetURL = ""
}
}
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
if len(statuses) == 0 {
@ -453,11 +434,19 @@ type SignCommitWithStatuses struct {
*asymkey_model.SignCommit
}
// ParseCommitsWithStatus checks commits latest statuses and calculates its worst status state
func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.SignCommit, repo *repo_model.Repository) []*SignCommitWithStatuses {
newCommits := make([]*SignCommitWithStatuses, 0, len(oldCommits))
// ParseCommitsWithStatus converts git commits into SignCommitWithStatuses (checks signature and calculates its worst status state)
func ParseCommitsWithStatus(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses {
commitsWithSignature := asymkey_model.ParseCommitsWithSignature(
ctx,
user_model.ValidateCommitsWithEmails(ctx, commits),
repo.GetTrustModel(),
func(user *user_model.User) (bool, error) {
return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID)
},
)
for _, c := range oldCommits {
commitsWithStatus := make([]*SignCommitWithStatuses, 0, len(commitsWithSignature))
for _, c := range commitsWithSignature {
commit := &SignCommitWithStatuses{
SignCommit: c,
}
@ -469,43 +458,12 @@ func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.Sig
commit.Status = CalcCommitStatus(statuses)
}
newCommits = append(newCommits, commit)
commitsWithStatus = append(commitsWithStatus, commit)
}
return newCommits
return commitsWithStatus
}
// hashCommitStatusContext hash context
func hashCommitStatusContext(context string) string {
return fmt.Sprintf("%x", sha1.Sum([]byte(context)))
}
// ConvertFromGitCommit converts git commits into SignCommitWithStatuses
func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses {
return ParseCommitsWithStatus(ctx,
asymkey_model.ParseCommitsWithSignature(
ctx,
user_model.ValidateCommitsWithEmails(ctx, commits),
repo.GetTrustModel(),
func(user *user_model.User) (bool, error) {
return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID)
},
),
repo,
)
}
// CommitStatusesHideActionsURL hide Gitea Actions urls
func CommitStatusesHideActionsURL(ctx context.Context, statuses []*CommitStatus) {
idToRepos := make(map[int64]*repo_model.Repository)
for _, status := range statuses {
if status == nil {
continue
}
if status.Repo == nil {
status.Repo = idToRepos[status.RepoID]
}
status.HideActionsURL(ctx)
idToRepos[status.RepoID] = status.Repo
}
}

View file

@ -4,11 +4,9 @@
package git_test
import (
"fmt"
"testing"
"time"
actions_model "forgejo.org/models/actions"
"forgejo.org/models/db"
git_model "forgejo.org/models/git"
repo_model "forgejo.org/models/repo"
@ -246,26 +244,3 @@ func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
assert.Equal(t, "compliance/lint-backend", contexts[0])
}
}
func TestCommitStatusesHideActionsURL(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 791, RepoID: repo.ID})
require.NoError(t, run.LoadAttributes(db.DefaultContext))
statuses := []*git_model.CommitStatus{
{
RepoID: repo.ID,
TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), run.Index),
},
{
RepoID: repo.ID,
TargetURL: "https://mycicd.org/1",
},
}
git_model.CommitStatusesHideActionsURL(db.DefaultContext, statuses)
assert.Empty(t, statuses[0].TargetURL)
assert.Equal(t, "https://mycicd.org/1", statuses[1].TargetURL)
}

View file

@ -802,7 +802,7 @@ func (c *Comment) LoadPushCommits(ctx context.Context) (err error) {
}
defer closer.Close()
c.Commits = git_model.ConvertFromGitCommit(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo)
c.Commits = git_model.ParseCommitsWithStatus(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo)
c.CommitsNum = int64(len(c.Commits))
}

View file

@ -66,6 +66,8 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) {
sess.Asc("issue.created_unix").Asc("issue.id")
case "recentupdate":
sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id")
case "recentclose":
sess.Desc("issue.closed_unix").Desc("issue.created_unix").Desc("issue.id")
case "leastupdate":
sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id")
case "mostcomment":

View file

@ -67,6 +67,13 @@ type Milestone struct {
TotalTrackedTime int64 `xorm:"-"`
}
// Ghost milestone is a milestone which has been deleted
const GhostMilestoneID = -1
func (m *Milestone) IsGhost() bool {
return m.ID == GhostMilestoneID
}
func init() {
db.RegisterModel(new(Milestone))
}

View file

@ -5,6 +5,7 @@
package issues
import (
"bufio"
"context"
"errors"
"fmt"
@ -923,31 +924,30 @@ func MergeBlockedByOutdatedBranch(protectBranch *git_model.ProtectedBranch, pr *
return protectBranch.BlockOnOutdatedBranch && pr.CommitsBehind > 0
}
// GetCodeOwnersFromContent returns the code owners configuration
// Return empty slice if files missing
// GetCodeOwnersFromReader returns the code owners configuration
// Return warning messages on parsing errors
// We're trying to do the best we can when parsing a file.
// Invalid lines are skipped. Non-existent users and teams too.
func GetCodeOwnersFromContent(ctx context.Context, data string) ([]*CodeOwnerRule, []string) {
if len(data) == 0 {
return nil, nil
}
func GetCodeOwnersFromReader(ctx context.Context, rc io.ReadCloser, truncated bool) ([]*CodeOwnerRule, []string) {
defer rc.Close()
scanner := bufio.NewScanner(rc)
rules := make([]*CodeOwnerRule, 0)
lines := strings.Split(data, "\n")
warnings := make([]string, 0)
var rules []*CodeOwnerRule
var warnings []string
line := 0
for scanner.Scan() {
line++
for i, line := range lines {
tokens := TokenizeCodeOwnersLine(line)
tokens := TokenizeCodeOwnersLine(scanner.Text())
if len(tokens) == 0 {
continue
} else if len(tokens) < 2 {
warnings = append(warnings, fmt.Sprintf("Line: %d: incorrect format", i+1))
warnings = append(warnings, fmt.Sprintf("Line: %d: incorrect format", line))
continue
}
rule, wr := ParseCodeOwnersLine(ctx, tokens)
for _, w := range wr {
warnings = append(warnings, fmt.Sprintf("Line: %d: %s", i+1, w))
warnings = append(warnings, fmt.Sprintf("Line: %d: %s", line, w))
}
if rule == nil {
continue
@ -955,6 +955,12 @@ func GetCodeOwnersFromContent(ctx context.Context, data string) ([]*CodeOwnerRul
rules = append(rules, rule)
}
if err := scanner.Err(); err != nil {
warnings = append(warnings, err.Error())
}
if truncated {
warnings = append(warnings, fmt.Sprintf("File too big: truncated while on line %d", line))
}
return rules, warnings
}

View file

@ -152,7 +152,8 @@ func PullRequests(ctx context.Context, baseRepoID int64, opts *PullRequestsOptio
applySorts(findSession, opts.SortType, 0)
findSession = db.SetSessionPagination(findSession, opts)
prs := make([]*PullRequest, 0, opts.PageSize)
return prs, maxResults, findSession.Find(&prs)
found := findSession.Find(&prs)
return prs, maxResults, found
}
// PullRequestList defines a list of pull requests

View file

@ -79,6 +79,47 @@ func TestPullRequestsNewest(t *testing.T) {
}
}
func TestPullRequests_Closed_RecentSortType(t *testing.T) {
// Issue ID | Closed At. | Updated At
// 2 | 1707270001 | 1707270001
// 3 | 1707271000 | 1707279999
// 11 | 1707279999 | 1707275555
tests := []struct {
sortType string
expectedIssueIDOrder []int64
}{
{"recentupdate", []int64{3, 11, 2}},
{"recentclose", []int64{11, 3, 2}},
}
require.NoError(t, unittest.PrepareTestDatabase())
_, err := db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707270001, updated_unix = 1707270001, is_closed = true WHERE id = 2")
require.NoError(t, err)
_, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707271000, updated_unix = 1707279999, is_closed = true WHERE id = 3")
require.NoError(t, err)
_, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707279999, updated_unix = 1707275555, is_closed = true WHERE id = 11")
require.NoError(t, err)
for _, test := range tests {
t.Run(test.sortType, func(t *testing.T) {
prs, _, err := issues_model.PullRequests(db.DefaultContext, 1, &issues_model.PullRequestsOptions{
ListOptions: db.ListOptions{
Page: 1,
},
State: "closed",
SortType: test.sortType,
})
require.NoError(t, err)
if assert.Len(t, prs, len(test.expectedIssueIDOrder)) {
for i := range test.expectedIssueIDOrder {
assert.Equal(t, test.expectedIssueIDOrder[i], prs[i].IssueID)
}
}
})
}
}
func TestLoadRequestedReviewers(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())

View file

@ -10,6 +10,7 @@ import (
"forgejo.org/models/db"
repo_model "forgejo.org/models/repo"
user_model "forgejo.org/models/user"
"forgejo.org/modules/log"
"forgejo.org/modules/timeutil"
)
@ -58,13 +59,15 @@ func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64,
return ErrAlreadyScheduledToAutoMerge{PullID: pullID}
}
_, err := db.GetEngine(ctx).Insert(&AutoMerge{
scheduledPRM, err := db.GetEngine(ctx).Insert(&AutoMerge{
DoerID: doer.ID,
PullID: pullID,
MergeStyle: style,
Message: message,
DeleteBranchAfterMerge: deleteBranch,
})
log.Trace("ScheduleAutoMerge %+v for PR %d", scheduledPRM, pullID)
return err
}
@ -81,6 +84,8 @@ func GetScheduledMergeByPullID(ctx context.Context, pullID int64) (bool, *AutoMe
return false, nil, err
}
log.Trace("GetScheduledMergeByPullID found %+v for PR %d", scheduledPRM, pullID)
scheduledPRM.Doer = doer
return true, scheduledPRM, nil
}
@ -94,6 +99,8 @@ func DeleteScheduledAutoMerge(ctx context.Context, pullID int64) error {
return db.ErrNotExist{Resource: "auto_merge", ID: pullID}
}
log.Trace("DeleteScheduledAutoMerge %+v for PR %d", scheduledPRM, pullID)
_, err = db.GetEngine(ctx).ID(scheduledPRM.ID).Delete(&AutoMerge{})
return err
}

View file

@ -11,19 +11,21 @@ import (
type FederatedUser struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL"`
UserID int64 `xorm:"NOT NULL INDEX user_id"`
ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"`
FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"`
KeyID sql.NullString `xorm:"key_id UNIQUE"`
PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"`
NormalizedOriginalURL string // This field is just to keep original information. Pls. do not use for search or as ID!
InboxPath string
NormalizedOriginalURL string // This field is just to keep original information. Pls. do not use for search or as ID!
}
func NewFederatedUser(userID int64, externalID string, federationHostID int64, normalizedOriginalURL string) (FederatedUser, error) {
func NewFederatedUser(userID int64, externalID string, federationHostID int64, inboxPath, normalizedOriginalURL string) (FederatedUser, error) {
result := FederatedUser{
UserID: userID,
ExternalID: externalID,
FederationHostID: federationHostID,
InboxPath: inboxPath,
NormalizedOriginalURL: normalizedOriginalURL,
}
if valid, err := validation.IsValid(result); !valid {
@ -32,10 +34,11 @@ func NewFederatedUser(userID int64, externalID string, federationHostID int64, n
return result, nil
}
func (user FederatedUser) Validate() []string {
func (federatedUser FederatedUser) Validate() []string {
var result []string
result = append(result, validation.ValidateNotEmpty(user.UserID, "UserID")...)
result = append(result, validation.ValidateNotEmpty(user.ExternalID, "ExternalID")...)
result = append(result, validation.ValidateNotEmpty(user.FederationHostID, "FederationHostID")...)
result = append(result, validation.ValidateNotEmpty(federatedUser.UserID, "UserID")...)
result = append(result, validation.ValidateNotEmpty(federatedUser.ExternalID, "ExternalID")...)
result = append(result, validation.ValidateNotEmpty(federatedUser.FederationHostID, "FederationHostID")...)
result = append(result, validation.ValidateNotEmpty(federatedUser.InboxPath, "InboxPath")...)
return result
}

View file

@ -0,0 +1,30 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import "forgejo.org/modules/validation"
type FederatedUserFollower struct {
ID int64 `xorm:"pk autoincr"`
FollowedUserID int64 `xorm:"NOT NULL unique(fuf_rel)"`
FollowingUserID int64 `xorm:"NOT NULL unique(fuf_rel)"`
}
func NewFederatedUserFollower(followedUserID, federatedUserID int64) (FederatedUserFollower, error) {
result := FederatedUserFollower{
FollowedUserID: followedUserID,
FollowingUserID: federatedUserID,
}
if valid, err := validation.IsValid(result); !valid {
return FederatedUserFollower{}, err
}
return result, nil
}
func (user FederatedUserFollower) Validate() []string {
var result []string
result = append(result, validation.ValidateNotEmpty(user.FollowedUserID, "FollowedUserID")...)
result = append(result, validation.ValidateNotEmpty(user.FollowingUserID, "FollowingUserID")...)
return result
}

View file

@ -0,0 +1,27 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import (
"testing"
"forgejo.org/modules/validation"
"github.com/stretchr/testify/assert"
)
func Test_FederatedUserFollowerValidation(t *testing.T) {
sut := FederatedUserFollower{
FollowedUserID: 12,
FollowingUserID: 1,
}
res, err := validation.IsValid(sut)
assert.Truef(t, res, "sut should be valid but was %q", err)
sut = FederatedUserFollower{
FollowedUserID: 1,
}
res, _ = validation.IsValid(sut)
assert.False(t, res, "sut should be invalid")
}

View file

@ -14,6 +14,7 @@ func Test_FederatedUserValidation(t *testing.T) {
UserID: 12,
ExternalID: "12",
FederationHostID: 1,
InboxPath: "/api/v1/activitypub/user-id/12/inbox",
}
if res, err := validation.IsValid(sut); !res {
t.Errorf("sut should be valid but was %q", err)
@ -22,6 +23,7 @@ func Test_FederatedUserValidation(t *testing.T) {
sut = FederatedUser{
ExternalID: "12",
FederationHostID: 1,
InboxPath: "/api/v1/activitypub/user-id/12/inbox",
}
if res, _ := validation.IsValid(sut); res {
t.Error("sut should be invalid")

View file

@ -11,6 +11,7 @@ import (
)
// Follow represents relations of user and their followers.
// TODO: We should unify Activity-pub-following and classical following (see models/user/user_repository.go#IsFollowingAp)
type Follow struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"UNIQUE(follow)"`

View file

@ -1,4 +1,4 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// Copyright 2024, 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
@ -8,12 +8,14 @@ import (
"fmt"
"forgejo.org/models/db"
"forgejo.org/modules/log"
"forgejo.org/modules/optional"
"forgejo.org/modules/validation"
)
func init() {
db.RegisterModel(new(FederatedUser))
db.RegisterModel(new(FederatedUserFollower))
}
func CreateFederatedUser(ctx context.Context, user *User, federatedUser *FederatedUser) error {
@ -30,7 +32,12 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat
if err != nil {
return err
}
defer committer.Close()
defer func() {
err := committer.Close()
if err != nil {
log.Error("Error closing committer: %v", err)
}
}()
if err := CreateUser(ctx, user, &overwrite); err != nil {
return err
@ -50,6 +57,14 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat
return committer.Commit()
}
func (federatedUser *FederatedUser) UpdateFederatedUser(ctx context.Context) error {
if _, err := validation.IsValid(federatedUser); err != nil {
return err
}
_, err := db.GetEngine(ctx).ID(federatedUser.ID).Cols("inbox_path").Update(federatedUser)
return err
}
func FindFederatedUser(ctx context.Context, externalID string, federationHostID int64) (*User, *FederatedUser, error) {
federatedUser := new(FederatedUser)
user := new(User)
@ -75,6 +90,41 @@ func FindFederatedUser(ctx context.Context, externalID string, federationHostID
return user, federatedUser, nil
}
func GetFederatedUser(ctx context.Context, externalID string, federationHostID int64) (*User, *FederatedUser, error) {
user, federatedUser, err := FindFederatedUser(ctx, externalID, federationHostID)
if err != nil {
return nil, nil, err
} else if federatedUser == nil {
return nil, nil, fmt.Errorf("FederatedUser for externalId = %v and federationHostId = %v does not exist", externalID, federationHostID)
}
return user, federatedUser, nil
}
func GetFederatedUserByUserID(ctx context.Context, userID int64) (*User, *FederatedUser, error) {
federatedUser := new(FederatedUser)
user := new(User)
has, err := db.GetEngine(ctx).Where("user_id=?", userID).Get(federatedUser)
if err != nil {
return nil, nil, err
} else if !has {
return nil, nil, fmt.Errorf("Federated user %v does not exist", federatedUser.UserID)
}
has, err = db.GetEngine(ctx).ID(federatedUser.UserID).Get(user)
if err != nil {
return nil, nil, err
} else if !has {
return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID)
}
if res, err := validation.IsValid(*user); !res {
return nil, nil, err
}
if res, err := validation.IsValid(*federatedUser); !res {
return nil, nil, err
}
return user, federatedUser, nil
}
func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *FederatedUser, error) {
federatedUser := new(FederatedUser)
user := new(User)
@ -101,7 +151,85 @@ func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *Federa
return user, federatedUser, nil
}
func UpdateFederatedUser(ctx context.Context, federatedUser *FederatedUser) error {
if res, err := validation.IsValid(federatedUser); !res {
return err
}
_, err := db.GetEngine(ctx).ID(federatedUser.ID).Update(federatedUser)
return err
}
func DeleteFederatedUser(ctx context.Context, userID int64) error {
_, err := db.GetEngine(ctx).Delete(&FederatedUser{UserID: userID})
return err
}
func GetFollowersForUser(ctx context.Context, user *User) ([]*FederatedUserFollower, error) {
if res, err := validation.IsValid(user); !res {
return nil, err
}
followers := make([]*FederatedUserFollower, 0, 8)
err := db.GetEngine(ctx).
Where("followed_user_id = ?", user.ID).
Find(&followers)
if err != nil {
return nil, err
}
for _, element := range followers {
if res, err := validation.IsValid(*element); !res {
return nil, err
}
}
return followers, nil
}
func AddFollower(ctx context.Context, followedUser *User, followingUser *FederatedUser) (*FederatedUserFollower, error) {
if res, err := validation.IsValid(followedUser); !res {
return nil, err
}
if res, err := validation.IsValid(followingUser); !res {
return nil, err
}
federatedUserFollower, err := NewFederatedUserFollower(followedUser.ID, followingUser.UserID)
if err != nil {
return nil, err
}
_, err = db.GetEngine(ctx).Insert(&federatedUserFollower)
if err != nil {
return nil, err
}
return &federatedUserFollower, err
}
func RemoveFollower(ctx context.Context, followedUser *User, followingUser *FederatedUser) error {
if res, err := validation.IsValid(followedUser); !res {
return err
}
if res, err := validation.IsValid(followingUser); !res {
return err
}
_, err := db.GetEngine(ctx).Delete(&FederatedUserFollower{
FollowedUserID: followedUser.ID,
FollowingUserID: followingUser.UserID,
})
return err
}
// TODO: We should unify Activity-pub-following and classical following (see models/user/follow.go)
func IsFollowingAp(ctx context.Context, followedUser *User, followingUser *FederatedUser) (bool, error) {
if res, err := validation.IsValid(followedUser); !res {
return false, err
}
if res, err := validation.IsValid(followingUser); !res {
return false, err
}
return db.GetEngine(ctx).Get(&FederatedUserFollower{
FollowedUserID: followedUser.ID,
FollowingUserID: followingUser.UserID,
})
}

View file

@ -12,6 +12,13 @@ import (
"forgejo.org/modules/structs"
)
// IsSystem returns true if the user has a fixed
// negative ID, is never stored in the database and
// is generated on the fly when needed.
func (u *User) IsSystem() bool {
return u.IsGhost() || u.IsActions()
}
const (
GhostUserID = -1
GhostUserName = "Ghost"

View file

@ -148,7 +148,7 @@ func TestAPActorID_APActorID(t *testing.T) {
assert.Equal(t, expected, url)
}
func TestAPActorKeyID(t *testing.T) {
func TestKeyID(t *testing.T) {
user := user_model.User{ID: 1}
url := user.APActorKeyID()
expected := "https://try.gitea.io/api/v1/activitypub/user-id/1#main-key"

View file

@ -323,6 +323,10 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
matchTimes++
}
case "paths":
if refName.IsTag() {
matchTimes++
break
}
filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)
@ -336,6 +340,10 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
}
}
case "paths-ignore":
if refName.IsTag() {
matchTimes++
break
}
filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)

View file

@ -150,6 +150,24 @@ func TestDetectMatched(t *testing.T) {
yamlOn: "on: workflow_dispatch",
expected: true,
},
{
desc: "push to tag matches workflow with paths condition (should skip paths check)",
triggeredEvent: webhook_module.HookEventPush,
payload: &api.PushPayload{
Ref: "refs/tags/v1.0.0",
Before: "0000000",
Commits: []*api.PayloadCommit{
{
ID: "abcdef123456",
Added: []string{"src/main.go"},
Message: "Release v1.0.0",
},
},
},
commit: nil,
yamlOn: "on:\n push:\n paths:\n - src/**",
expected: true,
},
}
for _, tc := range testCases {

View file

@ -12,7 +12,6 @@ import (
"forgejo.org/modules/log"
"forgejo.org/modules/typesniffer"
"forgejo.org/modules/util"
)
// Blob represents a Git object.
@ -25,42 +24,25 @@ type Blob struct {
repo *Repository
}
// DataAsync gets a ReadCloser for the contents of a blob without reading it all.
// Calling the Close function on the result will discard all unread output.
func (b *Blob) DataAsync() (io.ReadCloser, error) {
func (b *Blob) newReader() (*bufio.Reader, int64, func(), error) {
wr, rd, cancel, err := b.repo.CatFileBatch(b.repo.Ctx)
if err != nil {
return nil, err
return nil, 0, nil, err
}
_, err = wr.Write([]byte(b.ID.String() + "\n"))
if err != nil {
cancel()
return nil, err
return nil, 0, nil, err
}
_, _, size, err := ReadBatchLine(rd)
if err != nil {
cancel()
return nil, err
return nil, 0, nil, err
}
b.gotSize = true
b.size = size
if size < 4096 {
bs, err := io.ReadAll(io.LimitReader(rd, size))
defer cancel()
if err != nil {
return nil, err
}
_, err = rd.Discard(1)
return io.NopCloser(bytes.NewReader(bs)), err
}
return &blobReader{
rd: rd,
n: size,
cancel: cancel,
}, nil
return rd, size, cancel, err
}
// Size returns the uncompressed size of the blob
@ -91,10 +73,36 @@ func (b *Blob) Size() int64 {
return b.size
}
// DataAsync gets a ReadCloser for the contents of a blob without reading it all.
// Calling the Close function on the result will discard all unread output.
func (b *Blob) DataAsync() (io.ReadCloser, error) {
rd, size, cancel, err := b.newReader()
if err != nil {
return nil, err
}
if size < 4096 {
bs, err := io.ReadAll(io.LimitReader(rd, size))
defer cancel()
if err != nil {
return nil, err
}
_, err = rd.Discard(1)
return io.NopCloser(bytes.NewReader(bs)), err
}
return &blobReader{
rd: rd,
n: size,
cancel: cancel,
}, nil
}
type blobReader struct {
rd *bufio.Reader
n int64
cancel func()
rd *bufio.Reader
n int64 // number of bytes to read
additionalDiscard int64 // additional number of bytes to discard
cancel func()
}
func (b *blobReader) Read(p []byte) (n int, err error) {
@ -117,7 +125,8 @@ func (b *blobReader) Close() error {
defer b.cancel()
if err := DiscardFull(b.rd, b.n+1); err != nil {
// discard the unread bytes, the truncated bytes and the trailing newline
if err := DiscardFull(b.rd, b.n+b.additionalDiscard+1); err != nil {
return err
}
@ -131,47 +140,38 @@ func (b *Blob) Name() string {
return b.name
}
// GetBlobContent Gets the limited content of the blob as raw text
// NewTruncatedReader return a blob-reader which silently truncates when the limit is reached (io.EOF will be returned)
func (b *Blob) NewTruncatedReader(limit int64) (rc io.ReadCloser, fullSize int64, err error) {
r, fullSize, cancel, err := b.newReader()
if err != nil {
return nil, fullSize, err
}
limit = min(limit, fullSize)
return &blobReader{
rd: r,
n: limit,
additionalDiscard: fullSize - limit,
cancel: cancel,
}, fullSize, nil
}
// GetBlobContent Gets the truncated content of the blob as raw text
func (b *Blob) GetBlobContent(limit int64) (string, error) {
if limit <= 0 {
return "", nil
}
dataRc, err := b.DataAsync()
rc, fullSize, err := b.NewTruncatedReader(limit)
if err != nil {
return "", err
}
defer dataRc.Close()
buf, err := util.ReadWithLimit(dataRc, int(limit))
defer rc.Close()
buf := make([]byte, min(fullSize, limit))
_, err = io.ReadFull(rc, buf)
return string(buf), err
}
// GetBlobLineCount gets line count of the blob
func (b *Blob) GetBlobLineCount() (int, error) {
reader, err := b.DataAsync()
if err != nil {
return 0, err
}
defer reader.Close()
buf := make([]byte, 32*1024)
count := 1
lineSep := []byte{'\n'}
c, err := reader.Read(buf)
if c == 0 && err == io.EOF {
return 0, nil
}
for {
count += bytes.Count(buf[:c], lineSep)
switch {
case err == io.EOF:
return count, nil
case err != nil:
return count, err
}
c, err = reader.Read(buf)
}
}
// GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string
func (b *Blob) GetBlobContentBase64() (string, error) {
dataRc, err := b.DataAsync()

View file

@ -35,6 +35,106 @@ func TestBlob_Data(t *testing.T) {
assert.Equal(t, output, string(data))
}
func TestBlob(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
repo, err := openRepositoryWithDefaultContext(bareRepo1Path)
require.NoError(t, err)
defer repo.Close()
testBlob, err := repo.GetBlob("6c493ff740f9380390d5c9ddef4af18697ac9375")
require.NoError(t, err)
t.Run("GetBlobContent", func(t *testing.T) {
r, err := testBlob.GetBlobContent(100)
require.NoError(t, err)
require.Equal(t, "file2\n", r)
r, err = testBlob.GetBlobContent(-1)
require.NoError(t, err)
require.Empty(t, r)
r, err = testBlob.GetBlobContent(4)
require.NoError(t, err)
require.Equal(t, "file", r)
r, err = testBlob.GetBlobContent(6)
require.NoError(t, err)
require.Equal(t, "file2\n", r)
})
t.Run("NewTruncatedReader", func(t *testing.T) {
// read fewer than available
rc, size, err := testBlob.NewTruncatedReader(100)
require.NoError(t, err)
require.Equal(t, int64(6), size)
buf := make([]byte, 1)
n, err := rc.Read(buf)
require.NoError(t, err)
require.Equal(t, 1, n)
require.Equal(t, "f", string(buf))
n, err = rc.Read(buf)
require.NoError(t, err)
require.Equal(t, 1, n)
require.Equal(t, "i", string(buf))
require.NoError(t, rc.Close())
// read more than available
rc, size, err = testBlob.NewTruncatedReader(100)
require.NoError(t, err)
require.Equal(t, int64(6), size)
buf = make([]byte, 100)
n, err = rc.Read(buf)
require.NoError(t, err)
require.Equal(t, 6, n)
require.Equal(t, "file2\n", string(buf[:n]))
n, err = rc.Read(buf)
require.Error(t, err)
require.Equal(t, io.EOF, err)
require.Equal(t, 0, n)
require.NoError(t, rc.Close())
// read more than truncated
rc, size, err = testBlob.NewTruncatedReader(4)
require.NoError(t, err)
require.Equal(t, int64(6), size)
buf = make([]byte, 10)
n, err = rc.Read(buf)
require.NoError(t, err)
require.Equal(t, 4, n)
require.Equal(t, "file", string(buf[:n]))
n, err = rc.Read(buf)
require.Error(t, err)
require.Equal(t, io.EOF, err)
require.Equal(t, 0, n)
require.NoError(t, rc.Close())
})
t.Run("NonExisting", func(t *testing.T) {
nonExistingBlob, err := repo.GetBlob("00003ff740f9380390d5c9ddef4af18690000000")
require.NoError(t, err)
r, err := nonExistingBlob.GetBlobContent(100)
require.Error(t, err)
require.IsType(t, ErrNotExist{}, err)
require.Empty(t, r)
rc, size, err := nonExistingBlob.NewTruncatedReader(100)
require.Error(t, err)
require.IsType(t, ErrNotExist{}, err)
require.Empty(t, rc)
require.Empty(t, size)
})
}
func Benchmark_Blob_Data(b *testing.B) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
repo, err := openRepositoryWithDefaultContext(bareRepo1Path)

View file

@ -38,6 +38,7 @@ var (
InvertedGitFlushEnv bool // 2.43.1
SupportCheckAttrOnBare bool // >= 2.40
SupportGitMergeTree bool // >= 2.38
SupportGrepMaxCount bool // >= 2.38
HasSSHExecutable bool
@ -191,6 +192,7 @@ func InitFull(ctx context.Context) (err error) {
InvertedGitFlushEnv = CheckGitVersionEqual("2.43.1") == nil
SupportGitMergeTree = CheckGitVersionAtLeast("2.38") == nil
SupportGrepMaxCount = CheckGitVersionAtLeast("2.38") == nil
if setting.LFS.StartServer {
if CheckGitVersionAtLeast("2.1.2") != nil {

View file

@ -98,8 +98,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
cmd.AddOptionValues("--context", fmt.Sprint(opts.ContextLineNumber))
// --max-count requires at least git 2.38
if CheckGitVersionAtLeast("2.38.0") == nil {
if SupportGrepMaxCount {
cmd.AddOptionValues("--max-count", fmt.Sprint(opts.MatchesPerFile))
} else {
log.Warn("git-grep: --max-count requires at least git 2.38")

View file

@ -59,48 +59,55 @@ func TestGrepSearch(t *testing.T) {
},
}, res)
res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{MatchesPerFile: 1})
require.NoError(t, err)
assert.Equal(t, []*GrepResult{
{
Filename: "i-am-a-python.p",
LineNumbers: []int{1},
LineCodes: []string{"## This is a simple file to do a hello world"},
HighlightedRanges: [][3]int{{0, 39, 44}},
},
{
Filename: "java-hello/main.java",
LineNumbers: []int{1},
LineCodes: []string{"public class HelloWorld"},
HighlightedRanges: [][3]int{{0, 18, 23}},
},
{
Filename: "main.vendor.java",
LineNumbers: []int{1},
LineCodes: []string{"public class HelloWorld"},
HighlightedRanges: [][3]int{{0, 18, 23}},
},
{
Filename: "python-hello/hello.py",
LineNumbers: []int{1},
LineCodes: []string{"## This is a simple file to do a hello world"},
HighlightedRanges: [][3]int{{0, 39, 44}},
},
}, res)
t.Run("Max count", func(t *testing.T) {
if !SupportGrepMaxCount {
t.Skip("Skipping, git grep --max-count is not supported")
return
}
res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{
MatchesPerFile: 1,
Filename: "java-hello/",
res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{MatchesPerFile: 1})
require.NoError(t, err)
assert.Equal(t, []*GrepResult{
{
Filename: "i-am-a-python.p",
LineNumbers: []int{1},
LineCodes: []string{"## This is a simple file to do a hello world"},
HighlightedRanges: [][3]int{{0, 39, 44}},
},
{
Filename: "java-hello/main.java",
LineNumbers: []int{1},
LineCodes: []string{"public class HelloWorld"},
HighlightedRanges: [][3]int{{0, 18, 23}},
},
{
Filename: "main.vendor.java",
LineNumbers: []int{1},
LineCodes: []string{"public class HelloWorld"},
HighlightedRanges: [][3]int{{0, 18, 23}},
},
{
Filename: "python-hello/hello.py",
LineNumbers: []int{1},
LineCodes: []string{"## This is a simple file to do a hello world"},
HighlightedRanges: [][3]int{{0, 39, 44}},
},
}, res)
res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{
MatchesPerFile: 1,
Filename: "java-hello/",
})
require.NoError(t, err)
assert.Equal(t, []*GrepResult{
{
Filename: "java-hello/main.java",
LineNumbers: []int{1},
LineCodes: []string{"public class HelloWorld"},
HighlightedRanges: [][3]int{{0, 18, 23}},
},
}, res)
})
require.NoError(t, err)
assert.Equal(t, []*GrepResult{
{
Filename: "java-hello/main.java",
LineNumbers: []int{1},
LineCodes: []string{"public class HelloWorld"},
HighlightedRanges: [][3]int{{0, 18, 23}},
},
}, res)
res, err = GrepSearch(t.Context(), repo, "no-such-content", GrepOptions{})
require.NoError(t, err)

View file

@ -251,10 +251,14 @@ func TestGitAttributeCheckerError(t *testing.T) {
cancel()
ac, err := gitRepo.GitAttributeChecker("8fee858da5796dfb37704761701bb8e800ad9ef3", "linguist-language")
require.NoError(t, err)
if SupportCheckAttrOnBare {
require.NoError(t, err)
_, err = ac.CheckPath("i-am-a-python.p")
require.Error(t, err)
_, err = ac.CheckPath("i-am-a-python.p")
require.Error(t, err)
} else {
require.Error(t, err)
}
})
t.Run("Cancelled/DuringRun", func(t *testing.T) {

View file

@ -16,16 +16,10 @@ import (
)
func TestElasticsearchIndexer(t *testing.T) {
// The elasticsearch instance started by testing.yml > test-unit > services > elasticsearch
url := "http://elastic:changeme@elasticsearch:9200"
if os.Getenv("CI") == "" {
// Make it possible to run tests against a local elasticsearch instance
url = os.Getenv("TEST_ELASTICSEARCH_URL")
if url == "" {
t.Skip("TEST_ELASTICSEARCH_URL not set and not running in CI")
return
}
url := os.Getenv("TEST_ELASTICSEARCH_URL")
if url == "" {
t.Skip("TEST_ELASTICSEARCH_URL not set")
return
}
require.Eventually(t, func() bool {

View file

@ -25,7 +25,7 @@ type Token struct {
func (tk *Token) ParseIssueReference() (int64, error) {
term := tk.Term
if term[0] == '#' || term[0] == '!' {
if len(term) > 1 && (term[0] == '#' || term[0] == '!') {
term = term[1:]
}
return strconv.ParseInt(term, 10, 64)

View file

@ -169,3 +169,35 @@ func TestIssueQueryString(t *testing.T) {
})
}
}
func TestToken_ParseIssueReference(t *testing.T) {
var tk Token
{
tk.Term = "123"
id, err := tk.ParseIssueReference()
require.NoError(t, err)
assert.Equal(t, int64(123), id)
}
{
tk.Term = "#123"
id, err := tk.ParseIssueReference()
require.NoError(t, err)
assert.Equal(t, int64(123), id)
}
{
tk.Term = "!123"
id, err := tk.ParseIssueReference()
require.NoError(t, err)
assert.Equal(t, int64(123), id)
}
{
tk.Term = "text"
_, err := tk.ParseIssueReference()
require.Error(t, err)
}
{
tk.Term = ""
_, err := tk.ParseIssueReference()
require.Error(t, err)
}
}

View file

@ -69,6 +69,10 @@ var (
// https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type%3Demail)
emailRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)(?:\\s|$|\\)|\\]|;|,|\\?|!|\\.(\\s|$))")
// Fediverse handle regex (same as emailRegex but with additonal @ or !
// at start)
fediRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([@!]([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+)@([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+))(?:\\s|$|\\)|\\]|;|,|\\?|!|\\.(\\s|$))")
// blackfriday extensions create IDs like fn:user-content-footnote
blackfridayExtRegex = regexp.MustCompile(`[^:]*:user-content-`)
@ -153,6 +157,7 @@ var defaultProcessors = []processor{
issueIndexPatternProcessor,
commitCrossReferencePatternProcessor,
hashCurrentPatternProcessor,
fediAddressProcessor,
emailAddressProcessor,
emojiProcessor,
emojiShortCodeProcessor,
@ -1237,6 +1242,21 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
}
}
// fediAddressProcessor replaces raw fediverse handles with toolforge links
func fediAddressProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
m := fediRegex.FindStringSubmatchIndex(node.Data)
if m == nil {
return
}
fedihandle := node.Data[m[2]:m[3]]
replaceContent(node, m[2], m[3], createLink("https://fedirect.toolforge.org/?id="+url.QueryEscape(fedihandle), fedihandle, "fedihandle"))
node = node.NextSibling.NextSibling
}
}
// emailAddressProcessor replaces raw email addresses with a mailto: link.
func emailAddressProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling

View file

@ -307,6 +307,19 @@ func TestRender_email(t *testing.T) {
test(
"email@domain..com",
`<p>email@domain..com</p>`)
// Test fediverse handle
test(
"@forgejo@floss.social",
`<p><a href="https://fedirect.toolforge.org/?id=%40forgejo%40floss.social" rel="nofollow">@forgejo@floss.social</a></p>`)
test(
"!forgejo@programming.dev",
`<p><a href="https://fedirect.toolforge.org/?id=%21forgejo%40programming.dev" rel="nofollow">!forgejo@programming.dev</a></p>`)
test(
"@#&@forgejo.org",
`<p><a href="https://fedirect.toolforge.org/?id=%40%23%26%40forgejo.org" rel="nofollow">@#&amp;@forgejo.org</a></p>`)
}
func TestRender_emoji(t *testing.T) {

View file

@ -267,8 +267,13 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
// RenderString renders Markdown string to HTML with all specific handling stuff and return string
func RenderString(ctx *markup.RenderContext, content string) (template.HTML, error) {
return RenderReader(ctx, strings.NewReader(content))
}
// RenderReader renders Markdown io.Reader to HTML with all specific handling stuff and return string
func RenderReader(ctx *markup.RenderContext, input io.Reader) (template.HTML, error) {
var buf strings.Builder
if err := Render(ctx, strings.NewReader(content), &buf); err != nil {
if err := Render(ctx, input, &buf); err != nil {
return "", err
}
return template.HTML(buf.String()), nil

View file

@ -104,7 +104,7 @@ func TestRender_Images(t *testing.T) {
test(
"!["+title+"]("+url+")",
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`)
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`" loading="lazy"/></a></p>`)
test(
"[["+title+"|"+url+"]]",
@ -115,7 +115,7 @@ func TestRender_Images(t *testing.T) {
test(
"!["+title+"]("+url+")",
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`)
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`" loading="lazy"/></a></p>`)
test(
"[["+title+"|"+url+"]]",
@ -412,8 +412,8 @@ func TestRenderSiblingImages_Issue12925(t *testing.T) {
testcase := `![image1](/image1)
![image2](/image2)
`
expected := `<p><a href="/image1" target="_blank" rel="nofollow noopener"><img src="/image1" alt="image1"></a><br>
<a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2"></a></p>
expected := `<p><a href="/image1" target="_blank" rel="nofollow noopener"><img src="/image1" alt="image1" loading="lazy"></a><br>
<a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2" loading="lazy"></a></p>
`
res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase)
require.NoError(t, err)
@ -845,10 +845,10 @@ mail@domain.com
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/src/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a><br/>
<a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image"/></a><br/>
<a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -872,10 +872,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/image.jpg" alt="local image"/></a><br/>
<a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/wiki/raw/image.jpg" rel="nofollow"><img src="/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -901,10 +901,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/src/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/image.jpg" alt="local image"/></a><br/>
<a href="https://gitea.io/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/path/file" alt="local image"/></a><br/>
<a href="https://gitea.io/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="https://gitea.io/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="https://gitea.io/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://gitea.io/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="https://gitea.io/image.jpg" rel="nofollow"><img src="https://gitea.io/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -930,10 +930,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/image.jpg" alt="local image"/></a><br/>
<a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="https://gitea.io/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="https://gitea.io/wiki/raw/image.jpg" rel="nofollow"><img src="https://gitea.io/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -959,10 +959,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/src/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/image.jpg" alt="local image"/></a><br/>
<a href="/relative/path/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/path/file" alt="local image"/></a><br/>
<a href="/relative/path/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="/relative/path/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/relative/path/image.jpg" rel="nofollow"><img src="/relative/path/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -988,10 +988,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1018,10 +1018,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/src/branch/main/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/media/branch/main/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/image.jpg" alt="local image"/></a><br/>
<a href="/user/repo/media/branch/main/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/path/file" alt="local image"/></a><br/>
<a href="/user/repo/media/branch/main/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="/user/repo/media/branch/main/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/image.jpg" rel="nofollow"><img src="/user/repo/media/branch/main/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1048,10 +1048,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1078,10 +1078,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/src/sub/folder/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/image.jpg" alt="local image"/></a><br/>
<a href="/user/repo/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/path/file" alt="local image"/></a><br/>
<a href="/user/repo/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="/user/repo/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/user/repo/image.jpg" rel="nofollow"><img src="/user/repo/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1108,10 +1108,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1139,10 +1139,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/src/branch/main/sub/folder/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/media/branch/main/sub/folder/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" alt="local image"/></a><br/>
<a href="/user/repo/media/branch/main/sub/folder/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/path/file" alt="local image"/></a><br/>
<a href="/user/repo/media/branch/main/sub/folder/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="/user/repo/media/branch/main/sub/folder/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/sub/folder/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/sub/folder/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/sub/folder/image.jpg" rel="nofollow"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1170,10 +1170,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>

View file

@ -44,6 +44,7 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image)
for _, attr := range v.Attributes() {
image.SetAttribute(attr.Name, attr.Value)
}
image.SetAttributeString("loading", []byte("lazy"))
for child := v.FirstChild(); child != nil; {
next := child.NextSibling()
image.AppendChild(image, child)

View file

@ -108,6 +108,9 @@ func createDefaultPolicy() *bluemonday.Policy {
// Allow classes for emojis
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img")
// Allow attributes for images
policy.AllowAttrs("loading").Matching(regexp.MustCompile(`^lazy$`)).OnElements("img")
// Allow icons, emojis, chroma syntax and keyword markup on span
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
policy.AllowAttrs("data-alias").Matching(regexp.MustCompile(`^[a-zA-Z0-9-_+]+$`)).OnElements("span")

View file

@ -75,6 +75,10 @@ func Test_Sanitizer(t *testing.T) {
// Emoji
`<span class="emoji" aria-label="thumbs up" data-alias="+1">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up" data-alias="+1">THUMBS UP</span>`,
`<span class="emoji" aria-label="thumbs up" data-alias="(+!)">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`,
// Images lazy loading
`<img src="/image1" alt="image1" loading="lazy">`, `<img src="/image1" alt="image1" loading="lazy">`,
`<img src="/image1" alt="image1" loading="eager">`, `<img src="/image1" alt="image1">`,
}
for i := 0; i < len(testCases); i += 2 {

View file

@ -23,6 +23,11 @@ var wellKnownMimeTypesLower = map[string]string{
".wasm": "application/wasm",
".webp": "image/webp",
".xml": "text/xml; charset=utf-8",
".glb": "model/gltf-binary",
".gltf": "model/gltf+json",
".obj": "model/obj",
".stl": "model/stl",
".3mf": "model/3mf",
// well, there are some types missing from the builtin list
".txt": "text/plain; charset=utf-8",

View file

@ -18,13 +18,14 @@ import (
)
func TestMinioStorageIterator(t *testing.T) {
if os.Getenv("CI") == "" {
t.Skip("minioStorage not present outside of CI")
endpoint := os.Getenv("TEST_MINIO_ENDPOINT")
if endpoint == "" {
t.Skip("TEST_MINIO_ENDPOINT not set")
return
}
testStorageIterator(t, setting.MinioStorageType, &setting.Storage{
MinioConfig: setting.MinioStorageConfig{
Endpoint: "minio:9000",
Endpoint: endpoint,
AccessKeyID: "123456",
SecretAccessKey: "12345678",
Bucket: "gitea",
@ -34,13 +35,14 @@ func TestMinioStorageIterator(t *testing.T) {
}
func TestVirtualHostMinioStorage(t *testing.T) {
if os.Getenv("CI") == "" {
t.Skip("minioStorage not present outside of CI")
endpoint := os.Getenv("TEST_MINIO_ENDPOINT")
if endpoint == "" {
t.Skip("TEST_MINIO_ENDPOINT not set")
return
}
testStorageIterator(t, setting.MinioStorageType, &setting.Storage{
MinioConfig: setting.MinioStorageConfig{
Endpoint: "minio:9000",
Endpoint: endpoint,
AccessKeyID: "123456",
SecretAccessKey: "12345678",
Bucket: "gitea",
@ -85,13 +87,14 @@ func TestMinioStoragePath(t *testing.T) {
}
func TestS3StorageBadRequest(t *testing.T) {
if os.Getenv("CI") == "" {
t.Skip("S3Storage not present outside of CI")
endpoint := os.Getenv("TEST_MINIO_ENDPOINT")
if endpoint == "" {
t.Skip("TEST_MINIO_ENDPOINT not set")
return
}
cfg := &setting.Storage{
MinioConfig: setting.MinioStorageConfig{
Endpoint: "minio:9000",
Endpoint: endpoint,
AccessKeyID: "123456",
SecretAccessKey: "12345678",
Bucket: "bucket",

View file

@ -78,3 +78,9 @@ type ActionRun struct {
// the url of this action run
HTMLURL string `json:"html_url"`
}
// ListActionRunResponse return a list of ActionRun
type ListActionRunResponse struct {
Entries []*ActionRun `json:"workflow_runs"`
TotalCount int64 `json:"total_count"`
}

View file

@ -3,8 +3,8 @@
package structs
// GitBlobResponse represents a git blob
type GitBlobResponse struct {
// GitBlob represents a git blob
type GitBlob struct {
Content string `json:"content"`
Encoding string `json:"encoding"`
URL string `json:"url"`

View file

@ -53,8 +53,7 @@ type CreateHookOption struct {
BranchFilter string `json:"branch_filter" binding:"GlobPattern"`
AuthorizationHeader string `json:"authorization_header"`
// default: false
Active bool `json:"active"`
IsSystemWebhook bool `json:"is_system_webhook"`
Active bool `json:"active"`
}
// EditHookOption options when modify one hook

View file

@ -32,23 +32,3 @@ type ActionTaskResponse struct {
Entries []*ActionTask `json:"workflow_runs"`
TotalCount int64 `json:"total_count"`
}
// ActionRun represents an ActionRun
type RepoActionRun struct {
ID int64 `json:"id"`
Name string `json:"name"`
RunNumber int64 `json:"run_number"`
Event string `json:"event"`
Status string `json:"status"`
HeadBranch string `json:"head_branch"`
HeadSHA string `json:"head_sha"`
WorkflowID string `json:"workflow_id"`
URL string `json:"url"`
TriggeringActor *User `json:"triggering_actor"`
}
// ListActionRunResponse return a list of ActionRun
type ListRepoActionRunResponse struct {
Entries []*RepoActionRun `json:"workflow_runs"`
TotalCount int64 `json:"total_count"`
}

View file

@ -192,8 +192,8 @@ func TestRenderMarkdownToHtml(t *testing.T) {
<a href="https://example.com" rel="nofollow">remote link</a>
<a href="/src/file.bin" rel="nofollow">local link</a>
<a href="https://example.com" rel="nofollow">remote link</a>
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a>
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image" loading="lazy"/></a>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a>
<a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt=""/></a>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow"><code>88fc37a3c0...12fc37a3c0 (hash)</code></a>

View file

@ -24,6 +24,16 @@ const (
AvifMimeType = "image/avif"
// ApplicationOctetStream MIME type of binary files.
ApplicationOctetStream = "application/octet-stream"
// GLTFMimeType MIME type of GLTF files.
GLTFMimeType = "model/gltf+json"
// GLBMimeType MIME type of GLB files.
GLBMimeType = "model/gltf-binary"
// OBJMimeType MIME type of OBJ files.
OBJMimeType = "model/obj"
// STLMimeType MIME type of STL files.
STLMimeType = "model/stl"
// 3MFMimeType MIME type of 3MF files.
ThreeMFMimeType = "model/3mf"
)
var (
@ -67,6 +77,36 @@ func (ct SniffedType) IsAudio() bool {
return strings.Contains(ct.contentType, "audio/")
}
// Is3DModel detects if data is a 3D format
func (ct SniffedType) Is3DModel() bool {
return strings.Contains(ct.contentType, "model/")
}
// IsGLTFFile detects if data is an SVG image format
func (ct SniffedType) IsGLTF() bool {
return strings.Contains(ct.contentType, GLTFMimeType)
}
// IsGLBFile detects if data is an GLB image format
func (ct SniffedType) IsGLB() bool {
return strings.Contains(ct.contentType, GLBMimeType)
}
// IsOBJFile detects if data is an OBJ image format
func (ct SniffedType) IsOBJ() bool {
return strings.Contains(ct.contentType, OBJMimeType)
}
// IsSTLTextFile detects if data is an STL text format
func (ct SniffedType) IsSTL() bool {
return strings.Contains(ct.contentType, STLMimeType)
}
// Is3MFFile detects if data is an 3MF image format
func (ct SniffedType) Is3MF() bool {
return strings.Contains(ct.contentType, ThreeMFMimeType)
}
// IsRepresentableAsText returns true if file content can be represented as
// plain text or is empty.
func (ct SniffedType) IsRepresentableAsText() bool {
@ -75,7 +115,7 @@ func (ct SniffedType) IsRepresentableAsText() bool {
// IsBrowsableBinaryType returns whether a non-text type can be displayed in a browser
func (ct SniffedType) IsBrowsableBinaryType() bool {
return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio()
return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio() || ct.Is3DModel()
}
// GetMimeType returns the mime type
@ -135,6 +175,13 @@ func DetectContentType(data []byte) SniffedType {
ct = "audio/ogg" // for most cases, it is used as an audio container
}
}
// GLTF is unsupported by http.DetectContentType
// hexdump -n 4 -C glTF.glb
if bytes.HasPrefix(data, []byte("glTF")) {
ct = GLBMimeType
}
return SniffedType{ct}
}

View file

@ -117,6 +117,14 @@ func TestIsAudio(t *testing.T) {
assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."+"🌛"[0:2])).IsText()) // test ID3 tag with incomplete UTF8 char
}
func TestIsGLB(t *testing.T) {
glb, _ := hex.DecodeString("676c5446")
assert.True(t, DetectContentType(glb).IsGLB())
assert.True(t, DetectContentType(glb).Is3DModel())
assert.False(t, DetectContentType([]byte("plain text")).IsGLB())
assert.False(t, DetectContentType([]byte("plain text")).Is3DModel())
}
func TestDetectContentTypeFromReader(t *testing.T) {
mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl")
st, err := DetectContentTypeFromReader(bytes.NewReader(mp3))
@ -145,3 +153,15 @@ func TestDetectContentTypeAvif(t *testing.T) {
assert.True(t, st.IsImage())
}
func TestDetectContentTypeModelGLB(t *testing.T) {
glb, err := hex.DecodeString("676c5446")
require.NoError(t, err)
st, err := DetectContentTypeFromReader(bytes.NewReader(glb))
require.NoError(t, err)
// print st for debugging
assert.Equal(t, "model/gltf-binary", st.GetMimeType())
assert.True(t, st.IsGLB())
}

View file

@ -4,7 +4,6 @@
package util
import (
"bytes"
"errors"
"io"
)
@ -20,42 +19,6 @@ func ReadAtMost(r io.Reader, buf []byte) (n int, err error) {
return n, err
}
// ReadWithLimit reads at most "limit" bytes from r into buf.
// If EOF or ErrUnexpectedEOF occurs while reading, err will be nil.
func ReadWithLimit(r io.Reader, n int) (buf []byte, err error) {
return readWithLimit(r, 1024, n)
}
func readWithLimit(r io.Reader, batch, limit int) ([]byte, error) {
if limit <= batch {
buf := make([]byte, limit)
n, err := ReadAtMost(r, buf)
if err != nil {
return nil, err
}
return buf[:n], nil
}
res := bytes.NewBuffer(make([]byte, 0, batch))
bufFix := make([]byte, batch)
eof := false
for res.Len() < limit && !eof {
bufTmp := bufFix
if res.Len()+batch > limit {
bufTmp = bufFix[:limit-res.Len()]
}
n, err := io.ReadFull(r, bufTmp)
if err == io.EOF || err == io.ErrUnexpectedEOF {
eof = true
} else if err != nil {
return nil, err
}
if _, err = res.Write(bufTmp[:n]); err != nil {
return nil, err
}
}
return res.Bytes(), nil
}
// ErrNotEmpty is an error reported when there is a non-empty reader
var ErrNotEmpty = errors.New("not-empty")

View file

@ -1,67 +0,0 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package util
import (
"bytes"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type readerWithError struct {
buf *bytes.Buffer
}
func (r *readerWithError) Read(p []byte) (n int, err error) {
if r.buf.Len() < 2 {
return 0, errors.New("test error")
}
return r.buf.Read(p)
}
func TestReadWithLimit(t *testing.T) {
bs := []byte("0123456789abcdef")
// normal test
buf, err := readWithLimit(bytes.NewBuffer(bs), 5, 2)
require.NoError(t, err)
assert.Equal(t, []byte("01"), buf)
buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 5)
require.NoError(t, err)
assert.Equal(t, []byte("01234"), buf)
buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 6)
require.NoError(t, err)
assert.Equal(t, []byte("012345"), buf)
buf, err = readWithLimit(bytes.NewBuffer(bs), 5, len(bs))
require.NoError(t, err)
assert.Equal(t, []byte("0123456789abcdef"), buf)
buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 100)
require.NoError(t, err)
assert.Equal(t, []byte("0123456789abcdef"), buf)
// test with error
buf, err = readWithLimit(&readerWithError{bytes.NewBuffer(bs)}, 5, 10)
require.NoError(t, err)
assert.Equal(t, []byte("0123456789"), buf)
buf, err = readWithLimit(&readerWithError{bytes.NewBuffer(bs)}, 5, 100)
require.ErrorContains(t, err, "test error")
assert.Empty(t, buf)
// test public function
buf, err = ReadWithLimit(bytes.NewBuffer(bs), 2)
require.NoError(t, err)
assert.Equal(t, []byte("01"), buf)
buf, err = ReadWithLimit(bytes.NewBuffer(bs), 9999999)
require.NoError(t, err)
assert.Equal(t, []byte("0123456789abcdef"), buf)
}

View file

@ -54,3 +54,12 @@ func SplitTrimSpace(input, sep string) []string {
return stringList
}
// TruncateRunes returns a truncated string with given rune limit,
// it returns input string if its rune length doesn't exceed the limit.
func TruncateRunes(str string, limit int) string {
if utf8.RuneCountInString(str) < limit {
return str
}
return string([]rune(str)[:limit])
}

View file

@ -44,3 +44,18 @@ func TestSplitString(t *testing.T) {
}
test(tc, SplitStringAtByteN)
}
func TestTruncateRunes(t *testing.T) {
assert.Empty(t, TruncateRunes("", 0))
assert.Empty(t, TruncateRunes("", 1))
assert.Empty(t, TruncateRunes("ab", 0))
assert.Equal(t, "a", TruncateRunes("ab", 1))
assert.Equal(t, "ab", TruncateRunes("ab", 2))
assert.Equal(t, "ab", TruncateRunes("ab", 3))
assert.Empty(t, TruncateRunes("测试", 0))
assert.Equal(t, "测", TruncateRunes("测试", 1))
assert.Equal(t, "测试", TruncateRunes("测试", 2))
assert.Equal(t, "测试", TruncateRunes("测试", 3))
}

View file

@ -699,7 +699,7 @@ issues.filter_milestone_all = كل الأهداف
issues.unlock.notice_2 = - يمكنك دوما إقفال هذه المسألة من جديد في المستقبل.
issues.num_participants_few = %d متحاور
release.title = عنوان الإصدار
issues.closed_at = `أغلق هذه المسألة <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at = `أغلق هذه المسألة %s`
issues.lock.title = إقفال التحاور في هذه المسألة.
issues.new.no_label = بلا تصنيف
issues.filter_sort.mostforks = الأعلى اشتقاقا
@ -759,7 +759,7 @@ branch.renamed = غُيّر اسم الفرع %s إلى %s.
delete_preexisting = احذف الملفات الموجودة سابقا
branch.included_desc = هذا الفرع جزء من الفرع المبدئي
trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع
issues.reopened_at = `أعاد فتح هذه المسألة <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at = `أعاد فتح هذه المسألة %s`
issues.action_milestone = هدف
issues.new.assignees = المكلَّفون
release.tag_name_protected = اسم الوسم محمي.
@ -1166,7 +1166,7 @@ pulls.status_checking = في انتظار بعض الفحوص
pulls.status_checks_failure = بعض الفحوص فشلت
pulls.status_checks_success = جميع الفحوص ناجحة
pulls.status_checks_warning = بعض الفحوص تعطي تحذيرات
pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %s`
pulls.cmd_instruction_hint = `أظهر شرح استخدام سطر الأوامر.`
pulls.cmd_instruction_checkout_title = اسحب
pulls.cmd_instruction_checkout_desc = من مستودع مشروعك، اسحب (check out) فرعا جديدا واختبر التغييرات.
@ -1257,8 +1257,8 @@ pulls.status_checks_details = تفاصيل
pulls.status_checks_hide_all = أخفِ كل الفحوص
pulls.status_checks_show_all = أظهر كل الفحوص
pulls.close = أغلق طلب الدمج
pulls.closed_at = `أغلق طلب الدمج <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at = `أعاد فتح طلب الدمج <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at = `أغلق طلب الدمج %s`
pulls.reopened_at = `أعاد فتح طلب الدمج %s`
milestones.title = العنوان
milestones.desc = الوصف
milestones.edit = عدّل الهدف
@ -1302,11 +1302,11 @@ issues.closed_by_fake = من %[2]s أُغلقت %[1]s
issues.num_comments_1 = %d تعليق
issues.num_comments = %d تعليقا
issues.commented_at = `علّق <a href="#%s">%s</a>`
issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from = `<a href="%[3]s">أشار إلى هذه المسألة %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from = `<a href="%[3]s">أشار إلى هذا الطلب %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from = `<a href="%[3]s">أشار إلى طلب دمج %[4]s سيغلق هذه المسألة</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from = `<a href="%[3]s">أشار إلى طلب دمج %[4]s سيعيد فتح هذه المسألة</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع %s`
issues.ref_issue_from = `<a href="%[2]s">أشار إلى هذه المسألة %[3]s</a> %[1]s`
issues.ref_pull_from = `<a href="%[2]s">أشار إلى هذا الطلب %[3]s</a> %[1]s`
issues.ref_closing_from = `<a href="%[2]s">أشار إلى طلب دمج %[3]s سيغلق هذه المسألة</a> %[1]s`
issues.ref_reopening_from = `<a href="%[2]s">أشار إلى طلب دمج %[3]s سيعيد فتح هذه المسألة</a> %[1]s`
issues.ref_closed_from = `<a href="%[3]s">أغلق هذه المسألة %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from = `<a href="%[3]s">أعاد فتح هذه المسألة %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reference_issue.body = المحتوى

View file

@ -749,7 +749,7 @@ settings.admin_settings = Администраторски настройки
issues.role.owner = Притежател
settings.transfer.title = Прехвърляне на притежанието
issues.author = Автор
issues.closed_at = `затвори тази задача <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at = `затвори тази задача %s`
settings.collaborator_deletion_desc = Премахването на сътрудник ще отнеме достъпа му до това хранилище. Продължаване?
commits.message = Съобщение
issues.due_date_not_set = Няма зададен краен срок.
@ -773,9 +773,9 @@ issues.filter_type.all_issues = Всички задачи
issues.filter_poster_no_select = Всички автори
issues.opened_by = отворена %[1]s от <a href="%[2]s">%[3]s</a>
issues.action_open = Отваряне
pulls.closed_at = `затвори тази заявка за сливане <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at = `отвори наново тази заявка за сливане <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at = `отвори наново тази задача <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at = `затвори тази заявка за сливане %s`
pulls.reopened_at = `отвори наново тази заявка за сливане %s`
issues.reopened_at = `отвори наново тази задача %s`
projects.column.edit = Редактиране на колоната
issues.close = Затваряне на задачата
issues.ref_reopened_from = `<a href="%[3]s">отвори наново тази задача %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
@ -1205,7 +1205,7 @@ issues.dependency.cancel = Отказ
issues.dependency.add_error_dep_exists = Зависимостта вече съществува.
issues.dependency.add_error_dep_not_exist = Зависимостта не съществува.
issues.remove_ref_at = `премахна препратката <b>%s</b> %s`
issues.ref_pull_from = `<a href="%[3]s">спомена тази заявка за сливане %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from = `<a href="%[2]s">спомена тази заявка за сливане %[3]s</a> %[1]s`
issues.dependency.pr_no_dependencies = Няма зададени зависимости.
issues.dependency.remove_info = Премахване на тази зависимост
issues.dependency.removed_dependency = `премахна зависимостта %s`
@ -1230,11 +1230,11 @@ issues.dependency.title = Зависимости
issues.dependency.issue_no_dependencies = Няма зададени зависимости.
issues.dependency.pr_close_blocked = Трябва да затворите всички задачи, блокиращи тази заявка за сливане, преди да можете да я слеете.
issues.dependency.pr_close_blocks = Тази заявка за сливане блокира затварянето на следните задачи
issues.ref_issue_from = `<a href="%[3]s">спомена тази задача %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at = `спомена тази задача в подаване <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from = `<a href="%[2]s">спомена тази задача %[3]s</a> %[1]s`
issues.commit_ref_at = `спомена тази задача в подаване %s`
issues.add_ref_at = `добави препратка <b>%s</b> %s`
pulls.merged_info_text = Клонът %s вече може да бъде изтрит.
pulls.commit_ref_at = `спомена тази заявка за сливане в подаване <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.commit_ref_at = `спомена тази заявка за сливане в подаване %s`
issues.change_ref_at = `промени препратката от <b><strike>%s</strike></b> на <b>%s</b> %s`
diff.review.reject = Поискване на промени
diff.bin_not_shown = Двоичният файл не е показан.
@ -1299,9 +1299,9 @@ branch.create_new_branch = Създаване на клон от клон:
pulls.status_checks_show_all = Показване на всички проверки
size_format = %[1]s: %[2]s; %[3]s: %[4]s
pulls.filter_changes_by_commit = Филтриране по подаване
issues.ref_closing_from = `<a href="%[3]s">спомена тази задача в заявка за сливане %[4]s, която ще я затвори</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from = `<a href="%[2]s">спомена тази задача в заявка за сливане %[3]s, която ще я затвори</a>, %[1]s`
issues.ref_from = `от %[1]s`
issues.ref_reopening_from = `<a href="%[3]s">спомена тази задача в заявка за сливане %[4]s, която ще я отвори наново </a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from = `<a href="%[2]s">спомена тази задача в заявка за сливане %[3]s, която ще я отвори наново </a>, %[1]s`
issues.draft_title = Чернова
pulls.reopen_to_merge = Моля, отворете наново тази заявка за сливане, за да извършите сливане.
pulls.cant_reopen_deleted_branch = Тази заявка за сливане не може да бъде отворена наново, защото клонът е изтрит.

View file

@ -1633,13 +1633,13 @@ issues.opened_by_fake=otevřeno %[1]s uživatelem %[2]s
issues.closed_by_fake=od %[2]s byl uzavřen %[1]s
issues.previous=Předchozí
issues.next=Další
issues.open_title=Otevřeno
issues.closed_title=Uzavřeno
issues.open_title=Otevřené
issues.closed_title=Uzavřené
issues.draft_title=Koncept
issues.num_comments_1=%d komentář
issues.num_comments=%d komentářů
issues.commented_at=`okomentoval/a <a href="#%s">%s</a>`
issues.delete_comment_confirm=Jste si jist, že chcete smazat tento komentář?
issues.delete_comment_confirm=Opravdu chcete smazat tento komentář?
issues.context.copy_link=Kopírovat odkaz
issues.context.quote_reply=Citovat odpověď
issues.context.reference_issue=Odkázat v novém problému
@ -1653,13 +1653,13 @@ issues.close_comment_issue=Zavřít s komentářem
issues.reopen_issue=Znovu otevřít
issues.reopen_comment_issue=Znovu otevřít s komentářem
issues.create_comment=Komentovat
issues.closed_at=`uzavřel/a tento problém <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`znovu otevřel/a tento problém <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`odkázal/a na tento problém z revize <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from=`<a href="%[3]s">odkázal/a na tento problém %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from=`<a href="%[3]s">odkázal/a na tuto žádost o sloučení %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s">odkazoval/a na tento problém ze žádosti o sloučení %[4]s, která jej uzavře</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from=`<a href="%[3]s">odkazoval/a na tento problém ze žádosti o sloučení %[4]s, která jej znovu otevře</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at=`uzavřel/a tento problém %s`
issues.reopened_at=`znovu otevřel/a tento problém %s`
issues.commit_ref_at=`odkázal/a na tento problém z revize %s`
issues.ref_issue_from=`<a href="%[2]s">odkázal/a na tento problém %[3]s</a> %[1]s`
issues.ref_pull_from=`<a href="%[2]s">odkázal/a na tuto žádost o sloučení %[3]s</a> %[1]s`
issues.ref_closing_from=`<a href="%[2]s">odkázal/a na tento problém ze žádosti o sloučení %[3]s, která jej uzavře</a>, %[1]s`
issues.ref_reopening_from=`<a href="%[2]s">odkázal/a na tento problém ze žádosti o sloučení %[3]s, která jej znovu otevře</a>, %[1]s`
issues.ref_closed_from=`<a href="%[3]s">uzavřel/a tento problém %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">znovu otevřel/a tento problém %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`z %[1]s`
@ -1966,8 +1966,8 @@ pulls.update_branch_success=Aktualizace větve byla úspěšná
pulls.update_not_allowed=Nemáte oprávnění aktualizovat větev
pulls.outdated_with_base_branch=Tato větev je zastaralá oproti základní větvi
pulls.close=Zavřít žádost o sloučení
pulls.closed_at=`uzavřel/a tuto žádost o sloučení <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`znovu otevřel/a tuto žádost o sloučení <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at=`uzavřel/a tuto žádost o sloučení %s`
pulls.reopened_at=`znovu otevřel/a tuto žádost o sloučení %s`
pulls.cmd_instruction_hint=Zobrazit instrukce příkazové řádky
pulls.cmd_instruction_checkout_desc=Z vašeho repositáře projektu se podívejte na novou větev a vyzkoušejte změny.
pulls.cmd_instruction_merge_title=Sloučit
@ -2758,7 +2758,7 @@ settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning = Tuto ak
settings.new_owner_blocked_doer = Nový majitel vás zablokoval.
settings.mirror_settings.pushed_repository = Odeslaný repozitář
settings.add_collaborator_blocked_our = Nepodařilo se přidat spolupracovníka, jelikož byl zablokován majitelem repozitáře.
pulls.commit_ref_at = `se odkázal/a na tuto žádost o sloučení z revize <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.commit_ref_at = `odkázal/a na tuto žádost o sloučení z revize %s`
settings.wiki_rename_branch_main = Normalizovat název větve wiki
settings.wiki_rename_branch_main_desc = Přejmenovat větev interně používanou pro wiki na „%s“. Tato změna je trvalá a nelze ji vrátit.
pulls.fast_forward_only_merge_pull_request = Pouze zrychlené
@ -2922,6 +2922,7 @@ settings.event_action_success = Úspěch
settings.event_action_success_desc = Běh akce byl úspěšný.
settings.event_header_action = Události běhu akce
settings.event_action_recover_desc = Běh akce byl úspěšný, předchozí běh akce ve stejném workflow selhal.
issues.filter_type.all_pull_requests = Všechny žádosti o sloučení
[graphs]
component_loading_info = Tohle může chvíli trvat…

View file

@ -1520,15 +1520,15 @@ issues.add_labels = tilføjede %s etiketterne %s
issues.add_remove_labels = tilføjede %s og fjernede %s etiketter %s
issues.add_milestone_at = `føjede dette til <b>%s</b> milepælen %s`
issues.add_project_at = `føjede dette til <b>%s</b>- projektet %s`
issues.ref_reopening_from = `<a href="%[3]s">henviste til dette problem fra en pull-anmodning %[4]s, der vil genåbne den</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from = `<a href="%[2]s">henviste til dette problem fra en pull-anmodning %[3]s, der vil genåbne den</a>, %[1]s`
issues.ref_closed_from = `<a href="%[3]s">lukkede dette problem %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2 ]s</a>`
issues.ref_reopened_from = `<a href="%[3]s">genåbnede dette problem %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2 ]s</a>`
issues.ref_from = `fra %[1]s`
issues.author = Forfatter
issues.commit_ref_at = `henviste til dette problem fra en commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from = `<a href="%[3]s">henviste til dette problem %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2 ]s</a>`
issues.ref_pull_from = `<a href="%[3]s">henviste til denne pull-anmodning %[4]s</a> <a id="%[1]s" href="#%[1]s">%[ 2]s</a>`
issues.ref_closing_from = `<a href="%[3]s">henviste til dette problem fra en pull-anmodning %[4]s, der vil lukke det</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at = `henviste til dette problem fra en commit %s`
issues.ref_issue_from = `<a href="%[2]s">henviste til dette problem %[3]s</a> <a id="%[1]s" href="#%[1]s">%[2 ]s</a>`
issues.ref_pull_from = `<a href="%[2]s">henviste til denne pull-anmodning %[3]s</a> %[1]s`
issues.ref_closing_from = `<a href="%[2]s">henviste til dette problem fra en pull-anmodning %[3]s, der vil lukke det</a>, %[1]s`
issues.author.tooltip.issue = Denne bruger er forfatteren til dette problem.
issues.author.tooltip.pr = Denne bruger er forfatteren af denne pull-anmodning.
issues.role.owner = Ejer
@ -1564,8 +1564,8 @@ issues.reaction.alt_add = Tilføj %[1]s reaktion til kommentar.
issues.context.menu = Kommentar menu
issues.reopen_comment_issue = Genåbner med kommentar
issues.create_comment = Kommentar
issues.closed_at = `lukkede dette problem <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at = `genåbnede dette problem <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at = `lukkede dette problem %s`
issues.reopened_at = `genåbnede dette problem %s`
issues.remove_label = fjernede %s etiketten %s
issues.remove_labels = fjernede %s etiketterne %s
issues.change_project_at = `modificerede projektet fra <b>%s</b> til <b>%s</b> %s`
@ -1911,10 +1911,10 @@ pulls.editable_explanation = Denne pull-anmodning tillader redigeringer fra vedl
pulls.auto_merge_button_when_succeed = (Når kontroller lykkes)
pulls.status_checks_requested = Påkrævet
pulls.close = Luk pull anmodning
pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit %s`
pulls.cmd_instruction_hint = Se instruktionerne på kommandolinjen
pulls.reopened_at = `genåbnede denne pull-anmodning <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at = `lukkede denne pull-anmodning <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at = `genåbnede denne pull-anmodning %s`
pulls.closed_at = `lukkede denne pull-anmodning %s`
pulls.cmd_instruction_checkout_desc = Fra dit projektdepot, tjek en ny gren og test ændringerne.
pulls.editable = Redigerbar
pulls.made_using_agit = AGit
@ -2735,6 +2735,7 @@ settings.event_action_success = Success
settings.event_action_recover_desc = Handlingskørsel lykkedes efter at den sidste handlingskørsel i samme arbejdsgang mislykkedes.
settings.event_action_failure_desc = Handlingskørsel sluttede som en fejl.
settings.event_action_recover = Gendan
issues.filter_type.all_pull_requests = Alle pull-anmodninger
[notification]
watching = Overvåger

View file

@ -1577,7 +1577,7 @@ issues.remove_ref_at=`hat die Referenz <b>%s</b> %s entfernt`
issues.add_ref_at=`hat die Referenz <b>%s</b> %s hinzugefügt`
issues.delete_branch_at=`löschte den Branch <b>%s</b> %s`
issues.filter_label=Label
issues.filter_label_exclude=`<code>Alt</code> + <code>Klick/Enter</code> verwenden, um Labels auszuschließen`
issues.filter_label_exclude=`Verwende <kbd>Alt</kbd> + <kbd>Klick/Enter</kbd>, um Labels auszuschließen`
issues.filter_label_no_select=Alle Labels
issues.filter_label_select_no_label=Kein Label
issues.filter_milestone=Meilenstein
@ -1651,13 +1651,13 @@ issues.close_comment_issue=Mit Kommentar schließen
issues.reopen_issue=Wieder öffnen
issues.reopen_comment_issue=Mit Kommentar wieder öffnen
issues.create_comment=Kommentieren
issues.closed_at=`hat diesen Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen`
issues.reopened_at=`hat dieses Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet`
issues.commit_ref_at=`hat dieses Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> aus einem Commit referenziert`
issues.ref_issue_from=`<a href="%[3]s">hat</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> <a href="%[3]s">auf dieses Issue verwiesen %[4]s</a>`
issues.ref_pull_from=`<a href="%[3]s">hat</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> <a href="%[3]s">auf diesen Pull-Request verwiesen %[4]s</a>`
issues.ref_closing_from=`<a href="%[3]s">hat</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> <a href="%[3]s">in einem Pull-Request %[4]s auf dieses Issue verwiesen, welcher es schließen wird</a>`
issues.ref_reopening_from=`<a href="%[3]s">hat</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> <a href="%[3]s"> in einem Pull-Request %[4]s auf dieses Issue verwiesen, welcher es erneut öffnen wird</a>`
issues.closed_at=`hat dieses Issue %s geschlossen`
issues.reopened_at=`hat dieses Issue %s wieder geöffnet`
issues.commit_ref_at=`hat dieses Issue %s aus einem Commit referenziert`
issues.ref_issue_from=`<a href="%[2]s">hat</a> %[1]s <a href="%[2]s">auf dieses Issue verwiesen %[3]s</a>`
issues.ref_pull_from=`<a href="%[2]s">referenzierte diesen Pull-Request %[3]s</a> %[1]s`
issues.ref_closing_from=`<a href="%[2]s">referenzierte dieses Issue aus einem Pull-Request %[3]s der es schließen wird</a>, %[1]s`
issues.ref_reopening_from=`<a href="%[2]s">referenzierte dieses Issue aus einem Pull-Request %[3]s der es wieder öffnen wird</a>, %[1]s`
issues.ref_closed_from=`<a href="%[3]s">hat dieses Issue %[4]s geschlossen</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">hat dieses Issue %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet`
issues.ref_from=`von %[1]s`
@ -1962,8 +1962,8 @@ pulls.update_branch_success=Branch-Aktualisierung erfolgreich
pulls.update_not_allowed=Du hast keine Berechtigung, den Branch zu updaten
pulls.outdated_with_base_branch=Dieser Branch enthält nicht die neusten Commits des Basis-Branches
pulls.close=Pull-Request schließen
pulls.closed_at=`hat diesen Pull-Request <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen`
pulls.reopened_at=`hat diesen Pull-Request <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet`
pulls.closed_at=`hat diesen Pull-Request %s geschlossen`
pulls.reopened_at=`hat diesen Pull-Request %s wieder geöffnet`
pulls.clear_merge_message=Merge-Nachricht löschen
pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie „Co-Authored-By …“ erhalten.
@ -2767,7 +2767,7 @@ settings.wiki_globally_editable = Allen erlauben, das Wiki zu bearbeiten
settings.protect_branch_name_pattern_desc = Geschützte Branch-Namens-Patterns. Siehe <a href="%s">die Dokumentation</a> für Pattern-Syntax. Beispiele: main, release/**
settings.ignore_stale_approvals = Abgestandene Genehmigungen ignorieren
settings.ignore_stale_approvals_desc = Genehmigungen, welche für ältere Commits gemacht wurden (abgestandene Reviews), nicht in die Gesamtzahl der Genehmigung des PRs mitzählen. Irrelevant, falls abgestandene Reviews bereits verworfen werden.
pulls.commit_ref_at = `hat sich auf diesen Pull-Request von einem Commit <a id="%[1]s" href="#%[1]s">%[2]s</a> bezogen`
pulls.commit_ref_at = `referenzierte diesen Pull-Request aus einem Commit %s`
pulls.fast_forward_only_merge_pull_request = Nur Fast-forward
pulls.cmd_instruction_checkout_desc = Checke einen neuen Branch aus deinem Projekt-Repository aus und teste die Änderungen.
pulls.cmd_instruction_merge_title = Zusammenführen
@ -2924,6 +2924,7 @@ settings.event_action_success = Erfolg
settings.event_header_action = Action-Run-Ereignisse
settings.event_action_recover_desc = Action-Run war erfolgreich, nachdem der letzte Action-Run im selben Arbeitsablauf fehlgeschlagen ist.
settings.event_action_recover = Wiederherstellen
issues.filter_type.all_pull_requests = Alle Pull-Requests
[graphs]
component_loading_failed = Konnte %s nicht laden
@ -3060,8 +3061,8 @@ teams.invite.by=Von %s eingeladen
teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten.
follow_blocked_user = Du kannst dieser Organisation nicht folgen, weil diese Organisation dich blockiert hat.
open_dashboard = Übersicht öffnen
settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Organisationsname ist nach einer Abkühldauer von einem Tag wieder für alle verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen.
settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Organisationsname ist nach einer Abkühldauer von %[1]d Tagen wieder für alle verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen.
settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Organisationsname ist nach einer Schutzzeit von einem Tag wieder für alle verfügbar. Du kannst den alten Namen während dieser Schutzzeit erneut beanspruchen.
settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Organisationsname ist nach einer Schutzzeit von %[1]d Tagen wieder für alle verfügbar. Du kannst den alten Namen während dieser Schutzzeit erneut beanspruchen.
[admin]
dashboard=Übersicht

View file

@ -1626,13 +1626,13 @@ issues.close_comment_issue=Αποστολή σχολίου και κλείσιμ
issues.reopen_issue=Ανοίξτε ξανά
issues.reopen_comment_issue=Αποστολή σχολίου και επανάνοιγμα ζητήματος
issues.create_comment=Προσθήκη Σχολίου
issues.closed_at=`αυτό το ζήτημα έκλεισε <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`ξανά άνοιξε αυτό το ζήτημα <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`αναφορά σε αυτό το ζήτημα από την παραπομπή <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from=`<a href="%[3]s">αναφέρθηκε σε αυτό το ζήτημα %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from=`<a href="%[3]s">αναφέρθηκε σε αυτό το pull request %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s">ανέφερε αυτό το ζήτημα σε ένα pull request %[4]s που στοχεύει να κλείσει το ζήτημα</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from=`<a href="%[3]s">αναφέρθηκε σε αυτό το ζήτημα σε ένα pull request %[4]s που θα ξαναανοίξει αυτό το ζήτημα</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at=`αυτό το ζήτημα έκλεισε %s`
issues.reopened_at=`ξανά άνοιξε αυτό το ζήτημα %s`
issues.commit_ref_at=`αναφορά σε αυτό το ζήτημα από την παραπομπή %s`
issues.ref_issue_from=`<a href="%[2]s">αναφέρθηκε σε αυτό το ζήτημα %[3]s</a> %[1]s`
issues.ref_pull_from=`<a href="%[2]s">αναφέρθηκε σε αυτό το pull request %[3]s</a> %[1]s`
issues.ref_closing_from=`<a href="%[2]s">ανέφερε αυτό το ζήτημα σε ένα pull request %[3]s που στοχεύει να κλείσει το ζήτημα</a> %[1]s`
issues.ref_reopening_from=`<a href="%[2]s">αναφέρθηκε σε αυτό το ζήτημα σε ένα pull request %[3]s που θα ξαναανοίξει αυτό το ζήτημα</a> %[1]s`
issues.ref_closed_from=`<a href="%[3]s">έκλεισε αυτό το ζήτημα %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">άνοιξε ξανά αυτό το ζήτημα %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`από %[1]s`
@ -1939,8 +1939,8 @@ pulls.update_branch_success=Η ενημέρωση του κλάδου ήταν
pulls.update_not_allowed=Δεν επιτρέπεται να ενημερώσετε τον κλάδο
pulls.outdated_with_base_branch=Αυτός ο κλάδος δεν είναι ενημερωμένος με τον βασικό κλάδο
pulls.close=Κλείσιμο pull request
pulls.closed_at=`έκλεισε αυτό το pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`άνοιξε ξανά αυτό το pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at=`έκλεισε αυτό το pull request %s`
pulls.reopened_at=`άνοιξε ξανά αυτό το pull request %s`
pulls.cmd_instruction_hint=Προβολή οδηγιών γραμμής εντολών
pulls.cmd_instruction_checkout_title=Έλεγχος
pulls.cmd_instruction_checkout_desc=Από το repository του έργου σας, ελέγξτε έναν νέο κλάδο και δοκιμάστε τις αλλαγές.
@ -2720,7 +2720,7 @@ settings.new_owner_blocked_doer = Ο νέος κάτοχος του αποθετ
settings.enter_repo_name = Γράψτε το όνομα του κατόχου και του αποθετηρίου ακριβώς όπως το βλέπετε:
settings.confirmation_string = Κείμενο επιβεβαίωσης
settings.units.overview = Επισκόπηση
pulls.commit_ref_at = `ανέφερε το pull request στο commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.commit_ref_at = `ανέφερε το pull request στο commit %s`
contributors.contribution_type.filter_label = Είδος συνεισφοράς:
settings.wiki_rename_branch_main_notices_1 = Αυτή η ενέργεια <strong>ΔΕΝ</strong> αναιρείται.
activity.navbar.contributors = Συνεισφέροντες

View file

@ -768,8 +768,8 @@ update_profile_success = Your profile has been updated.
change_username = Your username has been changed.
change_username_prompt = Note: Changing your username also changes your account URL.
change_username_redirect_prompt = The old username will redirect until someone claims it.
change_username_redirect_prompt.with_cooldown.one = The old username will be available to everyone after a cooldown period of %[1]d day, you can still reclaim the old username during the cooldown period.
change_username_redirect_prompt.with_cooldown.few = The old username will be available to everyone after a cooldown period of %[1]d days, you can still reclaim the old username during the cooldown period.
change_username_redirect_prompt.with_cooldown.one = The old username will be available to everyone after a cooldown period of %[1]d day. You can still reclaim the old username during the cooldown period.
change_username_redirect_prompt.with_cooldown.few = The old username will be available to everyone after a cooldown period of %[1]d days. You can still reclaim the old username during the cooldown period.
continue = Continue
cancel = Cancel
language = Language
@ -1610,7 +1610,7 @@ issues.remove_ref_at = `removed reference <b>%s</b> %s`
issues.add_ref_at = `added reference <b>%s</b> %s`
issues.delete_branch_at = `deleted branch <b>%s</b> %s`
issues.filter_label = Label
issues.filter_label_exclude = `Use <code>alt</code> + <code>click/enter</code> to exclude labels`
issues.filter_label_exclude = Use <kbd>Alt</kbd> + <kbd>Click</kbd> to exclude labels
issues.filter_label_no_select = All labels
issues.filter_label_select_no_label = No label
issues.filter_milestone = Milestone
@ -1628,6 +1628,7 @@ issues.filter_poster = Author
issues.filter_poster_no_select = All authors
issues.filter_type = Type
issues.filter_type.all_issues = All issues
issues.filter_type.all_pull_requests = All pull requests
issues.filter_type.assigned_to_you = Assigned to you
issues.filter_type.created_by_you = Created by you
issues.filter_type.mentioning_you = Mentioning you
@ -1693,15 +1694,13 @@ issues.close_comment_issue = Close with comment
issues.reopen_issue = Reopen
issues.reopen_comment_issue = Reopen with comment
issues.create_comment = Comment
issues.closed_at = `closed this issue <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at = `reopened this issue <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at = `referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from = `<a href="%[3]s">referenced this issue %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from = `<a href="%[3]s">referenced this pull request %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from = `<a href="%[3]s">referenced this issue from a pull request %[4]s that will close it</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from = `<a href="%[3]s">referenced this issue from a pull request %[4]s that will reopen it</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closed_from = `<a href="%[3]s">closed this issue %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from = `<a href="%[3]s">reopened this issue %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at = `closed this issue %s`
issues.reopened_at = `reopened this issue %s`
issues.commit_ref_at = `referenced this issue from a commit %s`
issues.ref_issue_from = `<a href="%[2]s">referenced this issue %[3]s</a> %[1]s`
issues.ref_pull_from = `<a href="%[2]s">referenced this pull request %[3]s</a> %[1]s`
issues.ref_closing_from = `<a href="%[2]s">referenced this issue from a pull request %[3]s that will close it</a>, %[1]s`
issues.ref_reopening_from = `<a href="%[2]s">referenced this issue from a pull request %[3]s that will reopen it</a>, %[1]s`
issues.ref_from = `from %[1]s`
issues.author = Author
issues.author.tooltip.issue = This user is the author of this issue.
@ -2013,9 +2012,9 @@ pulls.update_branch_success = Branch update was successful
pulls.update_not_allowed = You are not allowed to update branch
pulls.outdated_with_base_branch = This branch is out-of-date with the base branch
pulls.close = Close pull request
pulls.closed_at = `closed this pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at = `reopened this pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.commit_ref_at = `referenced this pull request from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at = `closed this pull request %s`
pulls.reopened_at = `reopened this pull request %s`
pulls.commit_ref_at = `referenced this pull request from a commit %s`
pulls.cmd_instruction_hint = View command line instructions
pulls.cmd_instruction_checkout_title = Checkout
pulls.cmd_instruction_checkout_desc = From your project repository, check out a new branch and test the changes.
@ -2932,8 +2931,8 @@ settings.update_settings = Update settings
settings.update_setting_success = Organization settings have been updated.
settings.change_orgname_prompt = Note: Changing the organization name will also change your organization's URL and free the old name.
settings.change_orgname_redirect_prompt = The old name will redirect until it is claimed.
settings.change_orgname_redirect_prompt.with_cooldown.one = The old organization name will be available to everyone after a cooldown period of %[1]d day, you can still reclaim the old name during the cooldown period.
settings.change_orgname_redirect_prompt.with_cooldown.few = The old organization name will be available to everyone after a cooldown period of %[1]d days, you can still reclaim the old name during the cooldown period.
settings.change_orgname_redirect_prompt.with_cooldown.one = The old organization name will be available to everyone after a cooldown period of %[1]d day. You can still reclaim the old name during the cooldown period.
settings.change_orgname_redirect_prompt.with_cooldown.few = The old organization name will be available to everyone after a cooldown period of %[1]d days. You can still reclaim the old name during the cooldown period.
settings.update_avatar_success = The organization's avatar has been updated.
settings.delete = Delete organization
settings.delete_account = Delete this organization

View file

@ -1648,13 +1648,13 @@ issues.close_comment_issue=Cerrar con comentario
issues.reopen_issue=Reabrir
issues.reopen_comment_issue=Reabrir con comentario
issues.create_comment=Comentar
issues.closed_at=`cerró esta incidencia <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`reabrió esta incidencia <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`referenció esta incidencia en un commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from=`<a href="%[3]s">referenció esta incidencia %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from=`<a href="%[3]s">referenció este pull request %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s">hizo referencia a esta incidencia desde un pull request %[4]s que lo cerrará</a> , <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from=`<a href="%[3]s">hizo referencia a esta incidencia desde un pull request %[4]s que lo reabrirá</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at=`cerró esta incidencia %s`
issues.reopened_at=`reabrió esta incidencia %s`
issues.commit_ref_at=`referenció esta incidencia en un commit %s`
issues.ref_issue_from=`<a href="%[2]s">referenció esta incidencia %[3]s</a> %[1]s`
issues.ref_pull_from=`<a href="%[2]s">referenció este pull request %[3]s</a> %[1]s`
issues.ref_closing_from=`<a href="%[2]s">hizo referencia a esta incidencia desde un pull request %[3]s que lo cerrará</a> , %[1]s`
issues.ref_reopening_from=`<a href="%[2]s">hizo referencia a esta incidencia desde un pull request %[3]s que lo reabrirá</a>, %[1]s`
issues.ref_closed_from=`<a href="%[3]s">cerró esta incidencia %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">reabrió esta incidencia %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`de %[1]s`
@ -1959,8 +1959,8 @@ pulls.update_branch_success=La actualización de la rama ha finalizado correctam
pulls.update_not_allowed=No tiene permisos para actualizar esta rama
pulls.outdated_with_base_branch=Esta rama está desactualizada con la rama base
pulls.close=Cerrar pull request
pulls.closed_at=`cerró este pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`reabrió este pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at=`cerró este pull request %s`
pulls.reopened_at=`reabrió este pull request %s`
pulls.clear_merge_message=Borrar mensaje de fusión
pulls.clear_merge_message_hint=Limpiar el mensaje de fusión solo eliminará el contenido del mensaje de commit y mantendrá frases generadas como "Co-Autorizado por …".
@ -2789,7 +2789,7 @@ pulls.status_checks_hide_all = Ocultar todas las verificaciones
settings.federation_not_enabled = La federación no está habilitada en tu instancia.
wiki.search = Buscar en wiki
pulls.status_checks_show_all = Mostrar todas las verificaciones
pulls.commit_ref_at = `hizo referencia a este pull request desde un commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.commit_ref_at = `hizo referencia a este pull request desde un commit %s`
pulls.cmd_instruction_merge_title = Fusionar
contributors.contribution_type.deletions = Eliminaciones
contributors.contribution_type.filter_label = Tipo de contribución:

View file

@ -1250,13 +1250,13 @@ issues.close_comment_issue=ثبت دیدگاه و بستن
issues.reopen_issue=بازگشایی
issues.reopen_comment_issue=ثبت دیدگاه و بازگشایی
issues.create_comment=دیدگاه
issues.closed_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> این موضوع را بست`
issues.reopened_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> این موضوع را دوباره باز کرد`
issues.commit_ref_at=`ارجاع این مسئله به کامیت <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from=`<a href="%[3]s"> ارجاعات این مسائله %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from=`<a href="%[3]s"> ارجاعات این تقاضای ادغام %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s"> ارجاعات این تقاضای واکشی %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from=`<a href="%[3]s"> تقاضای واکشی ارجاع شده %[4] که مسائله بازگشایی خواهد کرد</a> <a id="%[1]s" href="#%[1]s">%[2] </a>`
issues.closed_at=`%s این موضوع را بست`
issues.reopened_at=`%s این موضوع را دوباره باز کرد`
issues.commit_ref_at=`ارجاع این مسئله به کامیت %s`
issues.ref_issue_from=`<a href="%[2]s"> ارجاعات این مسائله %[3]</a> %[1]s`
issues.ref_pull_from=`<a href="%[2]s"> ارجاعات این تقاضای ادغام %[4]</a> %[1]s`
issues.ref_closing_from=`<a href="%[2]s"> ارجاعات این تقاضای واکشی %[4]</a> %[1]s`
issues.ref_reopening_from=`<a href="%[2]s"> تقاضای واکشی ارجاع شده %[3]sکه مسائله بازگشایی خواهد کرد</a> <a id="%[1]s" href="#%[1]s">%[2] </a>`
issues.ref_closed_from=`<a href="%[3]s"> بسته شده این مسائله %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s"> بازگشایی این مسائله %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`از %[1]`
@ -1493,8 +1493,8 @@ pulls.update_branch_rebase=بروزآوری شاخه با بازسازی مجد
pulls.update_branch_success=شاخه به موفقیت بروز شد
pulls.update_not_allowed=شما اجازه بروزرسانی شاخه را ندارید
pulls.outdated_with_base_branch=این شاخه با شاخه پایه منسوخ شده است
pulls.closed_at=`این درخواست pull بسته شده <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`این درخواست pull را بازگشایی کرد <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at=`این درخواست pull بسته شده %s`
pulls.reopened_at=`این درخواست pull را بازگشایی کرد %s`

View file

@ -1293,9 +1293,9 @@ issues.close_comment_issue=Kommentoi ja sulje
issues.reopen_issue=Avaa uudelleen
issues.reopen_comment_issue=Kommentoi ja avaa uudelleen
issues.create_comment=Kommentoi
issues.closed_at=`sulki tämän ongelman <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`uudelleenavasi tämän ongelman <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`viittasi tähän ongelmaan kommitissa <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at=`sulki tämän ongelman %s`
issues.reopened_at=`uudelleenavasi tämän ongelman %s`
issues.commit_ref_at=`viittasi tähän ongelmaan kommitissa %s`
issues.author=Tekijä
issues.role.owner=Omistaja
issues.role.member=Jäsen
@ -2185,7 +2185,7 @@ settings.confirmation_string = Vahvistusteksti
settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi tietovaraston <strong>%s</strong> mukaan lukien koodin, ongelmat, kommentit, wikidatan ja avustaja-asetukset.
issues.filter_assginee_no_select = Kaikki käsittelijät
issues.new.assign_to_me = Osoita itselle
pulls.closed_at = `sulki tämän vetopyynnön <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at = `sulki tämän vetopyynnön %s`
tree_path_not_found_branch = Polkua %[1]s ei ole olemassa haarassa %[2]s
transfer.no_permission_to_reject = Sinulla ei ole oikeutta hylätä tätä siirtoa.
generate_repo = Luo tietovarasto
@ -2199,8 +2199,8 @@ issues.new.no_reviewers = Ei katselmoijia
issues.add_label = lisäsi nimilapun %s %s
issues.due_date_added = lisäsi eräpäivän %s %s
issues.review.add_review_request = pyysi katselmointia käyttäjältä %[1]s %[2]s
issues.ref_pull_from = `<a href="%[3]s">viittasi tähän vetopyyntöön %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from = `<a href="%[2]s">viittasi tähän vetopyyntöön %[3]s</a> %[1]s`
pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista %s`
issues.review.comment = katselmoi %s
issues.add_labels = lisäsi nimilaput %s %s
issues.review.add_review_requests = pyysi katselmointeja käyttäjiltä %[1]s %[2]s
@ -2381,7 +2381,7 @@ wiki.page_name_desc = Kirjoita tämän wikisivun nimi. Joitain erikoisnimiä ova
pulls.blocked_by_changed_protected_files_1 = Tämä vetopyyntö sisältää suojatun tiedoston ja on siksi estetty:
pulls.status_checks_warning = Jotkin tarkistukset raportoivat varoituksia
pulls.status_checks_error = Jotkin tarkistukset raportoivat virheitä
pulls.reopened_at = `avasi uudelleen tämän vetopyynnön <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at = `avasi uudelleen tämän vetopyynnön %s`
pulls.auto_merge_when_succeed = Yhdistä automaatisesti kun kaikki tarkistukset onnistuvat
signing.wont_sign.error = Tapahtui virhe tarkistaessa voiko kommitin allekirjoittaa.
signing.wont_sign.twofa = Sinulla tulee olla kaksivaiheinen todennus käytössä, jotta kommitit voi allekirjoittaa.

View file

@ -365,7 +365,7 @@ table_modal.label.columns = Mga Column
link_modal.header = Magdagdag ng link
link_modal.url = Url
link_modal.description = Deskripsyon
link_modal.paste_reminder = Pahiwatig: Kapag may URL sa clipboard, maari mong direktang i-paste sa editor para gumawa ng link.
link_modal.paste_reminder = Pahiwatig: Kapag may URL sa clipboard, maaari mong direktang i-paste sa editor para gumawa ng link.
[filter]
string.asc = A - Z
@ -432,7 +432,7 @@ openid_connect_desc = Ang piniling OpenID URI ay hindi alam. Iugnay iyan sa bago
invalid_code = Ang iyong confirmation code ay hindi wasto o nag-expire na.
oauth_signin_title = Mag-sign in para pahintulutan ang naka-link na account
invalid_code_forgot_password = Ang iyong confirmation code ay hindi wasto o nag-expire na. Mag-click <a href="%s">dito</a> para magsimula ng bagong session.
confirmation_mail_sent_prompt = Ang isang bagong email na pang-kumpirma ay ipinadala sa <b>%s</b>. Para kumpletuhin ang proseso ng pagrehistro, pakisuri ang iyong inbox at sundan ang ibinigay na link sa loob ng %s. Kung mali ang email, maari kang mag-log in, at humingi ng isa pang email pang-kumpirma na ipapadala sa ibang address.
confirmation_mail_sent_prompt = Ang isang bagong email na pang-kumpirma ay ipinadala sa <b>%s</b>. Para kumpletuhin ang proseso ng pagrehistro, pakisuri ang iyong inbox at sundan ang ibinigay na link sa loob ng %s. Kung mali ang email, maaari kang mag-log in, at humingi ng isa pang email pang-kumpirma na ipapadala sa ibang address.
invalid_password = Ang iyong password ay hindi tugma sa password na ginamit para gawin ang account.
twofa_scratch_used = Ginamit mo na ang scratch code. Na-redirect ka sa two-factor settings page para tanggalin ang device enrollment o mag-generate ng bagong scratch code.
manual_activation_only = Makipag-ugnayan sa tagapangangasiwa ng site para kumpletuhin ang pagrehistro.
@ -484,7 +484,7 @@ admin.new_user.text = Mangyaring <a href="%s">mag-click dito</a> para ipamahala
register_notify = Maligayang Pagdating sa %s
register_notify.title = %[1]s, maligayang pagdating sa %[2]s
register_notify.text_1 = ito ang iyong registration confirmation email para sa %s!
register_notify.text_2 = Maari kang mag-sign in sa iyong account gamit ng iyong username: %s
register_notify.text_2 = Maaari kang mag-sign in sa iyong account gamit ng iyong username: %s
reset_password = I-recover ang iyong account
reset_password.title = %s, nagkaroon kami ng hiling para i-recover ang iyong account
reset_password.text = Kung ikaw ito, paki-click ang sumusunod na link para i-recover ang iyong account sa loob ng <b>%s</b>:
@ -535,7 +535,7 @@ totp_disabled.text_1 = Ngayon lang na-disable ang Time-based one-time password (
totp_disabled.no_2fa = Wala nang mga ibang paraan ng 2FA ang naka-configure, nangangahulugan na hindi na kailangang mag-log in sa iyong account gamit ang 2FA.
removed_security_key.subject = May tinanggal na security key
removed_security_key.text_1 = Tinanggal ngayon lang ang security key na "%[1]s" sa iyong account.
account_security_caution.text_1 = Kung ikaw ito, maari mong ligtas na huwag pansinin ang mail na ito.
account_security_caution.text_1 = Kung ikaw ito, maaari mong ligtas na huwag pansinin ang mail na ito.
account_security_caution.text_2 = Kung hindi ito ikaw, nakompromiso ang iyong account. Mangyaring makipag-ugnayan sa mga tagapangasiwa ng site na ito.
totp_enrolled.subject = Nag-activate ka ng TOTP bilang paraan ng 2FA
totp_enrolled.text_1.has_webauthn = Na-enable mo lang ang TOTP para sa iyong account. Nangangahulugan ito na para sa lahat ng mga hinaharap na pag-login sa iyong account, kailangan mong gumamit ng TOTP bilang paraan ng 2FA o gamitin ang iyong mga security key.
@ -644,7 +644,7 @@ AccessToken = Token ng pag-access
Biography = Byograpya
Location = Lokasyon
visit_rate_limit = Natugunan ang limitasyon sa rate ng malayuang pagbisita.
username_claiming_cooldown = Hindi ma-claim ang username na ito, dahil hindi pa tapos ang panahon ng cooldown. Maari itong i-claim sa %[1]s.
username_claiming_cooldown = Hindi ma-claim ang username na ito, dahil hindi pa tapos ang panahon ng cooldown. Maaari itong i-claim sa %[1]s.
email_domain_is_not_allowed = Sumasalungat ang domain ng email address ng user <b>%s</b> sa EMAIL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Siguraduhing natakda mo ang email address nang tama.
[user]
@ -685,7 +685,7 @@ followers.title.few = Mga tagasunod
following.title.one = Sinusundan
followers.title.one = Tagasunod
public_activity.visibility_hint.self_public = Nakikita ng lahat ang iyong aktibidad, maliban sa mga interaksyon sa pribadong espasyo. <a href="%s">I-configure</a>.
public_activity.visibility_hint.admin_public = Nakikita ng lahat ang aktibidad na ito, ngunit bilang tagapangasiwa maari mo ring makita ang mga interaksyon sa mga pribadong espasyo.
public_activity.visibility_hint.admin_public = Nakikita ng lahat ang aktibidad na ito, ngunit bilang tagapangasiwa maaari mo ring makita ang mga interaksyon sa mga pribadong espasyo.
public_activity.visibility_hint.self_private = Nakikita mo lang at mga tagapangasiwa ng instansya ang iyong aktibidad. <a href="%s">I-configure</a>.
public_activity.visibility_hint.admin_private = Nakikita mo ang aktibidad na ito dahil isa kang tagapangasiwa, ngunit gusto ng user na panatilihin itong pribado.
public_activity.visibility_hint.self_private_profile = Ikaw lang at ang mga tagapangasiwa ng instansya ang makakakita ng iyong aktibidad dahil pribado ang iyong profile. <a href="%s">I-configure</a>.
@ -842,7 +842,7 @@ gpg_key_verify = I-verify
gpg_invalid_token_signature = Ang ibinigay na GPG key, signature, at token ay hindi tumutugma o luma.
gpg_token_required = Kailangan mong magbigay ng signature para sa token sa ibaba
gpg_token = Token
gpg_token_help = Maari kang mag-generate ng signature gamit ng:
gpg_token_help = Maaari kang mag-generate ng signature gamit ng:
gpg_token_signature = Naka-armor na GPG signature
key_signature_gpg_placeholder = Nagsisimula sa "-----BEGIN PGP SIGNATURE-----"
verify_gpg_key_success = Na-verify na ang GPG key na "%s".
@ -851,7 +851,7 @@ ssh_key_verify = I-verify
ssh_invalid_token_signature = Ang ibinigay na SSH key, signature, o token ay hindi tumutugma o luma.
ssh_token_required = Kailangan mong magbigay ng signature para sa token sa ibaba
ssh_token = Token
ssh_token_help = Maari kang mag-generate ng signature gamit ng:
ssh_token_help = Maaari kang mag-generate ng signature gamit ng:
ssh_token_signature = Naka-armor na SSH signature
key_signature_ssh_placeholder = Nagsisimula sa "-----BEGIN SSH SIGNATURE-----"
verify_ssh_key_success = Na-verify na ang SSH key na "%s".
@ -912,10 +912,10 @@ create_oauth2_application_success = Matagumpay kang gumawa ang bagong OAuth2 app
oauth2_confidential_client = Kumpidensyal na kliyente. Piliin para sa mga app na pinapatilihing kumpidensyal ang sikreto, tulad ng mga web app. Huwag piliin para sa mga web app kasama ang mga desktop at mobile app.
twofa_desc = Para protektahin ang iyong account laban sa pagnanakaw ng password, pwede mo gamitin ang iyong smartphone o ibang device para sa pagtanggap ng time-based one-time password ("TOTP").
twofa_scratch_token_regenerated = Ang iyong isang-beses na paggamit na recovery key ngayon ay %s. Ilagay ito sa ligtas na lugar, dahil hindi na ito ipapakita muli.
regenerate_scratch_token_desc = Kapag nawala mo ang iyong recovery key o ginamit mo na oara mag-sign in, maari mong i-reset dito.
regenerate_scratch_token_desc = Kapag nawala mo ang iyong recovery key o ginamit mo na oara mag-sign in, maaari mong i-reset dito.
twofa_disable_desc = Ang pag-disable ng authentikasyong two-factor ay gagawing hindi gaanong ligtas ang iyong account. Magpatuloy?
twofa_enrolled = Matagumpay na na-enroll ang iyong account. Ilagay ang iyong isang-beses na paggamit na recovery key (%s) sa isang ligtas na lugar, dahil hindi na ito ipapakita muli.
webauthn_desc = Ang mga security key ay isang hardware device na naglalaman ng mga cryptographic key. Maari silang gamitin para sa authentikasyong two-factor. Ang mga security key ay dapat suportahan ang <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a> na standard.
webauthn_desc = Ang mga security key ay isang hardware device na naglalaman ng mga cryptographic key. Maaari silang gamitin para sa authentikasyong two-factor. Ang mga security key ay dapat suportahan ang <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a> na standard.
remove_oauth2_application = Tanggalin ang OAuth2 Application
remove_oauth2_application_desc = Ang pagtanggal ng OAuth2 application ay babawiin ang access sa lahat ng mga naka-sign na access token. Magpatuloy?
remove_oauth2_application_success = Binura na ang application.
@ -931,13 +931,13 @@ oauth2_regenerate_secret = I-regenerate ang sikreto
oauth2_regenerate_secret_hint = Nawala mo ang iyong sikreto?
oauth2_client_secret_hint = Ang sikreto ay hindi ipapakita muli pagkatapos umalis ka o i-refresh ang page na ito. Mangyaring siguraduhin na na-save mo iyan.
oauth2_application_edit = I-edit
twofa_recovery_tip = Kapag mawala mo ang iyong device, maari kang gumamit ng isang isang-beses na paggamit na recovery key para makakuha muli ng access sa iyong account.
twofa_recovery_tip = Kapag mawala mo ang iyong device, maaari kang gumamit ng isang isang-beses na paggamit na recovery key para makakuha muli ng access sa iyong account.
twofa_is_enrolled = Ang iyong account ay kasalukuyang <strong>naka-enroll</strong> sa autentikasyong two-factor.
twofa_not_enrolled = Kasalukuyang hindi naka-enroll ang iyong account sa authentikasyong two-factor.
twofa_disable = I-disable ang authentikasyong two-factor
twofa_scratch_token_regenerate = I-regenerate ang isang-beses na paggamit na recovery key
twofa_enroll = Mag-enroll sa authentikasyong two-factor
twofa_disable_note = Maari mong i-disable ang authentikasyong two-factor kapag kinakailangan.
twofa_disable_note = Maaari mong i-disable ang authentikasyong two-factor kapag kinakailangan.
twofa_disabled = Na-disable na ang authentikasyong two-factor.
scan_this_image = I-scan ang image na ito gamit ng iyong aplikasyong pang-authentikasyon:
or_enter_secret = O ilagay ang sikreto: %s
@ -1005,8 +1005,8 @@ language.description = Mase-save ang wika sa iyong account at gagamitin bilang d
language.localization_project = Tulungan kaming isalin ang Forgejo sa iyong wika! <a href="%s">Matuto pa</a>.
pronouns_custom_label = Mga pasadyang pronoun
user_block_yourself = Hindi mo maaaring harangan ang sarili mo.
change_username_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown.
change_username_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown.
change_username_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown.
change_username_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown.
keep_pronouns_private = Ipakita lang ang mga panghalip sa mga naka-authenticate na user
keep_pronouns_private.description = Itatago nito ang iyong mga panghalip mula sa mga bisita na hindi naka-log in.
quota.applies_to_user = Nag-aapply ang mga sumusunod na panuntunan ng quota sa iyong account
@ -1071,7 +1071,7 @@ readme_helper_desc = Ito ang lugar kung saan makakasulat ka ng kumpletong deskri
trust_model_helper_collaborator_committer = Katulong+Committer: I-trust ang mga signature batay sa mga katulong na tumutugma sa committer
mirror_interval = Interval ng mirror (ang mga wastong unit ng oras ay "h", "m", "s"). 0 para i-disable ang periodic sync. (Pinakamababang interval: %s)
transfer.reject_desc = Kanselahin ang pag-transfer mula sa "%s"
mirror_lfs_endpoint_desc = Ang sync ay susubukang gamitin ang clone url upang <a target="_blank" rel="noopener noreferrer" href="%s">matukoy ang LFS server</a>. Maari ka rin tumukoy ng isang custom na endpoint kapag ang LFS data ng repositoryo ay nilalagay sa ibang lugar.
mirror_lfs_endpoint_desc = Ang sync ay susubukang gamitin ang clone url upang <a target="_blank" rel="noopener noreferrer" href="%s">matukoy ang LFS server</a>. Maaari ka rin tumukoy ng isang custom na endpoint kapag ang LFS data ng repositoryo ay nilalagay sa ibang lugar.
adopt_search = Ilagay ang username para maghanap ng mga unadopted na repositoryo… (iwanang walang laman para hanapin lahat)
object_format = Format ng object
readme_helper = Pumili ng README file template
@ -1164,8 +1164,8 @@ tree_path_not_found_commit = Hindi umiiral ang path na %[1]s sa commit %[2]s
tree_path_not_found_branch = Hindi umiiral ang daanang %[1]s sa branch %[2]s
migrate_items_pullrequests = Mga hiling sa paghila
archive.pull.nocomment = Naka-archive ang repositoryong ito. Hindi ka makakakomento sa mga pull request.
archive.title = Naka-archive ang repositoryong ito. Maari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado ito, tulad ng pagtulak at paggawa ng mga isyu, pull request o mga komento.
archive.title_date = Naka-archive ang repositoryo na ito noong %s. Maari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado nito, tulad ng pagtulak o paggawa ng mga bagong isyu, mga pull request, o komento.
archive.title = Naka-archive ang repositoryong ito. Maaari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado ito, tulad ng pagtulak at paggawa ng mga isyu, pull request o mga komento.
archive.title_date = Naka-archive ang repositoryo na ito noong %s. Maaari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado nito, tulad ng pagtulak o paggawa ng mga bagong isyu, mga pull request, o komento.
pulls = Mga hiling sa paghila
activity.merged_prs_count_n = Mga naisamang hiling sa paghila
wiki.last_updated = Huling binago %s
@ -1183,7 +1183,7 @@ issues.action_open = Buksan
issues.closed_title = Sarado
issues.reopen_issue = Buksang muli
pulls.merged = Naisama na
pulls.merged_info_text = Maari nang burahin ang branch %s.
pulls.merged_info_text = Maaari nang burahin ang branch %s.
milestones.update_ago = Binago %s
activity.closed_issue_label = Sarado
activity.merged_prs_label = Naisama
@ -1205,7 +1205,7 @@ migrate.clone_address_desc = Ang HTTP(S) o Git "clone" URL ng umiiral na reposit
need_auth = Awtorisasyon
migrate.github_token_desc = Maaari kang maglagay ng isa o higit pang mga token na hinihiwalay ng kuwit dito upang gawing mas-mabilis ang pagmigrate dahil sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaaring magdulot ng pag-block ng account.
template.invalid = Kailangang pumili ng kahit isang template na repositoryo
migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang <a target="_blank" rel="noopener noreferrer" href="%s">matukoy ang LFS server</a>. Maari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar.
migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang <a target="_blank" rel="noopener noreferrer" href="%s">matukoy ang LFS server</a>. Maaari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar.
blame.ignore_revs.failed = Nabigong hindi pansinin ang mga rebisyon sa <a href="%s">.git-blame-ignore-revs</a>.
tree_path_not_found_tag = Hindi umiiral ang path na %[1]s sa tag %[2]s
form.reach_limit_of_creation_n = Naabot na ng may-ari ang limitasyon na %d mga repositoryo.
@ -1471,10 +1471,10 @@ activity.new_issue_label = Nabuksan
activity.merged_prs_count_1 = Naisamang hiling sa paghila
activity.opened_prs_count_1 = Inimungkahing hiling sa paghila
activity.opened_prs_label = Inimungkahi
pulls.reopened_at = `nabuksang muli ang hiling sa paghatak na <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at = `binuksan muli ang hiling sa paghila %s`
issues.opened_by_fake = binuksan ang %[1]s ni/ng %[2]s
pulls.reopen_failed.base_branch = Hindi mabuksang muli ang hiling sa paghatak na ito dahil hindi na umiiral ang base branch.
issues.reopened_at = `binuksang muli ang isyung ito <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at = `binuksang muli ang isyung ito %s`
pulls.reopen_failed.head_branch = Hindi mabubuksan muli ang hiling sa paghila, dahil hindi na umiiral ang head branch.
settings.event_pull_request_desc = Binuksan, sinara, muling binuksan, o binago ang hiling sa paghatak.
activity.opened_prs_count_n = Mga inimungkahing hiling sa paghila
@ -1500,7 +1500,7 @@ issues.content_history.created = ginawa
editor.patching = Pina-patch:
editor.fail_to_apply_patch = Hindi malapat ang patch na "%s"
settings.danger_zone = Mapanganib na lugar
issues.closed_at = `isinara ang isyung <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at = `isinara ang isyung ito %s`
settings.collaboration.admin = Tagapangasiwa
settings.admin_settings = Mga setting ng tagapangasiwa
issues.start_tracking_history = `sinimulan ang trabaho %s`
@ -1627,7 +1627,7 @@ projects.column.edit_title = Pangalan
projects.column.new_title = Pangalan
projects.card_type.desc = Mga preview ng card
commits.desc = I-browse ang history ng pagbabago ng source code.
commits.search.tooltip = Maari kang mag-prefix ng mga keyword gamit ang "author:", "committer:", "after:", o "before:", hal. "revert author:Nijika before:2022-10-09".
commits.search.tooltip = Maaari kang mag-prefix ng mga keyword gamit ang "author:", "committer:", "after:", o "before:", hal. "revert author:Nijika before:2022-10-09".
issues.force_push_codes = `puwersahang itinulak ang %[1]s mula <a class="%[7]s" href="%[3]s"><code>%[2]s</code></a> sa <a class="%[7]s" href="%[5]s"><code>%[4]s</code></a> %[6]s`
issues.push_commit_1 = idinagdag ang %d commit %s
issues.push_commits_n = idinagdag ang %d mga commit %s
@ -1707,7 +1707,7 @@ issues.action_milestone = Milestone
issues.action_milestone_no_select = Walang milestone
issues.delete_branch_at = `binura ang branch na <b>%s</b> %s`
issues.filter_label = Label
issues.filter_label_exclude = `Gamitin ang <code>alt</code> + <code>click/enter</code> para hindi isama ang mga label`
issues.filter_label_exclude = `Gamitin ang <kbd>Alt</kbd> + <kbd>Click</kbd> para hindi isama ang mga label`
issues.filter_label_no_select = Lahat ng mga label
issues.filter_milestone_closed = Mga nakasarang milestone
issues.filter_assignee = Mangangasiwa
@ -1771,7 +1771,7 @@ issues.lock = I-lock ang usapan
issues.unlock = I-unlock ang usapan
issues.unlock_comment = na-unlock ang usapang ito %s
issues.unlock.notice_1 = - Makakakomento muli ang lahat ng mga tao sa isyung ito.
issues.unlock.notice_2 = - Maari mong i-lock muli ang isyung ito sa hinaharap.
issues.unlock.notice_2 = - Maaari mong i-lock muli ang isyung ito sa hinaharap.
issues.comment_on_locked = Hindi ka makakakomento sa naka-lock na isyu.
issues.closed_by_fake = ni/ng %[2]s ay isinara %[1]s
issues.comment_manually_pull_merged_at = manwal na isinama ang commit %[1]s sa %[2]s %[3]s
@ -1787,10 +1787,10 @@ issues.label_archive_tooltip = Ang mga naka-archive na label ay hindi isasama bi
issues.is_stale = May mga pagbabago sa PR na ito mula sa pagsuri na ito
issues.role.first_time_contributor = Unang-beses na contributor
issues.lock.notice_1 = - Hindi makakadagdag ng mga bagong komento ang mga ibang user sa isyu na ito.
issues.lock.notice_3 = - Maari mong i-unlock muli ang isyung ito sa hinaharap.
issues.lock.notice_3 = - Maaari mong i-unlock muli ang isyung ito sa hinaharap.
issues.label_deletion_desc = Ang pagbura ng label ay tatanggalin ito sa lahat ng mga isyu. Magpatuloy?
issues.commit_ref_at = `isinangguni ang isyu na ito mula sa commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from = `<a href="%[3]s">isinangguni ang isyu na ito sa %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at = `isinangguni ang isyu na ito mula sa commit %s`
issues.ref_issue_from = `<a href="%[2]s">isinangguni ang isyu na ito sa %[3]s</a> %[1]s`
issues.num_participants_one = %d kasali
issues.attachment.download = `I-click para i-download ang "%s" `
issues.num_participants_few = %d mga kasali
@ -1815,10 +1815,10 @@ issues.sign_in_require_desc = <a href="%s">Mag-sign in</a> upang sumali sa usapa
issues.num_comments = %d mga komento
issues.role.contributor_helper = Nakaraang nag-commit ang user na ito sa repositoryo na ito.
issues.comment_pull_merged_at = isinama ang commit %[1]s sa %[2]s %[3]s
pulls.commit_ref_at = `isinangguni ang hiling sa paghila mula sa isang commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.commit_ref_at = `isinangguni ang hiling sa paghila mula sa isang commit %s`
wiki.last_commit_info = Binago ni %s ang pahinang ito %s
issues.content_history.edited = binago
issues.ref_pull_from = `<a href="%[3]s">isinangguni ang hiling sa paghila na ito %[4]s </a><a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from = `<a href="%[2]s">isinangguni ang hiling sa paghila na ito %[3]s</a> %[1]s`
pulls.merged_title_desc_few = isinali ang %[1]d mga commit mula sa <code>%[2]s</code> patungong <code>%[3]s</code> %[4]s
settings.org_not_allowed_to_be_collaborator = Hindi maaaring idagdag ang mga organisasyon bilang tagatulong.
settings.add_collaborator_success = Naidagdag ang tagatulong.
@ -1828,7 +1828,7 @@ pulls.create = Gumawa ng hiling sa paghila
issues.dependency.pr_close_blocked = Kailangan mong isara ang lahat ng mga isyu na humaharang sa hiling sa paghila na ito bago mo ito isama.
pulls.delete.title = Burahin ang hiling sa paghila na ito?
issues.dependency.pr_closing_blockedby = Hinarang ng mga sumusunod na isyu mula sa pagsara ng hiling sa paghila na ito
pulls.closed_at = `isinara ang hiling sa paghila na <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at = `isinara ang hiling sa paghila na ito %s`
pulls.close = Isara ang hiling sa paghila
pulls.cmd_instruction_hint = Tingnan ang mga panuto para sa command line
project = Mga proyekto
@ -1836,8 +1836,8 @@ issues.content_history.deleted = binura
pulls.no_results = Walang mga nahanap na resulta.
pulls.closed = Sarado ang hiling sa paghila
pulls.is_closed = Naisara na ang hiling sa paghila.
issues.ref_closing_from = `<a href="%[3]s">nagsangguni ang isyu mula sa hiling sa paghila %[4]s na magsasara sa isyu</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from = `<a href="%[3]s">nagsangguni ang isyu na ito mula sa hiling sa paghila %[4]s na muling bubukas</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from = `<a href="%[2]s">nagsangguni ang isyu mula sa hiling sa paghila %[3]s na magsasara sa isyu</a>, %[1]s`
issues.ref_reopening_from = `<a href="%[2]s">nagsangguni ang isyu na ito mula sa hiling sa paghila %[3]s na muling bubukas nito</a>, %[1]s`
issues.ref_closed_from = `<a href="%[3]s">isinara ang isyung ito %[4]s</a><a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.review.wait = hiniling sa pagsuri %s
issues.review.reject = hinihiling ang mga pagbago %s
@ -2015,14 +2015,14 @@ wiki.cancel = Kanselahin
settings.collaboration.undefined = Hindi Natukoy
settings.federation_settings = Mga Setting ng Federation
settings = Mga Setting
settings.desc = Ang mga setting ang lugar kung saan maari mong ipamahala ang mga setting para sa repositoryo
settings.desc = Ang mga setting ang lugar kung saan maaari mong ipamahala ang mga setting para sa repositoryo
pulls.collapse_files = I-collapse ang lahat ng mga file
pulls.add_prefix = Magdagdag ng <strong>%s</strong> na prefix
pulls.still_in_progress = Ginagawa pa?
activity.title.prs_1 = %d hiling sa paghila
activity.active_issues_count_n = <strong>%d</strong> mga aktibong isyu
pulls.required_status_check_missing = Nawawala ang ilang mga kinakailangang pagsusuri.
pulls.required_status_check_administrator = Bilang tagapangasiwa, maari mo pa ring isama ang hiling sa paghila na ito.
pulls.required_status_check_administrator = Bilang tagapangasiwa, maaari mo pa ring isama ang hiling sa paghila na ito.
pulls.blocked_by_approvals = Wala pang sapat na pag-apruba ang hiling sa paghila na ito. %d ng %d na pag-apruba ang ibinigay.
settings.options = Repositoryo
wiki.back_to_wiki = Bumalik sa pahina ng wiki
@ -2110,7 +2110,7 @@ settings.actions_desc = I-enable ang mga kasamang CI/CD pipeline gamit ang Forge
settings.admin_indexer_commit_sha = Huling na-index na commit
settings.admin_indexer_unindexed = Hindi naka-index
settings.transfer_notices_3 = - Kung pribado ang repositoryo at ilipat sa isang indibidwal na user, ang aksyon na ito ay sinisigurado na ang user ay may pahintulot na basahin (at palitan ang mga pahintulot kung kailangan).
settings.convert_desc = Maari mong i-convert ang repositoryo na ito sa regular na repositoryo. Hindi ito mababawi.
settings.convert_desc = Maaari mong i-convert ang repositoryo na ito sa regular na repositoryo. Hindi ito mababawi.
settings.transfer.button = Ilipat ang pagmamay-ari
settings.signing_settings = Mga setting sa pagpapatunay ng pag-sign
settings.admin_enable_close_issues_via_commit_in_any_branch = Isara ang isyu sa pamamagitan ng commit na ginawa sa hindi default na branch
@ -2137,7 +2137,7 @@ settings.deploy_key_deletion = Tanggalin ang deploy key
settings.protect_enable_push = I-enable ang pagtulak
settings.discord_icon_url.exceeds_max_length = Kailangang bababa o equal sa 2048 characters ang URL ng icon
settings.protected_branch.save_rule = I-save ang rule
settings.mirror_settings.docs.can_still_use = Bagama't na hindi ka makakabago ng mga umiiral na mirror o gumawa ng bago, maari mo pa rin gamitin ang iyong umiiral na mirror.
settings.mirror_settings.docs.can_still_use = Bagama't na hindi ka makakabago ng mga umiiral na mirror o gumawa ng bago, maaari mo pa rin gamitin ang iyong umiiral na mirror.
settings.slack_color = Kulay
settings.discord_icon_url = URL ng icon
settings.convert_fork_confirm = I-convert ang repositoryo
@ -2254,7 +2254,7 @@ settings.pulls.allow_rebase_update = I-enable ang pag-update ng hiling sa paghil
settings.admin_enable_health_check = I-enable ang pagsusuri ng kalusugan ng repositoryo (git fsck)
settings.new_owner_has_same_repo = Ang bagong may-ari ay may repositoryo na may katulad na pangalan. Mangyaring pumili ng ibang pangalan.
settings.convert = I-convert sa regular na repositoryo
settings.convert_fork_desc = Maari mong i-convert ang fork na ito bilang regular na repositoryo. Hindi ito mababawi.
settings.convert_fork_desc = Maaari mong i-convert ang fork na ito bilang regular na repositoryo. Hindi ito mababawi.
settings.convert_fork_notices_1 = Ang operasyon na ito ay ico-convert ang fork bilang regular na repositoryo at hindi mababawi.
settings.transfer_abort_invalid = Hindi mo makakansela ang isang hindi umiiral na paglipat ng repositoryo.
settings.transfer_quota_exceeded = Ang bagong may-ari (%s) ay lumalagpas sa quota. Hindi nailipat ang repositoryo.
@ -2290,8 +2290,8 @@ settings.webhook.headers = Mga header
settings.webhook.payload = Nilalaman
settings.webhook.body = Katawan
settings.webhook.replay.description = I-replay ang webhook na ito.
settings.webhook.delivery.success = May nadagdag na event sa delivery queue. Maari magtagal ng ilang segundo bago makita sa delivery history.
settings.githooks_desc = Pinapagana ng Git ang mga Git hook. Maari mong baguhin ang mga hook file sa ibaba para mag-set up ng mga custom na operasyon.
settings.webhook.delivery.success = May nadagdag na event sa delivery queue. Maaari magtagal ng ilang segundo bago makita sa delivery history.
settings.githooks_desc = Pinapagana ng Git ang mga Git hook. Maaari mong baguhin ang mga hook file sa ibaba para mag-set up ng mga custom na operasyon.
settings.githook_name = Pangalan ng hook
settings.githook_content = Nilalaman ng hook
settings.update_githook = I-update ang hook
@ -2362,7 +2362,7 @@ settings.mirror_settings.docs.pull_mirror_instructions = Para mag-set up ng pull
milestones.invalid_due_date_format = Kailangang "yyyy-mm-dd" na format ang takdang petsa.
signing.wont_sign.nokey = Walang key ang instansya na ito para i-sign ang commit na ito.
activity.title.releases_1 = %d paglabas
settings.mirror_settings.docs.more_information_if_disabled = Maari kang matuto pa tungkol sa mga push at pull na mirror dito:
settings.mirror_settings.docs.more_information_if_disabled = Maaari kang matuto pa tungkol sa mga push at pull na mirror dito:
settings.branches.switch_default_branch = Magpalit ng default branch
settings.convert_notices_1 = Ang operasyon na ito ay ico-covert ang mirror sa regular na repositoryo at hindi mababawi.
settings.convert_fork_succeed = Na-convert na ang fork sa regular na repositoryo.
@ -2732,7 +2732,7 @@ settings.protect_protected_file_patterns = Mga pattern ng nakaprotektang file (h
settings.update_protect_branch_success = Binago na ang branch protection rule na "%s".
settings.remove_protected_branch_success = Tinanggal ang branch protection rule na "%s".
settings.tags.protection.pattern = Pattern ng tag
settings.tags.protection.pattern.description = Maari kang gumamit ng iisang pangalan o glob pattern o regular expression para magtugma ng maraming tag. Magbasa pa sa <a target="_blank" rel="noopener" href="%s">guide ng mga nakaprotektang tag</a>.
settings.tags.protection.pattern.description = Maaari kang gumamit ng iisang pangalan o glob pattern o regular expression para magtugma ng maraming tag. Magbasa pa sa <a target="_blank" rel="noopener" href="%s">guide ng mga nakaprotektang tag</a>.
settings.thread_id = ID ng thread
settings.matrix.room_id = ID ng room
diff.has_escaped = May mga nakatagong Unicode character ang linya na ito
@ -2746,7 +2746,7 @@ diff.bin = BIN
settings.default_update_style_desc = Ang default na istilio na gagamitin sa pag-update ng mga hiling sa paghila na nalilipas sa base branch.
pulls.sign_in_require = <a href="%s">Mag-sign in</a> para gumawa ng bagong hiling sa paghila.
new_from_template = Gumamit ng template
new_from_template_description = Maari kang pumili ng umiiral na repository template sa instansya na ito at i-apply ang mga setting nito.
new_from_template_description = Maaari kang pumili ng umiiral na repository template sa instansya na ito at i-apply ang mga setting nito.
new_advanced = Mga advanced na setting
new_advanced_expand = I-click para i-expand
auto_init_description = Simulan ang kasaysayan ng Git gamit ang README at opsyonal na magdagdag ng mga lisensya at .gitignore na file.
@ -2777,6 +2777,10 @@ settings.event_header_action = Mga event sa run ng aksyon
settings.event_action_failure = Pagkabigo
settings.event_action_failure_desc = Natapos ang action run bilang pagkabigo.
settings.event_action_recover = I-recover
settings.event_action_success = Matagumpay
settings.event_action_success_desc = Matagumpay na natapos ang Action Run.
settings.event_action_recover_desc = Matagumpay na natapos ang Action Run pagkatapos na nabigo ang huling Action Run sa katulad na workflow.
issues.filter_type.all_pull_requests = Lahat ng mga hiling sa paghila
[search]
commit_kind = Maghanap ng mga commit…
@ -3202,7 +3206,7 @@ self_check.database_collation_mismatch = Inaasahan ang database na gamitin ang c
auths.oauth2_admin_group = Group claim value para sa mga tagapangasiwa. (Opsyonal - kinakailangan ang claim name sa itaas)
auths.tip.facebook = Magrehistro ng bagong application sa %s at idagdag ang produktong "Facebook Login"
users.restricted.description = Payagan lamang ang interaksyon sa mga repositoryo at organisasyon kung saan ang user ay dinagdag bilang tagatulong. Iniiwasan nito ang pag-access sa publikong repositoryo sa instansya na ito.
users.local_import.description = Payagan ang pag-import ng mga repositoryo mula sa local file system ng user. Maari itong maging isyu sa seguridad.
users.local_import.description = Payagan ang pag-import ng mga repositoryo mula sa local file system ng user. Maaari itong maging isyu sa seguridad.
emails.delete = Burahin ang Email
emails.deletion_success = Binura na ang email address.
auths.oauth2_required_claim_value = Kinakailangan na claim value
@ -3447,8 +3451,8 @@ teams.owners_permission_desc = Ang mga owner ay may punong access sa <strong>lah
teams.add_nonexistent_repo = Hindi pa umiiral ang repositoryo na sinusubukan mong idagdag. Mangyaring gawin iyan muna.
teams.all_repositories = Lahat ng mga repositoryo
teams.all_repositories_helper = Ang koponan ay may access sa lahat ng mga repositoryo. Ang pagpili nito ay <strong>idadagdag ang lahat ng mga umiiral</strong> na repositoryo sa koponan.
settings.change_orgname_redirect_prompt.with_cooldown.few = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang pangalan sa panahon ng cooldown.
settings.change_orgname_redirect_prompt.with_cooldown.one = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang pangalan ng panahon ng cooldown.
settings.change_orgname_redirect_prompt.with_cooldown.few = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang pangalan sa panahon ng cooldown.
settings.change_orgname_redirect_prompt.with_cooldown.one = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang pangalan ng panahon ng cooldown.
[packages]

View file

@ -1062,8 +1062,8 @@ language.localization_project = Aidez-nous à traduire Forgejo dans votre langue
language.description = Cette langue sera enregistrée dans votre compte et utilisée comme langue par défaut après votre connexion.
user_block_yourself = Vous ne pouvez pas vous bloquer vous même.
pronouns_custom_label = Pronoms personnalisés
change_username_redirect_prompt.with_cooldown.one = L'ancien pseudonyme sera disponible pour n'importe qui après une période d'%[1]d jour, vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période.
change_username_redirect_prompt.with_cooldown.few = L'ancien pseudonyme sera disponible pour n'importe qui après une période de %[1]d jours, vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période.
change_username_redirect_prompt.with_cooldown.one = L'ancien pseudonyme sera disponible pour n'importe qui après une période d'%[1]d jour. Vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période.
change_username_redirect_prompt.with_cooldown.few = L'ancien pseudonyme sera disponible pour n'importe qui après une période de %[1]d jours. Vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période.
quota.rule.exceeded = Dépassé
regenerate_token = Régénérer
access_token_regeneration = Régénérer le token d'accès
@ -1653,13 +1653,13 @@ issues.close_comment_issue=Fermer avec le commentaire
issues.reopen_issue=Rouvrir
issues.reopen_comment_issue=Réouvrir avec le commentaire
issues.create_comment=Commenter
issues.closed_at=`a fermé ce ticket <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
issues.reopened_at=`a rouvert ce ticket <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
issues.commit_ref_at=`a référencé ce ticket depuis une révision <a id="%[1]s" href="#%[1]s"> %[2]s</a>.`
issues.ref_issue_from=`<a href="%[3]s">a fait référence à %[4]s</a> ce ticket <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
issues.ref_pull_from=`<a href="%[3]s">a fait référence</a> à cette demande d'ajout %[4]s <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
issues.ref_closing_from=`<a href="%[3]s">a fait référence</a> à une demande d'ajout %[4]s qui clora ce ticket, <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
issues.ref_reopening_from=`<a href="%[3]s">a référencé une pull request %[4]s qui va ré-ouvrir ce ticket</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at=`a fermé ce ticket %s`
issues.reopened_at=`a rouvert ce ticket %s`
issues.commit_ref_at=`a référencé ce ticket depuis une révision %s`
issues.ref_issue_from=`<a href="%[2]s">a fait référence à ce ticket %[3]s</a> %[1]s`
issues.ref_pull_from=`<a href="%[2]s">a fait référence à cette demande d'ajout %[3]s</a> %[1]s`
issues.ref_closing_from=`<a href="%[2]s">a fait référence à une demande d'ajout %[3]s qui clora ce ticket</a>, %[1]s`
issues.ref_reopening_from=`<a href="%[2]s">a référencé ce ticket dans une pull request %[3]s qui va ré-ouvrir ce ticket</a>, %[1]s`
issues.ref_closed_from=`<a href="%[3]s">a fermé ce ticket %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">a rouvert</a> ce ticket %[4]s <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
issues.ref_from=`de %[1]s`
@ -1967,8 +1967,8 @@ pulls.update_branch_success=La mise à jour de la branche a réussi
pulls.update_not_allowed=Vous n'êtes pas autorisé à mettre à jour la branche
pulls.outdated_with_base_branch=Cette branche est désynchronisée avec la branche de base
pulls.close=Fermer la demande dajout
pulls.closed_at=`a fermé cette demande d'ajout <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
pulls.reopened_at=`a rouvert cette demande d'ajout <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
pulls.closed_at=`a fermé cette demande d'ajout %s`
pulls.reopened_at=`a rouvert cette demande d'ajout %s`
pulls.cmd_instruction_hint=Voir les instructions en ligne de commande
pulls.cmd_instruction_checkout_title=Basculer
pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications.
@ -2762,7 +2762,7 @@ issues.blocked_by_user = Vous ne pouvez pas créer de tickets sur ce dépôt car
pulls.blocked_by_user = Vous ne pouvez pas créer une pull request sur ce dépôt car vous êtes bloqué par son propriétaire.
wiki.cancel = Annuler
settings.wiki_globally_editable = Permettre l'édition du wiki a tout le monde
pulls.commit_ref_at = `a référencé cette pull request depuis le commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.commit_ref_at = `a référencé cette pull request depuis un commit %s`
settings.new_owner_blocked_doer = Le nouveau propriétaire vous a bloqué.
settings.enter_repo_name = Confirmez en entrant le propriétaire et le nom du dépôt exactement comme affiché :
settings.wiki_rename_branch_main = Normalise le nom de la branche du Wiki
@ -2920,6 +2920,7 @@ settings.event_header_action = Événements d'exécution d'action
settings.event_action_success_desc = L'exécution de l'action a réussi.
settings.event_action_failure_desc = L'exécution de l'action a échoué.
settings.event_action_recover_desc = L'exécution de l'action a réussi après l'échec de la dernière exécution de l'action dans le même workflow.
issues.filter_type.all_pull_requests = Toutes les demandes d'ajout
[graphs]
component_loading = Chargement %s…
@ -3057,8 +3058,8 @@ teams.invite.by=Invité par %s
teams.invite.description=Veuillez cliquer sur le bouton ci-dessous pour rejoindre léquipe.
follow_blocked_user = Vous ne pouvez pas suivre cette organisation car elle vous a bloqué.
open_dashboard = Ouvrir le tableau de bord
settings.change_orgname_redirect_prompt.with_cooldown.few = L'ancien nom d'organisation sera disponible pour n'importe qui après une période de %[1]d jours, vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période.
settings.change_orgname_redirect_prompt.with_cooldown.one = L'ancien nom d'organisation sera disponible pour n'importe qui après une période d'%[1]d jour, vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période.
settings.change_orgname_redirect_prompt.with_cooldown.few = L'ancien nom d'organisation sera disponible pour n'importe qui après une période de %[1]d jours. Vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période.
settings.change_orgname_redirect_prompt.with_cooldown.one = L'ancien nom d'organisation sera disponible pour n'importe qui après une période d'%[1]d jour. Vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période.
[admin]
dashboard=Tableau de bord

View file

@ -1219,11 +1219,11 @@ issues.close_comment_issue = Dún le trácht
issues.reopen_issue = Athoscail
issues.reopen_comment_issue = Athoscail le trácht
issues.create_comment = Trácht
issues.closed_at = `dhún an cheist seo <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at = `athoscail an t-eagrán seo <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at = `rinne tagairt don cheist seo ó ghealltanas <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from = `<a href="%[3]s">rinne dagairt don cheist seo %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from = `<a href="%[3]s">rinne dagairt don iarratas tarraingthe seo %[4]s</a> <a id="%[1]s" href="#%[1]s">%[ 2]s</a>`
issues.closed_at = `dhún an cheist seo %s`
issues.reopened_at = `athoscail an t-eagrán seo %s`
issues.commit_ref_at = `rinne tagairt don cheist seo ó ghealltanas %s`
issues.ref_issue_from = `<a href="%[2]s">rinne dagairt don cheist seo %[3]s</a> %[1]s`
issues.ref_pull_from = `<a href="%[2]s">rinne dagairt don iarratas tarraingthe seo %[3]s</a> %[1]s`
issues.ref_closed_from = `<a href="%[3]s">dhún an cheist seo %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from = `<a href="%[3]s">d'athoscail an eagrán seo %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from = `ó %[1]s`
@ -1456,8 +1456,8 @@ pulls.update_branch_success = Bhí nuashonrú brainse rathúil
pulls.update_not_allowed = Ní cheadaítear duit brainse a nuashonrú
pulls.outdated_with_base_branch = Tá an brainse seo as dáta leis an mbunbhrainse
pulls.close = Dún Iarratas Tarraing
pulls.closed_at = `dhún an t-iarratas tarraingthe seo <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at = `athoscail an t-iarratas tarraingthe seo <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.closed_at = `dhún an t-iarratas tarraingthe seo %s`
pulls.reopened_at = `athoscail an t-iarratas tarraingthe seo %s`
pulls.cmd_instruction_checkout_title = Seiceáil
pulls.cmd_instruction_checkout_desc = Ó stór tionscadail, seiceáil brainse nua agus déan tástáil ar na hathruithe.
pulls.cmd_instruction_merge_title = Cumaisc

View file

@ -932,7 +932,7 @@ issues.close_comment_issue=Hozzászólás és lezárás
issues.reopen_issue=Újranyitás
issues.reopen_comment_issue=Hozzászólás és újranyitás
issues.create_comment=Hozzászólás
issues.commit_ref_at=`hivatkozott erre a hibajegyre egy commit-ból <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`hivatkozott erre a hibajegyre egy commit-ból %s`
issues.role.owner=Tulajdonos
issues.role.member=Tag
issues.re_request_review=Véleményezés újrakérése

View file

@ -796,7 +796,7 @@ issues.close_comment_issue=Komentar dan Tutup
issues.reopen_issue=Buka kembali
issues.reopen_comment_issue=Komentar dan Buka Kembali
issues.create_comment=Komentar
issues.commit_ref_at=`merujuk masalah dari komit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`merujuk masalah dari komit %s`
issues.role.owner=Pemilik
issues.role.member=Anggota
issues.sign_in_require_desc=<a href="%s">Masuk</a> untuk bergabung dengan percakapan ini.

View file

@ -805,8 +805,8 @@ issues.close_comment_issue=Senda ummæli og Loka
issues.reopen_issue=Enduropna
issues.reopen_comment_issue=Senda ummæli og Enduropna
issues.create_comment=Senda Ummæli
issues.closed_at=`lokaði þessu vandamáli <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`enduropnaði þetta vandamál <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at=`lokaði þessu vandamáli %s`
issues.reopened_at=`enduropnaði þetta vandamál %s`
issues.ref_reopened_from=`<a href="%[3]s">enduropnaði þetta vandamál %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.author=Höfundur
issues.role.owner=Eigandi

Some files were not shown because too many files have changed in this diff Show more