Merge branch 'main' into lunny/refactor_lastcommitcache
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Gitea DevContainer",
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1.24-bookworm",
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1.25-trixie",
|
||||
"containerEnv": {
|
||||
// override "local" from packaged version
|
||||
"GOTOOLCHAIN": "auto"
|
||||
@ -8,12 +8,12 @@
|
||||
"features": {
|
||||
// installs nodejs into container
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "lts"
|
||||
"version": "latest"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {},
|
||||
"ghcr.io/devcontainers/features/git-lfs:1.2.5": {},
|
||||
"ghcr.io/jsburckhardt/devcontainer-features/uv:1": {},
|
||||
"ghcr.io/devcontainers/features/python:1": {
|
||||
"version": "3.12"
|
||||
"version": "3.13"
|
||||
},
|
||||
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
|
||||
},
|
||||
|
||||
@ -65,6 +65,7 @@ cpu.out
|
||||
/yarn.lock
|
||||
/yarn-error.log
|
||||
/npm-debug.log*
|
||||
/pnpm-debug.log*
|
||||
/public/assets/js
|
||||
/public/assets/css
|
||||
/public/assets/fonts
|
||||
|
||||
2
.github/labeler.yml
vendored
@ -59,7 +59,7 @@ modifies/dependencies:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- "pnpm-lock.yaml"
|
||||
- "pyproject.toml"
|
||||
- "uv.lock"
|
||||
- "go.mod"
|
||||
|
||||
6
.github/workflows/files-changed.yml
vendored
@ -58,7 +58,7 @@ jobs:
|
||||
- "tools/*.ts"
|
||||
- "assets/emoji.json"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- "pnpm-lock.yaml"
|
||||
- "Makefile"
|
||||
- ".eslintrc.cjs"
|
||||
- ".npmrc"
|
||||
@ -67,7 +67,7 @@ jobs:
|
||||
- "**/*.md"
|
||||
- ".markdownlint.yaml"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- "pnpm-lock.yaml"
|
||||
|
||||
actions:
|
||||
- ".github/workflows/*"
|
||||
@ -90,7 +90,7 @@ jobs:
|
||||
- "templates/swagger/v1_input.json"
|
||||
- "Makefile"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- "pnpm-lock.yaml"
|
||||
- ".spectral.yaml"
|
||||
|
||||
yaml:
|
||||
|
||||
20
.github/workflows/pull-compliance.yml
vendored
@ -34,11 +34,12 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/setup-uv@v6
|
||||
- run: uv python install 3.12
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-py
|
||||
- run: make deps-frontend
|
||||
- run: make lint-templates
|
||||
@ -60,11 +61,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend
|
||||
- run: make lint-swagger
|
||||
|
||||
@ -131,11 +133,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend
|
||||
- run: make lint-frontend
|
||||
- run: make checks-frontend
|
||||
@ -180,11 +183,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend
|
||||
- run: make lint-md
|
||||
|
||||
|
||||
6
.github/workflows/pull-db-tests.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
||||
minio:
|
||||
# as github actions doesn't support "entrypoint", we need to use a non-official image
|
||||
# that has a custom entrypoint set to "minio server /data"
|
||||
image: bitnami/minio:2023.8.31
|
||||
image: bitnamilegacy/minio:2023.8.31
|
||||
env:
|
||||
MINIO_ROOT_USER: 123456
|
||||
MINIO_ROOT_PASSWORD: 12345678
|
||||
@ -113,7 +113,7 @@ jobs:
|
||||
ports:
|
||||
- 6379:6379
|
||||
minio:
|
||||
image: bitnami/minio:2021.3.17
|
||||
image: bitnamilegacy/minio:2021.3.17
|
||||
env:
|
||||
MINIO_ACCESS_KEY: 123456
|
||||
MINIO_SECRET_KEY: 12345678
|
||||
@ -155,7 +155,7 @@ jobs:
|
||||
services:
|
||||
mysql:
|
||||
# the bitnami mysql image has more options than the official one, it's easier to customize
|
||||
image: bitnami/mysql:8.0
|
||||
image: bitnamilegacy/mysql:8.0
|
||||
env:
|
||||
ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: testgitea
|
||||
|
||||
7
.github/workflows/pull-e2e-tests.yml
vendored
@ -23,13 +23,14 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend frontend deps-backend
|
||||
- run: npx playwright install --with-deps
|
||||
- run: pnpm exec playwright install --with-deps
|
||||
- run: make test-e2e-sqlite
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
|
||||
5
.github/workflows/release-nightly.yml
vendored
@ -20,11 +20,12 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
|
||||
5
.github/workflows/release-tag-rc.yml
vendored
@ -21,11 +21,12 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
|
||||
5
.github/workflows/release-tag-version.yml
vendored
@ -25,11 +25,12 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
cache: pnpm
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
- run: make deps-frontend deps-backend
|
||||
# xgo build
|
||||
- run: make release
|
||||
|
||||
1
.gitignore
vendored
@ -74,6 +74,7 @@ cpu.out
|
||||
/tests/*.ini
|
||||
/tests/**/*.git/**/*.sample
|
||||
/node_modules
|
||||
/tools/node_modules
|
||||
/.venv
|
||||
/yarn.lock
|
||||
/yarn-error.log
|
||||
|
||||
5
.npmrc
@ -1,6 +1,7 @@
|
||||
audit=false
|
||||
fund=false
|
||||
update-notifier=false
|
||||
package-lock=true
|
||||
save-exact=true
|
||||
lockfile-version=3
|
||||
auto-install-peers=true
|
||||
dedupe-peer-dependents=false
|
||||
enable-pre-post-scripts=true
|
||||
|
||||
@ -15,6 +15,7 @@ RUN apk --no-cache add \
|
||||
git \
|
||||
nodejs \
|
||||
npm \
|
||||
&& npm install -g pnpm@10 \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Setup repo
|
||||
|
||||
@ -15,6 +15,7 @@ RUN apk --no-cache add \
|
||||
git \
|
||||
nodejs \
|
||||
npm \
|
||||
&& npm install -g pnpm@10 \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Setup repo
|
||||
|
||||
73
Makefile
@ -29,7 +29,7 @@ AIR_PACKAGE ?= github.com/air-verse/air@v1
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.12
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.7.0
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.32.3
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
@ -218,15 +218,19 @@ node-check:
|
||||
$(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p'))
|
||||
$(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' ')))
|
||||
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');))
|
||||
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
|
||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
||||
echo "Gitea requires Node.js $(MIN_NODE_VERSION_STR) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||
$(eval PNPM_MISSING := $(shell hash pnpm > /dev/null 2>&1 || echo 1))
|
||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" ]; then \
|
||||
echo "Gitea requires Node.js $(MIN_NODE_VERSION_STR) or greater to build. You can get it at https://nodejs.org/en/download/"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ "$(PNPM_MISSING)" = "1" ]; then \
|
||||
echo "Gitea requires pnpm to build. You can install it at https://pnpm.io/installation"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: clean-all
|
||||
clean-all: clean ## delete backend, frontend and integration files
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules tools/node_modules
|
||||
|
||||
.PHONY: clean
|
||||
clean: ## delete backend and integration files
|
||||
@ -334,29 +338,29 @@ lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backen
|
||||
|
||||
.PHONY: lint-js
|
||||
lint-js: node_modules ## lint js files
|
||||
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES)
|
||||
npx vue-tsc
|
||||
pnpm exec eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES)
|
||||
pnpm exec vue-tsc
|
||||
|
||||
.PHONY: lint-js-fix
|
||||
lint-js-fix: node_modules ## lint js files and fix issues
|
||||
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) --fix
|
||||
npx vue-tsc
|
||||
pnpm exec eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) --fix
|
||||
pnpm exec vue-tsc
|
||||
|
||||
.PHONY: lint-css
|
||||
lint-css: node_modules ## lint css files
|
||||
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES)
|
||||
pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES)
|
||||
|
||||
.PHONY: lint-css-fix
|
||||
lint-css-fix: node_modules ## lint css files and fix issues
|
||||
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
|
||||
pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
|
||||
|
||||
.PHONY: lint-swagger
|
||||
lint-swagger: node_modules ## lint swagger files
|
||||
npx spectral lint -q -F hint $(SWAGGER_SPEC)
|
||||
pnpm exec spectral lint -q -F hint $(SWAGGER_SPEC)
|
||||
|
||||
.PHONY: lint-md
|
||||
lint-md: node_modules ## lint markdown files
|
||||
npx markdownlint *.md
|
||||
pnpm exec markdownlint *.md
|
||||
|
||||
.PHONY: lint-spell
|
||||
lint-spell: ## lint spelling
|
||||
@ -417,7 +421,7 @@ watch: ## watch everything and continuously rebuild
|
||||
.PHONY: watch-frontend
|
||||
watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild
|
||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
NODE_ENV=development npx webpack --watch --progress
|
||||
NODE_ENV=development pnpm exec webpack --watch --progress
|
||||
|
||||
.PHONY: watch-backend
|
||||
watch-backend: go-check ## watch backend files and continuously rebuild
|
||||
@ -433,7 +437,7 @@ test-backend: ## test backend files
|
||||
|
||||
.PHONY: test-frontend
|
||||
test-frontend: node_modules ## test frontend files
|
||||
npx vitest
|
||||
pnpm exec vitest
|
||||
|
||||
.PHONY: test-check
|
||||
test-check:
|
||||
@ -576,7 +580,7 @@ test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test
|
||||
|
||||
.PHONY: playwright
|
||||
playwright: deps-frontend
|
||||
npx playwright install $(PLAYWRIGHT_FLAGS)
|
||||
pnpm exec playwright install $(PLAYWRIGHT_FLAGS)
|
||||
|
||||
.PHONY: test-e2e%
|
||||
test-e2e%: TEST_TYPE ?= e2e
|
||||
@ -839,10 +843,14 @@ deps-tools: ## install tool dependencies
|
||||
$(GO) install $(GOPLS_MODERNIZE_PACKAGE) & \
|
||||
wait
|
||||
|
||||
node_modules: package-lock.json
|
||||
npm install --no-save
|
||||
node_modules: pnpm-lock.yaml
|
||||
pnpm install --frozen-lockfile
|
||||
@touch node_modules
|
||||
|
||||
tools/node_modules: tools/package.json
|
||||
cd tools && pnpm install
|
||||
@touch tools/node_modules
|
||||
|
||||
.venv: uv.lock
|
||||
uv sync
|
||||
@touch .venv
|
||||
@ -852,16 +860,16 @@ update: update-js update-py ## update js and py dependencies
|
||||
|
||||
.PHONY: update-js
|
||||
update-js: node-check | node_modules ## update js dependencies
|
||||
npx updates -u -f package.json
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install --package-lock
|
||||
npx nolyfill install
|
||||
npm install --package-lock
|
||||
pnpm exec updates -u -f package.json
|
||||
rm -rf node_modules pnpm-lock.yaml
|
||||
pnpm install
|
||||
pnpm exec nolyfill install
|
||||
pnpm install
|
||||
@touch node_modules
|
||||
|
||||
.PHONY: update-py
|
||||
update-py: node-check | node_modules ## update py dependencies
|
||||
npx updates -u -f pyproject.toml
|
||||
pnpm exec updates -u -f pyproject.toml
|
||||
rm -rf .venv uv.lock
|
||||
uv sync
|
||||
@touch .venv
|
||||
@ -869,11 +877,11 @@ update-py: node-check | node_modules ## update py dependencies
|
||||
.PHONY: webpack
|
||||
webpack: $(WEBPACK_DEST) ## build webpack files
|
||||
|
||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) pnpm-lock.yaml
|
||||
@$(MAKE) -s node-check node_modules
|
||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
@echo "Running webpack..."
|
||||
@BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack
|
||||
@BROWSERSLIST_IGNORE_OLD_DATA=true pnpm exec webpack
|
||||
@touch $(WEBPACK_DEST)
|
||||
|
||||
.PHONY: svg
|
||||
@ -893,11 +901,11 @@ svg-check: svg
|
||||
|
||||
.PHONY: lockfile-check
|
||||
lockfile-check:
|
||||
npm install --package-lock-only
|
||||
@diff=$$(git diff --color=always package-lock.json); \
|
||||
pnpm install --frozen-lockfile
|
||||
@diff=$$(git diff --color=always pnpm-lock.yaml); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "package-lock.json is inconsistent with package.json"; \
|
||||
echo "Please run 'npm install --package-lock-only' and commit the result:"; \
|
||||
echo "pnpm-lock.yaml is inconsistent with package.json"; \
|
||||
echo "Please run 'pnpm install --frozen-lockfile' and commit the result:"; \
|
||||
printf "%s" "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@ -917,9 +925,8 @@ generate-gitignore: ## update gitignore files
|
||||
$(GO) run build/generate-gitignores.go
|
||||
|
||||
.PHONY: generate-images
|
||||
generate-images: | node_modules
|
||||
npm install --no-save fabric@6 imagemin-zopfli@7
|
||||
node tools/generate-images.js $(TAGS)
|
||||
generate-images: | node_modules tools/node_modules ## generate images (requires cairo development packages)
|
||||
cd tools && node generate-images.js $(TAGS)
|
||||
|
||||
.PHONY: generate-manpage
|
||||
generate-manpage: ## generate manpage
|
||||
|
||||
@ -52,7 +52,7 @@ or if SQLite support is required:
|
||||
The `build` target is split into two sub-targets:
|
||||
|
||||
- `make backend` which requires [Go Stable](https://go.dev/dl/), the required version is defined in [go.mod](/go.mod).
|
||||
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater.
|
||||
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and [pnpm](https://pnpm.io/installation).
|
||||
|
||||
Internet connectivity is required to download the go and npm modules. When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js.
|
||||
|
||||
|
||||
77
assets/go-licenses.json
generated
37
cmd/dump.go
@ -20,7 +20,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"gitea.com/go-chi/session"
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
@ -146,22 +145,18 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
archiverGeneric, err := archiver.ByExtension("." + outType)
|
||||
dumper, err := dump.NewDumper(ctx, outType, outFile)
|
||||
if err != nil {
|
||||
fatal("Unable to get archiver for extension: %v", err)
|
||||
}
|
||||
|
||||
archiverWriter := archiverGeneric.(archiver.Writer)
|
||||
if err := archiverWriter.Create(outFile); err != nil {
|
||||
fatal("Creating archiver.Writer failed: %v", err)
|
||||
}
|
||||
defer archiverWriter.Close()
|
||||
|
||||
dumper := &dump.Dumper{
|
||||
Writer: archiverWriter,
|
||||
Verbose: verbose,
|
||||
fatal("Failed to create archive %q: %v", outFile, err)
|
||||
return err
|
||||
}
|
||||
dumper.Verbose = verbose
|
||||
dumper.GlobalExcludeAbsPath(outFileName)
|
||||
defer func() {
|
||||
if err := dumper.Close(); err != nil {
|
||||
fatal("Failed to save archive %q: %v", outFileName, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if cmd.IsSet("skip-repository") && cmd.Bool("skip-repository") {
|
||||
log.Info("Skip dumping local repositories")
|
||||
@ -180,7 +175,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dumper.AddReader(object, info, path.Join("data", "lfs", objPath))
|
||||
return dumper.AddFileByReader(object, info, path.Join("data", "lfs", objPath))
|
||||
}); err != nil {
|
||||
fatal("Failed to dump LFS objects: %v", err)
|
||||
}
|
||||
@ -218,13 +213,13 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
||||
fatal("Failed to dump database: %v", err)
|
||||
}
|
||||
|
||||
if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
|
||||
if err = dumper.AddFileByPath("gitea-db.sql", dbDump.Name()); err != nil {
|
||||
fatal("Failed to include gitea-db.sql: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
||||
if err = dumper.AddFile("app.ini", setting.CustomConf); err != nil {
|
||||
if err = dumper.AddFileByPath("app.ini", setting.CustomConf); err != nil {
|
||||
fatal("Failed to include specified app.ini: %v", err)
|
||||
}
|
||||
|
||||
@ -283,7 +278,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dumper.AddReader(object, info, path.Join("data", "attachments", objPath))
|
||||
return dumper.AddFileByReader(object, info, path.Join("data", "attachments", objPath))
|
||||
}); err != nil {
|
||||
fatal("Failed to dump attachments: %v", err)
|
||||
}
|
||||
@ -297,7 +292,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dumper.AddReader(object, info, path.Join("data", "packages", objPath))
|
||||
return dumper.AddFileByReader(object, info, path.Join("data", "packages", objPath))
|
||||
}); err != nil {
|
||||
fatal("Failed to dump packages: %v", err)
|
||||
}
|
||||
@ -322,10 +317,6 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
|
||||
if outFileName == "-" {
|
||||
log.Info("Finish dumping to stdout")
|
||||
} else {
|
||||
if err = archiverWriter.Close(); err != nil {
|
||||
_ = os.Remove(outFileName)
|
||||
fatal("Failed to save %q: %v", outFileName, err)
|
||||
}
|
||||
if err = os.Chmod(outFileName, 0o600); err != nil {
|
||||
log.Info("Can't change file access permissions mask to 0600: %v", err)
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v71/github"
|
||||
"github.com/google/go-github/v74/github"
|
||||
"github.com/urfave/cli/v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
go = go_1_25;
|
||||
nodejs = nodejs_24;
|
||||
python3 = python312;
|
||||
pnpm = pnpm_10;
|
||||
|
||||
# Platform-specific dependencies
|
||||
linuxOnlyInputs = lib.optionals pkgs.stdenv.isLinux [
|
||||
@ -43,6 +44,10 @@
|
||||
|
||||
# frontend
|
||||
nodejs
|
||||
pnpm
|
||||
cairo
|
||||
pixman
|
||||
pkg-config
|
||||
|
||||
# linting
|
||||
python3
|
||||
|
||||
194
go.mod
@ -10,7 +10,7 @@ godebug x509negativeserial=1
|
||||
require (
|
||||
code.gitea.io/actions-proto-go v0.4.1
|
||||
code.gitea.io/gitea-vet v0.2.3
|
||||
code.gitea.io/sdk/gitea v0.21.0
|
||||
code.gitea.io/sdk/gitea v0.22.0
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||
connectrpc.com/connect v1.18.1
|
||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
|
||||
@ -19,28 +19,28 @@ require (
|
||||
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||
github.com/42wim/httpsig v1.2.2
|
||||
github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0
|
||||
github.com/42wim/httpsig v1.2.3
|
||||
github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
||||
github.com/ProtonMail/go-crypto v1.2.0
|
||||
github.com/ProtonMail/go-crypto v1.3.0
|
||||
github.com/PuerkitoBio/goquery v1.10.3
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0
|
||||
github.com/alecthomas/chroma/v2 v2.20.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.67
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.28.2
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.10
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.32.2
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/blevesearch/bleve/v2 v2.5.0
|
||||
github.com/blevesearch/bleve/v2 v2.5.3
|
||||
github.com/bohde/codel v0.2.0
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.8
|
||||
github.com/caddyserver/certmagic v0.23.0
|
||||
github.com/caddyserver/certmagic v0.24.0
|
||||
github.com/charmbracelet/git-lfs-transfer v0.2.0
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
||||
github.com/djherbis/buffer v1.2.0
|
||||
github.com/djherbis/nio/v3 v3.0.1
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
|
||||
github.com/emersion/go-imap v1.2.1
|
||||
@ -49,25 +49,25 @@ require (
|
||||
github.com/felixge/fgprof v0.9.5
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/go-ap/activitypub v0.0.0-20250409143848-7113328b1f3d
|
||||
github.com/go-ap/activitypub v0.0.0-20250810115208-cb73b20a1742
|
||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
|
||||
github.com/go-chi/chi/v5 v5.2.2
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/chi/v5 v5.2.3
|
||||
github.com/go-chi/cors v1.2.2
|
||||
github.com/go-co-op/gocron v1.37.0
|
||||
github.com/go-enry/go-enry/v2 v2.9.2
|
||||
github.com/go-git/go-billy/v5 v5.6.2
|
||||
github.com/go-git/go-git/v5 v5.16.0
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/go-ldap/ldap/v3 v3.4.11
|
||||
github.com/go-redsync/redsync/v4 v4.13.0
|
||||
github.com/go-sql-driver/mysql v1.9.2
|
||||
github.com/go-webauthn/webauthn v0.12.3
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/go-webauthn/webauthn v0.13.4
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/google/go-github/v71 v71.0.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/google/go-github/v74 v74.0.0
|
||||
github.com/google/licenseclassifier/v2 v2.0.0
|
||||
github.com/google/pprof v0.0.0-20250422154841-e1f9c1950416
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/feeds v1.2.0
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
@ -79,53 +79,53 @@ require (
|
||||
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
|
||||
github.com/klauspost/cpuid/v2 v2.2.10
|
||||
github.com/klauspost/cpuid/v2 v2.3.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/markbates/goth v1.81.0
|
||||
github.com/markbates/goth v1.82.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-sqlite3 v1.14.28
|
||||
github.com/meilisearch/meilisearch-go v0.31.0
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/mattn/go-sqlite3 v1.14.32
|
||||
github.com/meilisearch/meilisearch-go v0.33.2
|
||||
github.com/mholt/archives v0.1.3
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/microsoft/go-mssqldb v1.8.0
|
||||
github.com/minio/minio-go/v7 v7.0.91
|
||||
github.com/microsoft/go-mssqldb v1.9.3
|
||||
github.com/minio/minio-go/v7 v7.0.95
|
||||
github.com/msteinert/pam v1.2.0
|
||||
github.com/nektos/act v0.2.63
|
||||
github.com/niklasfasching/go-org v1.8.0
|
||||
github.com/niklasfasching/go-org v1.9.1
|
||||
github.com/olivere/elastic/v7 v7.0.32
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/quasoft/websspi v1.1.2
|
||||
github.com/redis/go-redis/v9 v9.7.3
|
||||
github.com/redis/go-redis/v9 v9.12.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
||||
github.com/sassoftware/go-rpmutils v0.4.0
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/sergi/go-diff v1.4.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.12
|
||||
github.com/ulikunitz/xz v0.5.15
|
||||
github.com/urfave/cli-docs/v3 v3.0.0-alpha6
|
||||
github.com/urfave/cli/v3 v3.3.3
|
||||
github.com/urfave/cli/v3 v3.4.1
|
||||
github.com/wneessen/go-mail v0.6.2
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.7.10
|
||||
github.com/yuin/goldmark v1.7.13
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
gitlab.com/gitlab-org/api/client-go v0.127.0
|
||||
golang.org/x/crypto v0.39.0
|
||||
golang.org/x/image v0.26.0
|
||||
golang.org/x/net v0.40.0
|
||||
golang.org/x/oauth2 v0.29.0
|
||||
golang.org/x/sync v0.15.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.org/x/text v0.26.0
|
||||
google.golang.org/grpc v1.72.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
gitlab.com/gitlab-org/api/client-go v0.142.4
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/image v0.30.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/text v0.28.0
|
||||
google.golang.org/grpc v1.75.0
|
||||
google.golang.org/protobuf v1.36.8
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/xurls/v2 v2.6.0
|
||||
@ -135,43 +135,47 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||
github.com/DataDog/zstd v1.5.7 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/RoaringBitmap/roaring/v2 v2.10.0 // indirect
|
||||
github.com/STARRY-S/zip v0.2.1 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
|
||||
github.com/aws/smithy-go v1.22.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.38.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.6 // indirect
|
||||
github.com/aws/smithy-go v1.23.0 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.22.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.2.8 // indirect
|
||||
github.com/blevesearch/geo v0.2.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.2.9 // indirect
|
||||
github.com/blevesearch/geo v0.2.4 // indirect
|
||||
github.com/blevesearch/go-faiss v1.0.25 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.3.11 // indirect
|
||||
github.com/blevesearch/segment v0.9.1 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
||||
github.com/blevesearch/vellum v1.1.0 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.4.1 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.4.1 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.4.1 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.4.1 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.4.1 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.2.3 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.2 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.0 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||
@ -180,7 +184,7 @@ require (
|
||||
github.com/couchbase/go-couchbase v0.1.1 // indirect
|
||||
github.com/couchbase/gomemcached v0.3.3 // indirect
|
||||
github.com/couchbase/goutils v0.1.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
@ -188,15 +192,15 @@ require (
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20250527110557-c8db454e53fd // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-webauthn/x v0.1.20 // indirect
|
||||
github.com/go-webauthn/x v0.1.24 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
@ -207,50 +211,58 @@ require (
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/flatbuffers v25.2.10+incompatible // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/go-tpm v0.9.3 // indirect
|
||||
github.com/google/go-tpm v0.9.5 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/libdns/libdns v1.0.0-beta.1 // indirect
|
||||
github.com/libdns/libdns v1.1.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.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.2 // indirect
|
||||
github.com/miekg/dns v1.1.65 // indirect
|
||||
github.com/minio/crc64nvme v1.0.1 // indirect
|
||||
github.com/miekg/dns v1.1.68 // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minlz v1.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
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/nwaples/rardecode/v2 v2.1.0 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250817074551-3280053e4e00 // indirect
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/ll v0.1.0 // indirect
|
||||
github.com/olekukonko/tablewriter v1.0.9 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pjbgf/sha1cd v0.4.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.63.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/prometheus/common v0.65.0 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/rhysd/actionlint v1.7.7 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // 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.1 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.5 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/tinylib/msgp v1.4.0 // indirect
|
||||
github.com/unknwon/com v1.0.1 // indirect
|
||||
github.com/valyala/fastjson v1.6.4 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
@ -260,20 +272,23 @@ require (
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // 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
|
||||
go.etcd.io/bbolt v1.4.3 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/jaytaylor/html2text => github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347
|
||||
|
||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||
|
||||
replace github.com/nektos/act => gitea.com/gitea/act v0.261.6
|
||||
@ -281,9 +296,6 @@ replace github.com/nektos/act => gitea.com/gitea/act v0.261.6
|
||||
// TODO: the only difference is in `PutObject`: the fork doesn't use `NewVerifyingReader(r, sha256.New(), oid, expectedSize)`, need to figure out why
|
||||
replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0
|
||||
|
||||
// TODO: This could be removed after https://github.com/mholt/archiver/pull/396 merged
|
||||
replace github.com/mholt/archiver/v3 => github.com/anchore/archiver/v3 v3.5.2
|
||||
|
||||
replace git.sr.ht/~mariusor/go-xsd-duration => gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078
|
||||
|
||||
exclude github.com/gofrs/uuid v3.2.0+incompatible
|
||||
|
||||
@ -86,7 +86,7 @@ func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, er
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := db.Exec(ctx, "UPDATE "+db.TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil {
|
||||
if _, err := db.Exec(ctx, "UPDATE `session` SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
"testing"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
|
||||
@ -40,7 +39,7 @@ func (source *TestSource) ToDB() ([]byte, error) {
|
||||
func TestDumpAuthSource(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
authSourceSchema, err := db.TableInfo(new(auth_model.Source))
|
||||
authSourceSchema, err := unittest.GetXORMEngine().TableInfo(new(auth_model.Source))
|
||||
assert.NoError(t, err)
|
||||
|
||||
auth_model.RegisterTypeConfig(auth_model.OAuth2, new(TestSource))
|
||||
|
||||
@ -21,28 +21,8 @@ type engineContextKeyType struct{}
|
||||
|
||||
var engineContextKey = engineContextKeyType{}
|
||||
|
||||
type xormContextType struct {
|
||||
context.Context
|
||||
engine Engine
|
||||
}
|
||||
|
||||
var xormContext *xormContextType
|
||||
|
||||
func newContext(ctx context.Context, e Engine) *xormContextType {
|
||||
return &xormContextType{Context: ctx, engine: e}
|
||||
}
|
||||
|
||||
// Value shadows Value for context.Context but allows us to get ourselves and an Engined object
|
||||
func (ctx *xormContextType) Value(key any) any {
|
||||
if key == engineContextKey {
|
||||
return ctx
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
|
||||
// WithContext returns this engine tied to this context
|
||||
func (ctx *xormContextType) WithContext(other context.Context) *xormContextType {
|
||||
return newContext(ctx, ctx.engine.Context(other))
|
||||
func withContextEngine(ctx context.Context, e Engine) context.Context {
|
||||
return context.WithValue(ctx, engineContextKey, e)
|
||||
}
|
||||
|
||||
var (
|
||||
@ -81,17 +61,19 @@ func contextSafetyCheck(e Engine) {
|
||||
callerNum := runtime.Callers(3, callers) // skip 3: runtime.Callers, contextSafetyCheck, GetEngine
|
||||
for i := range callerNum {
|
||||
if slices.Contains(contextSafetyDeniedFuncPCs, callers[i]) {
|
||||
panic(errors.New("using database context in an iterator would cause corrupted results"))
|
||||
panic(errors.New("using session context in an iterator would cause corrupted results"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetEngine gets an existing db Engine/Statement or creates a new Session
|
||||
func GetEngine(ctx context.Context) (e Engine) {
|
||||
defer func() { contextSafetyCheck(e) }()
|
||||
if e := getExistingEngine(ctx); e != nil {
|
||||
return e
|
||||
func GetEngine(ctx context.Context) Engine {
|
||||
if engine, ok := ctx.Value(engineContextKey).(Engine); ok {
|
||||
// if reusing the existing session, need to do "contextSafetyCheck" because the Iterate creates a "autoResetStatement=false" session
|
||||
contextSafetyCheck(engine)
|
||||
return engine
|
||||
}
|
||||
// no need to do "contextSafetyCheck" because it's a new Session
|
||||
return xormEngine.Context(ctx)
|
||||
}
|
||||
|
||||
@ -99,17 +81,6 @@ func GetXORMEngineForTesting() *xorm.Engine {
|
||||
return xormEngine
|
||||
}
|
||||
|
||||
// getExistingEngine gets an existing db Engine/Statement from this context or returns nil
|
||||
func getExistingEngine(ctx context.Context) (e Engine) {
|
||||
if engined, ok := ctx.(*xormContextType); ok {
|
||||
return engined.engine
|
||||
}
|
||||
if engined, ok := ctx.Value(engineContextKey).(*xormContextType); ok {
|
||||
return engined.engine
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Committer represents an interface to Commit or Close the Context
|
||||
type Committer interface {
|
||||
Commit() error
|
||||
@ -152,8 +123,8 @@ func (c *halfCommitter) Close() error {
|
||||
// And all operations submitted by the caller stack will be rollbacked as well, not only the operations in the current function.
|
||||
// d. It doesn't mean rollback is forbidden, but always do it only when there is an error, and you do want to rollback.
|
||||
func TxContext(parentCtx context.Context) (context.Context, Committer, error) {
|
||||
if sess, ok := inTransaction(parentCtx); ok {
|
||||
return newContext(parentCtx, sess), &halfCommitter{committer: sess}, nil
|
||||
if sess := getTransactionSession(parentCtx); sess != nil {
|
||||
return withContextEngine(parentCtx, sess), &halfCommitter{committer: sess}, nil
|
||||
}
|
||||
|
||||
sess := xormEngine.NewSession()
|
||||
@ -161,15 +132,14 @@ func TxContext(parentCtx context.Context) (context.Context, Committer, error) {
|
||||
_ = sess.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return newContext(xormContext, sess), sess, nil
|
||||
return withContextEngine(parentCtx, sess), sess, nil
|
||||
}
|
||||
|
||||
// WithTx represents executing database operations on a transaction, if the transaction exist,
|
||||
// this function will reuse it otherwise will create a new one and close it when finished.
|
||||
func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error {
|
||||
if sess, ok := inTransaction(parentCtx); ok {
|
||||
err := f(newContext(parentCtx, sess))
|
||||
if sess := getTransactionSession(parentCtx); sess != nil {
|
||||
err := f(withContextEngine(parentCtx, sess))
|
||||
if err != nil {
|
||||
// rollback immediately, in case the caller ignores returned error and tries to commit the transaction.
|
||||
_ = sess.Close()
|
||||
@ -195,7 +165,7 @@ func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f(newContext(parentCtx, sess)); err != nil {
|
||||
if err := f(withContextEngine(parentCtx, sess)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -333,32 +303,15 @@ func CountByBean(ctx context.Context, bean any) (int64, error) {
|
||||
return GetEngine(ctx).Count(bean)
|
||||
}
|
||||
|
||||
// TableName returns the table name according a bean object
|
||||
func TableName(bean any) string {
|
||||
return xormEngine.TableName(bean)
|
||||
}
|
||||
|
||||
// InTransaction returns true if the engine is in a transaction otherwise return false
|
||||
func InTransaction(ctx context.Context) bool {
|
||||
_, ok := inTransaction(ctx)
|
||||
return ok
|
||||
return getTransactionSession(ctx) != nil
|
||||
}
|
||||
|
||||
func inTransaction(ctx context.Context) (*xorm.Session, bool) {
|
||||
e := getExistingEngine(ctx)
|
||||
if e == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
switch t := e.(type) {
|
||||
case *xorm.Engine:
|
||||
return nil, false
|
||||
case *xorm.Session:
|
||||
if t.IsInTx() {
|
||||
return t, true
|
||||
}
|
||||
return nil, false
|
||||
default:
|
||||
return nil, false
|
||||
func getTransactionSession(ctx context.Context) *xorm.Session {
|
||||
e, _ := ctx.Value(engineContextKey).(Engine)
|
||||
if sess, ok := e.(*xorm.Session); ok && sess.IsInTx() {
|
||||
return sess
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -100,31 +100,36 @@ func TestContextSafety(t *testing.T) {
|
||||
assert.NoError(t, db.Insert(t.Context(), &TestModel2{ID: int64(-i)}))
|
||||
}
|
||||
|
||||
actualCount := 0
|
||||
// here: db.GetEngine(t.Context()) is a new *Session created from *Engine
|
||||
_ = db.WithTx(t.Context(), func(ctx context.Context) error {
|
||||
_ = db.GetEngine(ctx).Iterate(&TestModel1{}, func(i int, bean any) error {
|
||||
// here: db.GetEngine(ctx) is always the unclosed "Iterate" *Session with autoResetStatement=false,
|
||||
// and the internal states (including "cond" and others) are always there and not be reset in this callback.
|
||||
m1 := bean.(*TestModel1)
|
||||
assert.EqualValues(t, i+1, m1.ID)
|
||||
t.Run("Show-XORM-Bug", func(t *testing.T) {
|
||||
actualCount := 0
|
||||
// here: db.GetEngine(t.Context()) is a new *Session created from *Engine
|
||||
_ = db.WithTx(t.Context(), func(ctx context.Context) error {
|
||||
_ = db.GetEngine(ctx).Iterate(&TestModel1{}, func(i int, bean any) error {
|
||||
// here: db.GetEngine(ctx) is always the unclosed "Iterate" *Session with autoResetStatement=false,
|
||||
// and the internal states (including "cond" and others) are always there and not be reset in this callback.
|
||||
m1 := bean.(*TestModel1)
|
||||
assert.EqualValues(t, i+1, m1.ID)
|
||||
|
||||
// here: XORM bug, it fails because the SQL becomes "WHERE id=-1", "WHERE id=-1 AND id=-2", "WHERE id=-1 AND id=-2 AND id=-3" ...
|
||||
// and it conflicts with the "Iterate"'s internal states.
|
||||
// has, err := db.GetEngine(ctx).Get(&TestModel2{ID: -m1.ID})
|
||||
// here: XORM bug, it fails because the SQL becomes "WHERE id=-1", "WHERE id=-1 AND id=-2", "WHERE id=-1 AND id=-2 AND id=-3" ...
|
||||
// and it conflicts with the "Iterate"'s internal states.
|
||||
// has, err := db.GetEngine(ctx).Get(&TestModel2{ID: -m1.ID})
|
||||
|
||||
actualCount++
|
||||
actualCount++
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
assert.Equal(t, testCount, actualCount)
|
||||
})
|
||||
assert.Equal(t, testCount, actualCount)
|
||||
|
||||
// deny the bad usages
|
||||
assert.PanicsWithError(t, "using database context in an iterator would cause corrupted results", func() {
|
||||
_ = unittest.GetXORMEngine().Iterate(&TestModel1{}, func(i int, bean any) error {
|
||||
_ = db.GetEngine(t.Context())
|
||||
return nil
|
||||
t.Run("DenyBadUsage", func(t *testing.T) {
|
||||
assert.PanicsWithError(t, "using session context in an iterator would cause corrupted results", func() {
|
||||
_ = db.WithTx(t.Context(), func(ctx context.Context) error {
|
||||
return db.GetEngine(ctx).Iterate(&TestModel1{}, func(i int, bean any) error {
|
||||
_ = db.GetEngine(ctx)
|
||||
return nil
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/schemas"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver
|
||||
_ "github.com/lib/pq" // Needed for the Postgresql driver
|
||||
@ -67,11 +66,6 @@ var (
|
||||
_ Engine = (*xorm.Session)(nil)
|
||||
)
|
||||
|
||||
// TableInfo returns table's information via an object
|
||||
func TableInfo(v any) (*schemas.Table, error) {
|
||||
return xormEngine.TableInfo(v)
|
||||
}
|
||||
|
||||
// RegisterModel registers model, if initFuncs provided, it will be invoked after data model sync
|
||||
func RegisterModel(bean any, initFunc ...func() error) {
|
||||
registeredModels = append(registeredModels, bean)
|
||||
|
||||
@ -86,7 +86,6 @@ func InitEngine(ctx context.Context) error {
|
||||
func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) {
|
||||
xormEngine = eng
|
||||
xormEngine.SetDefaultContext(ctx)
|
||||
xormContext = &xormContextType{Context: ctx, engine: xormEngine}
|
||||
}
|
||||
|
||||
// UnsetDefaultEngine closes and unsets the default engine
|
||||
@ -98,7 +97,6 @@ func UnsetDefaultEngine() {
|
||||
_ = xormEngine.Close()
|
||||
xormEngine = nil
|
||||
}
|
||||
xormContext = nil
|
||||
}
|
||||
|
||||
// InitEngineWithMigration initializes a new xorm.Engine and sets it as the XORM's default context
|
||||
|
||||
@ -70,7 +70,7 @@ func TestPrimaryKeys(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, bean := range beans {
|
||||
table, err := db.TableInfo(bean)
|
||||
table, err := db.GetXORMEngineForTesting().TableInfo(bean)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -19,12 +19,7 @@ type ResourceIndex struct {
|
||||
MaxIndex int64 `xorm:"index"`
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrResouceOutdated represents an error when request resource outdated
|
||||
ErrResouceOutdated = errors.New("resource outdated")
|
||||
// ErrGetResourceIndexFailed represents an error when resource index retries 3 times
|
||||
ErrGetResourceIndexFailed = errors.New("get resource index failed")
|
||||
)
|
||||
var ErrGetResourceIndexFailed = errors.New("get resource index failed")
|
||||
|
||||
// SyncMaxResourceIndex sync the max index with the resource
|
||||
func SyncMaxResourceIndex(ctx context.Context, tableName string, groupID, maxIndex int64) (err error) {
|
||||
|
||||
@ -106,8 +106,8 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) {
|
||||
"WHEN milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL THEN issue.deadline_unix " +
|
||||
"WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " +
|
||||
"ELSE issue.deadline_unix END ASC").
|
||||
Desc("issue.created_unix").
|
||||
Desc("issue.id")
|
||||
Asc("issue.created_unix").
|
||||
Asc("issue.id")
|
||||
case "farduedate":
|
||||
sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id").
|
||||
OrderBy("CASE " +
|
||||
|
||||
@ -105,11 +105,6 @@ type MinimalOrg = Organization
|
||||
|
||||
// GetUserOrgsList returns all organizations the given user has access to
|
||||
func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, error) {
|
||||
schema, err := db.TableInfo(new(user_model.User))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputCols := []string{
|
||||
"id",
|
||||
"name",
|
||||
@ -122,7 +117,7 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg,
|
||||
|
||||
selectColumns := &strings.Builder{}
|
||||
for i, col := range outputCols {
|
||||
fmt.Fprintf(selectColumns, "`%s`.%s", schema.Name, col)
|
||||
_, _ = fmt.Fprintf(selectColumns, "`user`.%s", col)
|
||||
if i < len(outputCols)-1 {
|
||||
selectColumns.WriteString(", ")
|
||||
}
|
||||
|
||||
@ -282,11 +282,8 @@ func (opts FindReleasesOptions) ToOrders() string {
|
||||
|
||||
// GetTagNamesByRepoID returns a list of release tag names of repository.
|
||||
func GetTagNamesByRepoID(ctx context.Context, repoID int64) ([]string, error) {
|
||||
listOptions := db.ListOptions{
|
||||
ListAll: true,
|
||||
}
|
||||
opts := FindReleasesOptions{
|
||||
ListOptions: listOptions,
|
||||
ListOptions: db.ListOptionsAll,
|
||||
IncludeDrafts: true,
|
||||
IncludeTags: true,
|
||||
HasSha1: optional.Some(true),
|
||||
|
||||
@ -229,6 +229,10 @@ func RelativePath(ownerName, repoName string) string {
|
||||
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git"
|
||||
}
|
||||
|
||||
func RelativeWikiPath(ownerName, repoName string) string {
|
||||
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".wiki.git"
|
||||
}
|
||||
|
||||
// RelativePath should be an unix style path like username/reponame.git
|
||||
func (repo *Repository) RelativePath() string {
|
||||
return RelativePath(repo.OwnerName, repo.Name)
|
||||
@ -241,8 +245,10 @@ func (sr StorageRepo) RelativePath() string {
|
||||
return string(sr)
|
||||
}
|
||||
|
||||
// WikiStorageRepo returns the storage repo for the wiki
|
||||
// The wiki repository should have the same object format as the code repository
|
||||
func (repo *Repository) WikiStorageRepo() StorageRepo {
|
||||
return StorageRepo(strings.ToLower(repo.OwnerName) + "/" + strings.ToLower(repo.Name) + ".wiki.git")
|
||||
return StorageRepo(RelativeWikiPath(repo.OwnerName, repo.Name))
|
||||
}
|
||||
|
||||
// SanitizedOriginalURL returns a sanitized OriginalURL
|
||||
|
||||
@ -11,7 +11,6 @@ import (
|
||||
"strings"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
@ -86,12 +85,3 @@ func WikiPath(userName, repoName string) string {
|
||||
func (repo *Repository) WikiPath() string {
|
||||
return WikiPath(repo.OwnerName, repo.Name)
|
||||
}
|
||||
|
||||
// HasWiki returns true if repository has wiki.
|
||||
func (repo *Repository) HasWiki() bool {
|
||||
isDir, err := util.IsDir(repo.WikiPath())
|
||||
if err != nil {
|
||||
log.Error("Unable to check if %s is a directory: %v", repo.WikiPath(), err)
|
||||
}
|
||||
return isDir
|
||||
}
|
||||
|
||||
@ -35,11 +35,3 @@ func TestRepository_WikiPath(t *testing.T) {
|
||||
expected := filepath.Join(setting.RepoRootPath, "user2/repo1.wiki.git")
|
||||
assert.Equal(t, expected, repo.WikiPath())
|
||||
}
|
||||
|
||||
func TestRepository_HasWiki(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
assert.True(t, repo1.HasWiki())
|
||||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
assert.False(t, repo2.HasWiki())
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ func CheckConsistencyFor(t TestingT, beansToCheck ...any) {
|
||||
}
|
||||
|
||||
func checkForConsistency(t TestingT, bean any) {
|
||||
tb, err := db.TableInfo(bean)
|
||||
tb, err := GetXORMEngine().TableInfo(bean)
|
||||
assert.NoError(t, err)
|
||||
f := consistencyCheckMap[tb.Name]
|
||||
require.NotNil(t, f, "unknown bean type: %#v", bean)
|
||||
|
||||
@ -218,7 +218,7 @@ func NewFixturesLoader(x *xorm.Engine, opts FixturesOptions) (FixturesLoader, er
|
||||
xormBeans, _ := db.NamesToBean()
|
||||
f.xormTableNames = map[string]bool{}
|
||||
for _, bean := range xormBeans {
|
||||
f.xormTableNames[db.TableName(bean)] = true
|
||||
f.xormTableNames[x.TableName(bean)] = true
|
||||
}
|
||||
|
||||
return f, nil
|
||||
|
||||
@ -159,7 +159,7 @@ func DumpQueryResult(t require.TestingT, sqlOrBean any, sqlArgs ...any) {
|
||||
goDB := x.DB().DB
|
||||
sql, ok := sqlOrBean.(string)
|
||||
if !ok {
|
||||
sql = "SELECT * FROM " + db.TableName(sqlOrBean)
|
||||
sql = "SELECT * FROM " + x.TableName(sqlOrBean)
|
||||
} else if !strings.Contains(sql, " ") {
|
||||
sql = "SELECT * FROM " + sql
|
||||
}
|
||||
|
||||
@ -30,6 +30,8 @@ func CreateReader(input io.Reader, delimiter rune) *stdcsv.Reader {
|
||||
// thus would change `\t\t` to just `\t` or ` ` (two spaces) to just ` ` (single space)
|
||||
rd.TrimLeadingSpace = true
|
||||
}
|
||||
// Don't force validation of every row to have the same number of entries as the first row.
|
||||
rd.FieldsPerRecord = -1
|
||||
return rd
|
||||
}
|
||||
|
||||
|
||||
@ -94,6 +94,24 @@ j, ,\x20
|
||||
},
|
||||
expectedDelimiter: ',',
|
||||
},
|
||||
// case 3 - every delimiter used, default to comma and handle differing number of fields per record
|
||||
{
|
||||
csv: `col1,col2
|
||||
a;b
|
||||
c@e
|
||||
f g
|
||||
h|i
|
||||
jkl`,
|
||||
expectedRows: [][]string{
|
||||
{"col1", "col2"},
|
||||
{"a;b"},
|
||||
{"c@e"},
|
||||
{"f g"},
|
||||
{"h|i"},
|
||||
{"jkl"},
|
||||
},
|
||||
expectedDelimiter: ',',
|
||||
},
|
||||
}
|
||||
|
||||
for n, c := range cases {
|
||||
@ -119,21 +137,6 @@ func TestDetermineDelimiterShortBufferError(t *testing.T) {
|
||||
assert.Nil(t, rd, "CSV reader should be mnil")
|
||||
}
|
||||
|
||||
func TestDetermineDelimiterReadAllError(t *testing.T) {
|
||||
rd, err := CreateReaderAndDetermineDelimiter(nil, strings.NewReader(`col1,col2
|
||||
a;b
|
||||
c@e
|
||||
f g
|
||||
h|i
|
||||
jkl`))
|
||||
assert.NoError(t, err, "CreateReaderAndDetermineDelimiter() shouldn't throw error")
|
||||
assert.NotNil(t, rd, "CSV reader should not be mnil")
|
||||
rows, err := rd.ReadAll()
|
||||
assert.Error(t, err, "RaadAll() should throw error")
|
||||
assert.ErrorIs(t, err, csv.ErrFieldCount)
|
||||
assert.Empty(t, rows, "rows should be empty")
|
||||
}
|
||||
|
||||
func TestDetermineDelimiter(t *testing.T) {
|
||||
cases := []struct {
|
||||
csv string
|
||||
|
||||
@ -4,8 +4,11 @@
|
||||
package dump
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@ -16,7 +19,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/mholt/archives"
|
||||
)
|
||||
|
||||
var SupportedOutputTypes = []string{"zip", "tar", "tar.sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4", "tar.zst"}
|
||||
@ -60,37 +63,122 @@ func IsSubdir(upper, lower string) (bool, error) {
|
||||
}
|
||||
|
||||
type Dumper struct {
|
||||
Writer archiver.Writer
|
||||
Verbose bool
|
||||
|
||||
jobs chan archives.ArchiveAsyncJob
|
||||
errArchiveAsync chan error
|
||||
errArchiveJob chan error
|
||||
|
||||
globalExcludeAbsPaths []string
|
||||
}
|
||||
|
||||
func (dumper *Dumper) AddReader(r io.ReadCloser, info os.FileInfo, customName string) error {
|
||||
if dumper.Verbose {
|
||||
log.Info("Adding file %s", customName)
|
||||
func NewDumper(ctx context.Context, format string, output io.Writer) (*Dumper, error) {
|
||||
d := &Dumper{
|
||||
jobs: make(chan archives.ArchiveAsyncJob, 1),
|
||||
errArchiveAsync: make(chan error, 1),
|
||||
errArchiveJob: make(chan error, 1),
|
||||
}
|
||||
|
||||
return dumper.Writer.Write(archiver.File{
|
||||
FileInfo: archiver.FileInfo{
|
||||
FileInfo: info,
|
||||
CustomName: customName,
|
||||
},
|
||||
ReadCloser: r,
|
||||
// TODO: in the future, we could completely drop the "mholt/archives" dependency.
|
||||
// Then we only need to support "zip" and ".tar.gz" natively, and let users provide custom command line tools
|
||||
// like "zstd" or "xz" with compression-level arguments.
|
||||
var comp archives.ArchiverAsync
|
||||
switch format {
|
||||
case "zip":
|
||||
comp = archives.Zip{}
|
||||
case "tar":
|
||||
comp = archives.Tar{}
|
||||
case "tar.sz":
|
||||
comp = archives.CompressedArchive{Compression: archives.Sz{}, Archival: archives.Tar{}}
|
||||
case "tar.gz":
|
||||
comp = archives.CompressedArchive{Compression: archives.Gz{}, Archival: archives.Tar{}}
|
||||
case "tar.xz":
|
||||
comp = archives.CompressedArchive{Compression: archives.Xz{}, Archival: archives.Tar{}}
|
||||
case "tar.bz2":
|
||||
comp = archives.CompressedArchive{Compression: archives.Bz2{}, Archival: archives.Tar{}}
|
||||
case "tar.br":
|
||||
comp = archives.CompressedArchive{Compression: archives.Brotli{}, Archival: archives.Tar{}}
|
||||
case "tar.lz4":
|
||||
comp = archives.CompressedArchive{Compression: archives.Lz4{}, Archival: archives.Tar{}}
|
||||
case "tar.zst":
|
||||
comp = archives.CompressedArchive{Compression: archives.Zstd{}, Archival: archives.Tar{}}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported format: %s", format)
|
||||
}
|
||||
go func() {
|
||||
d.errArchiveAsync <- comp.ArchiveAsync(ctx, output, d.jobs)
|
||||
close(d.errArchiveAsync)
|
||||
}()
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (dumper *Dumper) runArchiveJob(job archives.ArchiveAsyncJob) error {
|
||||
dumper.jobs <- job
|
||||
select {
|
||||
case err := <-dumper.errArchiveAsync:
|
||||
if err == nil {
|
||||
return errors.New("archiver has been closed")
|
||||
}
|
||||
return err
|
||||
case err := <-dumper.errArchiveJob:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// AddFileByPath adds a file by its filesystem path
|
||||
func (dumper *Dumper) AddFileByPath(filePath, absPath string) error {
|
||||
if dumper.Verbose {
|
||||
log.Info("Adding local file %s", filePath)
|
||||
}
|
||||
|
||||
fileInfo, err := os.Stat(absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
archiveFileInfo := archives.FileInfo{
|
||||
FileInfo: fileInfo,
|
||||
NameInArchive: filePath,
|
||||
Open: func() (fs.File, error) { return os.Open(absPath) },
|
||||
}
|
||||
|
||||
return dumper.runArchiveJob(archives.ArchiveAsyncJob{
|
||||
File: archiveFileInfo,
|
||||
Result: dumper.errArchiveJob,
|
||||
})
|
||||
}
|
||||
|
||||
func (dumper *Dumper) AddFile(filePath, absPath string) error {
|
||||
file, err := os.Open(absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
type readerFile struct {
|
||||
r io.Reader
|
||||
info os.FileInfo
|
||||
}
|
||||
|
||||
var _ fs.File = (*readerFile)(nil)
|
||||
|
||||
func (f *readerFile) Stat() (fs.FileInfo, error) { return f.info, nil }
|
||||
func (f *readerFile) Read(bytes []byte) (int, error) { return f.r.Read(bytes) }
|
||||
func (f *readerFile) Close() error { return nil }
|
||||
|
||||
// AddFileByReader adds a file's contents from a Reader
|
||||
func (dumper *Dumper) AddFileByReader(r io.Reader, info os.FileInfo, customName string) error {
|
||||
if dumper.Verbose {
|
||||
log.Info("Adding storage file %s", customName)
|
||||
}
|
||||
defer file.Close()
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
fileInfo := archives.FileInfo{
|
||||
FileInfo: info,
|
||||
NameInArchive: customName,
|
||||
Open: func() (fs.File, error) { return &readerFile{r, info}, nil },
|
||||
}
|
||||
return dumper.AddReader(file, fileInfo, filePath)
|
||||
return dumper.runArchiveJob(archives.ArchiveAsyncJob{
|
||||
File: fileInfo,
|
||||
Result: dumper.errArchiveJob,
|
||||
})
|
||||
}
|
||||
|
||||
func (dumper *Dumper) Close() error {
|
||||
close(dumper.jobs)
|
||||
return <-dumper.errArchiveAsync
|
||||
}
|
||||
|
||||
func (dumper *Dumper) normalizeFilePath(absPath string) string {
|
||||
@ -143,7 +231,7 @@ func (dumper *Dumper) addFileOrDir(insidePath, absPath string, excludes []string
|
||||
|
||||
currentInsidePath := path.Join(insidePath, file.Name())
|
||||
if file.IsDir() {
|
||||
if err := dumper.AddFile(currentInsidePath, currentAbsPath); err != nil {
|
||||
if err := dumper.AddFileByPath(currentInsidePath, currentAbsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dumper.addFileOrDir(currentInsidePath, currentAbsPath, excludes); err != nil {
|
||||
@ -164,7 +252,7 @@ func (dumper *Dumper) addFileOrDir(insidePath, absPath string, excludes []string
|
||||
shouldAdd = targetStat.Mode().IsRegular()
|
||||
}
|
||||
if shouldAdd {
|
||||
if err = dumper.AddFile(currentInsidePath, currentAbsPath); err != nil {
|
||||
if err = dumper.AddFileByPath(currentInsidePath, currentAbsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
package dump
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@ -14,8 +16,8 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPrepareFileNameAndType(t *testing.T) {
|
||||
@ -67,28 +69,26 @@ func TestIsSubDir(t *testing.T) {
|
||||
assert.False(t, isSub)
|
||||
}
|
||||
|
||||
type testWriter struct {
|
||||
added []string
|
||||
}
|
||||
func TestDumperIntegration(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
dumper, err := NewDumper(t.Context(), "zip", &buf)
|
||||
require.NoError(t, err)
|
||||
|
||||
func (t *testWriter) Create(out io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
tmpDir := t.TempDir()
|
||||
_ = os.WriteFile(filepath.Join(tmpDir, "test.txt"), nil, 0o644)
|
||||
f, _ := os.Open(filepath.Join(tmpDir, "test.txt"))
|
||||
|
||||
func (t *testWriter) Write(f archiver.File) error {
|
||||
t.added = append(t.added, f.Name())
|
||||
return nil
|
||||
}
|
||||
fi, _ := f.Stat()
|
||||
err = dumper.AddFileByReader(f, fi, "test.txt")
|
||||
require.NoError(t, err)
|
||||
|
||||
func (t *testWriter) Close() error {
|
||||
return nil
|
||||
err = dumper.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Positive(t, buf.Len())
|
||||
}
|
||||
|
||||
func TestDumper(t *testing.T) {
|
||||
sortStrings := func(s []string) []string {
|
||||
sort.Strings(s)
|
||||
return s
|
||||
}
|
||||
tmpDir := t.TempDir()
|
||||
_ = os.MkdirAll(filepath.Join(tmpDir, "include/exclude1"), 0o755)
|
||||
_ = os.MkdirAll(filepath.Join(tmpDir, "include/exclude2"), 0o755)
|
||||
@ -98,16 +98,54 @@ func TestDumper(t *testing.T) {
|
||||
_ = os.WriteFile(filepath.Join(tmpDir, "include/exclude1/a-1"), nil, 0o644)
|
||||
_ = os.WriteFile(filepath.Join(tmpDir, "include/exclude2/a-2"), nil, 0o644)
|
||||
|
||||
tw := &testWriter{}
|
||||
d := &Dumper{Writer: tw}
|
||||
d.GlobalExcludeAbsPath(filepath.Join(tmpDir, "include/exclude1"))
|
||||
err := d.AddRecursiveExclude("include", filepath.Join(tmpDir, "include"), []string{filepath.Join(tmpDir, "include/exclude2")})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sortStrings([]string{"include/a", "include/sub", "include/sub/b"}), sortStrings(tw.added))
|
||||
sortStrings := func(s []string) []string {
|
||||
sort.Strings(s)
|
||||
return s
|
||||
}
|
||||
|
||||
tw = &testWriter{}
|
||||
d = &Dumper{Writer: tw}
|
||||
err = d.AddRecursiveExclude("include", filepath.Join(tmpDir, "include"), nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sortStrings([]string{"include/exclude2", "include/exclude2/a-2", "include/a", "include/sub", "include/sub/b", "include/exclude1", "include/exclude1/a-1"}), sortStrings(tw.added))
|
||||
t.Run("IncludesWithExcludes", func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
dumper, err := NewDumper(t.Context(), "tar", &buf)
|
||||
require.NoError(t, err)
|
||||
dumper.GlobalExcludeAbsPath(filepath.Join(tmpDir, "include/exclude1"))
|
||||
err = dumper.AddRecursiveExclude("include", filepath.Join(tmpDir, "include"), []string{filepath.Join(tmpDir, "include/exclude2")})
|
||||
require.NoError(t, err)
|
||||
err = dumper.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
files := extractTarFileNames(t, &buf)
|
||||
expected := []string{"include/a", "include/sub", "include/sub/b"}
|
||||
assert.Equal(t, sortStrings(expected), sortStrings(files))
|
||||
})
|
||||
|
||||
t.Run("IncludesAll", func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
dumper, err := NewDumper(t.Context(), "tar", &buf)
|
||||
require.NoError(t, err)
|
||||
err = dumper.AddRecursiveExclude("include", filepath.Join(tmpDir, "include"), nil)
|
||||
require.NoError(t, err)
|
||||
err = dumper.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
files := extractTarFileNames(t, &buf)
|
||||
expected := []string{
|
||||
"include/exclude2", "include/exclude2/a-2",
|
||||
"include/a", "include/sub", "include/sub/b",
|
||||
"include/exclude1", "include/exclude1/a-1",
|
||||
}
|
||||
assert.Equal(t, sortStrings(expected), sortStrings(files))
|
||||
})
|
||||
}
|
||||
|
||||
func extractTarFileNames(t *testing.T, buf *bytes.Buffer) (fileNames []string) {
|
||||
tr := tar.NewReader(buf)
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(t, err, "Error reading tar archive")
|
||||
fileNames = append(fileNames, hdr.Name)
|
||||
}
|
||||
return fileNames
|
||||
}
|
||||
|
||||
@ -30,6 +30,10 @@ type Parser struct {
|
||||
func NewParser(r io.Reader, format Format) *Parser {
|
||||
scanner := bufio.NewScanner(r)
|
||||
|
||||
// default MaxScanTokenSize = 64 kiB may be too small for some references,
|
||||
// so allow the buffer to grow up to 4x if needed
|
||||
scanner.Buffer(nil, 4*bufio.MaxScanTokenSize)
|
||||
|
||||
// in addition to the reference delimiter we specified in the --format,
|
||||
// `git for-each-ref` will always add a newline after every reference.
|
||||
refDelim := make([]byte, 0, len(format.refDelim)+1)
|
||||
@ -70,6 +74,9 @@ func NewParser(r io.Reader, format Format) *Parser {
|
||||
// { "objecttype": "tag", "refname:short": "v1.16.4", "object": "f460b7543ed500e49c133c2cd85c8c55ee9dbe27" }
|
||||
func (p *Parser) Next() map[string]string {
|
||||
if !p.scanner.Scan() {
|
||||
if err := p.scanner.Err(); err != nil {
|
||||
p.err = err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
fields, err := p.parseRef(p.scanner.Text())
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Fsck verifies the connectivity and validity of the objects in the database
|
||||
func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args TrustedCmdArgs) error {
|
||||
return NewCommand("fsck").AddArguments(args...).Run(ctx, &RunOpts{Timeout: timeout, Dir: repoPath})
|
||||
}
|
||||
@ -9,7 +9,6 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
giturl "code.gitea.io/gitea/modules/git/url"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
@ -33,15 +32,6 @@ func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string,
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetRemoteURL returns the url of a specific remote of the repository.
|
||||
func GetRemoteURL(ctx context.Context, repoPath, remoteName string) (*giturl.GitURL, error) {
|
||||
addr, err := GetRemoteAddress(ctx, repoPath, remoteName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return giturl.ParseGitURL(addr)
|
||||
}
|
||||
|
||||
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
|
||||
type ErrInvalidCloneAddr struct {
|
||||
Host string
|
||||
|
||||
@ -38,6 +38,17 @@ func (repo *Repository) GetAllCommitsCount() (int64, error) {
|
||||
return AllCommitsCount(repo.Ctx, repo.Path, false)
|
||||
}
|
||||
|
||||
func (repo *Repository) ShowPrettyFormatLogToList(ctx context.Context, revisionRange string) ([]*Commit, error) {
|
||||
// avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]'
|
||||
logs, _, err := NewCommand("log").AddArguments(prettyLogFormat).
|
||||
AddDynamicArguments(revisionRange).AddArguments("--").
|
||||
RunStdBytes(ctx, &RunOpts{Dir: repo.Path})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.parsePrettyFormatLogToList(logs)
|
||||
}
|
||||
|
||||
func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, error) {
|
||||
var commits []*Commit
|
||||
if len(logs) == 0 {
|
||||
|
||||
@ -79,12 +79,6 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveRemote removes a remote from repository.
|
||||
func (repo *Repository) RemoveRemote(name string) error {
|
||||
_, _, err := NewCommand("remote", "rm").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
|
||||
return err
|
||||
}
|
||||
|
||||
// RenameBranch rename a branch
|
||||
func (repo *Repository) RenameBranch(from, to string) error {
|
||||
_, _, err := NewCommand("branch", "-m").AddDynamicArguments(from, to).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
|
||||
|
||||
@ -16,20 +16,8 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
logger "code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// CompareInfo represents needed information for comparing references.
|
||||
type CompareInfo struct {
|
||||
MergeBase string
|
||||
BaseCommitID string
|
||||
HeadCommitID string
|
||||
Commits []*Commit
|
||||
NumFiles int
|
||||
}
|
||||
|
||||
// GetMergeBase checks and returns merge base of two branches and the reference used as base.
|
||||
func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, string, error) {
|
||||
if tmpRemote == "" {
|
||||
@ -49,83 +37,6 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri
|
||||
return strings.TrimSpace(stdout), base, err
|
||||
}
|
||||
|
||||
// GetCompareInfo generates and returns compare information between base and head branches of repositories.
|
||||
func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string, directComparison, fileOnly bool) (_ *CompareInfo, err error) {
|
||||
var (
|
||||
remoteBranch string
|
||||
tmpRemote string
|
||||
)
|
||||
|
||||
// We don't need a temporary remote for same repository.
|
||||
if repo.Path != basePath {
|
||||
// Add a temporary remote
|
||||
tmpRemote = strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
if err = repo.AddRemote(tmpRemote, basePath, false); err != nil {
|
||||
return nil, fmt.Errorf("AddRemote: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := repo.RemoveRemote(tmpRemote); err != nil {
|
||||
logger.Error("GetPullRequestInfo: RemoveRemote: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
compareInfo := new(CompareInfo)
|
||||
|
||||
compareInfo.HeadCommitID, err = GetFullCommitID(repo.Ctx, repo.Path, headBranch)
|
||||
if err != nil {
|
||||
compareInfo.HeadCommitID = headBranch
|
||||
}
|
||||
|
||||
compareInfo.MergeBase, remoteBranch, err = repo.GetMergeBase(tmpRemote, baseBranch, headBranch)
|
||||
if err == nil {
|
||||
compareInfo.BaseCommitID, err = GetFullCommitID(repo.Ctx, repo.Path, remoteBranch)
|
||||
if err != nil {
|
||||
compareInfo.BaseCommitID = remoteBranch
|
||||
}
|
||||
separator := "..."
|
||||
baseCommitID := compareInfo.MergeBase
|
||||
if directComparison {
|
||||
separator = ".."
|
||||
baseCommitID = compareInfo.BaseCommitID
|
||||
}
|
||||
|
||||
// We have a common base - therefore we know that ... should work
|
||||
if !fileOnly {
|
||||
// avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]'
|
||||
var logs []byte
|
||||
logs, _, err = NewCommand("log").AddArguments(prettyLogFormat).
|
||||
AddDynamicArguments(baseCommitID+separator+headBranch).AddArguments("--").
|
||||
RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
compareInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsePrettyFormatLogToList: %w", err)
|
||||
}
|
||||
} else {
|
||||
compareInfo.Commits = []*Commit{}
|
||||
}
|
||||
} else {
|
||||
compareInfo.Commits = []*Commit{}
|
||||
compareInfo.MergeBase, err = GetFullCommitID(repo.Ctx, repo.Path, remoteBranch)
|
||||
if err != nil {
|
||||
compareInfo.MergeBase = remoteBranch
|
||||
}
|
||||
compareInfo.BaseCommitID = compareInfo.MergeBase
|
||||
}
|
||||
|
||||
// Count number of changed files.
|
||||
// This probably should be removed as we need to use shortstat elsewhere
|
||||
// Now there is git diff --shortstat but this appears to be slower than simply iterating with --nameonly
|
||||
compareInfo.NumFiles, err = repo.GetDiffNumChangedFiles(remoteBranch, headBranch, directComparison)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return compareInfo, nil
|
||||
}
|
||||
|
||||
type lineCountWriter struct {
|
||||
numLines int
|
||||
}
|
||||
|
||||
@ -7,8 +7,6 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
@ -20,40 +18,6 @@ func (repo *Repository) IsTagExist(name string) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// GetTags returns all tags of the repository.
|
||||
// returning at most limit tags, or all if limit is 0.
|
||||
func (repo *Repository) GetTags(skip, limit int) ([]string, error) {
|
||||
var tagNames []string
|
||||
|
||||
tags, err := repo.gogitRepo.Tags()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = tags.ForEach(func(tag *plumbing.Reference) error {
|
||||
tagNames = append(tagNames, strings.TrimPrefix(tag.Name().String(), TagPrefix))
|
||||
return nil
|
||||
})
|
||||
|
||||
// Reverse order
|
||||
for i := 0; i < len(tagNames)/2; i++ {
|
||||
j := len(tagNames) - i - 1
|
||||
tagNames[i], tagNames[j] = tagNames[j], tagNames[i]
|
||||
}
|
||||
|
||||
// since we have to reverse order we can paginate only afterwards
|
||||
if len(tagNames) < skip {
|
||||
tagNames = []string{}
|
||||
} else {
|
||||
tagNames = tagNames[skip:]
|
||||
}
|
||||
if limit != 0 && len(tagNames) > limit {
|
||||
tagNames = tagNames[:limit]
|
||||
}
|
||||
|
||||
return tagNames, nil
|
||||
}
|
||||
|
||||
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
|
||||
func (repo *Repository) GetTagType(id ObjectID) (string, error) {
|
||||
// Get tag type
|
||||
|
||||
@ -22,13 +22,6 @@ func (repo *Repository) IsTagExist(name string) bool {
|
||||
return repo.IsReferenceExist(TagPrefix + name)
|
||||
}
|
||||
|
||||
// GetTags returns all tags of the repository.
|
||||
// returning at most limit tags, or all if limit is 0.
|
||||
func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
|
||||
tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, TrustedCmdArgs{TagPrefix, "--sort=-taggerdate"}, skip, limit)
|
||||
return tags, err
|
||||
}
|
||||
|
||||
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
|
||||
func (repo *Repository) GetTagType(id ObjectID) (string, error) {
|
||||
wr, rd, cancel, err := repo.CatFileBatchCheck(repo.Ctx)
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRepository_GetTags(t *testing.T) {
|
||||
func TestRepository_GetTagInfos(t *testing.T) {
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
||||
bareRepo1, err := OpenRepository(t.Context(), bareRepo1Path)
|
||||
if err != nil {
|
||||
|
||||
48
modules/gitrepo/config.go
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
)
|
||||
|
||||
func GitConfigGet(ctx context.Context, repo Repository, key string) (string, error) {
|
||||
result, _, err := git.NewCommand("config", "--get").
|
||||
AddDynamicArguments(key).
|
||||
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(result), nil
|
||||
}
|
||||
|
||||
func getRepoConfigLockKey(repoStoragePath string) string {
|
||||
return "repo-config:" + repoStoragePath
|
||||
}
|
||||
|
||||
// GitConfigAdd add a git configuration key to a specific value for the given repository.
|
||||
func GitConfigAdd(ctx context.Context, repo Repository, key, value string) error {
|
||||
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
|
||||
_, _, err := git.NewCommand("config", "--add").
|
||||
AddDynamicArguments(key, value).
|
||||
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// GitConfigSet updates a git configuration key to a specific value for the given repository.
|
||||
// If the key does not exist, it will be created.
|
||||
// If the key exists, it will be updated to the new value.
|
||||
func GitConfigSet(ctx context.Context, repo Repository, key, value string) error {
|
||||
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
|
||||
_, _, err := git.NewCommand("config").
|
||||
AddDynamicArguments(key, value).
|
||||
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
|
||||
return err
|
||||
})
|
||||
}
|
||||
16
modules/gitrepo/fsck.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
)
|
||||
|
||||
// Fsck verifies the connectivity and validity of the objects in the database
|
||||
func Fsck(ctx context.Context, repo Repository, timeout time.Duration, args git.TrustedCmdArgs) error {
|
||||
return git.NewCommand("fsck").AddArguments(args...).Run(ctx, &git.RunOpts{Timeout: timeout, Dir: repoPath(repo)})
|
||||
}
|
||||
@ -69,7 +69,8 @@ func IsRepositoryExist(ctx context.Context, repo Repository) (bool, error) {
|
||||
return util.IsExist(repoPath(repo))
|
||||
}
|
||||
|
||||
// DeleteRepository deletes the repository directory from the disk
|
||||
// DeleteRepository deletes the repository directory from the disk, it will return
|
||||
// nil if the repository does not exist.
|
||||
func DeleteRepository(ctx context.Context, repo Repository) error {
|
||||
return util.RemoveAll(repoPath(repo))
|
||||
}
|
||||
@ -81,3 +82,7 @@ func RenameRepository(ctx context.Context, repo, newRepo Repository) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitRepository(ctx context.Context, repo Repository, objectFormatName string) error {
|
||||
return git.InitRepository(ctx, repoPath(repo), true, objectFormatName)
|
||||
}
|
||||
|
||||
85
modules/gitrepo/remote.go
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
giturl "code.gitea.io/gitea/modules/git/url"
|
||||
"code.gitea.io/gitea/modules/globallock"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
type RemoteOption string
|
||||
|
||||
const (
|
||||
RemoteOptionMirrorPush RemoteOption = "--mirror=push"
|
||||
RemoteOptionMirrorFetch RemoteOption = "--mirror=fetch"
|
||||
)
|
||||
|
||||
func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL string, options ...RemoteOption) error {
|
||||
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
|
||||
cmd := git.NewCommand("remote", "add")
|
||||
if len(options) > 0 {
|
||||
switch options[0] {
|
||||
case RemoteOptionMirrorPush:
|
||||
cmd.AddArguments("--mirror=push")
|
||||
case RemoteOptionMirrorFetch:
|
||||
cmd.AddArguments("--mirror=fetch")
|
||||
default:
|
||||
return errors.New("unknown remote option: " + string(options[0]))
|
||||
}
|
||||
}
|
||||
_, _, err := cmd.
|
||||
AddDynamicArguments(remoteName, remoteURL).
|
||||
RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func GitRemoteRemove(ctx context.Context, repo Repository, remoteName string) error {
|
||||
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
|
||||
cmd := git.NewCommand("remote", "rm").AddDynamicArguments(remoteName)
|
||||
_, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// GitRemoteGetURL returns the url of a specific remote of the repository.
|
||||
func GitRemoteGetURL(ctx context.Context, repo Repository, remoteName string) (*giturl.GitURL, error) {
|
||||
addr, err := git.GetRemoteAddress(ctx, repoPath(repo), remoteName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addr == "" {
|
||||
return nil, util.NewNotExistErrorf("remote '%s' does not exist", remoteName)
|
||||
}
|
||||
return giturl.ParseGitURL(addr)
|
||||
}
|
||||
|
||||
// GitRemotePrune prunes the remote branches that no longer exist in the remote repository.
|
||||
func GitRemotePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
|
||||
return git.NewCommand("remote", "prune").AddDynamicArguments(remoteName).
|
||||
Run(ctx, &git.RunOpts{
|
||||
Timeout: timeout,
|
||||
Dir: repoPath(repo),
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
})
|
||||
}
|
||||
|
||||
// GitRemoteUpdatePrune updates the remote branches and prunes the ones that no longer exist in the remote repository.
|
||||
func GitRemoteUpdatePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
|
||||
return git.NewCommand("remote", "update", "--prune").AddDynamicArguments(remoteName).
|
||||
Run(ctx, &git.RunOpts{
|
||||
Timeout: timeout,
|
||||
Dir: repoPath(repo),
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
})
|
||||
}
|
||||
@ -14,6 +14,7 @@ import (
|
||||
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
|
||||
inner_meilisearch "code.gitea.io/gitea/modules/indexer/internal/meilisearch"
|
||||
"code.gitea.io/gitea/modules/indexer/issues/internal"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
)
|
||||
@ -106,7 +107,8 @@ func (b *Indexer) Index(_ context.Context, issues ...*internal.IndexerData) erro
|
||||
return nil
|
||||
}
|
||||
for _, issue := range issues {
|
||||
_, err := b.inner.Client.Index(b.inner.VersionedIndexName()).AddDocuments(issue)
|
||||
// use default primary key which should be "id"
|
||||
_, err := b.inner.Client.Index(b.inner.VersionedIndexName()).AddDocuments(issue, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -299,18 +301,13 @@ func doubleQuoteKeyword(k string) string {
|
||||
func convertHits(searchRes *meilisearch.SearchResponse) ([]internal.Match, error) {
|
||||
hits := make([]internal.Match, 0, len(searchRes.Hits))
|
||||
for _, hit := range searchRes.Hits {
|
||||
hit, ok := hit.(map[string]any)
|
||||
if !ok {
|
||||
return nil, ErrMalformedResponse
|
||||
}
|
||||
|
||||
issueID, ok := hit["id"].(float64)
|
||||
if !ok {
|
||||
var issueID int64
|
||||
if err := json.Unmarshal(hit["id"], &issueID); err != nil {
|
||||
return nil, ErrMalformedResponse
|
||||
}
|
||||
|
||||
hits = append(hits, internal.Match{
|
||||
ID: int64(issueID),
|
||||
ID: issueID,
|
||||
})
|
||||
}
|
||||
return hits, nil
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/indexer/issues/internal"
|
||||
"code.gitea.io/gitea/modules/indexer/issues/internal/tests"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -45,30 +46,42 @@ func TestMeilisearchIndexer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConvertHits(t *testing.T) {
|
||||
convert := func(d any) []byte {
|
||||
b, _ := json.Marshal(d)
|
||||
return b
|
||||
}
|
||||
|
||||
_, err := convertHits(&meilisearch.SearchResponse{
|
||||
Hits: []any{"aa", "bb", "cc", "dd"},
|
||||
Hits: []meilisearch.Hit{
|
||||
{
|
||||
"aa": convert(1),
|
||||
"bb": convert(2),
|
||||
"cc": convert(3),
|
||||
"dd": convert(4),
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.ErrorIs(t, err, ErrMalformedResponse)
|
||||
|
||||
validResponse := &meilisearch.SearchResponse{
|
||||
Hits: []any{
|
||||
map[string]any{
|
||||
"id": float64(11),
|
||||
"title": "a title",
|
||||
"content": "issue body with no match",
|
||||
"comments": []any{"hey whats up?", "I'm currently bowling", "nice"},
|
||||
Hits: []meilisearch.Hit{
|
||||
{
|
||||
"id": convert(float64(11)),
|
||||
"title": convert("a title"),
|
||||
"content": convert("issue body with no match"),
|
||||
"comments": convert([]any{"hey whats up?", "I'm currently bowling", "nice"}),
|
||||
},
|
||||
map[string]any{
|
||||
"id": float64(22),
|
||||
"title": "Bowling as title",
|
||||
"content": "",
|
||||
"comments": []any{},
|
||||
{
|
||||
"id": convert(float64(22)),
|
||||
"title": convert("Bowling as title"),
|
||||
"content": convert(""),
|
||||
"comments": convert([]any{}),
|
||||
},
|
||||
map[string]any{
|
||||
"id": float64(33),
|
||||
"title": "Bowl-ing as fuzzy match",
|
||||
"content": "",
|
||||
"comments": []any{},
|
||||
{
|
||||
"id": convert(float64(33)),
|
||||
"title": convert("Bowl-ing as fuzzy match"),
|
||||
"content": convert(""),
|
||||
"comments": convert([]any{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -282,9 +282,9 @@ type CreateBranchRepoOption struct {
|
||||
OldRefName string `json:"old_ref_name" binding:"GitRefName;MaxSize(100)"`
|
||||
}
|
||||
|
||||
// UpdateBranchRepoOption options when updating a branch in a repository
|
||||
// RenameBranchRepoOption options when renaming a branch in a repository
|
||||
// swagger:model
|
||||
type UpdateBranchRepoOption struct {
|
||||
type RenameBranchRepoOption struct {
|
||||
// New branch name
|
||||
//
|
||||
// required: true
|
||||
|
||||
@ -14,8 +14,7 @@ import (
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
giturl "code.gitea.io/gitea/modules/git/url"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
@ -145,18 +144,12 @@ type remoteAddress struct {
|
||||
|
||||
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
|
||||
ret := remoteAddress{}
|
||||
remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
|
||||
u, err := gitrepo.GitRemoteGetURL(ctx, m, remoteName)
|
||||
if err != nil {
|
||||
log.Error("GetRemoteURL %v", err)
|
||||
return ret
|
||||
}
|
||||
|
||||
u, err := giturl.ParseGitURL(remoteURL)
|
||||
if err != nil {
|
||||
log.Error("giturl.Parse %v", err)
|
||||
return ret
|
||||
}
|
||||
|
||||
if u.Scheme != "ssh" && u.Scheme != "file" {
|
||||
if u.User != nil {
|
||||
ret.Username = u.User.Username()
|
||||
|
||||
@ -551,6 +551,14 @@ repo.transfer.body = To accept or reject it, visit %s or just ignore it.
|
||||
repo.collaborator.added.subject = %s added you to %s
|
||||
repo.collaborator.added.text = You have been added as a collaborator of repository:
|
||||
|
||||
repo.actions.run.failed = Run failed
|
||||
repo.actions.run.succeeded = Run succeeded
|
||||
repo.actions.run.cancelled = Run cancelled
|
||||
repo.actions.jobs.all_succeeded = All jobs have succeeded
|
||||
repo.actions.jobs.all_failed = All jobs have failed
|
||||
repo.actions.jobs.some_not_successful = Some jobs were not successful
|
||||
repo.actions.jobs.all_cancelled = All jobs have been cancelled
|
||||
|
||||
team_invite.subject = %[1]s has invited you to join the %[2]s organization
|
||||
team_invite.text_1 = %[1]s has invited you to join team %[2]s in organization %[3]s.
|
||||
team_invite.text_2 = Please click the following link to join the team:
|
||||
|
||||
@ -120,6 +120,7 @@ error404=Níl an leathanach atá tú ag iarraidh a bhaint amach <strong>ann</str
|
||||
error503=Níorbh fhéidir leis an bhfreastalaí d’iarratas a chomhlánú. Déan iarracht arís ar ball.
|
||||
go_back=Ar ais
|
||||
invalid_data=Sonraí neamhbhailí: %v
|
||||
nothing_has_been_changed=Níl aon rud athraithe.
|
||||
|
||||
never=Riamh
|
||||
unknown=Anaithnid
|
||||
@ -2843,6 +2844,11 @@ settings.location=Suíomh
|
||||
settings.permission=Ceadanna
|
||||
settings.repoadminchangeteam=Is féidir le riarthóir an stórais rochtain d'fhoirne a chur leis agus a bhaint
|
||||
settings.visibility=Infheictheacht
|
||||
settings.change_visibility=Athraigh Infheictheacht
|
||||
settings.change_visibility_notices_1=Má dhéantar an eagraíocht a thiontú go príobháideach, bainfear réaltaí an stórais agus ní féidir iad a athchóiriú.
|
||||
settings.change_visibility_notices_2=Caillfidh daoine nach baill iad rochtain ar stórtha na heagraíochta má athraítear an infheictheacht go príobháideach.
|
||||
settings.change_visibility_success=Tá infheictheacht na heagraíochta %s athraithe go rathúil.
|
||||
settings.visibility_desc=Athraigh cé a fhéadfaidh an eagraíocht agus a stórtha a fheiceáil.
|
||||
settings.visibility.public=Poiblí
|
||||
settings.visibility.limited=Teoranta (Infheicthe d'úsáideoirí fíordheimhnithe amháin)
|
||||
settings.visibility.limited_shortname=Teoranta
|
||||
@ -3419,6 +3425,7 @@ config.picture_service=Seirbhís Pictiúr
|
||||
config.disable_gravatar=Díchumasaigh Gravatar
|
||||
config.enable_federated_avatar=Cumasaigh Avatars Cónaidhme
|
||||
config.open_with_editor_app_help=Na heagarthóirí "Oscailte le" don roghchlár Clón. Má fhágtar folamh é, úsáidfear an réamhshocrú. Leathnaigh chun an réamhshocrú a fheiceáil.
|
||||
config.git_guide_remote_name=Ainm iargúlta stórais le haghaidh orduithe git sa treoir
|
||||
|
||||
config.git_config=Cumraíocht Git
|
||||
config.git_disable_diff_highlight=Díchumasaigh Aibhsiú Comhréire Diff
|
||||
|
||||
@ -120,6 +120,7 @@ error404=アクセスしようとしたページは<strong>存在しない</stro
|
||||
error503=サーバーはリクエストを完了できませんでした。 後でもう一度お試しください。
|
||||
go_back=戻る
|
||||
invalid_data=無効なデータ: %v
|
||||
nothing_has_been_changed=何も変更されていません。
|
||||
|
||||
never=無し
|
||||
unknown=不明
|
||||
@ -1322,6 +1323,7 @@ commit_graph.color=カラー
|
||||
commit.contained_in=このコミットが含まれているのは:
|
||||
commit.contained_in_default_branch=このコミットはデフォルトブランチに含まれています
|
||||
commit.load_referencing_branches_and_tags=このコミットを参照しているブランチやタグを取得
|
||||
commit.merged_in_pr=このコミットはプルリクエスト %s でマージされました。
|
||||
blame=Blame
|
||||
download_file=ファイルをダウンロード
|
||||
normal_view=通常表示
|
||||
@ -2842,6 +2844,11 @@ settings.location=場所
|
||||
settings.permission=許可
|
||||
settings.repoadminchangeteam=リポジトリ管理者はチームのアクセス権の追加・削除が可能
|
||||
settings.visibility=表示
|
||||
settings.change_visibility=公開範囲を変更
|
||||
settings.change_visibility_notices_1=組織をプライベートに変換すると、リポジトリのスターが削除され、復元することはできません。
|
||||
settings.change_visibility_notices_2=公開範囲をプライベートに変更すると、非メンバーは組織のリポジトリにアクセスできなくなります。
|
||||
settings.change_visibility_success=組織 %s の公開範囲を変更しました。
|
||||
settings.visibility_desc=組織とそのリポジトリを誰が閲覧できるかを変更します。
|
||||
settings.visibility.public=公開
|
||||
settings.visibility.limited=限定 (認証済みユーザーにのみ表示)
|
||||
settings.visibility.limited_shortname=限定
|
||||
@ -3418,6 +3425,7 @@ config.picture_service=画像サービス
|
||||
config.disable_gravatar=Gravatarが無効
|
||||
config.enable_federated_avatar=フェデレーテッド・アバター有効
|
||||
config.open_with_editor_app_help=クローンメニューの「~で開く」に表示するエディタ。 空白のままにするとデフォルトが使用されます。 展開するとデフォルトを確認できます。
|
||||
config.git_guide_remote_name=操作説明内のgitコマンドで使うリポジトリリモート名
|
||||
|
||||
config.git_config=Git設定
|
||||
config.git_disable_diff_highlight=Diffのシンタックスハイライトが無効
|
||||
|
||||
@ -159,7 +159,7 @@ filter.not_archived=Não Arquivados
|
||||
filter.is_fork=Fork
|
||||
filter.not_fork=Não Fork
|
||||
filter.is_mirror=Espelhado
|
||||
filter.not_mirror=Não espelhado
|
||||
filter.not_mirror=Não Espelhado
|
||||
filter.is_template=Template
|
||||
filter.not_template=Não Modelo
|
||||
filter.public=Pública
|
||||
@ -529,8 +529,8 @@ release.new.text=<b>@%[1]s</b> lançou a versão %[2]s em %[3]s
|
||||
release.title=Título: %s
|
||||
release.note=Nota:
|
||||
release.downloads=Downloads:
|
||||
release.download.zip=Código Fonte (ZIP)
|
||||
release.download.targz=Código Fonte (TAR.GZ)
|
||||
release.download.zip=Código-Fonte (ZIP)
|
||||
release.download.targz=Código-Fonte (TAR.GZ)
|
||||
|
||||
repo.transfer.subject_to=%s gostaria de transferir "%s" para %s
|
||||
repo.transfer.subject_to_you=%s gostaria de transferir "%s" para você
|
||||
@ -1010,6 +1010,7 @@ new_repo_helper=Um repositório contém todos os arquivos do projeto, inclusive
|
||||
owner=Proprietário
|
||||
owner_helper=Algumas organizações podem não aparecer no menu devido a um limite de contagem dos repositórios.
|
||||
repo_name=Nome do repositório
|
||||
repo_name_helper=Bons nomes de repositórios usam palavras-chave curtas, memorizáveis e únicas. Um repositório chamado ".profile" ou ".profile-private" pode ser usado para adicionar um README.md para seu perfil de usuário/organização.
|
||||
repo_size=Tamanho do repositório
|
||||
template=Modelo
|
||||
template_select=Selecione um modelo.
|
||||
@ -1021,7 +1022,7 @@ visibility_helper=Tornar o repositório privado
|
||||
visibility_helper_forced=O administrador do site força novos repositórios a serem privados.
|
||||
visibility_fork_helper=(Esta alteração irá afetar todos os forks.)
|
||||
clone_helper=Precisa de ajuda com o clone? Visite a <a target="_blank" rel="noopener noreferrer" href="%s">Ajuda</a>.
|
||||
fork_repo=Fork do repositório
|
||||
fork_repo=Fork do Repositório
|
||||
fork_from=Fork de
|
||||
already_forked=Você já fez o fork de %s
|
||||
fork_to_different_account=Faça um fork para uma conta diferente
|
||||
@ -1030,12 +1031,13 @@ fork_branch=Branch a ser clonado para o fork
|
||||
all_branches=Todos os branches
|
||||
view_all_branches=Ver todos branches
|
||||
view_all_tags=Ver todas as tags
|
||||
fork_no_valid_owners=Não é possível fazer um fork desse repositório porque não há proprietários validos.
|
||||
use_template=Usar este modelo
|
||||
open_with_editor=Abrir com %s
|
||||
download_zip=Baixar ZIP
|
||||
download_tar=Baixar TAR.GZ
|
||||
download_bundle=Baixar PACOTE
|
||||
generate_repo=Gerar repositório
|
||||
generate_repo=Gerar Repositório
|
||||
generate_from=Gerar de
|
||||
repo_desc=Descrição
|
||||
repo_desc_helper=Digite uma breve descrição (opcional)
|
||||
@ -1043,7 +1045,7 @@ repo_no_desc=Descrição não fornecida
|
||||
repo_lang=Linguagens
|
||||
repo_gitignore_helper=Selecione modelos do .gitignore.
|
||||
repo_gitignore_helper_desc=Escolha os arquivos que não serão rastreados da lista de modelos para linguagens comuns. Artefatos típicos gerados pelos compiladores de cada linguagem estão incluídos no .gitignore por padrão.
|
||||
issue_labels=Etiquetas de issue
|
||||
issue_labels=Etiquetas de Issue
|
||||
issue_labels_helper=Selecione um conjunto de etiquetas de issue.
|
||||
license=Licença
|
||||
license_helper=Selecione um arquivo de licença.
|
||||
@ -1058,7 +1060,7 @@ trust_model_helper_collaborator=Colaborador: Confiar em assinaturas de colaborad
|
||||
trust_model_helper_committer=Committer: Confiar em assinaturas que correspondem aos committers
|
||||
trust_model_helper_collaborator_committer=Colaborador+Committer: Confiar em assinaturas dos colaboradores que correspondem ao committer
|
||||
trust_model_helper_default=Padrão: Usar o modelo de confiança padrão para esta instalação
|
||||
create_repo=Criar repositório
|
||||
create_repo=Criar Repositório
|
||||
default_branch=Branch Padrão
|
||||
default_branch_label=padrão
|
||||
default_branch_helper=O branch padrão é o branch base para pull requests e commits de código.
|
||||
@ -1088,6 +1090,7 @@ stars=Favoritos
|
||||
reactions_more=e %d mais
|
||||
unit_disabled=O administrador do site desabilitou esta seção do repositório.
|
||||
language_other=Outra
|
||||
adopt_search=Digite o nome de usuário para pesquisar por repositórios órfãos… (deixe em branco para encontrar todos)
|
||||
adopt_preexisting_label=Adotar Arquivos
|
||||
adopt_preexisting=Adotar arquivos pré-existentes
|
||||
adopt_preexisting_content=Criar repositório a partir de %s
|
||||
@ -1125,6 +1128,8 @@ template.issue_labels=Etiquetas de issue
|
||||
template.one_item=Deve-se selecionar pelo menos um item de modelo
|
||||
template.invalid=Deve-se selecionar um repositório de modelo
|
||||
|
||||
archive.title=Este repositório está arquivado. Você pode visualizar arquivos e cloná-lo. Você não pode abrir issues ou pull requests ou fazer push de commits.
|
||||
archive.title_date=Este repositório foi arquivado em %s. Você pode visualizar arquivos e cloná-lo. Você não pode abrir issues ou pull requests ou fazer push de commits.
|
||||
archive.issue.nocomment=Este repositório está arquivado. Você não pode comentar nas issues.
|
||||
archive.pull.nocomment=Este repositório está arquivado. Você não pode comentar nos pull requests.
|
||||
|
||||
@ -1141,6 +1146,7 @@ migrate_options_lfs=Migrar arquivos LFS
|
||||
migrate_options_lfs_endpoint.label=Destino LFS
|
||||
migrate_options_lfs_endpoint.description=A migração tentará usar seu controle remoto Git para <a target="_blank" rel="noopener noreferrer" href="%s">determinar o servidor LFS</a>. Você também pode especificar um destino personalizado se os dados do repositório LFS forem armazenados em outro lugar.
|
||||
migrate_options_lfs_endpoint.description.local=Um caminho de servidor local também é suportado.
|
||||
migrate_options_lfs_endpoint.placeholder=Se for deixado em branco, o destino será derivado do URL de clone.
|
||||
migrate_items=Itens da migração
|
||||
migrate_items_wiki=Wiki
|
||||
migrate_items_milestones=Marcos
|
||||
@ -1149,11 +1155,13 @@ migrate_items_issues=Issues
|
||||
migrate_items_pullrequests=Pull Requests
|
||||
migrate_items_merge_requests=Requisições de merge
|
||||
migrate_items_releases=Versões
|
||||
migrate_repo=Migrar repositório
|
||||
migrate_repo=Migrar Repositório
|
||||
migrate.clone_address=Migrar / Clonar de URL
|
||||
migrate.clone_address_desc=URL HTTP (S) ou Git 'clone' de um repositório existente
|
||||
migrate.github_token_desc=Você pode colocar um ou mais tokens aqui, separados por vírgulas, para tornar a migração mais rápida, contornando os limites da API do GitHub. AVISO: abusar deste recurso pode violar a política do provedor de serviços e pode levar a que a sua(s) conta(s) seja bloqueada.
|
||||
migrate.clone_local_path=ou um caminho de servidor local
|
||||
migrate.permission_denied=Você não pode importar repositórios locais.
|
||||
migrate.permission_denied_blocked=Você não pode importar dos hosts não permitidos, por favor peça ao administrador para verificar as configurações ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS.
|
||||
migrate.invalid_local_path=O caminho local é inválido. Ele não existe ou não é um diretório.
|
||||
migrate.invalid_lfs_endpoint=O destino LFS não é válido.
|
||||
migrate.failed=Migração falhou: %v
|
||||
@ -1161,6 +1169,7 @@ migrate.migrate_items_options=Um Token de Acesso é necessário para migrar iten
|
||||
migrated_from=Migrado de <a href="%[1]s">%[2]s</a>
|
||||
migrated_from_fake=Migrado de %[1]s
|
||||
migrate.migrate=Migrar de %s
|
||||
migrate.migrating=Migrando de <b>%s</b>…
|
||||
migrate.migrating_failed=Migração a partir de <b>%s</b> falhou.
|
||||
migrate.migrating_failed.error=Falha ao migrar: %s
|
||||
migrate.migrating_failed_no_addr=A migração falhou.
|
||||
@ -1172,14 +1181,19 @@ migrate.gogs.description=Migrar dados de notabug.org ou de outras instâncias do
|
||||
migrate.onedev.description=Migrar dados de code.onedev.io ou de outras instâncias do OneDev.
|
||||
migrate.codebase.description=Migrar dados de codebasehq.com.
|
||||
migrate.gitbucket.description=Migrar dados de instâncias do GitBucket.
|
||||
migrate.codecommit.description=Migrar dados do AWS CodeCommit.
|
||||
migrate.codecommit.aws_access_key_id=Chave ID AWS (Access Key ID)
|
||||
migrate.codecommit.aws_secret_access_key=Chave Secreta AWS (Secret Access Key)
|
||||
migrate.codecommit.https_git_credentials_username=Nome de Usuário para Git HTTPS
|
||||
migrate.codecommit.https_git_credentials_password=Senha para Git HTTPS
|
||||
migrate.migrating_git=Migrando dados Git
|
||||
migrate.migrating_topics=Migrando tópicos
|
||||
migrate.migrating_topics=Migrando Tópicos
|
||||
migrate.migrating_milestones=Migrando Marcos
|
||||
migrate.migrating_labels=Migrando Rótulos
|
||||
migrate.migrating_releases=Migrando Versões
|
||||
migrate.migrating_issues=Migrando Issues
|
||||
migrate.migrating_pulls=Migrando Pull Requests
|
||||
migrate.cancel_migrating_title=Cancelar migração
|
||||
migrate.cancel_migrating_title=Cancelar Migração
|
||||
migrate.cancel_migrating_confirm=Você quer cancelar essa migração?
|
||||
migration_status=Status da migração
|
||||
|
||||
@ -1195,7 +1209,8 @@ watch=Observar
|
||||
unstar=Retirar dos favoritos
|
||||
star=Juntar aos favoritos
|
||||
fork=Fork
|
||||
download_archive=Baixar repositório
|
||||
action.blocked_user=Não é possível executar a ação porque você está bloqueado pelo proprietário do repositório.
|
||||
download_archive=Baixar Repositório
|
||||
more_operations=Mais Operações
|
||||
|
||||
quick_guide=Guia Rápido
|
||||
@ -1203,6 +1218,7 @@ clone_this_repo=Clonar este repositório
|
||||
cite_this_repo=Citar este repositório
|
||||
create_new_repo_command=Criando um novo repositório por linha de comando
|
||||
push_exist_repo=Realizando push para um repositório existente por linha de comando
|
||||
empty_message=Este repositório está vazio.
|
||||
broken_message=Os dados Git subjacentes a este repositório não podem ser lidos. Entre em contato com o administrador desta instância ou exclua este repositório.
|
||||
|
||||
code=Código
|
||||
@ -1220,6 +1236,7 @@ projects=Projetos
|
||||
packages=Pacotes
|
||||
actions=Ações
|
||||
labels=Etiquetas
|
||||
org_labels_desc=Etiquetas a nível de organização que podem ser usadas com <strong>todos os repositórios</strong> sob esta organização
|
||||
org_labels_desc_manage=gerenciar
|
||||
|
||||
milestone=Marco
|
||||
@ -1234,9 +1251,9 @@ tagged_this=criou essa tag
|
||||
file.title=%s em %s
|
||||
file_raw=Original
|
||||
file_history=Histórico
|
||||
file_view_source=Exibir código-fonte
|
||||
file_view_source=Exibir Código-Fonte
|
||||
file_view_rendered=Ver Renderizado
|
||||
file_view_raw=Ver original
|
||||
file_view_raw=Ver Original
|
||||
file_permalink=Link permanente
|
||||
file_too_large=O arquivo é muito grande para ser mostrado.
|
||||
file_is_empty=O arquivo está vazio.
|
||||
@ -1254,9 +1271,9 @@ view_git_blame=Ver Git Blame
|
||||
video_not_supported_in_browser=Seu navegador não suporta a tag 'video' do HTML5.
|
||||
audio_not_supported_in_browser=Seu navegador não suporta a tag 'audio' do HTML5.
|
||||
symbolic_link=Link simbólico
|
||||
executable_file=Arquivo executável
|
||||
executable_file=Arquivo Executável
|
||||
generated=Gerado
|
||||
commit_graph=Gráfico de commits
|
||||
commit_graph=Gráfico de Commits
|
||||
commit_graph.select=Selecionar branches
|
||||
commit_graph.hide_pr_refs=Esconder Pull Requests
|
||||
commit_graph.monochrome=Monocromático
|
||||
@ -1272,34 +1289,35 @@ lines=linhas
|
||||
from_comment=(comentário)
|
||||
|
||||
editor.add_file=Adicionar Arquivo
|
||||
editor.new_file=Novo arquivo
|
||||
editor.upload_file=Enviar arquivo
|
||||
editor.edit_file=Editar arquivo
|
||||
editor.preview_changes=Visualizar alterações
|
||||
editor.new_file=Novo Arquivo
|
||||
editor.upload_file=Enviar Arquivo
|
||||
editor.edit_file=Editar Arquivo
|
||||
editor.preview_changes=Visualizar Alterações
|
||||
editor.cannot_edit_lfs_files=Arquivos LFS não podem ser editados na interface web.
|
||||
editor.cannot_edit_too_large_file=O arquivo é muito grande para ser editado.
|
||||
editor.cannot_edit_non_text_files=Arquivos binários não podem ser editados na interface web.
|
||||
editor.file_not_editable_hint=Mas você ainda pode renomear ou movê-lo.
|
||||
editor.edit_this_file=Editar arquivo
|
||||
editor.edit_this_file=Editar Arquivo
|
||||
editor.this_file_locked=Arquivo está bloqueado
|
||||
editor.must_be_on_a_branch=Você deve estar em um branch para propor alterações neste arquivo.
|
||||
editor.fork_before_edit=Você deve fazer um fork desse repositório para fazer ou propor alterações neste arquivo.
|
||||
editor.delete_this_file=Excluir arquivo
|
||||
editor.delete_this_file=Excluir Arquivo
|
||||
editor.must_have_write_access=Você deve ter permissão de escrita para fazer ou propor alterações neste arquivo.
|
||||
editor.file_delete_success=O arquivo "%s" foi excluído.
|
||||
editor.name_your_file=Nomeie o seu arquivo…
|
||||
editor.filename_help=Adicione um diretório digitando seu nome seguido por uma barra ('/'). Remova um diretório digitando o backspace no início do campo de entrada.
|
||||
editor.or=ou
|
||||
editor.cancel_lower=Cancelar
|
||||
editor.commit_signed_changes=Commit de alteradores assinadas
|
||||
editor.commit_changes=Aplicar commit das alterações
|
||||
editor.commit_signed_changes=Criar Commit das Alterações Assinadas
|
||||
editor.commit_changes=Criar Commit das Alterações
|
||||
editor.add_tmpl=Adicionar '{filename}'
|
||||
editor.add=Adicionar %s
|
||||
editor.update=Atualizar %s
|
||||
editor.delete=Excluir %s
|
||||
editor.patch=Aplicar Correção
|
||||
editor.patching=Corrigindo:
|
||||
editor.new_patch=Nova correção
|
||||
editor.fail_to_apply_patch=Não foi possível aplicar correção
|
||||
editor.new_patch=Nova Correção
|
||||
editor.commit_message_desc=Adicione uma descrição detalhada (opcional)...
|
||||
editor.signoff_desc=Adicione um assinado-por-committer no final do log do commit.
|
||||
editor.commit_directly_to_this_branch=Commit diretamente no branch <strong class="branch-name">%s</strong>.
|
||||
@ -1323,7 +1341,7 @@ editor.commit_empty_file_text=O arquivo que você está prestes fazer commit est
|
||||
editor.no_changes_to_show=Nenhuma alteração a mostrar.
|
||||
editor.push_rejected_no_message=A alteração foi rejeitada pelo servidor sem uma mensagem. Por favor, verifique os Hooks Git.
|
||||
editor.push_rejected=A alteração foi rejeitada pelo servidor. Por favor, verifique os Hooks Git.
|
||||
editor.push_rejected_summary=Mensagem completa de rejeição:
|
||||
editor.push_rejected_summary=Mensagem Completa de Rejeição:
|
||||
editor.add_subdir=Adicionar um subdiretório...
|
||||
editor.unable_to_upload_files=Ocorreu um erro ao enviar arquivos para "%s": %v
|
||||
editor.upload_file_is_locked=Arquivo "%s" está bloqueado por %s.
|
||||
@ -1338,11 +1356,12 @@ editor.failed_to_commit=Falha ao criar commit das alterações.
|
||||
editor.failed_to_commit_summary=Mensagem de Erro:
|
||||
|
||||
|
||||
commits.desc=Veja o histórico de alterações do código de fonte.
|
||||
commits.desc=Veja o histórico de alterações do código-fonte.
|
||||
commits.commits=Commits
|
||||
commits.no_commits=Nenhum commit em comum. "%s" e "%s" tem históricos completamente diferentes.
|
||||
commits.nothing_to_compare=Estes branches são iguais.
|
||||
commits.search.tooltip=Você pode prefixar as palavras-chave com "author:" (autor da mudança), "committer:" (autor do commit), "after:" (depois) ou "before:" (antes). Por exemplo: "revert author:Ana before:2019-01-13".\
|
||||
commits.search_branch=Este Branch
|
||||
commits.search_all=Todos os branches
|
||||
commits.author=Autor
|
||||
commits.message=Mensagem
|
||||
@ -1415,40 +1434,40 @@ issues.filter_labels=Filtrar Rótulo
|
||||
issues.filter_reviewers=Filtrar Revisor
|
||||
issues.filter_no_results=Nenhum resultado
|
||||
issues.filter_no_results_placeholder=Tente ajustar seus filtros de pesquisa.
|
||||
issues.new=Nova issue
|
||||
issues.new=Nova Issue
|
||||
issues.new.title_empty=Título não pode ser em branco
|
||||
issues.new.labels=Etiquetas
|
||||
issues.new.no_label=Sem etiqueta
|
||||
issues.new.no_label=Sem Etiqueta
|
||||
issues.new.clear_labels=Limpar etiquetas
|
||||
issues.new.projects=Projetos
|
||||
issues.new.clear_projects=Limpar projetos
|
||||
issues.new.no_projects=Sem projeto
|
||||
issues.new.open_projects=Abrir Projetos
|
||||
issues.new.closed_projects=Projetos fechados
|
||||
issues.new.closed_projects=Projetos Fechados
|
||||
issues.new.no_items=Nenhum item
|
||||
issues.new.milestone=Marco
|
||||
issues.new.no_milestone=Sem marco
|
||||
issues.new.no_milestone=Sem Marco
|
||||
issues.new.clear_milestone=Limpar marco
|
||||
issues.new.assignees=Responsáveis
|
||||
issues.new.clear_assignees=Limpar responsáveis
|
||||
issues.new.no_assignees=Sem responsável
|
||||
issues.choose.get_started=Primeiros passos
|
||||
issues.new.no_assignees=Sem Responsáveis
|
||||
issues.choose.get_started=Primeiros Passos
|
||||
issues.choose.open_external_link=Abrir
|
||||
issues.choose.blank=Padrão
|
||||
issues.choose.blank_about=Criar uma issue a partir do modelo padrão.
|
||||
issues.choose.ignore_invalid_templates=Modelos inválidos foram ignorados
|
||||
issues.choose.invalid_templates=%v modelo(s) inválido(s) encontrado(s)
|
||||
issues.choose.invalid_config=A configuração da issue contém erros:
|
||||
issues.no_ref=Nenhum branch/tag especificado
|
||||
issues.create=Criar issue
|
||||
issues.no_ref=Nenhum Branch/Tag Especificado
|
||||
issues.create=Criar Issue
|
||||
issues.new_label=Nova Etiqueta
|
||||
issues.new_label_placeholder=Nome da etiqueta
|
||||
issues.new_label_desc_placeholder=Descrição
|
||||
issues.create_label=Criar etiqueta
|
||||
issues.create_label=Criar Etiqueta
|
||||
issues.label_templates.title=Carregue um conjunto de etiquetas pré-definidas
|
||||
issues.label_templates.info=Ainda não existem etiquetas. Crie uma etiqueta em 'Nova etiqueta' ou use um conjunto de etiquetas predefinida:
|
||||
issues.label_templates.helper=Selecione um conjunto de etiquetas
|
||||
issues.label_templates.use=Use o conjunto de etiquetas
|
||||
issues.label_templates.use=Usar Conjunto de Etiquetas
|
||||
issues.label_templates.fail_to_load_file=Falha ao carregar o modelo de etiquetas "%s": %v
|
||||
issues.add_label=adicionou o rótulo %s %s
|
||||
issues.add_labels=adicionou os rótulos %s %s
|
||||
@ -1531,11 +1550,11 @@ issues.commented_at=`comentou <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Tem certeza que deseja excluir este comentário?
|
||||
issues.context.copy_link=Copiar Link
|
||||
issues.context.quote_reply=Citar Resposta
|
||||
issues.context.reference_issue=Referência em uma nova issue
|
||||
issues.context.reference_issue=Referência em uma Nova Issue
|
||||
issues.context.edit=Editar
|
||||
issues.context.delete=Excluir
|
||||
issues.no_content=Nenhuma descrição fornecida.
|
||||
issues.close=Fechar issue
|
||||
issues.close=Fechar Issue
|
||||
issues.comment_pull_merged_at=aplicou o merge do commit %[1]s em %[2]s %[3]s
|
||||
issues.comment_manually_pull_merged_at=aplicou o merge manual do commit %[1]s em %[2]s %[3]s
|
||||
issues.close_comment_issue=Comentar e Fechar
|
||||
@ -1568,7 +1587,7 @@ issues.re_request_review=Re-solicitar revisão
|
||||
issues.is_stale=Houve alterações nessa PR desde essa revisão
|
||||
issues.remove_request_review=Remover solicitação de revisão
|
||||
issues.remove_request_review_block=Não é possível remover a solicitação de revisão
|
||||
issues.dismiss_review=Descartar revisão
|
||||
issues.dismiss_review=Descartar Revisão
|
||||
issues.dismiss_review_warning=Tem certeza de que deseja descartar esta revisão?
|
||||
issues.sign_in_require_desc=<a href="%s">Acesse</a> para participar desta conversação.
|
||||
issues.edit=Editar
|
||||
@ -1579,17 +1598,18 @@ issues.label_description=Descrição da etiqueta
|
||||
issues.label_color=Cor da etiqueta
|
||||
issues.label_color_invalid=Cor inválida
|
||||
issues.label_exclusive=Exclusivo
|
||||
issues.label_archive=Arquivar etiqueta
|
||||
issues.label_archive=Arquivar Etiqueta
|
||||
issues.label_archived_filter=Mostrar etiquetas arquivadas
|
||||
issues.label_archive_tooltip=Etiquetas arquivadas são excluídas, por padrão, das sugestões ao pesquisar por etiqueta.
|
||||
issues.label_exclusive_desc=Nomeie o rótulo <code>escopo/item</code> para torná-lo mutuamente exclusivo com outros rótulos do <code>escopo/</code>.
|
||||
issues.label_exclusive_warning=Quaisquer rótulos com escopo conflitantes serão removidos ao editar os rótulos de uma issue ou pull request.
|
||||
issues.label_exclusive_order=Ordem
|
||||
issues.label_count=%d etiquetas
|
||||
issues.label_open_issues=%d issues abertas
|
||||
issues.label_edit=Editar
|
||||
issues.label_delete=Excluir
|
||||
issues.label_modify=Editar etiqueta
|
||||
issues.label_deletion=Excluir etiqueta
|
||||
issues.label_modify=Editar Etiqueta
|
||||
issues.label_deletion=Excluir Etiqueta
|
||||
issues.label_deletion_desc=A exclusão desta etiqueta irá removê-la de todas as issues. Tem certeza que deseja continuar?
|
||||
issues.label_deletion_success=A etiqueta foi excluída.
|
||||
issues.label.filter_sort.alphabetically=Alfabeticamente
|
||||
@ -1643,7 +1663,7 @@ issues.del_time_history=`removeu tempo gasto %s`
|
||||
issues.add_time_hours=Horas
|
||||
issues.add_time_minutes=Minutos
|
||||
issues.add_time_sum_to_small=Nenhum tempo foi inserido.
|
||||
issues.time_spent_total=Tempo total gasto
|
||||
issues.time_spent_total=Tempo Total Gasto
|
||||
issues.time_spent_from_all_authors=`Tempo total gasto: %s`
|
||||
|
||||
issues.due_date=Data Limite
|
||||
@ -1683,7 +1703,7 @@ issues.dependency.pr_close_blocks=Este pull request bloqueia o fechamento das se
|
||||
issues.dependency.issue_batch_close_blocked=Não é possível fechar as issues que você escolheu, porque a issue #%d ainda tem dependências abertas
|
||||
issues.dependency.blocks_short=Bloqueia
|
||||
issues.dependency.blocked_by_short=Depende de
|
||||
issues.dependency.remove_header=Remover dependência
|
||||
issues.dependency.remove_header=Remover Dependência
|
||||
issues.dependency.issue_remove_text=Isto removerá a dependência desta issue. Continuar?
|
||||
issues.dependency.pr_remove_text=Isto removerá a dependência deste pull request. Continuar?
|
||||
issues.dependency.setting=Habilitar Dependências para Issues e Pull Requests
|
||||
@ -1871,7 +1891,8 @@ milestones.no_due_date=Sem data limite
|
||||
milestones.open=Reabrir
|
||||
milestones.close=Fechar
|
||||
milestones.new_subheader=Os marcos podem ajudá-lo a organizar os problemas e acompanhar seu progresso.
|
||||
milestones.create=Criar marco
|
||||
milestones.completeness=<strong>%d%%</strong> Concluído
|
||||
milestones.create=Criar Marco
|
||||
milestones.title=Título
|
||||
milestones.desc=Descrição
|
||||
milestones.due_date=Data limite (opcional)
|
||||
@ -1883,7 +1904,7 @@ milestones.edit_subheader=Marcos organizam as issues e acompanham o progresso.
|
||||
milestones.cancel=Cancelar
|
||||
milestones.modify=Atualizar Marco
|
||||
milestones.edit_success=O marco "%s" foi atualizado.
|
||||
milestones.deletion=Excluir marco
|
||||
milestones.deletion=Excluir Marco
|
||||
milestones.deletion_desc=A exclusão deste marco irá removê-lo de todas as issues. Tem certeza que deseja continuar?
|
||||
milestones.deletion_success=O marco foi excluído.
|
||||
milestones.filter_sort.name=Nome
|
||||
@ -1938,6 +1959,7 @@ wiki.original_git_entry_tooltip=Ver o arquivo Git original em vez de usar o link
|
||||
activity=Atividade
|
||||
activity.navbar.pulse=Pulso
|
||||
activity.navbar.contributors=Contribuidores
|
||||
activity.navbar.recent_commits=Commits Recentes
|
||||
activity.period.filter_label=Período:
|
||||
activity.period.daily=1 dia
|
||||
activity.period.halfweekly=3 dias
|
||||
@ -2044,6 +2066,7 @@ settings.branches.add_new_rule=Adicionar Nova Regra
|
||||
settings.advanced_settings=Configurações avançadas
|
||||
settings.wiki_desc=Habilitar a wiki do repositório
|
||||
settings.use_internal_wiki=Usar a wiki nativa
|
||||
settings.default_wiki_branch_name=Nome Padrão do Branch da Wiki
|
||||
settings.use_external_wiki=Usar wiki externa
|
||||
settings.external_wiki_url=URL externa da wiki
|
||||
settings.external_wiki_url_error=A URL da wiki externa não é válida.
|
||||
@ -2073,6 +2096,7 @@ settings.pulls.default_delete_branch_after_merge=Excluir o branch de pull reques
|
||||
settings.pulls.default_allow_edits_from_maintainers=Permitir edições de mantenedores por padrão
|
||||
settings.releases_desc=Habilitar versões do Repositório
|
||||
settings.packages_desc=Habilitar Registro de Pacotes de Repositório
|
||||
settings.projects_desc=Habilitar Projetos
|
||||
settings.projects_mode_all=Todos os projetos
|
||||
settings.actions_desc=Habilitar ações do repositório
|
||||
settings.admin_settings=Configurações do administrador
|
||||
@ -2083,6 +2107,7 @@ settings.admin_indexer_commit_sha=Último SHA indexado
|
||||
settings.admin_indexer_unindexed=Não indexado
|
||||
settings.reindex_button=Adicionar à fila de reindexação
|
||||
settings.reindex_requested=Reindexação Requisitada
|
||||
settings.admin_enable_close_issues_via_commit_in_any_branch=Fechar issue via commit em um branch não padrão
|
||||
settings.danger_zone=Zona de perigo
|
||||
settings.new_owner_has_same_repo=O novo proprietário já tem um repositório com o mesmo nome. Por favor, escolha outro nome.
|
||||
settings.convert=Converter para repositório tradicional
|
||||
@ -2100,6 +2125,7 @@ settings.transfer.rejected=A transferência do repositório foi rejeitada.
|
||||
settings.transfer.success=A transferência do repositório foi bem sucedida.
|
||||
settings.transfer_abort=Cancelar transferência
|
||||
settings.transfer_abort_invalid=Não é possível cancelar uma transferência de repositório não existente.
|
||||
settings.transfer_abort_success=A transferência do repositório para %s foi cancelada com sucesso.
|
||||
settings.transfer_desc=Transferir este repositório para outro usuário ou para uma organização onde você tem direitos de administrador.
|
||||
settings.transfer_form_title=Digite o nome do repositório para confirmar:
|
||||
settings.transfer_notices_1=- Você perderá o acesso ao repositório se transferir para um usuário individual.
|
||||
@ -2373,16 +2399,16 @@ settings.rename_branch_from=nome antigo do branch
|
||||
settings.rename_branch_to=novo nome do branch
|
||||
settings.rename_branch=Renomear branch
|
||||
|
||||
diff.browse_source=Ver código fonte
|
||||
diff.browse_source=Ver Código-Fonte
|
||||
diff.parent=pai
|
||||
diff.commit=commit
|
||||
diff.git-notes=Notas
|
||||
diff.data_not_available=Conteúdo de diff não disponível
|
||||
diff.options_button=Opções de diferenças
|
||||
diff.download_patch=Baixar arquivo de patch
|
||||
diff.download_diff=Baixar arquivo de diferenças
|
||||
diff.show_split_view=Visão dividida
|
||||
diff.show_unified_view=Visão unificada
|
||||
diff.data_not_available=Conteúdo de Diff Não Disponível
|
||||
diff.options_button=Opções de Diff
|
||||
diff.download_patch=Baixar Arquivo de Patch
|
||||
diff.download_diff=Baixar Arquivo de Diff
|
||||
diff.show_split_view=Visão Dividida
|
||||
diff.show_unified_view=Visão Unificada
|
||||
diff.whitespace_button=Espaço em branco
|
||||
diff.whitespace_show_everything=Mostrar todas as alterações
|
||||
diff.whitespace_ignore_all_whitespace=Ignorar todas as alterações de espaço em branco
|
||||
@ -2392,7 +2418,7 @@ diff.stats_desc=<strong> %d arquivos alterados</strong> com <strong>%d adições
|
||||
diff.stats_desc_file=%d alterações: %d adições e %d exclusões
|
||||
diff.bin=BIN
|
||||
diff.bin_not_shown=Arquivo binário não exibido.
|
||||
diff.view_file=Ver arquivo
|
||||
diff.view_file=Ver Arquivo
|
||||
diff.file_before=Antes
|
||||
diff.file_after=Depois
|
||||
diff.file_image_width=Largura
|
||||
@ -2401,7 +2427,7 @@ diff.file_byte_size=Tamanho
|
||||
diff.file_suppressed=Diferenças do arquivo suprimidas por serem muito extensas
|
||||
diff.file_suppressed_line_too_long=Diff do arquivo suprimido porque uma ou mais linhas são muito longas
|
||||
diff.too_many_files=Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff
|
||||
diff.show_more=Mostrar mais
|
||||
diff.show_more=Mostrar Mais
|
||||
diff.load=Carregar Diff
|
||||
diff.generated=gerado
|
||||
diff.vendored=externo
|
||||
@ -2441,7 +2467,7 @@ release.edit=editar
|
||||
release.ahead.commits=<strong>%d</strong> commits
|
||||
release.ahead.target=para %s desde esta versão
|
||||
tag.ahead.target=para %s desde esta tag
|
||||
release.source_code=Código Fonte
|
||||
release.source_code=Código-Fonte
|
||||
release.new_subheader=Lançamentos organizam versões do projeto.
|
||||
release.edit_subheader=Lançamentos organizam versões do projeto.
|
||||
release.tag_name=Nome da tag
|
||||
@ -2667,8 +2693,8 @@ view_as_member_hint=Você está vendo o README como um membro desta organizaçã
|
||||
maintenance=Manutenção
|
||||
dashboard=Painel
|
||||
self_check=Auto-verificação
|
||||
identity_access=Identidade e acesso
|
||||
users=Contas de usuário
|
||||
identity_access=Identidade e Acesso
|
||||
users=Contas de Usuário
|
||||
organizations=Organizações
|
||||
repositories=Repositórios
|
||||
hooks=Webhooks
|
||||
@ -2687,8 +2713,8 @@ settings=Configurações de Administrador
|
||||
|
||||
dashboard.statistic=Resumo
|
||||
dashboard.maintenance_operations=Operações de Manutenção
|
||||
dashboard.system_status=Status do sistema
|
||||
dashboard.operation_name=Nome da operação
|
||||
dashboard.system_status=Status do Sistema
|
||||
dashboard.operation_name=Nome da Operação
|
||||
dashboard.operation_switch=Trocar
|
||||
dashboard.operation_run=Executar
|
||||
dashboard.clean_unbind_oauth=Limpar conexões OAuth não vinculadas
|
||||
@ -2706,7 +2732,7 @@ dashboard.delete_inactive_accounts=Excluir todas as contas não ativadas
|
||||
dashboard.delete_repo_archives=Excluir todos os arquivos dos repositórios (ZIP, TAR.GZ, etc..)
|
||||
dashboard.delete_missing_repos=Excluir todos os repositórios que não possuem seus arquivos Git
|
||||
dashboard.delete_generated_repository_avatars=Excluir avatares gerados do repositório
|
||||
dashboard.update_mirrors=Atualizar espelhamentos
|
||||
dashboard.update_mirrors=Atualizar Espelhamentos
|
||||
dashboard.repo_health_check=Verificar estado de saúde de todos os repositórios
|
||||
dashboard.check_repo_stats=Verificar estatísticas de todos os repositórios
|
||||
dashboard.archive_cleanup=Apagar arquivos antigos de repositório
|
||||
@ -2720,27 +2746,27 @@ dashboard.cleanup_packages=Limpar pacotes expirados
|
||||
dashboard.cleanup_actions=Limpar recursos de actions expiradas
|
||||
dashboard.server_uptime=Tempo de atividade do Servidor
|
||||
dashboard.current_goroutine=Goroutines Atuais
|
||||
dashboard.current_memory_usage=Uso de memória atual
|
||||
dashboard.total_memory_allocated=Total de memória alocada
|
||||
dashboard.memory_obtained=Memória obtida
|
||||
dashboard.pointer_lookup_times=Nº de consultas a ponteiros
|
||||
dashboard.memory_allocate_times=Alocações de memória
|
||||
dashboard.memory_free_times=Liberações de memória
|
||||
dashboard.current_heap_usage=Uso atual da heap
|
||||
dashboard.heap_memory_obtained=Memória de heap obtida
|
||||
dashboard.heap_memory_idle=Memória da heap ociosa
|
||||
dashboard.heap_memory_in_use=Memória da heap em uso
|
||||
dashboard.heap_memory_released=Memória da heap liberada
|
||||
dashboard.heap_objects=Objetos na heap
|
||||
dashboard.bootstrap_stack_usage=Uso de pilha bootstrap
|
||||
dashboard.stack_memory_obtained=Memória de pilha obtida
|
||||
dashboard.current_memory_usage=Uso de Memória Atual
|
||||
dashboard.total_memory_allocated=Total de Memória Alocada
|
||||
dashboard.memory_obtained=Memória Obtida
|
||||
dashboard.pointer_lookup_times=Nº de Consultas a Ponteiros
|
||||
dashboard.memory_allocate_times=Alocações de Memória
|
||||
dashboard.memory_free_times=Liberações de Memória
|
||||
dashboard.current_heap_usage=Uso Atual da Heap
|
||||
dashboard.heap_memory_obtained=Memória de Heap Obtida
|
||||
dashboard.heap_memory_idle=Memória da Heap Ociosa
|
||||
dashboard.heap_memory_in_use=Memória da Heap em Uso
|
||||
dashboard.heap_memory_released=Memória da Heap Liberada
|
||||
dashboard.heap_objects=Objetos na Heap
|
||||
dashboard.bootstrap_stack_usage=Uso de Pilha Bootstrap
|
||||
dashboard.stack_memory_obtained=Memória de Pilha Obtida
|
||||
dashboard.mspan_structures_usage=Uso de estruturas de MSpan
|
||||
dashboard.mspan_structures_obtained=Estruturas de MSpan obtidas
|
||||
dashboard.mcache_structures_usage=Uso de estruturas de MCache
|
||||
dashboard.mcache_structures_obtained=Estruturas de MCache obtidas
|
||||
dashboard.profiling_bucket_hash_table_obtained=Perfil obtido da Bucket Hash Table
|
||||
dashboard.gc_metadata_obtained=Metadados do GC obtidos
|
||||
dashboard.other_system_allocation_obtained=Outra alocação de sistema obtida
|
||||
dashboard.other_system_allocation_obtained=Outra Alocação de Sistema Obtida
|
||||
dashboard.next_gc_recycle=Próxima reciclagem do GC
|
||||
dashboard.last_gc_time=Desde da ultima vez do GC
|
||||
dashboard.total_gc_time=Pausa total do GC
|
||||
@ -2752,7 +2778,7 @@ dashboard.delete_old_system_notices=Excluir todos os avisos de sistema antigos d
|
||||
dashboard.sync_tag.started=Sincronização de Tags iniciada
|
||||
dashboard.rebuild_issue_indexer=Reconstruir indexador de issues
|
||||
|
||||
users.user_manage_panel=Gerenciamento de conta de usuário
|
||||
users.user_manage_panel=Gerenciamento de Conta de Usuário
|
||||
users.new_account=Criar Conta de Usuário
|
||||
users.name=Nome de usuário
|
||||
users.full_name=Nome Completo
|
||||
@ -2764,33 +2790,33 @@ users.remote=Remoto
|
||||
users.2fa=2FA
|
||||
users.repos=Repositórios
|
||||
users.created=Criado
|
||||
users.last_login=Último acesso
|
||||
users.last_login=Último Acesso
|
||||
users.never_login=Nunca Acessado
|
||||
users.send_register_notify=Enviar notificação de cadastro de usuário
|
||||
users.send_register_notify=Enviar Notificação de Cadastro de Usuário
|
||||
users.new_success=Usuário "%s" criado.
|
||||
users.edit=Editar
|
||||
users.auth_source=Fonte da autenticação
|
||||
users.auth_source=Fonte da Autenticação
|
||||
users.local=Local
|
||||
users.auth_login_name=Nome de acesso da autenticação
|
||||
users.auth_login_name=Nome de Acesso da Autenticação
|
||||
users.password_helper=Deixe a senha em branco para mantê-la inalterada.
|
||||
users.update_profile_success=A conta de usuário foi atualizada.
|
||||
users.edit_account=Editar Conta de Usuário
|
||||
users.max_repo_creation=Número máximo de repositórios
|
||||
users.max_repo_creation=Número Máximo de Repositórios
|
||||
users.max_repo_creation_desc=(Use -1 para usar o limite padrão global.)
|
||||
users.is_activated=Conta de usuário está ativada
|
||||
users.prohibit_login=Desabilitar acesso
|
||||
users.is_admin=É administrador
|
||||
users.is_restricted=Está restrito
|
||||
users.is_activated=Conta de Usuário está Ativada
|
||||
users.prohibit_login=Desabilitar Acesso
|
||||
users.is_admin=É Administrador
|
||||
users.is_restricted=Está Restrito
|
||||
users.allow_git_hook=Pode criar hooks Git
|
||||
users.allow_git_hook_tooltip=Hooks Git são executados como o usuário do SO que executa Gitea e terá o mesmo nível de acesso ao servidor. Como resultado, os usuários com esse privilégio especial de Hook do Git podem acessar e modificar todos os repositórios do Gitea, bem como o banco de dados usado pelo Gitea. Por conseguinte, podem também obter privilégios de administrador do Gitea.
|
||||
users.allow_import_local=Pode importar repositórios locais
|
||||
users.allow_create_organization=Pode criar organizações
|
||||
users.allow_import_local=Pode Importar Repositórios Locais
|
||||
users.allow_create_organization=Pode Criar Organizações
|
||||
users.update_profile=Atualizar Conta de Usuário
|
||||
users.delete_account=Excluir conta de usuário
|
||||
users.delete_account=Excluir Conta de Usuário
|
||||
users.cannot_delete_self=Você não pode excluir você mesmo
|
||||
users.still_own_repo=Este usuário ainda possui um ou mais repositórios. Exclua ou transfira esses repositórios primeiro.
|
||||
users.still_has_org=Este usuário é membro de uma organização. Remova o usuário de qualquer organização primeiro.
|
||||
users.purge=Eliminar usuário
|
||||
users.purge=Eliminar Usuário
|
||||
users.purge_help=Exclua forçosamente o usuário e quaisquer repositórios, organizações e pacotes pertencentes ao usuário. Todos os comentários também serão excluídos.
|
||||
users.deletion_success=A conta de usuário foi excluída.
|
||||
users.reset_2fa=Reinicializar 2FA
|
||||
@ -2801,17 +2827,20 @@ users.list_status_filter.not_active=Inativo
|
||||
users.list_status_filter.is_admin=Administrador
|
||||
users.list_status_filter.not_admin=Não Administrador
|
||||
users.list_status_filter.is_restricted=Restrito
|
||||
users.list_status_filter.not_restricted=Não restrito
|
||||
users.list_status_filter.is_prohibit_login=Proibir login
|
||||
users.list_status_filter.not_prohibit_login=Permitir login
|
||||
users.list_status_filter.not_restricted=Não Restrito
|
||||
users.list_status_filter.is_prohibit_login=Proibir Login
|
||||
users.list_status_filter.not_prohibit_login=Permitir Login
|
||||
users.list_status_filter.is_2fa_enabled=2FA Ativado
|
||||
users.list_status_filter.not_2fa_enabled=2FA Desativado
|
||||
users.details=Detalhes do usuário
|
||||
users.details=Detalhes do Usuário
|
||||
|
||||
emails.email_manage_panel=Gerenciamento de E-mail de Usuário
|
||||
emails.primary=Principal
|
||||
emails.activated=Ativado
|
||||
emails.filter_sort.name=Nome de usuário
|
||||
emails.filter_sort.email=E-mail
|
||||
emails.filter_sort.email_reverse=E-mail (reverso)
|
||||
emails.filter_sort.name=Nome de Usuário
|
||||
emails.filter_sort.name_reverse=Nome de Usuário (reverso)
|
||||
emails.updated=Endereço de e-mail atualizado
|
||||
emails.not_updated=Falha ao atualizar o endereço de e-mail solicitado: %v
|
||||
emails.duplicate_active=Este endereço de e-mail já está ativo para um usuário diferente.
|
||||
@ -2820,13 +2849,13 @@ emails.change_email_text=Tem certeza que deseja atualizar este e-mail?
|
||||
emails.delete=Excluir E-mail
|
||||
emails.delete_desc=Tem certeza que deseja excluir este e-mail?
|
||||
|
||||
orgs.org_manage_panel=Gerenciamento da organização
|
||||
orgs.org_manage_panel=Gerenciamento da Organização
|
||||
orgs.name=Nome
|
||||
orgs.teams=Equipes
|
||||
orgs.members=Membros
|
||||
orgs.new_orga=Nova organização
|
||||
orgs.new_orga=Nova Organização
|
||||
|
||||
repos.repo_manage_panel=Gerenciamento do repositório
|
||||
repos.repo_manage_panel=Gerenciamento do Repositório
|
||||
repos.unadopted=Repositórios Não Adotados
|
||||
repos.unadopted.no_more=Não foram encontrados mais repositórios não adotados
|
||||
repos.owner=Proprietário
|
||||
@ -2965,33 +2994,33 @@ auths.login_source_of_type_exist=Uma fonte de autenticação deste tipo já exis
|
||||
auths.unable_to_initialize_openid=Não é possível inicializar o Provedor OpenID Connect: %s
|
||||
auths.invalid_openIdConnectAutoDiscoveryURL=URL do Auto Discovery inválida (deve ser uma URL válida, começando com http:// ou https://)
|
||||
|
||||
config.server_config=Configuração do servidor
|
||||
config.server_config=Configuração do Servidor
|
||||
config.app_name=Nome do Site
|
||||
config.app_ver=Versão do Gitea
|
||||
config.app_url=URL base do Gitea
|
||||
config.custom_conf=Caminho do Arquivo de Configuração
|
||||
config.custom_file_root_path=Caminho Raiz para Arquivo Personalizado
|
||||
config.domain=Domínio do Servidor
|
||||
config.offline_mode=Modo local
|
||||
config.offline_mode=Modo Local
|
||||
config.disable_router_log=Desabilitar o Log do roteador
|
||||
config.run_user=Executar como nome de usuário
|
||||
config.run_user=Executar como Usuário
|
||||
config.run_mode=Modo de Execução
|
||||
config.git_version=Versão do Git
|
||||
config.repo_root_path=Caminho raiz do repositório
|
||||
config.repo_root_path=Caminho Raiz do Repositório
|
||||
config.lfs_root_path=Caminho raiz do LFS
|
||||
config.log_file_root_path=Caminho do log
|
||||
config.script_type=Tipo de script
|
||||
config.reverse_auth_user=Usuário de autenticação reversa
|
||||
config.log_file_root_path=Caminho do Log
|
||||
config.script_type=Tipo de Script
|
||||
config.reverse_auth_user=Usuário de Autenticação Reversa
|
||||
|
||||
config.ssh_config=Configuração de SSH
|
||||
config.ssh_enabled=Habilitado
|
||||
config.ssh_start_builtin_server=Usar o servidor embutido
|
||||
config.ssh_start_builtin_server=Usar o Servidor Embutido
|
||||
config.ssh_domain=Domínio do servidor SSH
|
||||
config.ssh_port=Porta
|
||||
config.ssh_listen_port=Porta de escuta
|
||||
config.ssh_root_path=Caminho da raiz
|
||||
config.ssh_minimum_key_size_check=Verificar tamanho mínimo da chave
|
||||
config.ssh_minimum_key_sizes=Tamanhos mínimos da chave
|
||||
config.ssh_listen_port=Porta de Escuta
|
||||
config.ssh_root_path=Caminho da Raiz
|
||||
config.ssh_minimum_key_size_check=Verificar Tamanho Mínimo da Chave
|
||||
config.ssh_minimum_key_sizes=Tamanhos Mínimos da Chave
|
||||
|
||||
config.lfs_config=Configuração de LFS
|
||||
config.lfs_enabled=Habilitado
|
||||
@ -3007,30 +3036,30 @@ config.db_schema=Esquema
|
||||
config.db_ssl_mode=SSL
|
||||
config.db_path=Caminho
|
||||
|
||||
config.service_config=Configuração do serviço
|
||||
config.register_email_confirm=Exigir confirmação de e-mail para se cadastrar
|
||||
config.service_config=Configuração do Serviço
|
||||
config.register_email_confirm=Exigir Confirmação de E-mail para se Cadastrar
|
||||
config.disable_register=Desabilitar Auto-Cadastro
|
||||
config.allow_only_internal_registration=Permitir Registro Somente Através do Próprio Gitea
|
||||
config.allow_only_external_registration=Permitir Cadastro Somente por Meio de Serviços Externos
|
||||
config.enable_openid_signup=Habilitar o auto-cadastro via OpenID
|
||||
config.enable_openid_signin=Habilitar acesso via OpenID
|
||||
config.show_registration_button=Mostrar botão de cadastro
|
||||
config.require_sign_in_view=Exigir acesso do usuário para a visualização de páginas
|
||||
config.mail_notify=Habilitar notificações de e-mail
|
||||
config.show_registration_button=Mostrar Botão de Cadastro
|
||||
config.require_sign_in_view=Exigir Acesso do Usuário para a Visualização de Páginas
|
||||
config.mail_notify=Habilitar Notificações de E-mail
|
||||
config.enable_captcha=Habilitar o CAPTCHA
|
||||
config.active_code_lives=Ativar Code Lives
|
||||
config.reset_password_code_lives=Tempo de expiração do código de recuperação de conta
|
||||
config.reset_password_code_lives=Tempo de Expiração do Código de Recuperação de Conta
|
||||
config.default_keep_email_private=Ocultar Endereços de E-mail por Padrão
|
||||
config.default_allow_create_organization=Permitir a Criação de Organizações por Padrão
|
||||
config.enable_timetracking=Habilitar Cronômetro
|
||||
config.default_enable_timetracking=Habilitar o Cronômetro por Padrão
|
||||
config.default_allow_only_contributors_to_track_time=Permitir que apenas os colaboradores acompanhem o cronômetro
|
||||
config.no_reply_address=Ocultar domínio de e-mail
|
||||
config.default_allow_only_contributors_to_track_time=Permitir que Apenas os Colaboradores Acompanhem o Cronômetro
|
||||
config.no_reply_address=Ocultar Domínio de E-mail
|
||||
config.default_visibility_organization=Visibilidade padrão para novas organizações
|
||||
|
||||
config.webhook_config=Configuração de Hook da Web
|
||||
config.queue_length=Tamanho da fila
|
||||
config.deliver_timeout=Intervalo de entrega
|
||||
config.queue_length=Tamanho da Fila
|
||||
config.deliver_timeout=Intervalo de Entrega
|
||||
config.skip_tls_verify=Ignorar verificação de TLS
|
||||
|
||||
config.mailer_config=Configuração de Envio de E-mail
|
||||
@ -3047,7 +3076,7 @@ config.mailer_sendmail_args=Argumentos extras para o Sendmail
|
||||
config.mailer_sendmail_timeout=Tempo limite do Sendmail
|
||||
config.mailer_use_dummy=Dummy
|
||||
config.test_email_placeholder=E-mail (por exemplo, teste@exemplo.com.br)
|
||||
config.send_test_mail=Enviar e-mail de teste
|
||||
config.send_test_mail=Enviar E-mail de Teste
|
||||
config.send_test_mail_submit=Enviar
|
||||
config.test_mail_failed=Ocorreu um erro ao enviar um e-mail de teste para "%s": %v
|
||||
config.test_mail_sent=Um e-mail de teste foi enviado para "%s".
|
||||
@ -3061,33 +3090,33 @@ config.cache_interval=Intervalo de Cache
|
||||
config.cache_conn=Conexão de Cache
|
||||
config.cache_item_ttl=Item de cache TTL
|
||||
|
||||
config.session_config=Configuração da sessão
|
||||
config.session_provider=Provedor da sessão
|
||||
config.provider_config=Configuração do provedor
|
||||
config.session_config=Configuração da Sessão
|
||||
config.session_provider=Provedor da Sessão
|
||||
config.provider_config=Configuração do Provedor
|
||||
config.cookie_name=Nome do Cookie
|
||||
config.gc_interval_time=Tempo de Intervalo do GC
|
||||
config.session_life_time=Tempo de vida da sessão
|
||||
config.session_life_time=Tempo de Vida da Sessão
|
||||
config.https_only=Apenas HTTPS
|
||||
config.cookie_life_time=Tempo de Vida do Cookie
|
||||
|
||||
config.picture_config=Configuração de imagem e avatar
|
||||
config.picture_service=Serviço de imagens
|
||||
config.picture_config=Configuração de Imagem e Avatar
|
||||
config.picture_service=Serviço de Imagens
|
||||
config.disable_gravatar=Desabilitar o Gravatar
|
||||
config.enable_federated_avatar=Habilitar avatares federativos
|
||||
config.enable_federated_avatar=Habilitar Avatares Federativos
|
||||
|
||||
config.git_config=Configuração do Git
|
||||
config.git_disable_diff_highlight=Desabilitar realce de mudanças no diff
|
||||
config.git_disable_diff_highlight=Desabilitar Realce de Mudanças no Diff
|
||||
config.git_max_diff_lines=Máximo de linhas mostradas no diff (para um único arquivo)
|
||||
config.git_max_diff_line_characters=Máximo de caracteres mostrados no diff (para uma única linha)
|
||||
config.git_max_diff_files=Máximo de arquivos a serem mostrados no diff
|
||||
config.git_max_diff_files=Máximo de Arquivos a Serem Mostrados no Diff
|
||||
config.git_gc_args=Argumentos do GC
|
||||
config.git_migrate_timeout=Tempo limite de migração
|
||||
config.git_mirror_timeout=Tempo limite de atualização de espelhamento
|
||||
config.git_clone_timeout=Tempo limite para operação de clone
|
||||
config.git_pull_timeout=Tempo limite para operação de pull
|
||||
config.git_migrate_timeout=Tempo Limite de Migração
|
||||
config.git_mirror_timeout=Tempo Limite de Atualização de Espelhamento
|
||||
config.git_clone_timeout=Tempo Limite para Operação de Clone
|
||||
config.git_pull_timeout=Tempo Limite para Operação de Pull
|
||||
config.git_gc_timeout=Tempo limite para execução do GC
|
||||
|
||||
config.log_config=Configuração de log
|
||||
config.log_config=Configuração de Log
|
||||
config.logger_name_fmt=Logger: %s
|
||||
config.disabled_logger=Desabilitado
|
||||
config.access_log_mode=Modo log Access
|
||||
@ -3098,18 +3127,18 @@ config.set_setting_failed=Falha ao definir configuração %s
|
||||
|
||||
monitor.stats=Estatísticas
|
||||
|
||||
monitor.cron=Tarefas cron
|
||||
monitor.cron=Tarefas Cron
|
||||
monitor.name=Nome
|
||||
monitor.schedule=Cronograma
|
||||
monitor.next=Próxima vez
|
||||
monitor.previous=Vez anterior
|
||||
monitor.next=Próxima Vez
|
||||
monitor.previous=Vez Anterior
|
||||
monitor.execute_times=Execuções
|
||||
monitor.process=Processos em execução
|
||||
monitor.process=Processos em Execução
|
||||
monitor.processes_count=%d processos
|
||||
monitor.download_diagnosis_report=Baixar relatório de diagnóstico
|
||||
monitor.desc=Descrição
|
||||
monitor.start=Hora de início
|
||||
monitor.execute_time=Tempo de execução
|
||||
monitor.start=Hora de Início
|
||||
monitor.execute_time=Tempo de Execução
|
||||
monitor.last_execution_result=Resultado
|
||||
monitor.process.cancel=Cancelar processo
|
||||
monitor.process.cancel_desc=Cancelar um processo pode causar perda de dados
|
||||
@ -3119,26 +3148,29 @@ monitor.queues=Filas
|
||||
monitor.queue=Fila: %s
|
||||
monitor.queue.name=Nome
|
||||
monitor.queue.type=Tipo
|
||||
monitor.queue.exemplar=Tipo de modelo
|
||||
monitor.queue.numberworkers=Número de executores
|
||||
monitor.queue.maxnumberworkers=Número máximo de executores
|
||||
monitor.queue.exemplar=Tipo de Modelo
|
||||
monitor.queue.numberworkers=Número de Executores
|
||||
monitor.queue.activeworkers=Executores Ativos
|
||||
monitor.queue.maxnumberworkers=Número Máximo de Executores
|
||||
monitor.queue.numberinqueue=Número na Fila
|
||||
monitor.queue.settings.title=Configurações do conjunto
|
||||
monitor.queue.review_add=Revisar / Adicionar Executores
|
||||
monitor.queue.settings.title=Configurações do Conjunto
|
||||
monitor.queue.settings.maxnumberworkers=Número máximo de executores
|
||||
monitor.queue.settings.maxnumberworkers.placeholder=Atualmente %[1]d
|
||||
monitor.queue.settings.maxnumberworkers.error=Número máximo de executores deve ser um número
|
||||
monitor.queue.settings.submit=Atualizar configurações
|
||||
monitor.queue.settings.changed=Configurações atualizadas
|
||||
monitor.queue.settings.submit=Atualizar Configurações
|
||||
monitor.queue.settings.changed=Configurações Atualizadas
|
||||
monitor.queue.settings.remove_all_items=Remover tudo
|
||||
monitor.queue.settings.remove_all_items_done=Todos os itens da fila foram removidos.
|
||||
|
||||
notices.system_notice_list=Avisos do Sistema
|
||||
notices.view_detail_header=Ver detalhes do aviso
|
||||
notices.view_detail_header=Ver Detalhes do Aviso
|
||||
notices.operations=Operações
|
||||
notices.select_all=Marcar todos
|
||||
notices.deselect_all=Desmarcar todos
|
||||
notices.inverse_selection=Inverter seleção
|
||||
notices.select_all=Marcar Todos
|
||||
notices.deselect_all=Desmarcar Todos
|
||||
notices.inverse_selection=Inverter Seleção
|
||||
notices.delete_selected=Excluir Seleção
|
||||
notices.delete_all=Excluir todos os avisos
|
||||
notices.delete_all=Excluir Todos os Avisos
|
||||
notices.type=Tipo
|
||||
notices.type_1=Repositório
|
||||
notices.type_2=Tarefa
|
||||
|
||||
@ -120,6 +120,7 @@ error404=您正尝试访问的页面 <strong>不存在</strong> 或 <strong>您
|
||||
error503=服务器无法完成您的请求,请稍后重试。
|
||||
go_back=返回
|
||||
invalid_data=无效数据: %v
|
||||
nothing_has_been_changed=没有任何更改。
|
||||
|
||||
never=从不
|
||||
unknown=未知
|
||||
@ -2844,6 +2845,11 @@ settings.location=所在地区
|
||||
settings.permission=权限
|
||||
settings.repoadminchangeteam=仓库管理员可以添加或移除团队的访问权限
|
||||
settings.visibility=可见性
|
||||
settings.change_visibility=更改可见性
|
||||
settings.change_visibility_notices_1=如果组织被转换为私有,仓库的所有点赞将被删除且无法恢复。
|
||||
settings.change_visibility_notices_2=如果可见性更改为私有,非成员将无法访问该组织的仓库。
|
||||
settings.change_visibility_success=组织 %s 的可见性已成功更改。
|
||||
settings.visibility_desc=更改谁可以查看组织及其仓库。
|
||||
settings.visibility.public=公开
|
||||
settings.visibility.limited=受限 (仅对认证用户可见)
|
||||
settings.visibility.limited_shortname=受限
|
||||
@ -3420,6 +3426,7 @@ config.picture_service=图片服务
|
||||
config.disable_gravatar=禁用 Gravatar 头像
|
||||
config.enable_federated_avatar=启用 Federated 头像
|
||||
config.open_with_editor_app_help=用于克隆菜单的编辑器。如果为空将使用默认值。展开可以查看默认值。
|
||||
config.git_guide_remote_name=指南中 git 命令使用的仓库远程名称
|
||||
|
||||
config.git_config=Git 配置
|
||||
config.git_disable_diff_highlight=禁用差异对比语法高亮
|
||||
|
||||
14391
package-lock.json
generated
@ -1,7 +1,9 @@
|
||||
{
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.0.0",
|
||||
"engines": {
|
||||
"node": ">= 20.0.0"
|
||||
"node": ">= 20.0.0",
|
||||
"pnpm": ">=10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@citation-js/core": "0.7.18",
|
||||
@ -14,6 +16,7 @@
|
||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
||||
"@primer/octicons": "19.15.5",
|
||||
"@silverwind/vue3-calendar-heatmap": "2.0.6",
|
||||
"@techknowlogick/license-checker-webpack-plugin": "0.3.0",
|
||||
"add-asset-webpack-plugin": "3.0.0",
|
||||
"ansi_up": "6.0.6",
|
||||
"asciinema-player": "3.10.0",
|
||||
@ -32,7 +35,6 @@
|
||||
"idiomorph": "0.7.3",
|
||||
"jquery": "3.7.1",
|
||||
"katex": "0.16.22",
|
||||
"license-checker-webpack-plugin": "0.2.1",
|
||||
"mermaid": "11.10.0",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"minimatch": "10.0.3",
|
||||
@ -69,6 +71,7 @@
|
||||
"@stoplight/spectral-cli": "6.15.0",
|
||||
"@stylistic/eslint-plugin-js": "3.1.0",
|
||||
"@stylistic/stylelint-plugin": "4.0.0",
|
||||
"@types/codemirror": "5.60.16",
|
||||
"@types/dropzone": "5.7.9",
|
||||
"@types/jquery": "3.5.32",
|
||||
"@types/katex": "0.16.7",
|
||||
|
||||
10440
pnpm-lock.yaml
generated
Normal file
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 21 KiB |
@ -1256,7 +1256,7 @@ func Routes() *web.Router {
|
||||
m.Get("/*", repo.GetBranch)
|
||||
m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch)
|
||||
m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateBranchRepoOption{}), repo.CreateBranch)
|
||||
m.Patch("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.UpdateBranchRepoOption{}), repo.UpdateBranch)
|
||||
m.Patch("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.RenameBranchRepoOption{}), repo.RenameBranch)
|
||||
}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode))
|
||||
m.Group("/branch_protections", func() {
|
||||
m.Get("", repo.ListBranchProtections)
|
||||
|
||||
@ -380,11 +380,11 @@ func ListBranches(ctx *context.APIContext) {
|
||||
ctx.JSON(http.StatusOK, apiBranches)
|
||||
}
|
||||
|
||||
// UpdateBranch updates a repository's branch.
|
||||
func UpdateBranch(ctx *context.APIContext) {
|
||||
// swagger:operation PATCH /repos/{owner}/{repo}/branches/{branch} repository repoUpdateBranch
|
||||
// RenameBranch renames a repository's branch.
|
||||
func RenameBranch(ctx *context.APIContext) {
|
||||
// swagger:operation PATCH /repos/{owner}/{repo}/branches/{branch} repository repoRenameBranch
|
||||
// ---
|
||||
// summary: Update a branch
|
||||
// summary: Rename a branch
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
@ -408,7 +408,7 @@ func UpdateBranch(ctx *context.APIContext) {
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UpdateBranchRepoOption"
|
||||
// "$ref": "#/definitions/RenameBranchRepoOption"
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
@ -419,7 +419,7 @@ func UpdateBranch(ctx *context.APIContext) {
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
|
||||
opt := web.GetForm(ctx).(*api.UpdateBranchRepoOption)
|
||||
opt := web.GetForm(ctx).(*api.RenameBranchRepoOption)
|
||||
|
||||
oldName := ctx.PathParam("*")
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
@ -1085,7 +1085,7 @@ func MergePullRequest(ctx *context.APIContext) {
|
||||
type parseCompareInfoResult struct {
|
||||
headRepo *repo_model.Repository
|
||||
headGitRepo *git.Repository
|
||||
compareInfo *git.CompareInfo
|
||||
compareInfo *pull_service.CompareInfo
|
||||
baseRef git.RefName
|
||||
headRef git.RefName
|
||||
}
|
||||
@ -1201,7 +1201,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseRef.ShortName(), headRef.ShortName(), false, false)
|
||||
compareInfo, err := pull_service.GetCompareInfo(ctx, baseRepo, headRepo, headGitRepo, baseRef.ShortName(), headRef.ShortName(), false, false)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return nil, nil
|
||||
@ -1452,7 +1452,7 @@ func GetPullRequestCommits(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
var prInfo *git.CompareInfo
|
||||
var prInfo *pull_service.CompareInfo
|
||||
baseGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pr.BaseRepo)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
@ -1461,9 +1461,9 @@ func GetPullRequestCommits(ctx *context.APIContext) {
|
||||
defer closer.Close()
|
||||
|
||||
if pr.HasMerged {
|
||||
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitHeadRefName(), false, false)
|
||||
prInfo, err = pull_service.GetCompareInfo(ctx, pr.BaseRepo, pr.BaseRepo, baseGitRepo, pr.MergeBase, pr.GetGitHeadRefName(), false, false)
|
||||
} else {
|
||||
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitHeadRefName(), false, false)
|
||||
prInfo, err = pull_service.GetCompareInfo(ctx, pr.BaseRepo, pr.BaseRepo, baseGitRepo, pr.BaseBranch, pr.GetGitHeadRefName(), false, false)
|
||||
}
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
@ -1582,11 +1582,11 @@ func GetPullRequestFiles(ctx *context.APIContext) {
|
||||
|
||||
baseGitRepo := ctx.Repo.GitRepo
|
||||
|
||||
var prInfo *git.CompareInfo
|
||||
var prInfo *pull_service.CompareInfo
|
||||
if pr.HasMerged {
|
||||
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitHeadRefName(), true, false)
|
||||
prInfo, err = pull_service.GetCompareInfo(ctx, pr.BaseRepo, pr.BaseRepo, baseGitRepo, pr.MergeBase, pr.GetGitHeadRefName(), true, false)
|
||||
} else {
|
||||
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitHeadRefName(), true, false)
|
||||
prInfo, err = pull_service.GetCompareInfo(ctx, pr.BaseRepo, pr.BaseRepo, baseGitRepo, pr.BaseBranch, pr.GetGitHeadRefName(), true, false)
|
||||
}
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
|
||||
@ -736,14 +736,14 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
|
||||
// Default branch only updated if changed and exist or the repository is empty
|
||||
updateRepoLicense := false
|
||||
if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, *opts.DefaultBranch)) {
|
||||
repo.DefaultBranch = *opts.DefaultBranch
|
||||
if !repo.IsEmpty {
|
||||
if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, *opts.DefaultBranch); err != nil {
|
||||
if err := gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return err
|
||||
}
|
||||
updateRepoLicense = true
|
||||
}
|
||||
repo.DefaultBranch = *opts.DefaultBranch
|
||||
}
|
||||
|
||||
if err := repo_service.UpdateRepository(ctx, repo, visibilityChanged); err != nil {
|
||||
|
||||
@ -90,7 +90,7 @@ type swaggerParameterBodies struct {
|
||||
// in:body
|
||||
EditRepoOption api.EditRepoOption
|
||||
// in:body
|
||||
UpdateBranchRepoOption api.UpdateBranchRepoOption
|
||||
RenameBranchRepoOption api.RenameBranchRepoOption
|
||||
// in:body
|
||||
TransferRepoOption api.TransferRepoOption
|
||||
// in:body
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
)
|
||||
|
||||
// CompareInfo represents the collected results from ParseCompareInfo
|
||||
@ -14,7 +15,7 @@ type CompareInfo struct {
|
||||
HeadUser *user_model.User
|
||||
HeadRepo *repo_model.Repository
|
||||
HeadGitRepo *git.Repository
|
||||
CompareInfo *git.CompareInfo
|
||||
CompareInfo *pull_service.CompareInfo
|
||||
BaseBranch string
|
||||
HeadBranch string
|
||||
DirectComparison bool
|
||||
|
||||
@ -27,7 +27,7 @@ import (
|
||||
|
||||
const (
|
||||
tplConfig templates.TplName = "admin/config"
|
||||
tplConfigSettings templates.TplName = "admin/config_settings"
|
||||
tplConfigSettings templates.TplName = "admin/config_settings/config_settings"
|
||||
)
|
||||
|
||||
// SendTestMail send test mail to confirm mail service is OK
|
||||
|
||||
@ -41,6 +41,7 @@ import (
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/context/upload"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -550,7 +551,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
|
||||
headBranchRef = git.TagPrefix + ci.HeadBranch
|
||||
}
|
||||
|
||||
ci.CompareInfo, err = ci.HeadGitRepo.GetCompareInfo(baseRepo.RepoPath(), baseBranchRef, headBranchRef, ci.DirectComparison, fileOnly)
|
||||
ci.CompareInfo, err = pull_service.GetCompareInfo(ctx, baseRepo, ci.HeadRepo, ci.HeadGitRepo, baseBranchRef, headBranchRef, ci.DirectComparison, fileOnly)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCompareInfo", err)
|
||||
return nil
|
||||
@ -707,12 +708,6 @@ func PrepareCompareDiff(
|
||||
}
|
||||
|
||||
func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repository) (branches, tags []string, err error) {
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
branches, err = git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||
RepoID: repo.ID,
|
||||
ListOptions: db.ListOptionsAll,
|
||||
@ -721,7 +716,7 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
tags, err = gitRepo.GetTags(0, 0)
|
||||
tags, err = repo_model.GetTagNamesByRepoID(ctx, repo.ID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@ -254,7 +254,7 @@ func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) stri
|
||||
return baseCommit
|
||||
}
|
||||
|
||||
func preparePullViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.CompareInfo {
|
||||
func preparePullViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *pull_service.CompareInfo {
|
||||
if !issue.IsPull {
|
||||
return nil
|
||||
}
|
||||
@ -265,7 +265,7 @@ func preparePullViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *g
|
||||
}
|
||||
|
||||
// prepareMergedViewPullInfo show meta information for a merged pull request view page
|
||||
func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.CompareInfo {
|
||||
func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *pull_service.CompareInfo {
|
||||
pull := issue.PullRequest
|
||||
|
||||
setMergeTarget(ctx, pull)
|
||||
@ -273,7 +273,7 @@ func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
|
||||
|
||||
baseCommit := GetMergedBaseCommitID(ctx, issue)
|
||||
|
||||
compareInfo, err := ctx.Repo.GitRepo.GetCompareInfo(ctx.Repo.Repository.RepoPath(),
|
||||
compareInfo, err := pull_service.GetCompareInfo(ctx, ctx.Repo.Repository, ctx.Repo.Repository, ctx.Repo.GitRepo,
|
||||
baseCommit, pull.GetGitHeadRefName(), false, false)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "fatal: Not a valid object name") || strings.Contains(err.Error(), "unknown revision or path not in the working tree") {
|
||||
@ -311,7 +311,7 @@ func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
|
||||
}
|
||||
|
||||
// prepareViewPullInfo show meta information for a pull request preview page
|
||||
func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.CompareInfo {
|
||||
func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *pull_service.CompareInfo {
|
||||
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
|
||||
|
||||
repo := ctx.Repo.Repository
|
||||
@ -373,7 +373,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
|
||||
ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(commitStatuses)
|
||||
}
|
||||
|
||||
compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(),
|
||||
compareInfo, err := pull_service.GetCompareInfo(ctx, pull.BaseRepo, pull.BaseRepo, baseGitRepo,
|
||||
pull.MergeBase, pull.GetGitHeadRefName(), false, false)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "fatal: Not a valid object name") {
|
||||
@ -521,7 +521,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
|
||||
}
|
||||
}
|
||||
|
||||
compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(),
|
||||
compareInfo, err := pull_service.GetCompareInfo(ctx, pull.BaseRepo, pull.BaseRepo, baseGitRepo,
|
||||
git.BranchPrefix+pull.BaseBranch, pull.GetGitHeadRefName(), false, false)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "fatal: Not a valid object name") {
|
||||
|
||||
@ -16,6 +16,7 @@ import (
|
||||
unit_model "code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/indexer/code"
|
||||
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
|
||||
"code.gitea.io/gitea/modules/indexer/stats"
|
||||
@ -258,7 +259,7 @@ func handleSettingsPostMirror(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
u, err := git.GetRemoteURL(ctx, ctx.Repo.Repository.RepoPath(), pullMirror.GetRemoteName())
|
||||
u, err := gitrepo.GitRemoteGetURL(ctx, ctx.Repo.Repository, pullMirror.GetRemoteName())
|
||||
if err != nil {
|
||||
ctx.Data["Err_MirrorAddress"] = true
|
||||
handleSettingRemoteAddrError(ctx, err, form)
|
||||
|
||||
@ -33,6 +33,7 @@ import (
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
git_service "code.gitea.io/gitea/services/git"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
wiki_service "code.gitea.io/gitea/services/wiki"
|
||||
)
|
||||
|
||||
@ -474,7 +475,7 @@ func Wiki(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.Repository.HasWiki() {
|
||||
if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.wiki")
|
||||
ctx.HTML(http.StatusOK, tplWikiStart)
|
||||
return
|
||||
@ -510,7 +511,7 @@ func Wiki(ctx *context.Context) {
|
||||
func WikiRevision(ctx *context.Context) {
|
||||
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
|
||||
|
||||
if !ctx.Repo.Repository.HasWiki() {
|
||||
if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.wiki")
|
||||
ctx.HTML(http.StatusOK, tplWikiStart)
|
||||
return
|
||||
@ -540,7 +541,7 @@ func WikiRevision(ctx *context.Context) {
|
||||
|
||||
// WikiPages render wiki pages list page
|
||||
func WikiPages(ctx *context.Context) {
|
||||
if !ctx.Repo.Repository.HasWiki() {
|
||||
if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
|
||||
return
|
||||
}
|
||||
@ -648,7 +649,7 @@ func WikiRaw(ctx *context.Context) {
|
||||
func NewWiki(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
|
||||
|
||||
if !ctx.Repo.Repository.HasWiki() {
|
||||
if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
|
||||
ctx.Data["title"] = "Home"
|
||||
}
|
||||
if ctx.FormString("title") != "" {
|
||||
@ -701,7 +702,7 @@ func NewWikiPost(ctx *context.Context) {
|
||||
func EditWiki(ctx *context.Context) {
|
||||
ctx.Data["PageIsWikiEdit"] = true
|
||||
|
||||
if !ctx.Repo.Repository.HasWiki() {
|
||||
if !repo_service.HasWiki(ctx, ctx.Repo.Repository) {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
|
||||
return
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/services/contexttest"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
wiki_service "code.gitea.io/gitea/services/wiki"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -240,7 +241,7 @@ func TestDefaultWikiBranch(t *testing.T) {
|
||||
|
||||
// repo with no wiki
|
||||
repoWithNoWiki := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
assert.False(t, repoWithNoWiki.HasWiki())
|
||||
assert.False(t, repo_service.HasWiki(t.Context(), repoWithNoWiki))
|
||||
assert.NoError(t, wiki_service.ChangeDefaultWikiBranch(t.Context(), repoWithNoWiki, "main"))
|
||||
|
||||
// repo with wiki
|
||||
|
||||
@ -91,18 +91,12 @@ func checkEnablePushOptions(ctx context.Context, logger log.Logger, autofix bool
|
||||
|
||||
if err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
|
||||
numRepos++
|
||||
r, err := gitrepo.OpenRepository(ctx, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
if autofix {
|
||||
_, _, err := git.NewCommand("config", "receive.advertisePushOptions", "true").RunStdString(ctx, &git.RunOpts{Dir: r.Path})
|
||||
return err
|
||||
return gitrepo.GitConfigSet(ctx, repo, "receive.advertisePushOptions", "true")
|
||||
}
|
||||
|
||||
value, _, err := git.NewCommand("config", "receive.advertisePushOptions").RunStdString(ctx, &git.RunOpts{Dir: r.Path})
|
||||
value, err := gitrepo.GitConfigGet(ctx, repo, "receive.advertisePushOptions")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -11,26 +11,28 @@ import (
|
||||
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
org_model "code.gitea.io/gitea/models/organization"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func getMergeBase(repo *git.Repository, pr *issues_model.PullRequest, baseBranch, headBranch string) (string, error) {
|
||||
func getMergeBase(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, pr *issues_model.PullRequest, baseBranch, headBranch string) (string, error) {
|
||||
// Add a temporary remote
|
||||
tmpRemote := fmt.Sprintf("mergebase-%d-%d", pr.ID, time.Now().UnixNano())
|
||||
if err := repo.AddRemote(tmpRemote, repo.Path, false); err != nil {
|
||||
return "", fmt.Errorf("AddRemote: %w", err)
|
||||
if err := gitrepo.GitRemoteAdd(ctx, repo, tmpRemote, gitRepo.Path); err != nil {
|
||||
return "", fmt.Errorf("GitRemoteAdd: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := repo.RemoveRemote(tmpRemote); err != nil {
|
||||
log.Error("getMergeBase: RemoveRemote: %v", err)
|
||||
if err := gitrepo.GitRemoteRemove(graceful.GetManager().ShutdownContext(), repo, tmpRemote); err != nil {
|
||||
log.Error("getMergeBase: GitRemoteRemove: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
mergeBase, _, err := repo.GetMergeBase(tmpRemote, baseBranch, headBranch)
|
||||
mergeBase, _, err := gitRepo.GetMergeBase(tmpRemote, baseBranch, headBranch)
|
||||
return mergeBase, err
|
||||
}
|
||||
|
||||
@ -97,7 +99,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pr *issues_model.PullReque
|
||||
}
|
||||
|
||||
// get the mergebase
|
||||
mergeBase, err := getMergeBase(repo, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitHeadRefName())
|
||||
mergeBase, err := getMergeBase(ctx, pr.BaseRepo, repo, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitHeadRefName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -260,18 +260,18 @@ func actionToTemplate(issue *issues_model.Issue, actionType activities_model.Act
|
||||
}
|
||||
}
|
||||
|
||||
template = typeName + "/" + name
|
||||
template = "repo/" + typeName + "/" + name
|
||||
ok := LoadedTemplates().BodyTemplates.Lookup(template) != nil
|
||||
if !ok && typeName != "issue" {
|
||||
template = "issue/" + name
|
||||
template = "repo/issue/" + name
|
||||
ok = LoadedTemplates().BodyTemplates.Lookup(template) != nil
|
||||
}
|
||||
if !ok {
|
||||
template = typeName + "/default"
|
||||
template = "repo/" + typeName + "/default"
|
||||
ok = LoadedTemplates().BodyTemplates.Lookup(template) != nil
|
||||
}
|
||||
if !ok {
|
||||
template = "issue/default"
|
||||
template = "repo/issue/default"
|
||||
}
|
||||
return typeName, name, template
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ import (
|
||||
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
||||
)
|
||||
|
||||
const tplNewReleaseMail templates.TplName = "release"
|
||||
const tplNewReleaseMail templates.TplName = "repo/release"
|
||||
|
||||
func generateMessageIDForRelease(release *repo_model.Release) string {
|
||||
return fmt.Sprintf("<%s/releases/%d@%s>", release.Repo.FullName(), release.ID, setting.Domain)
|
||||
|
||||
@ -11,13 +11,17 @@ import (
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
||||
)
|
||||
|
||||
const mailRepoTransferNotify templates.TplName = "notify/repo_transfer"
|
||||
const (
|
||||
mailNotifyCollaborator templates.TplName = "repo/collaborator"
|
||||
mailRepoTransferNotify templates.TplName = "repo/transfer"
|
||||
)
|
||||
|
||||
// SendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created
|
||||
func SendRepoTransferNotifyMail(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) error {
|
||||
@ -91,3 +95,33 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendCollaboratorMail sends mail notification to new collaborator.
|
||||
func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) {
|
||||
if setting.MailService == nil || !u.IsActive {
|
||||
return
|
||||
}
|
||||
locale := translation.NewLocale(u.Language)
|
||||
repoName := repo.FullName()
|
||||
|
||||
subject := locale.TrString("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName)
|
||||
data := map[string]any{
|
||||
"locale": locale,
|
||||
"Subject": subject,
|
||||
"RepoName": repoName,
|
||||
"Link": repo.HTMLURL(),
|
||||
"Language": locale.Language(),
|
||||
}
|
||||
|
||||
var content bytes.Buffer
|
||||
|
||||
if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
|
||||
log.Error("Template: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg := sender_service.NewMessage(u.EmailTo(), subject, content.String())
|
||||
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
|
||||
|
||||
SendAsync(msg)
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ import (
|
||||
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
||||
)
|
||||
|
||||
const tplTeamInviteMail templates.TplName = "team_invite"
|
||||
const tplTeamInviteMail templates.TplName = "org/team_invite"
|
||||
|
||||
// MailTeamInvite sends team invites
|
||||
func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_model.Team, invite *org_model.TeamInvite) error {
|
||||
|
||||
@ -115,7 +115,7 @@ func TestComposeIssueComment(t *testing.T) {
|
||||
setting.IncomingEmail.Enabled = true
|
||||
defer func() { setting.IncomingEmail.Enabled = false }()
|
||||
|
||||
prepareMailTemplates("issue/comment", subjectTpl, bodyTpl)
|
||||
prepareMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)
|
||||
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
|
||||
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
|
||||
@ -160,7 +160,7 @@ func TestComposeIssueComment(t *testing.T) {
|
||||
func TestMailMentionsComment(t *testing.T) {
|
||||
doer, _, issue, comment := prepareMailerTest(t)
|
||||
comment.Poster = doer
|
||||
prepareMailTemplates("issue/comment", subjectTpl, bodyTpl)
|
||||
prepareMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)
|
||||
mails := 0
|
||||
|
||||
defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) {
|
||||
@ -175,7 +175,7 @@ func TestMailMentionsComment(t *testing.T) {
|
||||
func TestComposeIssueMessage(t *testing.T) {
|
||||
doer, _, issue, _ := prepareMailerTest(t)
|
||||
|
||||
prepareMailTemplates("issue/new", subjectTpl, bodyTpl)
|
||||
prepareMailTemplates("repo/issue/new", subjectTpl, bodyTpl)
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
|
||||
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
|
||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
|
||||
@ -204,14 +204,14 @@ func TestTemplateSelection(t *testing.T) {
|
||||
doer, repo, issue, comment := prepareMailerTest(t)
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
|
||||
|
||||
prepareMailTemplates("issue/default", "issue/default/subject", "issue/default/body")
|
||||
prepareMailTemplates("repo/issue/default", "repo/issue/default/subject", "repo/issue/default/body")
|
||||
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("issue/new").Parse("issue/new/subject"))
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("pull/comment").Parse("pull/comment/subject"))
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("issue/close").Parse("")) // Must default to a fallback subject
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("issue/new").Parse("issue/new/body"))
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("pull/comment").Parse("pull/comment/body"))
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("issue/close").Parse("issue/close/body"))
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/issue/new").Parse("repo/issue/new/subject"))
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/pull/comment").Parse("repo/pull/comment/subject"))
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/issue/close").Parse("")) // Must default to a fallback subject
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("repo/issue/new").Parse("repo/issue/new/body"))
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("repo/pull/comment").Parse("repo/pull/comment/body"))
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("repo/issue/close").Parse("repo/issue/close/body"))
|
||||
|
||||
expect := func(t *testing.T, msg *sender_service.Message, expSubject, expBody string) {
|
||||
subject := msg.ToMessage().GetGenHeader("Subject")
|
||||
@ -226,13 +226,13 @@ func TestTemplateSelection(t *testing.T) {
|
||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
|
||||
Content: "test body",
|
||||
}, recipients, false, "TestTemplateSelection")
|
||||
expect(t, msg, "issue/new/subject", "issue/new/body")
|
||||
expect(t, msg, "repo/issue/new/subject", "repo/issue/new/body")
|
||||
|
||||
msg = testComposeIssueCommentMessage(t, &mailComment{
|
||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
|
||||
Content: "test body", Comment: comment,
|
||||
}, recipients, false, "TestTemplateSelection")
|
||||
expect(t, msg, "issue/default/subject", "issue/default/body")
|
||||
expect(t, msg, "repo/issue/default/subject", "repo/issue/default/body")
|
||||
|
||||
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer})
|
||||
comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull})
|
||||
@ -240,13 +240,13 @@ func TestTemplateSelection(t *testing.T) {
|
||||
Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull,
|
||||
Content: "test body", Comment: comment,
|
||||
}, recipients, false, "TestTemplateSelection")
|
||||
expect(t, msg, "pull/comment/subject", "pull/comment/body")
|
||||
expect(t, msg, "repo/pull/comment/subject", "repo/pull/comment/body")
|
||||
|
||||
msg = testComposeIssueCommentMessage(t, &mailComment{
|
||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue,
|
||||
Content: "test body", Comment: comment,
|
||||
}, recipients, false, "TestTemplateSelection")
|
||||
expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "issue/close/body")
|
||||
expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "repo/issue/close/body")
|
||||
}
|
||||
|
||||
func TestTemplateServices(t *testing.T) {
|
||||
@ -256,7 +256,7 @@ func TestTemplateServices(t *testing.T) {
|
||||
expect := func(t *testing.T, issue *issues_model.Issue, comment *issues_model.Comment, doer *user_model.User,
|
||||
actionType activities_model.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string,
|
||||
) {
|
||||
prepareMailTemplates("issue/default", tplSubject, tplBody)
|
||||
prepareMailTemplates("repo/issue/default", tplSubject, tplBody)
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
|
||||
msg := testComposeIssueCommentMessage(t, &mailComment{
|
||||
Issue: issue, Doer: doer, ActionType: actionType,
|
||||
@ -523,7 +523,7 @@ func TestEmbedBase64Images(t *testing.T) {
|
||||
att2ImgBase64 := fmt.Sprintf(`<img src="%s"/>`, att2Base64)
|
||||
|
||||
t.Run("ComposeMessage", func(t *testing.T) {
|
||||
prepareMailTemplates("issue/new", subjectTpl, bodyTpl)
|
||||
prepareMailTemplates("repo/issue/new", subjectTpl, bodyTpl)
|
||||
|
||||
issue.Content = fmt.Sprintf(`MSG-BEFORE <image src="attachments/%s"> MSG-AFTER`, att1.UUID)
|
||||
require.NoError(t, issues_model.UpdateIssueCols(t.Context(), issue, "content"))
|
||||
|
||||
@ -7,7 +7,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@ -18,11 +17,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
mailAuthActivate templates.TplName = "auth/activate"
|
||||
mailAuthActivateEmail templates.TplName = "auth/activate_email"
|
||||
mailAuthResetPassword templates.TplName = "auth/reset_passwd"
|
||||
mailAuthRegisterNotify templates.TplName = "auth/register_notify"
|
||||
mailNotifyCollaborator templates.TplName = "notify/collaborator"
|
||||
mailAuthActivate templates.TplName = "user/auth/activate"
|
||||
mailAuthActivateEmail templates.TplName = "user/auth/activate_email"
|
||||
mailAuthResetPassword templates.TplName = "user/auth/reset_passwd"
|
||||
mailAuthRegisterNotify templates.TplName = "user/auth/register_notify"
|
||||
)
|
||||
|
||||
// sendUserMail sends a mail to the user
|
||||
@ -128,34 +126,3 @@ func SendRegisterNotifyMail(u *user_model.User) {
|
||||
|
||||
SendAsync(msg)
|
||||
}
|
||||
|
||||
// SendCollaboratorMail sends mail notification to new collaborator.
|
||||
func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) {
|
||||
if setting.MailService == nil || !u.IsActive {
|
||||
// No mail service configured OR the user is inactive
|
||||
return
|
||||
}
|
||||
locale := translation.NewLocale(u.Language)
|
||||
repoName := repo.FullName()
|
||||
|
||||
subject := locale.TrString("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName)
|
||||
data := map[string]any{
|
||||
"locale": locale,
|
||||
"Subject": subject,
|
||||
"RepoName": repoName,
|
||||
"Link": repo.HTMLURL(),
|
||||
"Language": locale.Language(),
|
||||
}
|
||||
|
||||
var content bytes.Buffer
|
||||
|
||||
if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
|
||||
log.Error("Template: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg := sender_service.NewMessage(u.EmailTo(), subject, content.String())
|
||||
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
|
||||
|
||||
SendAsync(msg)
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
@ -15,18 +16,20 @@ import (
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
||||
)
|
||||
|
||||
const tplWorkflowRun = "notify/workflow_run"
|
||||
const tplWorkflowRun templates.TplName = "repo/actions/workflow_run"
|
||||
|
||||
type convertedWorkflowJob struct {
|
||||
HTMLURL string
|
||||
Status actions_model.Status
|
||||
Name string
|
||||
Attempt int64
|
||||
HTMLURL string
|
||||
Name string
|
||||
Status actions_model.Status
|
||||
Attempt int64
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
func generateMessageIDForActionsWorkflowRunStatusEmail(repo *repo_model.Repository, run *actions_model.ActionRun) string {
|
||||
@ -45,16 +48,15 @@ func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo
|
||||
}
|
||||
}
|
||||
|
||||
subject := "Run"
|
||||
var subjectTrString string
|
||||
switch run.Status {
|
||||
case actions_model.StatusFailure:
|
||||
subject += " failed"
|
||||
subjectTrString = "mail.repo.actions.run.failed"
|
||||
case actions_model.StatusCancelled:
|
||||
subject += " cancelled"
|
||||
subjectTrString = "mail.repo.actions.run.cancelled"
|
||||
case actions_model.StatusSuccess:
|
||||
subject += " succeeded"
|
||||
subjectTrString = "mail.repo.actions.run.succeeded"
|
||||
}
|
||||
subject = fmt.Sprintf("%s: %s (%s)", subject, run.WorkflowID, base.ShortSha(run.CommitSHA))
|
||||
displayName := fromDisplayName(sender)
|
||||
messageID := generateMessageIDForActionsWorkflowRunStatusEmail(repo, run)
|
||||
metadataHeaders := generateMetadataHeaders(repo)
|
||||
@ -80,10 +82,11 @@ func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo
|
||||
continue
|
||||
}
|
||||
convertedJobs = append(convertedJobs, convertedWorkflowJob{
|
||||
HTMLURL: converted0.HTMLURL,
|
||||
Name: converted0.Name,
|
||||
Status: job.Status,
|
||||
Attempt: converted0.RunAttempt,
|
||||
HTMLURL: converted0.HTMLURL,
|
||||
Name: converted0.Name,
|
||||
Status: job.Status,
|
||||
Attempt: converted0.RunAttempt,
|
||||
Duration: job.Duration(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -93,27 +96,28 @@ func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo
|
||||
}
|
||||
for lang, tos := range langMap {
|
||||
locale := translation.NewLocale(lang)
|
||||
var runStatusText string
|
||||
var runStatusTrString string
|
||||
switch run.Status {
|
||||
case actions_model.StatusSuccess:
|
||||
runStatusText = "All jobs have succeeded"
|
||||
runStatusTrString = "mail.repo.actions.jobs.all_succeeded"
|
||||
case actions_model.StatusFailure:
|
||||
runStatusText = "All jobs have failed"
|
||||
runStatusTrString = "mail.repo.actions.jobs.all_failed"
|
||||
for _, job := range jobs {
|
||||
if !job.Status.IsFailure() {
|
||||
runStatusText = "Some jobs were not successful"
|
||||
runStatusTrString = "mail.repo.actions.jobs.some_not_successful"
|
||||
break
|
||||
}
|
||||
}
|
||||
case actions_model.StatusCancelled:
|
||||
runStatusText = "All jobs have been cancelled"
|
||||
runStatusTrString = "mail.repo.actions.jobs.all_cancelled"
|
||||
}
|
||||
subject := fmt.Sprintf("%s: %s (%s)", locale.TrString(subjectTrString), run.WorkflowID, base.ShortSha(run.CommitSHA))
|
||||
var mailBody bytes.Buffer
|
||||
if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&mailBody, tplWorkflowRun, map[string]any{
|
||||
if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&mailBody, string(tplWorkflowRun), map[string]any{
|
||||
"Subject": subject,
|
||||
"Repo": repo,
|
||||
"Run": run,
|
||||
"RunStatusText": runStatusText,
|
||||
"RunStatusText": locale.TrString(runStatusTrString),
|
||||
"Jobs": convertedJobs,
|
||||
"locale": locale,
|
||||
}); err != nil {
|
||||
|
||||
@ -7,7 +7,7 @@ package migrations
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/google/go-github/v71/github"
|
||||
"github.com/google/go-github/v74/github"
|
||||
)
|
||||
|
||||
// ErrRepoNotCreated returns the error that repository not created
|
||||
|
||||
@ -63,7 +63,7 @@ func TestGiteaUploadRepo(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, Name: repoName})
|
||||
assert.True(t, repo.HasWiki())
|
||||
assert.True(t, repo_service.HasWiki(ctx, repo))
|
||||
assert.Equal(t, repo_model.RepositoryReady, repo.Status)
|
||||
|
||||
milestones, err := db.Find[issues_model.Milestone](t.Context(), issues_model.FindMilestoneOptions{
|
||||
@ -236,6 +236,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
|
||||
//
|
||||
fromRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
baseRef := "master"
|
||||
// this is very different from the real situation. It should be a bare repository for all the Gitea managed repositories
|
||||
assert.NoError(t, git.InitRepository(t.Context(), fromRepo.RepoPath(), false, fromRepo.ObjectFormatName))
|
||||
err := git.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef).Run(t.Context(), &git.RunOpts{Dir: fromRepo.RepoPath()})
|
||||
assert.NoError(t, err)
|
||||
|
||||
@ -20,7 +20,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/proxy"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/google/go-github/v71/github"
|
||||
"github.com/google/go-github/v74/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@ func (f *GitlabDownloaderFactory) New(ctx context.Context, opts base.MigrateOpti
|
||||
|
||||
log.Trace("Create gitlab downloader. BaseURL: %s RepoName: %s", baseURL, repoNameSpace)
|
||||
|
||||
return NewGitlabDownloader(ctx, baseURL, repoNameSpace, opts.AuthUsername, opts.AuthPassword, opts.AuthToken)
|
||||
return NewGitlabDownloader(ctx, baseURL, repoNameSpace, opts.AuthToken)
|
||||
}
|
||||
|
||||
// GitServiceType returns the type of git service
|
||||
@ -93,14 +93,8 @@ type GitlabDownloader struct {
|
||||
//
|
||||
// Use either a username/password, personal token entered into the username field, or anonymous/public access
|
||||
// Note: Public access only allows very basic access
|
||||
func NewGitlabDownloader(ctx context.Context, baseURL, repoPath, username, password, token string) (*GitlabDownloader, error) {
|
||||
func NewGitlabDownloader(ctx context.Context, baseURL, repoPath, token string) (*GitlabDownloader, error) {
|
||||
gitlabClient, err := gitlab.NewClient(token, gitlab.WithBaseURL(baseURL), gitlab.WithHTTPClient(NewMigrationHTTPClient()))
|
||||
// Only use basic auth if token is blank and password is NOT
|
||||
// Basic auth will fail with empty strings, but empty token will allow anonymous public API usage
|
||||
if token == "" && password != "" {
|
||||
gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL), gitlab.WithHTTPClient(NewMigrationHTTPClient()))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Trace("Error logging into gitlab: %v", err)
|
||||
return nil, err
|
||||
@ -206,7 +200,7 @@ func (g *GitlabDownloader) GetTopics(ctx context.Context) ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gr.TagList, err
|
||||
return gr.Topics, err
|
||||
}
|
||||
|
||||
// GetMilestones returns milestones
|
||||
|
||||
@ -31,7 +31,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
|
||||
t.Skipf("Can't access test repo, skipping %s", t.Name())
|
||||
}
|
||||
ctx := t.Context()
|
||||
downloader, err := NewGitlabDownloader(ctx, "https://gitlab.com", "gitea/test_repo", "", "", gitlabPersonalAccessToken)
|
||||
downloader, err := NewGitlabDownloader(ctx, "https://gitlab.com", "gitea/test_repo", gitlabPersonalAccessToken)
|
||||
if err != nil {
|
||||
t.Fatalf("NewGitlabDownloader is nil: %v", err)
|
||||
}
|
||||
|
||||
@ -39,30 +39,27 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
|
||||
}
|
||||
|
||||
remoteName := m.GetRemoteName()
|
||||
repoPath := m.GetRepository(ctx).RepoPath()
|
||||
repo := m.GetRepository(ctx)
|
||||
// Remove old remote
|
||||
_, _, err = git.NewCommand("remote", "rm").AddDynamicArguments(remoteName).RunStdString(ctx, &git.RunOpts{Dir: repoPath})
|
||||
err = gitrepo.GitRemoteRemove(ctx, repo, remoteName)
|
||||
if err != nil && !git.IsRemoteNotExistError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := git.NewCommand("remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr)
|
||||
_, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath})
|
||||
err = gitrepo.GitRemoteAdd(ctx, repo, remoteName, addr, gitrepo.RemoteOptionMirrorFetch)
|
||||
if err != nil && !git.IsRemoteNotExistError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.Repo.HasWiki() {
|
||||
wikiPath := m.Repo.WikiPath()
|
||||
if repo_service.HasWiki(ctx, m.Repo) {
|
||||
wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr)
|
||||
// Remove old remote of wiki
|
||||
_, _, err = git.NewCommand("remote", "rm").AddDynamicArguments(remoteName).RunStdString(ctx, &git.RunOpts{Dir: wikiPath})
|
||||
err = gitrepo.GitRemoteRemove(ctx, repo.WikiStorageRepo(), remoteName)
|
||||
if err != nil && !git.IsRemoteNotExistError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd = git.NewCommand("remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(wikiRemotePath)
|
||||
_, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: wikiPath})
|
||||
err = gitrepo.GitRemoteAdd(ctx, repo.WikiStorageRepo(), remoteName, wikiRemotePath, gitrepo.RemoteOptionMirrorFetch)
|
||||
if err != nil && !git.IsRemoteNotExistError(err) {
|
||||
return err
|
||||
}
|
||||
@ -197,25 +194,21 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
|
||||
|
||||
func pruneBrokenReferences(ctx context.Context,
|
||||
m *repo_model.Mirror,
|
||||
repoPath string,
|
||||
timeout time.Duration,
|
||||
stdoutBuilder, stderrBuilder *strings.Builder,
|
||||
isWiki bool,
|
||||
) error {
|
||||
wiki := ""
|
||||
var storageRepo gitrepo.Repository = m.Repo
|
||||
if isWiki {
|
||||
wiki = "Wiki "
|
||||
storageRepo = m.Repo.WikiStorageRepo()
|
||||
}
|
||||
|
||||
stderrBuilder.Reset()
|
||||
stdoutBuilder.Reset()
|
||||
pruneErr := git.NewCommand("remote", "prune").AddDynamicArguments(m.GetRemoteName()).
|
||||
Run(ctx, &git.RunOpts{
|
||||
Timeout: timeout,
|
||||
Dir: repoPath,
|
||||
Stdout: stdoutBuilder,
|
||||
Stderr: stderrBuilder,
|
||||
})
|
||||
|
||||
pruneErr := gitrepo.GitRemotePrune(ctx, storageRepo, m.GetRemoteName(), timeout, stdoutBuilder, stderrBuilder)
|
||||
if pruneErr != nil {
|
||||
stdout := stdoutBuilder.String()
|
||||
stderr := stderrBuilder.String()
|
||||
@ -226,7 +219,7 @@ func pruneBrokenReferences(ctx context.Context,
|
||||
stdoutMessage := util.SanitizeCredentialURLs(stdout)
|
||||
|
||||
log.Error("Failed to prune mirror repository %s%-v references:\nStdout: %s\nStderr: %s\nErr: %v", wiki, m.Repo, stdoutMessage, stderrMessage, pruneErr)
|
||||
desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, repoPath, stderrMessage)
|
||||
desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, storageRepo.RelativePath(), stderrMessage)
|
||||
if err := system_model.CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error("CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
@ -268,9 +261,9 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
||||
}
|
||||
cmd.AddArguments("--tags").AddDynamicArguments(m.GetRemoteName())
|
||||
|
||||
remoteURL, remoteErr := git.GetRemoteURL(ctx, repoPath, m.GetRemoteName())
|
||||
remoteURL, remoteErr := gitrepo.GitRemoteGetURL(ctx, m.Repo, m.GetRemoteName())
|
||||
if remoteErr != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: GetRemoteAddress Error %v", m.Repo, remoteErr)
|
||||
log.Error("SyncMirrors [repo: %-v]: GetRemoteURL Error %v", m.Repo, remoteErr)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@ -298,7 +291,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
||||
err = nil
|
||||
|
||||
// Attempt prune
|
||||
pruneErr := pruneBrokenReferences(ctx, m, repoPath, timeout, &stdoutBuilder, &stderrBuilder, false)
|
||||
pruneErr := pruneBrokenReferences(ctx, m, timeout, &stdoutBuilder, &stderrBuilder, false)
|
||||
if pruneErr == nil {
|
||||
// Successful prune - reattempt mirror
|
||||
stderrBuilder.Reset()
|
||||
@ -347,7 +340,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
||||
endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint)
|
||||
lfsClient := lfs.NewClient(endpoint, nil)
|
||||
if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err)
|
||||
log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo.FullName(), err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,20 +357,16 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
||||
|
||||
log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo)
|
||||
if err := repo_module.UpdateRepoSize(ctx, m.Repo); err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: failed to update size for mirror repository: %v", m.Repo, err)
|
||||
log.Error("SyncMirrors [repo: %-v]: failed to update size for mirror repository: %v", m.Repo.FullName(), err)
|
||||
}
|
||||
|
||||
if m.Repo.HasWiki() {
|
||||
if repo_service.HasWiki(ctx, m.Repo) {
|
||||
log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo)
|
||||
stderrBuilder.Reset()
|
||||
stdoutBuilder.Reset()
|
||||
if err := git.NewCommand("remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
|
||||
Run(ctx, &git.RunOpts{
|
||||
Timeout: timeout,
|
||||
Dir: wikiPath,
|
||||
Stdout: &stdoutBuilder,
|
||||
Stderr: &stderrBuilder,
|
||||
}); err != nil {
|
||||
|
||||
if err := gitrepo.GitRemoteUpdatePrune(ctx, m.Repo.WikiStorageRepo(), m.GetRemoteName(),
|
||||
timeout, &stdoutBuilder, &stderrBuilder); err != nil {
|
||||
stdout := stdoutBuilder.String()
|
||||
stderr := stderrBuilder.String()
|
||||
|
||||
@ -391,19 +380,14 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
||||
err = nil
|
||||
|
||||
// Attempt prune
|
||||
pruneErr := pruneBrokenReferences(ctx, m, repoPath, timeout, &stdoutBuilder, &stderrBuilder, true)
|
||||
pruneErr := pruneBrokenReferences(ctx, m, timeout, &stdoutBuilder, &stderrBuilder, true)
|
||||
if pruneErr == nil {
|
||||
// Successful prune - reattempt mirror
|
||||
stderrBuilder.Reset()
|
||||
stdoutBuilder.Reset()
|
||||
|
||||
if err = git.NewCommand("remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
|
||||
Run(ctx, &git.RunOpts{
|
||||
Timeout: timeout,
|
||||
Dir: wikiPath,
|
||||
Stdout: &stdoutBuilder,
|
||||
Stderr: &stderrBuilder,
|
||||
}); err != nil {
|
||||
if err = gitrepo.GitRemoteUpdatePrune(ctx, m.Repo.WikiStorageRepo(), m.GetRemoteName(),
|
||||
timeout, &stdoutBuilder, &stderrBuilder); err != nil {
|
||||
stdout := stdoutBuilder.String()
|
||||
stderr := stderrBuilder.String()
|
||||
stderrMessage = util.SanitizeCredentialURLs(stderr)
|
||||
|
||||
@ -23,34 +23,31 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
)
|
||||
|
||||
var stripExitStatus = regexp.MustCompile(`exit status \d+ - `)
|
||||
|
||||
// AddPushMirrorRemote registers the push mirror remote.
|
||||
func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error {
|
||||
addRemoteAndConfig := func(addr, path string) error {
|
||||
cmd := git.NewCommand("remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr)
|
||||
if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: path}); err != nil {
|
||||
addRemoteAndConfig := func(storageRepo gitrepo.Repository, addr string) error {
|
||||
if err := gitrepo.GitRemoteAdd(ctx, storageRepo, m.RemoteName, addr, gitrepo.RemoteOptionMirrorPush); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, _, err := git.NewCommand("config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(ctx, &git.RunOpts{Dir: path}); err != nil {
|
||||
if err := gitrepo.GitConfigAdd(ctx, storageRepo, "remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*"); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, _, err := git.NewCommand("config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(ctx, &git.RunOpts{Dir: path}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return gitrepo.GitConfigAdd(ctx, storageRepo, "remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*")
|
||||
}
|
||||
|
||||
if err := addRemoteAndConfig(addr, m.Repo.RepoPath()); err != nil {
|
||||
if err := addRemoteAndConfig(m.Repo, addr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.Repo.HasWiki() {
|
||||
if repo_service.HasWiki(ctx, m.Repo) {
|
||||
wikiRemoteURL := repository.WikiRemoteURL(ctx, addr)
|
||||
if len(wikiRemoteURL) > 0 {
|
||||
if err := addRemoteAndConfig(wikiRemoteURL, m.Repo.WikiPath()); err != nil {
|
||||
if err := addRemoteAndConfig(m.Repo.WikiStorageRepo(), wikiRemoteURL); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -61,15 +58,13 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str
|
||||
|
||||
// RemovePushMirrorRemote removes the push mirror remote.
|
||||
func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error {
|
||||
cmd := git.NewCommand("remote", "rm").AddDynamicArguments(m.RemoteName)
|
||||
_ = m.GetRepository(ctx)
|
||||
|
||||
if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: m.Repo.RepoPath()}); err != nil {
|
||||
if err := gitrepo.GitRemoteRemove(ctx, m.Repo, m.RemoteName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.Repo.HasWiki() {
|
||||
if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: m.Repo.WikiPath()}); err != nil {
|
||||
if repo_service.HasWiki(ctx, m.Repo) {
|
||||
if err := gitrepo.GitRemoteRemove(ctx, m.Repo.WikiStorageRepo(), m.RemoteName); err != nil {
|
||||
// The wiki remote may not exist
|
||||
log.Warn("Wiki Remote[%d] could not be removed: %v", m.ID, err)
|
||||
}
|
||||
@ -128,25 +123,22 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
|
||||
timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second
|
||||
|
||||
performPush := func(repo *repo_model.Repository, isWiki bool) error {
|
||||
var storageRepo gitrepo.Repository = repo
|
||||
path := repo.RepoPath()
|
||||
if isWiki {
|
||||
storageRepo = repo.WikiStorageRepo()
|
||||
path = repo.WikiPath()
|
||||
}
|
||||
remoteURL, err := git.GetRemoteURL(ctx, path, m.RemoteName)
|
||||
remoteURL, err := gitrepo.GitRemoteGetURL(ctx, storageRepo, m.RemoteName)
|
||||
if err != nil {
|
||||
log.Error("GetRemoteAddress(%s) Error %v", path, err)
|
||||
log.Error("GetRemoteURL(%s) Error %v", path, err)
|
||||
return errors.New("Unexpected error")
|
||||
}
|
||||
|
||||
if setting.LFS.StartServer {
|
||||
log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
|
||||
|
||||
var gitRepo *git.Repository
|
||||
if isWiki {
|
||||
gitRepo, err = gitrepo.OpenRepository(ctx, repo.WikiStorageRepo())
|
||||
} else {
|
||||
gitRepo, err = gitrepo.OpenRepository(ctx, repo)
|
||||
}
|
||||
gitRepo, err := gitrepo.OpenRepository(ctx, storageRepo)
|
||||
if err != nil {
|
||||
log.Error("OpenRepository: %v", err)
|
||||
return errors.New("Unexpected error")
|
||||
@ -183,15 +175,14 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.Repo.HasWiki() {
|
||||
_, err := git.GetRemoteAddress(ctx, m.Repo.WikiPath(), m.RemoteName)
|
||||
if err == nil {
|
||||
if repo_service.HasWiki(ctx, m.Repo) {
|
||||
if _, err := gitrepo.GitRemoteGetURL(ctx, m.Repo.WikiStorageRepo(), m.RemoteName); err == nil {
|
||||
err := performPush(m.Repo, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Trace("Skipping wiki: No remote configured")
|
||||
} else if !errors.Is(err, util.ErrNotExist) {
|
||||
log.Error("GetRemote of wiki failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||