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:
commit
2ce4db6822
266 changed files with 6145 additions and 2215 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: |
|
||||
|
|
|
@ -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: |
|
||||
|
|
2
.forgejo/testdata/build-release/Dockerfile
vendored
2
.forgejo/testdata/build-release/Dockerfile
vendored
|
@ -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}"
|
||||
|
|
|
@ -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 }}"
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" \
|
||||
|
|
|
@ -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" \
|
||||
|
|
20
Makefile
20
Makefile
|
@ -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
|
||||
|
|
34
assets/go-licenses.json
generated
34
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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.")))
|
||||
})
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
24
go.mod
|
@ -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
51
go.sum
|
@ -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=
|
||||
|
|
2
main.go
2
main.go
|
@ -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
|
||||
|
|
|
@ -55,6 +55,7 @@ type ActionRun struct {
|
|||
PreviousDuration time.Duration
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
NotifyEmail bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.")
|
||||
|
|
106
models/activities/federated_user_activity.go
Normal file
106
models/activities/federated_user_activity.go
Normal 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
|
||||
}
|
24
models/activities/federated_user_activity_test.go
Normal file
24
models/activities/federated_user_activity_test.go
Normal 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())
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
1
models/fixtures/pull_auto_merge.yml
Normal file
1
models/fixtures/pull_auto_merge.yml
Normal file
|
@ -0,0 +1 @@
|
|||
[] # empty
|
|
@ -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
|
||||
|
|
|
@ -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 ------------------------------------------------
|
||||
|
|
|
@ -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.
|
||||
|
|
126
models/forgejo_migrations/v33.go
Normal file
126
models/forgejo_migrations/v33.go
Normal 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
|
||||
}
|
46
models/forgejo_migrations/v33_test.go
Normal file
46
models/forgejo_migrations/v33_test.go
Normal 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)
|
||||
}
|
14
models/forgejo_migrations/v34.go
Normal file
14
models/forgejo_migrations/v34.go
Normal 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))
|
||||
}
|
19
models/forgejo_migrations/v35.go
Normal file
19
models/forgejo_migrations/v35.go
Normal 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{})
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
30
models/user/federated_user_follower.go
Normal file
30
models/user/federated_user_follower.go
Normal 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
|
||||
}
|
27
models/user/federated_user_follower_test.go
Normal file
27
models/user/federated_user_follower_test.go
Normal 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")
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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)"`
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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">@#&@forgejo.org</a></p>`)
|
||||
}
|
||||
|
||||
func TestRender_emoji(t *testing.T) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -104,7 +104,7 @@ func TestRender_Images(t *testing.T) {
|
|||
|
||||
test(
|
||||
"",
|
||||
`<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(
|
||||
"",
|
||||
`<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 := `
|
||||

|
||||
`
|
||||
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/>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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])
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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 = المحتوى
|
||||
|
|
|
@ -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 = Тази заявка за сливане не може да бъде отворена наново, защото клонът е изтрит.
|
||||
|
|
|
@ -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…
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = Συνεισφέροντες
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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`
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 d’ajout
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue