diff options
42 files changed, 965 insertions, 741 deletions
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ab30e1789d..4128f1466f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,7 +4,7 @@ "features": { // installs nodejs into container "ghcr.io/devcontainers/features/node:1": { - "version": "20" + "version": "lts" }, "ghcr.io/devcontainers/features/git-lfs:1.2.2": {}, "ghcr.io/devcontainers-contrib/features/poetry:2": {}, diff --git a/.github/workflows/pull-compliance.yml b/.github/workflows/pull-compliance.yml index 64090d6490..f6720bf2f6 100644 --- a/.github/workflows/pull-compliance.yml +++ b/.github/workflows/pull-compliance.yml @@ -37,7 +37,7 @@ jobs: python-version: "3.12" - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: npm cache-dependency-path: package-lock.json - run: pip install poetry @@ -66,7 +66,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: npm cache-dependency-path: package-lock.json - run: make deps-frontend @@ -137,7 +137,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: npm cache-dependency-path: package-lock.json - run: make deps-frontend @@ -186,7 +186,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: npm cache-dependency-path: package-lock.json - run: make deps-frontend diff --git a/.github/workflows/pull-e2e-tests.yml b/.github/workflows/pull-e2e-tests.yml index 87e931117c..cc3fbd9c34 100644 --- a/.github/workflows/pull-e2e-tests.yml +++ b/.github/workflows/pull-e2e-tests.yml @@ -25,7 +25,7 @@ jobs: check-latest: true - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: npm cache-dependency-path: package-lock.json - run: make deps-frontend frontend deps-backend diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index 2558a16a71..c2cc14f771 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -22,7 +22,7 @@ jobs: check-latest: true - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: npm cache-dependency-path: package-lock.json - run: make deps-frontend deps-backend diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml index 37b3ff57d2..c9c15c31a0 100644 --- a/.github/workflows/release-tag-rc.yml +++ b/.github/workflows/release-tag-rc.yml @@ -23,7 +23,7 @@ jobs: check-latest: true - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: npm cache-dependency-path: package-lock.json - run: make deps-frontend deps-backend diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml index 4250623da0..ae717c7cec 100644 --- a/.github/workflows/release-tag-version.yml +++ b/.github/workflows/release-tag-version.yml @@ -27,7 +27,7 @@ jobs: check-latest: true - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: npm cache-dependency-path: package-lock.json - run: make deps-frontend deps-backend diff --git a/.gitignore b/.gitignore index 272ea2b5ed..0791a17c71 100644 --- a/.gitignore +++ b/.gitignore @@ -42,14 +42,10 @@ _testmain.go coverage.all cpu.out -/modules/migration/bindata.go -/modules/migration/bindata.go.hash -/modules/options/bindata.go -/modules/options/bindata.go.hash -/modules/public/bindata.go -/modules/public/bindata.go.hash -/modules/templates/bindata.go -/modules/templates/bindata.go.hash +/modules/migration/bindata.* +/modules/options/bindata.* +/modules/public/bindata.* +/modules/templates/bindata.* *.db *.log @@ -120,8 +120,7 @@ WEBPACK_CONFIGS := webpack.config.js tailwind.config.js WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts -BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go -BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST)) +BINDATA_DEST := modules/public/bindata.dat modules/options/bindata.dat modules/templates/bindata.dat GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go @@ -149,14 +148,8 @@ SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini GO_SOURCES := $(wildcard *.go) -GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/options/bindata.go ! -path modules/public/bindata.go ! -path modules/templates/bindata.go) +GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go") GO_SOURCES += $(GENERATED_GO_DEST) -GO_SOURCES_NO_BINDATA := $(GO_SOURCES) - -ifeq ($(filter $(TAGS_SPLIT),bindata),bindata) - GO_SOURCES += $(BINDATA_DEST) - GENERATED_GO_DEST += $(BINDATA_DEST) -endif # Force installation of playwright dependencies by setting this flag ifdef DEPS_PLAYWRIGHT @@ -226,7 +219,7 @@ clean-all: clean ## delete backend, frontend and integration files .PHONY: clean clean: ## delete backend and integration files - rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \ + rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) \ integrations*.test \ e2e*.test \ tests/integration/gitea-integration-* \ @@ -268,7 +261,7 @@ endif .PHONY: generate-swagger generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments -$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA) $(SWAGGER_SPEC_INPUT) +$(SWAGGER_SPEC): $(GO_SOURCES) $(SWAGGER_SPEC_INPUT) $(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)' .PHONY: swagger-check @@ -373,7 +366,7 @@ lint-go-gitea-vet: ## lint go files with gitea-vet .PHONY: lint-go-gopls lint-go-gopls: ## lint go files with gopls @echo "Running gopls check..." - @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA) + @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES) .PHONY: lint-editorconfig lint-editorconfig: @@ -5,19 +5,10 @@ package main -// Libraries that are included to vendor utilities used during build. +// Libraries that are included to vendor utilities used during Makefile build. // These libraries will not be included in a normal compilation. import ( - // for embed - _ "github.com/shurcooL/vfsgen" - - // for cover merge - _ "golang.org/x/tools/cover" - // for vet _ "code.gitea.io/gitea-vet" - - // for swagger - _ "github.com/go-swagger/go-swagger/cmd/swagger" ) diff --git a/build/generate-bindata.go b/build/generate-bindata.go index 2fcb7c2f2a..2553770762 100644 --- a/build/generate-bindata.go +++ b/build/generate-bindata.go @@ -6,87 +6,22 @@ package main import ( - "bytes" - "crypto/sha1" "fmt" - "log" - "net/http" "os" - "path/filepath" - "strconv" - "github.com/shurcooL/vfsgen" + "code.gitea.io/gitea/modules/assetfs" ) -func needsUpdate(dir, filename string) (bool, []byte) { - needRegen := false - _, err := os.Stat(filename) - if err != nil { - needRegen = true - } - - oldHash, err := os.ReadFile(filename + ".hash") - if err != nil { - oldHash = []byte{} - } - - hasher := sha1.New() - - err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error { - if err != nil { - return err - } - info, err := d.Info() - if err != nil { - return err - } - _, _ = hasher.Write([]byte(d.Name())) - _, _ = hasher.Write([]byte(info.ModTime().String())) - _, _ = hasher.Write([]byte(strconv.FormatInt(info.Size(), 16))) - return nil - }) - if err != nil { - return true, oldHash - } - - newHash := hasher.Sum([]byte{}) - - if bytes.Compare(oldHash, newHash) != 0 { - return true, newHash - } - - return needRegen, newHash -} - func main() { - if len(os.Args) < 4 { - log.Fatal("Insufficient number of arguments. Need: directory packageName filename") - } - - dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3] - var useGlobalModTime bool - if len(os.Args) == 5 { - useGlobalModTime, _ = strconv.ParseBool(os.Args[4]) - } - - update, newHash := needsUpdate(dir, filename) - - if !update { - fmt.Printf("bindata for %s already up-to-date\n", packageName) - return + if len(os.Args) != 3 { + fmt.Println("usage: ./generate-bindata {local-directory} {bindata-filename}") + os.Exit(1) } - fmt.Printf("generating bindata for %s\n", packageName) - var fsTemplates http.FileSystem = http.Dir(dir) - err := vfsgen.Generate(fsTemplates, vfsgen.Options{ - PackageName: packageName, - BuildTags: "bindata", - VariableName: "Assets", - Filename: filename, - UseGlobalModTime: useGlobalModTime, - }) - if err != nil { - log.Fatalf("%v\n", err) + dir, filename := os.Args[1], os.Args[2] + fmt.Printf("generating bindata for %s to %s\n", dir, filename) + if err := assetfs.GenerateEmbedBindata(dir, filename); err != nil { + fmt.Printf("failed: %s\n", err.Error()) + os.Exit(1) } - _ = os.WriteFile(filename+".hash", newHash, 0o666) } diff --git a/cmd/embedded.go b/cmd/embedded.go index 086bc06863..6a2fa07a93 100644 --- a/cmd/embedded.go +++ b/cmd/embedded.go @@ -118,7 +118,7 @@ func initEmbeddedExtractor(c *cli.Command) error { func runList(_ context.Context, c *cli.Command) error { if err := runListDo(c); err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) return err } return nil @@ -126,7 +126,7 @@ func runList(_ context.Context, c *cli.Command) error { func runView(_ context.Context, c *cli.Command) error { if err := runViewDo(c); err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) return err } return nil @@ -134,7 +134,7 @@ func runView(_ context.Context, c *cli.Command) error { func runExtract(_ context.Context, c *cli.Command) error { if err := runExtractDo(c); err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) return err } return nil @@ -217,7 +217,7 @@ func runExtractDo(c *cli.Command) error { for _, a := range matchedAssetFiles { if err := extractAsset(destdir, a, overwrite, rename); err != nil { // Non-fatal error - fmt.Fprintf(os.Stderr, "%s: %v", a.path, err) + _, _ = fmt.Fprintf(os.Stderr, "%s: %v\n", a.path, err) } } diff --git a/cmd/serv.go b/cmd/serv.go index e4434450d6..8c6001e727 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -212,7 +212,7 @@ func runServ(ctx context.Context, c *cli.Command) error { if git.DefaultFeatures().SupportProcReceive { // for AGit Flow if cmd == "ssh_info" { - fmt.Print(`{"type":"gitea","version":1}`) + fmt.Print(`{"type":"agit","version":1}`) return nil } } @@ -60,7 +60,6 @@ require ( 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-swagger/go-swagger v0.31.0 github.com/go-webauthn/webauthn v0.12.3 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f @@ -105,7 +104,6 @@ require ( 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/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.0 github.com/tstranex/u2f v1.0.0 @@ -126,7 +124,6 @@ require ( golang.org/x/sync v0.15.0 golang.org/x/sys v0.33.0 golang.org/x/text v0.26.0 - golang.org/x/tools v0.33.0 google.golang.org/grpc v1.72.0 google.golang.org/protobuf v1.36.6 gopkg.in/ini.v1 v1.67.0 @@ -144,15 +141,11 @@ require ( 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/DataDog/zstd v1.5.7 // indirect - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.3.1 // indirect - github.com/Masterminds/sprig/v3 v3.3.0 // 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/andybalholm/cascadia v1.3.3 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // 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 @@ -195,7 +188,6 @@ 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/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650 // indirect @@ -204,18 +196,6 @@ require ( 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-openapi/analysis v0.23.0 // indirect - github.com/go-openapi/errors v0.22.1 // indirect - github.com/go-openapi/inflect v0.21.2 // indirect - github.com/go-openapi/jsonpointer v0.21.1 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/loads v0.22.0 // indirect - github.com/go-openapi/runtime v0.28.0 // indirect - github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/strfmt v0.23.0 // indirect - github.com/go-openapi/swag v0.23.1 // indirect - github.com/go-openapi/validate v0.24.0 // indirect - github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/go-webauthn/x v0.1.20 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect @@ -229,7 +209,6 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-tpm v0.9.3 // indirect github.com/gorilla/css v1.0.1 // indirect - github.com/gorilla/handlers v1.5.2 // 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 @@ -237,12 +216,9 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jessevdk/go-flags v1.6.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/kr/text v0.2.0 // indirect github.com/libdns/libdns v1.0.0-beta.1 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/markbates/going v1.0.3 // indirect @@ -253,19 +229,15 @@ require ( github.com/miekg/dns v1.1.65 // indirect github.com/minio/crc64nvme v1.0.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // 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/oklog/ulid v1.3.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -274,22 +246,11 @@ require ( github.com/prometheus/procfs v0.16.1 // indirect github.com/rhysd/actionlint v1.7.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/xid v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.9.0 // indirect - github.com/shopspring/decimal v1.4.0 // indirect - github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.1 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.14.0 // indirect - github.com/spf13/cast v1.7.1 // indirect - github.com/spf13/pflag v1.0.6 // indirect - github.com/spf13/viper v1.20.1 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect - github.com/subosito/gotenv v1.6.0 // indirect - github.com/toqueteos/webbrowser v1.2.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 @@ -300,7 +261,6 @@ require ( 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.mongodb.org/mongo-driver v1.17.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 @@ -308,6 +268,7 @@ require ( 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 gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -315,8 +276,6 @@ require ( replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 -replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 - 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 @@ -62,12 +62,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE= github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= -github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= -github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= -github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -103,8 +97,6 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuW github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= @@ -274,12 +266,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= @@ -325,28 +313,6 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU= github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM= -github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= -github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= -github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU= -github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= -github.com/go-openapi/inflect v0.21.2 h1:0gClGlGcxifcJR56zwvhaOulnNgnhc4qTAkob5ObnSM= -github.com/go-openapi/inflect v0.21.2/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw= -github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= -github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= -github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= -github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= -github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= -github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= -github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= -github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= -github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= -github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= -github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= -github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI= @@ -357,13 +323,9 @@ github.com/go-redsync/redsync/v4 v4.13.0 h1:49X6GJfnbLGaIpBBREM/zA4uIMDXKAh1NDkv github.com/go-redsync/redsync/v4 v4.13.0/go.mod h1:HMW4Q224GZQz6x1Xc7040Yfgacukdzu7ifTDAKiyErQ= github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/go-swagger/go-swagger v0.31.0 h1:H8eOYQnY2u7vNKWDNykv2xJP3pBhRG/R+SOCAmKrLlc= -github.com/go-swagger/go-swagger v0.31.0/go.mod h1:WSigRRWEig8zV6t6Sm8Y+EmUjlzA/HoaZJ5edupq7po= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= -github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-webauthn/webauthn v0.12.3 h1:hHQl1xkUuabUU9uS+ISNCMLs9z50p9mDUZI/FmkayNE= github.com/go-webauthn/webauthn v0.12.3/go.mod h1:4JRe8Z3W7HIw8NGEWn2fnUwecoDzkkeach/NnvhkqGY= github.com/go-webauthn/x v0.1.20 h1:brEBDqfiPtNNCdS/peu8gARtq8fIPsHz0VzpPjGvgiw= @@ -446,8 +408,6 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc= github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= -github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= -github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY= @@ -497,8 +457,6 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6 github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= -github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw= github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -540,8 +498,6 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ= github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= -github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 h1:F/3FfGmKdiKFa8kL3YrpZ7pe9H4l4AzA1pbaOUnRvPI= -github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0/go.mod h1:JEfTc3+2DF9Z4PXhLLvXL42zexJyh8rIq3OzUj/0rAk= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= @@ -577,14 +533,10 @@ github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.91 h1:tWLZnEfo3OZl5PoXQwcwTAPNNrjyWwOh6cbZitW5JQc= github.com/minio/minio-go/v7 v7.0.91/go.mod h1:uvMUcGrpgeSAAI6+sD3818508nUyMULw94j2Nxku/Go= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -607,8 +559,6 @@ github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWk github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E= @@ -629,8 +579,6 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= @@ -674,7 +622,6 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= @@ -682,8 +629,6 @@ github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= -github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg= @@ -692,10 +637,6 @@ github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLS github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs= -github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -707,22 +648,12 @@ github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYl github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= -github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= -github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= @@ -745,13 +676,9 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ= -github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ= github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -809,8 +736,6 @@ gitlab.com/gitlab-org/api/client-go v0.127.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAj go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= -go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= -go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= diff --git a/modules/assetfs/embed.go b/modules/assetfs/embed.go new file mode 100644 index 0000000000..95176372d1 --- /dev/null +++ b/modules/assetfs/embed.go @@ -0,0 +1,375 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package assetfs + +import ( + "bytes" + "compress/gzip" + "io" + "io/fs" + "os" + "path" + "path/filepath" + "strings" + "sync" + "time" + + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/util" +) + +type EmbeddedFile interface { + io.ReadSeeker + fs.ReadDirFile + ReadDir(n int) ([]fs.DirEntry, error) +} + +type EmbeddedFileInfo interface { + fs.FileInfo + fs.DirEntry + GetGzipContent() ([]byte, bool) +} + +type decompressor interface { + io.Reader + Close() error + Reset(io.Reader) error +} + +type embeddedFileInfo struct { + fs *embeddedFS + fullName string + data []byte + + BaseName string `json:"n"` + OriginSize int64 `json:"s,omitempty"` + DataBegin int64 `json:"b,omitempty"` + DataLen int64 `json:"l,omitempty"` + Children []*embeddedFileInfo `json:"c,omitempty"` +} + +func (fi *embeddedFileInfo) GetGzipContent() ([]byte, bool) { + // when generating the bindata, if the compressed data equals or is larger than the original data, we store the original data + if fi.DataLen == fi.OriginSize { + return nil, false + } + return fi.data, true +} + +type EmbeddedFileBase struct { + info *embeddedFileInfo + dataReader io.ReadSeeker + seekPos int64 +} + +func (f *EmbeddedFileBase) ReadDir(n int) ([]fs.DirEntry, error) { + // this method is used to satisfy the "func (f ioFile) ReadDir(...)" in httpfs + l, err := f.info.fs.ReadDir(f.info.fullName) + if err != nil { + return nil, err + } + if n < 0 || n > len(l) { + return l, nil + } + return l[:n], nil +} + +type EmbeddedOriginFile struct { + EmbeddedFileBase +} + +type EmbeddedCompressedFile struct { + EmbeddedFileBase + decompressor decompressor + decompressorPos int64 +} + +type embeddedFS struct { + meta func() *EmbeddedMeta + + files map[string]*embeddedFileInfo + filesMu sync.RWMutex + + data []byte +} + +type EmbeddedMeta struct { + Root *embeddedFileInfo +} + +func NewEmbeddedFS(data []byte) fs.ReadDirFS { + efs := &embeddedFS{data: data, files: make(map[string]*embeddedFileInfo)} + efs.meta = sync.OnceValue(func() *EmbeddedMeta { + var meta EmbeddedMeta + p := bytes.LastIndexByte(data, '\n') + if p < 0 { + return &meta + } + if err := json.Unmarshal(data[p+1:], &meta); err != nil { + panic("embedded file is not valid") + } + return &meta + }) + return efs +} + +var _ fs.ReadDirFS = (*embeddedFS)(nil) + +func (e *embeddedFS) ReadDir(name string) (l []fs.DirEntry, err error) { + fi, err := e.getFileInfo(name) + if err != nil { + return nil, err + } + if !fi.IsDir() { + return nil, fs.ErrNotExist + } + l = make([]fs.DirEntry, len(fi.Children)) + for i, child := range fi.Children { + l[i], err = e.getFileInfo(name + "/" + child.BaseName) + if err != nil { + return nil, err + } + } + return l, nil +} + +func (e *embeddedFS) getFileInfo(fullName string) (*embeddedFileInfo, error) { + // no need to do heavy "path.Clean()" because we don't want to support "foo/../bar" or absolute paths + fullName = strings.TrimPrefix(fullName, "./") + if fullName == "" { + fullName = "." + } + + e.filesMu.RLock() + fi := e.files[fullName] + e.filesMu.RUnlock() + if fi != nil { + return fi, nil + } + + fields := strings.Split(fullName, "/") + fi = e.meta().Root + if fullName != "." { + found := true + for _, field := range fields { + for _, child := range fi.Children { + if found = child.BaseName == field; found { + fi = child + break + } + } + if !found { + return nil, fs.ErrNotExist + } + } + } + + e.filesMu.Lock() + defer e.filesMu.Unlock() + if fi != nil { + fi.fs = e + fi.fullName = fullName + fi.data = e.data[fi.DataBegin : fi.DataBegin+fi.DataLen] + e.files[fullName] = fi // do not cache nil, otherwise keeping accessing random non-existing file will cause OOM + return fi, nil + } + return nil, fs.ErrNotExist +} + +func (e *embeddedFS) Open(name string) (fs.File, error) { + info, err := e.getFileInfo(name) + if err != nil { + return nil, err + } + base := EmbeddedFileBase{info: info} + base.dataReader = bytes.NewReader(base.info.data) + if info.DataLen != info.OriginSize { + decomp, err := gzip.NewReader(base.dataReader) + if err != nil { + return nil, err + } + return &EmbeddedCompressedFile{EmbeddedFileBase: base, decompressor: decomp}, nil + } + return &EmbeddedOriginFile{base}, nil +} + +var ( + _ EmbeddedFileInfo = (*embeddedFileInfo)(nil) + _ EmbeddedFile = (*EmbeddedOriginFile)(nil) + _ EmbeddedFile = (*EmbeddedCompressedFile)(nil) +) + +func (f *EmbeddedOriginFile) Read(p []byte) (n int, err error) { + return f.dataReader.Read(p) +} + +func (f *EmbeddedCompressedFile) Read(p []byte) (n int, err error) { + if f.decompressorPos > f.seekPos { + if err = f.decompressor.Reset(bytes.NewReader(f.info.data)); err != nil { + return 0, err + } + f.decompressorPos = 0 + } + if f.decompressorPos < f.seekPos { + if _, err = io.CopyN(io.Discard, f.decompressor, f.seekPos-f.decompressorPos); err != nil { + return 0, err + } + f.decompressorPos = f.seekPos + } + n, err = f.decompressor.Read(p) + f.decompressorPos += int64(n) + f.seekPos = f.decompressorPos + return n, err +} + +func (f *EmbeddedFileBase) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekStart: + f.seekPos = offset + case io.SeekCurrent: + f.seekPos += offset + case io.SeekEnd: + f.seekPos = f.info.OriginSize + offset + } + return f.seekPos, nil +} + +func (f *EmbeddedFileBase) Stat() (fs.FileInfo, error) { + return f.info, nil +} + +func (f *EmbeddedOriginFile) Close() error { + return nil +} + +func (f *EmbeddedCompressedFile) Close() error { + return f.decompressor.Close() +} + +func (fi *embeddedFileInfo) Name() string { + return fi.BaseName +} + +func (fi *embeddedFileInfo) Size() int64 { + return fi.OriginSize +} + +func (fi *embeddedFileInfo) Mode() fs.FileMode { + return util.Iif(fi.IsDir(), fs.ModeDir|0o555, 0o444) +} + +func (fi *embeddedFileInfo) ModTime() time.Time { + return getExecutableModTime() +} + +func (fi *embeddedFileInfo) IsDir() bool { + return fi.Children != nil +} + +func (fi *embeddedFileInfo) Sys() any { + return nil +} + +func (fi *embeddedFileInfo) Type() fs.FileMode { + return util.Iif(fi.IsDir(), fs.ModeDir, 0) +} + +func (fi *embeddedFileInfo) Info() (fs.FileInfo, error) { + return fi, nil +} + +// getExecutableModTime returns the modification time of the executable file. +// In bindata, we can't use the ModTime of the files because we need to make the build reproducible +var getExecutableModTime = sync.OnceValue(func() (modTime time.Time) { + exePath, err := os.Executable() + if err != nil { + return modTime + } + exePath, err = filepath.Abs(exePath) + if err != nil { + return modTime + } + exePath, err = filepath.EvalSymlinks(exePath) + if err != nil { + return modTime + } + st, err := os.Stat(exePath) + if err != nil { + return modTime + } + return st.ModTime() +}) + +func GenerateEmbedBindata(fsRootPath, outputFile string) error { + output, err := os.OpenFile(outputFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) + if err != nil { + return err + } + defer output.Close() + + meta := &EmbeddedMeta{} + meta.Root = &embeddedFileInfo{} + var outputOffset int64 + var embedFiles func(parent *embeddedFileInfo, fsPath, embedPath string) error + embedFiles = func(parent *embeddedFileInfo, fsPath, embedPath string) error { + dirEntries, err := os.ReadDir(fsPath) + if err != nil { + return err + } + for _, dirEntry := range dirEntries { + if err != nil { + return err + } + if dirEntry.IsDir() { + child := &embeddedFileInfo{ + BaseName: dirEntry.Name(), + Children: []*embeddedFileInfo{}, // non-nil means it's a directory + } + parent.Children = append(parent.Children, child) + if err = embedFiles(child, filepath.Join(fsPath, dirEntry.Name()), path.Join(embedPath, dirEntry.Name())); err != nil { + return err + } + } else { + data, err := os.ReadFile(filepath.Join(fsPath, dirEntry.Name())) + if err != nil { + return err + } + var compressed bytes.Buffer + gz, _ := gzip.NewWriterLevel(&compressed, gzip.BestCompression) + if _, err = gz.Write(data); err != nil { + return err + } + if err = gz.Close(); err != nil { + return err + } + + // only use the compressed data if it is smaller than the original data + outputBytes := util.Iif(len(compressed.Bytes()) < len(data), compressed.Bytes(), data) + child := &embeddedFileInfo{ + BaseName: dirEntry.Name(), + OriginSize: int64(len(data)), + DataBegin: outputOffset, + DataLen: int64(len(outputBytes)), + } + if _, err = output.Write(outputBytes); err != nil { + return err + } + outputOffset += child.DataLen + parent.Children = append(parent.Children, child) + } + } + return nil + } + + if err = embedFiles(meta.Root, fsRootPath, ""); err != nil { + return err + } + jsonBuf, err := json.Marshal(meta) // can't use json.NewEncoder here because it writes extra EOL + if err != nil { + return err + } + _, _ = output.Write([]byte{'\n'}) + _, err = output.Write(jsonBuf) + return err +} diff --git a/modules/assetfs/embed_test.go b/modules/assetfs/embed_test.go new file mode 100644 index 0000000000..06598da4c4 --- /dev/null +++ b/modules/assetfs/embed_test.go @@ -0,0 +1,98 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package assetfs + +import ( + "bytes" + "io/fs" + "net/http" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEmbed(t *testing.T) { + tmpDir := t.TempDir() + tmpDataDir := tmpDir + "/data" + _ = os.MkdirAll(tmpDataDir+"/foo/bar", 0o755) + _ = os.WriteFile(tmpDataDir+"/a.txt", []byte("a"), 0o644) + _ = os.WriteFile(tmpDataDir+"/foo/bar/b.txt", bytes.Repeat([]byte("a"), 1000), 0o644) + _ = os.WriteFile(tmpDataDir+"/foo/c.txt", []byte("c"), 0o644) + require.NoError(t, GenerateEmbedBindata(tmpDataDir, tmpDir+"/out.dat")) + + data, err := os.ReadFile(tmpDir + "/out.dat") + require.NoError(t, err) + efs := NewEmbeddedFS(data) + + // test a non-existing file + _, err = fs.ReadFile(efs, "not exist") + assert.ErrorIs(t, err, fs.ErrNotExist) + + // test a normal file (no compression) + content, err := fs.ReadFile(efs, "a.txt") + require.NoError(t, err) + assert.Equal(t, "a", string(content)) + fi, err := fs.Stat(efs, "a.txt") + require.NoError(t, err) + _, ok := fi.(EmbeddedFileInfo).GetGzipContent() + assert.False(t, ok) + + // test a compressed file + content, err = fs.ReadFile(efs, "foo/bar/b.txt") + require.NoError(t, err) + assert.Equal(t, bytes.Repeat([]byte("a"), 1000), content) + fi, err = fs.Stat(efs, "foo/bar/b.txt") + require.NoError(t, err) + assert.False(t, fi.Mode().IsDir()) + assert.True(t, fi.Mode().IsRegular()) + gzipContent, ok := fi.(EmbeddedFileInfo).GetGzipContent() + assert.True(t, ok) + assert.Greater(t, len(gzipContent), 1) + assert.Less(t, len(gzipContent), 1000) + + // test list root directory + entries, err := fs.ReadDir(efs, ".") + require.NoError(t, err) + assert.Len(t, entries, 2) + assert.Equal(t, "a.txt", entries[0].Name()) + assert.False(t, entries[0].IsDir()) + + // test list subdirectory + entries, err = fs.ReadDir(efs, "foo") + require.NoError(t, err) + require.Len(t, entries, 2) + assert.Equal(t, "bar", entries[0].Name()) + assert.True(t, entries[0].IsDir()) + assert.Equal(t, "c.txt", entries[1].Name()) + assert.False(t, entries[1].IsDir()) + + // test directory mode + fi, err = fs.Stat(efs, "foo") + require.NoError(t, err) + assert.True(t, fi.IsDir()) + assert.True(t, fi.Mode().IsDir()) + assert.False(t, fi.Mode().IsRegular()) + + // test httpfs + hfs := http.FS(efs) + hf, err := hfs.Open("foo/bar/b.txt") + require.NoError(t, err) + hi, err := hf.Stat() + require.NoError(t, err) + fiEmbedded, ok := hi.(EmbeddedFileInfo) + require.True(t, ok) + gzipContent, ok = fiEmbedded.GetGzipContent() + assert.True(t, ok) + assert.Greater(t, len(gzipContent), 1) + assert.Less(t, len(gzipContent), 1000) + + // test httpfs directory listing + hf, err = hfs.Open("foo") + require.NoError(t, err) + dirs, err := hf.Readdir(1) + require.NoError(t, err) + assert.Len(t, dirs, 1) +} diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go index 4f3811ba2b..ce55475bd9 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -52,8 +52,8 @@ func Local(name, base string, sub ...string) *Layer { } // Bindata returns a new Layer with the given name, it serves files from the given bindata asset. -func Bindata(name string, fs http.FileSystem) *Layer { - return &Layer{name: name, fs: fs} +func Bindata(name string, fs fs.FS) *Layer { + return &Layer{name: name, fs: http.FS(fs)} } // LayeredFS is a layered asset file-system. It works like http.FileSystem, but it can have multiple layers. diff --git a/modules/migration/schemas_bindata.go b/modules/migration/schemas_bindata.go index c5db3b3461..695c2c1135 100644 --- a/modules/migration/schemas_bindata.go +++ b/modules/migration/schemas_bindata.go @@ -3,6 +3,28 @@ //go:build bindata +//go:generate go run ../../build/generate-bindata.go ../../modules/migration/schemas bindata.dat + package migration -//go:generate go run ../../build/generate-bindata.go ../../modules/migration/schemas migration bindata.go +import ( + "io" + "io/fs" + "path" + "sync" + + _ "embed" + + "code.gitea.io/gitea/modules/assetfs" +) + +//go:embed bindata.dat +var bindata []byte + +var BuiltinAssets = sync.OnceValue(func() fs.FS { + return assetfs.NewEmbeddedFS(bindata) +}) + +func openSchema(filename string) (io.ReadCloser, error) { + return BuiltinAssets().Open(path.Base(filename)) +} diff --git a/modules/migration/schemas_static.go b/modules/migration/schemas_static.go deleted file mode 100644 index 8a0c340a65..0000000000 --- a/modules/migration/schemas_static.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -//go:build bindata - -package migration - -import ( - "io" - "path" -) - -func openSchema(filename string) (io.ReadCloser, error) { - return Assets.Open(path.Base(filename)) -} diff --git a/modules/options/options_bindata.go b/modules/options/options_bindata.go index 29151cb3cb..b2321d7eb5 100644 --- a/modules/options/options_bindata.go +++ b/modules/options/options_bindata.go @@ -3,6 +3,21 @@ //go:build bindata +//go:generate go run ../../build/generate-bindata.go ../../options bindata.dat + package options -//go:generate go run ../../build/generate-bindata.go ../../options options bindata.go +import ( + "sync" + + _ "embed" + + "code.gitea.io/gitea/modules/assetfs" +) + +//go:embed bindata.dat +var bindata []byte + +var BuiltinAssets = sync.OnceValue(func() *assetfs.Layer { + return assetfs.Bindata("builtin(bindata)", assetfs.NewEmbeddedFS(bindata)) +}) diff --git a/modules/options/dynamic.go b/modules/options/options_dynamic.go index 085492d11c..085492d11c 100644 --- a/modules/options/dynamic.go +++ b/modules/options/options_dynamic.go diff --git a/modules/options/static.go b/modules/options/static.go deleted file mode 100644 index 72b28e990e..0000000000 --- a/modules/options/static.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -//go:build bindata - -package options - -import ( - "code.gitea.io/gitea/modules/assetfs" -) - -func BuiltinAssets() *assetfs.Layer { - return assetfs.Bindata("builtin(bindata)", Assets) -} diff --git a/modules/public/public.go b/modules/public/public.go index 7f8ce29056..2bc55b7869 100644 --- a/modules/public/public.go +++ b/modules/public/public.go @@ -89,19 +89,16 @@ func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, servePublicAsset(w, req, fi, fi.ModTime(), f) } -type GzipBytesProvider interface { - GzipBytes() []byte -} - // servePublicAsset serve http content func servePublicAsset(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modtime time.Time, content io.ReadSeeker) { setWellKnownContentType(w, fi.Name()) httpcache.SetCacheControlInHeader(w.Header(), httpcache.CacheControlForPublicStatic()) encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding")) - if encodings.Contains("gzip") { - // try to provide gzip content directly from bindata (provided by vfsgen۰CompressedFileInfo) - if compressed, ok := fi.(GzipBytesProvider); ok { - rdGzip := bytes.NewReader(compressed.GzipBytes()) + fiEmbedded, _ := fi.(assetfs.EmbeddedFileInfo) + if encodings.Contains("gzip") && fiEmbedded != nil { + // try to provide gzip content directly from bindata + if gzipBytes, ok := fiEmbedded.GetGzipContent(); ok { + rdGzip := bytes.NewReader(gzipBytes) // all gzipped static files (from bindata) are managed by Gitea, so we can make sure every file has the correct ext name // then we can get the correct Content-Type, we do not need to do http.DetectContentType on the decompressed data if w.Header().Get("Content-Type") == "" { diff --git a/modules/public/public_bindata.go b/modules/public/public_bindata.go index 4878f88ad1..2dcf3e72e4 100644 --- a/modules/public/public_bindata.go +++ b/modules/public/public_bindata.go @@ -5,4 +5,19 @@ package public -//go:generate go run ../../build/generate-bindata.go ../../public public bindata.go true +//go:generate go run ../../build/generate-bindata.go ../../public bindata.dat + +import ( + "sync" + + _ "embed" + + "code.gitea.io/gitea/modules/assetfs" +) + +//go:embed bindata.dat +var bindata []byte + +var BuiltinAssets = sync.OnceValue(func() *assetfs.Layer { + return assetfs.Bindata("builtin(bindata)", assetfs.NewEmbeddedFS(bindata)) +}) diff --git a/modules/public/serve_dynamic.go b/modules/public/public_dynamic.go index a668b17c34..a668b17c34 100644 --- a/modules/public/serve_dynamic.go +++ b/modules/public/public_dynamic.go diff --git a/modules/public/serve_static.go b/modules/public/serve_static.go deleted file mode 100644 index e79085021e..0000000000 --- a/modules/public/serve_static.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2016 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -//go:build bindata - -package public - -import ( - "time" - - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/timeutil" -) - -var _ GzipBytesProvider = (*vfsgen۰CompressedFileInfo)(nil) - -// GlobalModTime provide a global mod time for embedded asset files -func GlobalModTime(filename string) time.Time { - return timeutil.GetExecutableModTime() -} - -func BuiltinAssets() *assetfs.Layer { - return assetfs.Bindata("builtin(bindata)", Assets) -} diff --git a/modules/templates/static.go b/modules/templates/static.go deleted file mode 100644 index b5a7e561ec..0000000000 --- a/modules/templates/static.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -//go:build bindata - -package templates - -import ( - "time" - - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/timeutil" -) - -// GlobalModTime provide a global mod time for embedded asset files -func GlobalModTime(filename string) time.Time { - return timeutil.GetExecutableModTime() -} - -func BuiltinAssets() *assetfs.Layer { - return assetfs.Bindata("builtin(bindata)", Assets) -} diff --git a/modules/templates/templates_bindata.go b/modules/templates/templates_bindata.go index 6f1d3cf539..a919591ecf 100644 --- a/modules/templates/templates_bindata.go +++ b/modules/templates/templates_bindata.go @@ -3,6 +3,21 @@ //go:build bindata +//go:generate go run ../../build/generate-bindata.go ../../templates bindata.dat + package templates -//go:generate go run ../../build/generate-bindata.go ../../templates templates bindata.go true +import ( + "sync" + + _ "embed" + + "code.gitea.io/gitea/modules/assetfs" +) + +//go:embed bindata.dat +var bindata []byte + +var BuiltinAssets = sync.OnceValue(func() *assetfs.Layer { + return assetfs.Bindata("builtin(bindata)", assetfs.NewEmbeddedFS(bindata)) +}) diff --git a/modules/templates/dynamic.go b/modules/templates/templates_dynamic.go index e1babd83c9..e1babd83c9 100644 --- a/modules/templates/dynamic.go +++ b/modules/templates/templates_dynamic.go diff --git a/modules/timeutil/executable.go b/modules/timeutil/executable.go deleted file mode 100644 index 57ae8b2a9d..0000000000 --- a/modules/timeutil/executable.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package timeutil - -import ( - "os" - "path/filepath" - "sync" - "time" - - "code.gitea.io/gitea/modules/log" -) - -var ( - executablModTime = time.Now() - executablModTimeOnce sync.Once -) - -// GetExecutableModTime get executable file modified time of current process. -func GetExecutableModTime() time.Time { - executablModTimeOnce.Do(func() { - exePath, err := os.Executable() - if err != nil { - log.Error("os.Executable: %v", err) - return - } - - exePath, err = filepath.Abs(exePath) - if err != nil { - log.Error("filepath.Abs: %v", err) - return - } - - exePath, err = filepath.EvalSymlinks(exePath) - if err != nil { - log.Error("filepath.EvalSymlinks: %v", err) - return - } - - st, err := os.Stat(exePath) - if err != nil { - log.Error("os.Stat: %v", err) - return - } - - executablModTime = st.ModTime() - }) - return executablModTime -} diff --git a/modules/zstd/zstd_test.go b/modules/zstd/zstd_test.go index c3ca8e78f7..7fd30484ca 100644 --- a/modules/zstd/zstd_test.go +++ b/modules/zstd/zstd_test.go @@ -16,7 +16,7 @@ import ( ) func TestWriterReader(t *testing.T) { - testData := prepareTestData(t, 20_000_000) + testData := prepareTestData(t, 1_000_000) result := bytes.NewBuffer(nil) @@ -64,7 +64,7 @@ func TestWriterReader(t *testing.T) { } func TestSeekableWriterReader(t *testing.T) { - testData := prepareTestData(t, 20_000_000) + testData := prepareTestData(t, 2_000_000) result := bytes.NewBuffer(nil) @@ -109,7 +109,7 @@ func TestSeekableWriterReader(t *testing.T) { reader, err := NewSeekableReader(assertReader) require.NoError(t, err) - _, err = reader.Seek(10_000_000, io.SeekStart) + _, err = reader.Seek(1_000_000, io.SeekStart) require.NoError(t, err) data := make([]byte, 1000) @@ -117,7 +117,7 @@ func TestSeekableWriterReader(t *testing.T) { require.NoError(t, err) require.NoError(t, reader.Close()) - assert.Equal(t, testData[10_000_000:10_000_000+1000], data) + assert.Equal(t, testData[1_000_000:1_000_000+1000], data) // Should seek 3 times, // the first two times are for getting the index, diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index 79df1d26d0..3804339585 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -1652,6 +1652,7 @@ issues.save=Sábháil issues.label_title=Ainm issues.label_description=Cur síos issues.label_color=Dath +issues.label_color_invalid=Dath neamhbhailí issues.label_exclusive=Eisiach issues.label_archive=Lipéad Cartlann issues.label_archived_filter=Taispeáin lipéid cartlainne diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 6ab4d7800c..e89a299b56 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -520,58 +520,58 @@ Content=Зміст SSPISeparatorReplacement=Розділювач SSPIDefaultLanguage=Типова мова -require_error=` не може бути пустим.` -alpha_dash_error=` повинен містити тільки літерно-цифрові символи, дефіс ('-') та підкреслення ('_'). ` -alpha_dash_dot_error=` повинен містити тільки літерно-цифрові символи, дефіс ('-') , підкреслення ('_') та точки ('.'). ` -git_ref_name_error=` повинен бути правильним посилальним ім'ям Git.` +require_error=` не може бути порожнім.` +alpha_dash_error=` повинен містити тільки алфавітно-цифрові символи, дефіс ('-') та підкреслення ('_').` +alpha_dash_dot_error=` повинен містити тільки алфавітно-цифрові символи, дефіс ('-'), підкреслення ('_') та крапку ('.').` +git_ref_name_error=` повинен бути правильно сформованим ім'ям-посиланням на Git.` size_error=` повинен бути розмір %s.` -min_size_error=` повинен бути принаймні %s символів.` -max_size_error=` повинен бути не більш як %s символів.` -email_error=` не є адресою електронної пошти.` +min_size_error=` має містити принаймні %s символів.` +max_size_error=` має містити не більше %s символів.` +email_error=` не є дійсною адресою електронної пошти.` url_error=`"%s" не є дійсною URL-адресою.` include_error=` повинен містити підрядок "%s".` -glob_pattern_error=` неприпустимий шаблон glob: %s.` -regex_pattern_error=` неприпустимий шаблон regex: %s.` +glob_pattern_error=` недійсний шаблон glob: %s.` +regex_pattern_error=` недійсний шаблон регулярного виразу: %s.` unknown_error=Невідома помилка: captcha_incorrect=Код CAPTCHA неправильний. -password_not_match=Паролі не співпадають. -lang_select_error=Оберіть мову з переліку. +password_not_match=Паролі не збігаються. +lang_select_error=Оберіть мову зі списку. -username_been_taken=Ім'я користувача вже зайнято. -username_change_not_local_user=Нелокальні користувачі не можуть змінити своє ім'я користувача. +username_been_taken=Ім'я користувача вже зайняте. +username_change_not_local_user=Нелокальні користувачі не можуть змінювати своє ім'я користувача. username_has_not_been_changed=Ім'я користувача не змінено -repo_name_been_taken=Ім'я репозіторію вже використовується. -repository_files_already_exist=Файли вже існують для цього репозитарію. Зверніться до системного адміністратора. -repository_files_already_exist.adopt=Файли вже існують для цього репозиторію і можуть бути лише прийняті. -repository_files_already_exist.delete=Файли вже існують для цього сховища. Ви повинні видалити їх. -repository_files_already_exist.adopt_or_delete=Файли вже існують для цього репозиторію. Їх можливо прийняти або видалити. -visit_rate_limit=Обмеження швидкості віддаленого доступу. -2fa_auth_required=Для віддаленого доступу необхідна двуфакторна аутентифікація. -org_name_been_taken=Назва організації вже зайнято. -team_name_been_taken=Назва команди вже зайнято. -team_no_units_error=Дозволити доступ до принаймні одного розділу репозитарію. -email_been_used=Ця електронна адреса вже використовується. -email_invalid=Адреса електронної пошти помилкова. +repo_name_been_taken=Назва сховища вже використовується. +repository_files_already_exist=Для цього сховища вже існують файли. Зверніться до системного адміністратора. +repository_files_already_exist.adopt=Для цього сховища вже існують файли, і їх можна лише прийняти. +repository_files_already_exist.delete=Для цього сховища вже існують файли. Ви повинні видалити їх. +repository_files_already_exist.adopt_or_delete=Для цього сховища вже існують файли. Прийміть їх або видаліть. +visit_rate_limit=Віддалений доступ відхилено у зв'язку з обмеженням кількості спроб. +2fa_auth_required=Для віддаленого доступу необхідна двофакторна автентифікація. +org_name_been_taken=Назва організації вже зайнята. +team_name_been_taken=Назва команди вже зайнята. +team_no_units_error=Дозволити доступ до принаймні одного розділу сховища. +email_been_used=Адреса електронної пошти вже використовується. +email_invalid=Адреса електронної пошти недійсна. openid_been_used=Адреса OpenID '%s' вже використовується. username_password_incorrect=Неправильне ім'я користувача або пароль. -password_complexity=Пароль не відповідає вимогам до складності: -password_lowercase_one=Принаймні одна буква в нижньому регістрі -password_uppercase_one=Принаймні одна буква в верхньому регістрі +password_complexity=Пароль не відповідає вимогам складності: +password_lowercase_one=Принаймні один символ нижнього регістру +password_uppercase_one=Принаймні один символ верхнього регістру password_digit_one=Принаймні одна цифра -password_special_one=Принаймні один спеціальний символ (пунктуація, дужки, лапки тощо) -enterred_invalid_repo_name=Невірно введено ім'я репозиторію. -enterred_invalid_org_name=Невірно введено ім'я організації. -enterred_invalid_owner_name=Ім'я нового власника не є дійсним. -enterred_invalid_password=Введений вами пароль некоректний. +password_special_one=Принаймні один спеціальний символ (розділові знаки, дужки, лапки тощо) +enterred_invalid_repo_name=Ви ввели неправильну назву сховища. +enterred_invalid_org_name=Ви ввели неправильну назву організації. +enterred_invalid_owner_name=Ім'я нового власника недійсне. +enterred_invalid_password=Ви ввели неправильний пароль. unset_password=Користувач не встановив пароль. -user_not_exist=Даний користувач не існує. +user_not_exist=Користувач не існує. team_not_exist=Команда не існує. -last_org_owner=Ви не можете видалити останнього користувача з команди 'власники'. У кожній команді має бути принаймні один власник. +last_org_owner=Ви не можете видалити останнього користувача з групи 'власників'. В організації має бути принаймні один власник. cannot_add_org_to_team=Організацію неможливо додати як учасника команди. -invalid_ssh_key=Неможливо перевірити ваш SSH ключ: %s -invalid_gpg_key=Неможливо перевірити ваш GPG ключ: %s -invalid_ssh_principal=Некоректний відповідальний: %s +invalid_ssh_key=Не вдається перевірити ключ SSH: %s +invalid_gpg_key=Не вдається перевірити ключ GPG: %s +invalid_ssh_principal=Невірна ідентичність: %s auth_failed=Помилка автентифікації: %v @@ -581,21 +581,20 @@ target_ref_not_exist=Цільове посилання не існує %s admin_cannot_delete_self=Ви не можете видалити себе, допоки ви адміністратор. Будь ласка, спочатку видаліть права адміністратора. [user] -change_avatar=Змінити свій аватар… -repositories=Репозиторії +change_avatar=Змінити аватар… +repositories=Сховища activity=Публічна активність -followers=Читачі +followers=Послідовники show_more=Показати більше -starred=Обрані Репозиторії -watched=Відстежувані репозиторії +starred=Обрані сховища +watched=Відстежувані сховища code=Код -projects=Проєкт +projects=Проєкти overview=Огляд -following=Читає -follow=Підписатися -unfollow=Відписатися +follow=Стежити +unfollow=Не стежити user_bio=Біографія -disabled_public_activity=Цей користувач вимкнув публічний показ діяльності. +disabled_public_activity=Цей користувач вимкнув публічну видимість активності. show_on_map=Показати це місце на карті settings=Налаштування користувача @@ -625,7 +624,7 @@ appearance=Зовнішній вигляд password=Пароль security=Безпека avatar=Аватар -ssh_gpg_keys=SSH / GPG ключі +ssh_gpg_keys=Ключі SSH / GPG social=Соціальні облікові записи applications=Додатки orgs=Керування організаціями @@ -644,7 +643,7 @@ update_profile=Оновити профіль update_language=Оновити мову update_language_success=Мову оновлено. update_profile_success=Профіль успішно оновлено. -change_username=Ваше Ім'я кристувача було змінено. +change_username=Ваше ім'я користувача змінено. continue=Продовжити cancel=Відмінити language=Мова @@ -670,13 +669,13 @@ keep_activity_private_popup=Показувати вашу активність lookup_avatar_by_mail=Знайти Аватар за адресою електронної пошти federated_avatar_lookup=Знайти зовнішній аватар -enable_custom_avatar=Увімкнути користувацькі аватари -choose_new_avatar=Оберіть новий аватар +enable_custom_avatar=Увімкнути користувацький аватар +choose_new_avatar=Обрати новий аватар update_avatar=Оновити аватар delete_current_avatar=Видалити поточний аватар uploaded_avatar_not_a_image=Завантажений файл не є зображенням. uploaded_avatar_is_too_big=Розмір завантаженого файлу (%d KiB) перевищує максимальний розмір (%d KiB). -update_avatar_success=Ваш аватар був змінений. +update_avatar_success=Ваш аватар оновлено. update_user_avatar_success=Аватар користувача оновлено. cropper_prompt=Ви можете відредагувати зображення перед збереженням. Відредаговане зображення буде збережено як PNG. @@ -685,64 +684,64 @@ old_password=Поточний пароль new_password=Новий пароль retype_new_password=Підтвердити новий пароль password_incorrect=Поточний пароль неправильний. -change_password_success=Ваш пароль був оновлений. Тепер увійдіть в систему, використовуючи новий пароль. -password_change_disabled=Нелокальні акаунти не можуть змінити пароль через Gitea. +change_password_success=Ваш пароль оновлено. Відтепер входьте в систему, використовуючи новий пароль. +password_change_disabled=Нелокальні користувачі не можуть оновити свій пароль через вебінтерфейс Gitea. emails=Адреса електронної пошти -manage_emails=Керування адресами ел. пошти -manage_themes=Виберіть тему за замовчуванням -manage_openid=Керування OpenID +manage_emails=Керування адресами електронної пошти +manage_themes=Обрати типову тему +manage_openid=Керування адресами OpenID theme_desc=Ця тема буде типовою для всього сайту. primary=Основний activated=Активовано requires_activation=Потрібна активація -primary_email=Зробити основним +primary_email=Зробити основною activate_email=Надіслати активацію -activations_pending=Активації в очікуванні +activations_pending=Очікування активації delete_email=Видалити email_deletion=Видалити адресу електронної пошти -email_deletion_desc=Електронна адреса та пов'язана з нею інформація буде видалена з вашого облікового запису. Git коміти, здійснені через цю електронну адресу, залишиться без змін. Продовжити? -email_deletion_success=Адресу електронної пошти було видалено. +email_deletion_desc=Адреса електронної пошти та пов'язана з нею інформація буде видалена з вашого облікового запису. Коміти Git, здійснені через цю адресу електронну пошту, залишиться без змін. Продовжити? +email_deletion_success=Адресу електронної пошти видалено. theme_update_success=Тему оновлено. -theme_update_error=Вибрана тема не існує. +theme_update_error=Обрана тема не існує. openid_deletion=Видалити адресу OpenID -openid_deletion_desc=Видалення цієї OpenID-адреси з вашого облікового запису забороняє вам входити з ним. Продовжити? -openid_deletion_success=Адреса OpenID була видалена. +openid_deletion_desc=Видалення цієї адреси OpenID не дозволить вам увійти за нею. Продовжити? +openid_deletion_success=Адресу OpenID видалено. add_new_email=Додати нову адресу електронної пошти add_new_openid=Додати новий OpenID URI add_email=Додати адресу електронної пошти add_openid=Додати OpenID URI add_email_success=Додано нову адресу електронної пошти. -email_preference_set_success=Налаштування електронної пошти успішно встановлені. -add_openid_success=Нова адреса OpenID була додана. +email_preference_set_success=Налаштування електронної пошти успішно встановлено. +add_openid_success=Додано нову адресу OpenID. keep_email_private=Приховати адресу електронної пошти -openid_desc=OpenID дозволяє делегувати аутентифікацію зовнішньому постачальнику послуг. +openid_desc=OpenID дозволяє делегувати автентифікацію зовнішньому постачальнику послуг. -manage_ssh_keys=Керувати SSH ключами -manage_ssh_principals=Управління SSH сертифікатами користувачів -manage_gpg_keys=Керувати GPG ключами +manage_ssh_keys=Керувати ключами SSH +manage_ssh_principals=Керування ідентичностями сертифікатів SSH +manage_gpg_keys=Керувати ключами SSH add_key=Додати ключ -ssh_desc=Ці відкриті SSH-ключі пов'язані з вашим обліковим записом. Відповідні приватні ключі дозволяють отримати повний доступ до ваших репозиторіїв. -principal_desc=Ці настройки SSH сертифікатів вказані у вашому обліковому записі та надають повний доступ до ваших репозиторіїв. -gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють здійснювати перевірку комітів. -ssh_helper=<strong>Потрібна допомога?</strong> Дивіться гід на GitHub з <a href="%s"> генерації ключів SSH</a> або виправлення <a href="%s">типових неполадок SSH</a>. -gpg_helper=<strong> Потрібна допомога? </strong> Перегляньте посібник GitHub <a href="%s"> про GPG </a>. -add_new_key=Додати SSH ключ -add_new_gpg_key=Додати GPG ключ +ssh_desc=Ці публічні ключі SSH пов'язані з вашим обліковим записом. Відповідні приватні ключі надають повний доступ до ваших сховищ. +principal_desc=Ці ідентифікатори сертифікатів SSH прив'язані до вашого облікового запису і надають повний доступ до ваших сховищ. +gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом. Зберігайте свої приватні ключі в безпеці, оскільки вони дозволяють підтверджувати коміти. +ssh_helper=<strong>Потрібна допомога?</strong> Ознайомтеся з інструкцією GitHub щодо <a href="%s">створення власних ключів SSH</a> або виправлення <a href="%s">типових неполадок SSH.</a> +gpg_helper=<strong>Потрібна допомога?</strong> Перегляньте посібник GitHub <a href="%s">про GPG</a>. +add_new_key=Додати ключ SSH +add_new_gpg_key=Додати ключ GPG key_content_ssh_placeholder=Починається з 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', або 'sk-ssh-ed25519@openssh.com' key_content_gpg_placeholder=Починається з '-----BEGIN PGP PUBLIC KEY BLOCK-----' -add_new_principal=Додати користувача -ssh_key_been_used=Цей SSH ключ вже був додано до сервера. +add_new_principal=Додати ідентичність +ssh_key_been_used=Цей ключ SSH вже було додано до сервера. ssh_key_name_used=Ключ SSH з таким ім'ям вже існує у вашому обліковому записі. -ssh_principal_been_used=Цей користувач вже був доданий на сервер. +ssh_principal_been_used=Цю ідентичність вже було додано до сервера. gpg_key_id_used=Публічний ключ GPG з таким самим ідентифікатором вже існує. -gpg_no_key_email_found=Цей ключ GPG не відповідає жодній активованій поштовій адресі, яка пов'язана з вашим обліковим записом. Його все рівно можна додати, якщо ви підпишете наданий токен. -gpg_key_matched_identities=Відповідні отримувачі: -gpg_key_matched_identities_long=Вбудовані ідентифікатори цього ключа збігаються з наступними активованими адресами електронної пошти вказаного користувача. Коміти, які відповідають цим адресам, можуть бути підтверджені цим ключем. +gpg_no_key_email_found=Цей ключ GPG не відповідає жодній активованій адресі електронної пошти, пов'язаній з вашим обліковим записом. Його все одно можна додати, якщо ви підпишете наданий токен. +gpg_key_matched_identities=Відповідні ідентичності: +gpg_key_matched_identities_long=Вбудовані ідентифікатори цього ключа збігаються з наступними активованими адресами електронної пошти цього користувача. За допомогою цього ключа можна перевіряти коміти, що відповідають цим адресам електронної пошти. gpg_key_verified=Перевірений ключ -gpg_key_verified_long=Ключ перевірений за допомогою токена і може бути використано для підтвердження комітів, які відповідають будь-якій з активованих адрес електронної пошти для цього користувача, на додачу до будь-яких відповідних ідентифікацій для цього ключа. +gpg_key_verified_long=Ключ був перевірений токеном і може бути використаний для перевірки комітів, що відповідають будь-яким активованим адресам електронної пошти для цього користувача, а також будь-яких ідентифікаторів, що відповідають цьому ключу. gpg_key_verify=Підтвердити -gpg_invalid_token_signature=Наданий ключ GPG, підпис і токен не співпадають або токен застарів. +gpg_invalid_token_signature=Надані ключ GPG, підпис і токен не збігаються або токен застарілий. gpg_token_required=Вам потрібно надати підпис для нижчевказаного токена gpg_token=Токен gpg_token_help=Ви можете створити підпис за допомогою: @@ -750,128 +749,128 @@ gpg_token_signature=Текстовий (armored) підпис GPG key_signature_gpg_placeholder=`Починається з "-----BEGIN PGP SIGNATURE-----"` verify_gpg_key_success=Ключ GPG '%s' перевірено. ssh_key_verified=Перевірений ключ -ssh_key_verify=Підтвердити +ssh_key_verify=Перевірити ssh_token_required=Вам потрібно надати підпис для нижчевказаного токена ssh_token=Токен ssh_token_help=Ви можете створити підпис за допомогою: verify_ssh_key_success=Ключ SSH '%s' перевірено. subkeys=Підключі -key_id=ID ключа -key_name=Ім'я ключа +key_id=Ідентифікатор ключа +key_name=Назва ключа key_content=Зміст principal_content=Зміст -add_key_success=SSH ключ '%s' додано. -add_gpg_key_success=GPG ключ '%s' додано. +add_key_success=Ключ SSH '%s' додано. +add_gpg_key_success=Ключ GPG '%s' додано. delete_key=Видалити -ssh_key_deletion=Видалити SSH ключ -gpg_key_deletion=Видалити GPG ключ -ssh_principal_deletion=Видалити SSH сертифікат користувача +ssh_key_deletion=Видалити ключ SSH +gpg_key_deletion=Видалити ключ GPG +ssh_principal_deletion=Видалити ідентичність сертифікату SSH ssh_key_deletion_desc=Видалення ключа SSH скасовує доступ до вашого облікового запису. Продовжити? -gpg_key_deletion_desc=Видалення GPG ключа скасовує перевірку підписаних ним комітів. Продовжити? -ssh_principal_deletion_desc=Видалення ключа SSH скасовує доступ до вашого облікового запису. Продовжити? -ssh_key_deletion_success=SSH ключ був видалений. -gpg_key_deletion_success=GPG було видалено. -ssh_principal_deletion_success=Користувача видалено. +gpg_key_deletion_desc=Видалення ключа GPG скасовує перевірку підписаних ним комітів. Продовжити? +ssh_principal_deletion_desc=Видалення ідентичності сертифіката SSH скасовує доступ до вашого облікового запису. Продовжити? +ssh_key_deletion_success=Ключ SSH видалено. +gpg_key_deletion_success=Ключ GPG видалено. +ssh_principal_deletion_success=Ідентичність видалено. added_on=Додано %s valid_until_date=Дійсно до %s valid_forever=Дійсний завжди -last_used=Останнє використання -no_activity=Жодної діяльності -can_read_info=Читати -can_write_info=Написати -key_state_desc=Цей ключ використовувався в останні 7 днів -token_state_desc=Цей токен використовувався в останні 7 днів -principal_state_desc=Участник був на сайті в останні 7 днів +last_used=Востаннє використано +no_activity=Нещодавня активність відсутня +can_read_info=Читання +can_write_info=Запис +key_state_desc=Цей ключ використовувався протягом останніх 7 днів +token_state_desc=Цей токен використовувався протягом останніх 7 днів +principal_state_desc=Ця ідентичність використовувалася протягом останніх 7 днів show_openid=Показати у профілю -hide_openid=Не показувати у профілі +hide_openid=Приховати з профілю ssh_disabled=SSH вимкнено ssh_signonly=SSH наразі вимкнено, тому ці ключі використовуються лише для перевірки підпису комітів. -ssh_externally_managed=Цей ключ SSH має зовнішнє управління для цього користувача -manage_social=Керувати зв'язаними обліковими записами соціальних мереж +ssh_externally_managed=Цей ключ SSH керується ззовні для цього користувача +manage_social=Керувати пов'язаними обліковими записами соціальних мереж unbind=Від'єднати -manage_access_token=Керування токенами доступу -generate_new_token=Згенерувати новий токен -tokens_desc=Ці токени надають доступ до вашого облікового запису за допомогою Gitea API. -token_name=Ім'я токену +manage_access_token=Керувати токенами доступу +generate_new_token=Створити новий токен +tokens_desc=Ці токени надають доступ до вашого облікового запису за допомогою API Gitea. +token_name=Назва токену generate_token=Згенерувати токен -generate_token_success=Ваш новий токен був створений. Скопіюйте його зараз, оскільки він не буде показаний знову. +generate_token_success=Ваш новий токен створено. Скопіюйте його зараз, оскільки він не буде показаний знову. generate_token_name_duplicate=Назва програми <strong>%s</strong> вже використовується. Будь ласка, використайте нову. delete_token=Видалити access_token_deletion=Видалити токен доступу access_token_deletion_cancel_action=Відмінити access_token_deletion_confirm_action=Видалити -delete_token_success=Токен був знищений. Програми, що використовують його, більше не мають доступу до вашого облікового запису. +delete_token_success=Токен знищено. Додатки, що використовують його, більше не мають доступу до вашого облікового запису. permissions_access_all=Всі (загальнодоступні, приватні та з обмеженим доступом) permission_not_set=Не встановлено permission_no_access=Немає доступу permission_read=Прочитані permission_write=Читання і запис permission_anonymous_read=Анонімне читання -permission_everyone_read=Усі читають -permission_everyone_write=Усі пишуть +permission_everyone_read=Читання для всіх +permission_everyone_write=Запис для всіх permissions_list=Дозволи: -manage_oauth2_applications=Керування програмами OAuth2 -edit_oauth2_application=Редагувати програму OAuth2 -oauth2_applications_desc=Програми OAuth2 дають можливість вашим стороннім програмам надійно аутентифікувати користувачів у цьому екземплярі Gitea. -remove_oauth2_application=Видалити програму OAuth2 -remove_oauth2_application_desc=Видалення програми OAuth2 скасовує доступ до всіх підписаних маркерів доступу. Продовжити? -remove_oauth2_application_success=Програму видалено. -create_oauth2_application=Створити нову програму OAuth2 -create_oauth2_application_button=Створити програму +manage_oauth2_applications=Керування додатками OAuth2 +edit_oauth2_application=Редагувати додаток OAuth2 +oauth2_applications_desc=Додатки OAuth2 дозволяють вашому сторонньому додатку безпечно автентифікувати користувачів у цьому екземплярі Gitea. +remove_oauth2_application=Видалити додаток OAuth2 +remove_oauth2_application_desc=Видалення додатка OAuth2 скасує доступ до всіх підписаних токенів доступу. Продовжити? +remove_oauth2_application_success=Додаток видалено. +create_oauth2_application=Створити новий додаток OAuth2 +create_oauth2_application_button=Створити додаток create_oauth2_application_success=Ви успішно створили новий додаток OAuth2. update_oauth2_application_success=Ви успішно оновили додаток OAuth2. -oauth2_application_name=Назва програми +oauth2_application_name=Назва додатка save_application=Зберегти -oauth2_client_id=ID Клієнта +oauth2_client_id=Ідентифікатор клієнта oauth2_client_secret=Ключ клієнта oauth2_regenerate_secret=Відновити ключ -oauth2_regenerate_secret_hint=Ви втратили свій ключ? +oauth2_regenerate_secret_hint=Втратили ключ? oauth2_application_edit=Редагувати oauth2_application_create_description=Програми OAuth2 надають вашим стороннім програмам доступ до облікових записів користувачів у цьому екземплярі. authorized_oauth2_applications=Авторизовані програми OAuth2 revoke_key=Відкликати revoke_oauth2_grant=Скасувати доступ -revoke_oauth2_grant_description=Скасування доступу для цієї програми третьої сторони не дозволить їй отримувати доступ до ваших даних. Ви впевнені? +revoke_oauth2_grant_description=Скасування доступу для цього стороннього додатка не дозволить йому отримувати доступ до ваших даних. Ви впевнені? -twofa_is_enrolled=Ваш обліковий запис на даний час <strong>використовує</strong> двофакторну автентифікацію. -twofa_not_enrolled=Ваш обліковий запис наразі не використовує двофакторну автентифікаціїю. +twofa_is_enrolled=Ваш обліковий запис наразі <strong>використовує</strong> двофакторну автентифікацію. +twofa_not_enrolled=Ваш обліковий запис наразі не використовує двофакторну автентифікацію. twofa_disable=Вимкнути двофакторну автентифікацію twofa_enroll=Увімкнути двофакторну автентифікацію -twofa_disable_note=При необхідності можна відключити двофакторну автентифікацію. +twofa_disable_note=За потреби ви можете вимкнути двофакторну автентифікацію. twofa_disable_desc=Вимкнення двофакторної автентифікації зробить ваш обліковий запис менш безпечним. Продовжити? -twofa_disabled=Двофакторна автентифікація вимкнена. -scan_this_image=Проскануйте це зображення вашим додатком для двуфакторної автентифікації: -or_enter_secret=Або введіть секрет: %s +twofa_disabled=Двофакторну автентифікацію вимкнено. +scan_this_image=Відскануйте це зображення вашим додатком для двофакторної автентифікації: +or_enter_secret=Або введіть код: %s then_enter_passcode=І введіть пароль, який відображається в додатку: passcode_invalid=Некоректний пароль. Спробуй ще раз. -twofa_failed_get_secret=Не вдалося отримати секрет. +twofa_failed_get_secret=Не вдалося отримати код. -manage_account_links=Керування обліковими записами -manage_account_links_desc=Ці зовнішні акаунти прив'язані до вашого аккаунту Gitea. +manage_account_links=Керування прив'язаними обліковими записами +manage_account_links_desc=Ці зовнішні облікові записи прив'язані до вашого облікового запису Gitea. account_links_not_available=Наразі немає зовнішніх облікових записів, пов'язаних із вашим обліковим записом Gitea. link_account=Прив'язати обліковий запис -remove_account_link=Видалити облікові записи +remove_account_link=Видалити обліковий запис remove_account_link_desc=Видалення пов'язаного облікового запису відкликає його доступ до вашого облікового запису Gitea. Продовжити? -remove_account_link_success=Зв'язаний обліковий запис видалено. +remove_account_link_success=Прив'язаний обліковий запис видалено. -orgs_none=Ви не є учасником будь-якої організації. +orgs_none=Ви не є членом організації. -delete_account=Видалити ваш обліковий запис -delete_prompt=Ця операція остаточно видалить обліковий запис користувача. Це <strong>НЕ МОЖЛИВО</strong> відмінити. -delete_with_all_comments=Ваш обліковий запис молодший за %s днів. Щоб уникнути коментарів-привидів, всі запити/PR коментрарі будуть видалені з ним. -confirm_delete_account=Підтвердження видалення -delete_account_title=Видалити цей обліковий запис +delete_account=Видалити обліковий запис +delete_prompt=Ця операція остаточно видалить ваш обліковий запис. Її <strong>НЕ МОЖЛИВО</strong> скасувати. +delete_with_all_comments=Ваш обліковий запис молодший за %s днів. Щоб уникнути коментарів-привидів, усі ваші коментарі будуть видалені разом з ним. +confirm_delete_account=Підтвердити видалення +delete_account_title=Видалити обліковий запис delete_account_desc=Ви впевнені, що хочете остаточно видалити цей обліковий запис? -email_notifications.enable=Увімкнути сповіщення email -email_notifications.onmention=Повідомлення email тільки коли згадують -email_notifications.disable=Вимкнути email сповіщення -email_notifications.submit=Налаштувати параметри email +email_notifications.enable=Увімкнути сповіщення електронною поштою +email_notifications.onmention=Повідомляти електронною поштою коли згадують +email_notifications.disable=Вимкнути сповіщення електронною поштою +email_notifications.submit=Налаштувати параметри електронної пошти email_notifications.andyourown=І ваші власні повідомлення visibility=Видимість користувача @@ -881,22 +880,22 @@ visibility.private=Приватний [repo] owner=Власник -owner_helper=Деякі організації можуть не відображатися у випадаючому списку через максимальну кількість репозиторііїв. -repo_name=Назва репозиторію -repo_size=Розмір репозиторію +owner_helper=Деякі організації можуть не відображатися у списку через обмеження на максимальну кількість сховищ. +repo_name=Назва сховища +repo_size=Розмір сховища template=Шаблон -template_select=Оберіть шаблон. -template_helper=Зробити репозиторій шаблоном -template_description=Шаблонні репозиторії дозволяють користувачам генерувати нові репозиторії із такою ж структурою директорій, файлами та додатковими налаштуваннями. +template_select=Обрати шаблон. +template_helper=Зробити сховище шаблоном +template_description=Шаблонні сховища дозволяють користувачам створювати нові сховища з такою ж структурою каталогів, файлами та додатковими налаштуваннями. visibility=Видимість -visibility_description=Тільки власник або члени організації які мають віповідні права, зможуть побачити. +visibility_description=Тільки власник або члени організації, якщо вони мають дозвіл, зможуть його побачити. visibility_helper=Зробити сховище приватним visibility_helper_forced=Адміністратор вашого сайту налаштував параметри: всі нові репозиторії будуть приватними. visibility_fork_helper=(Ці зміни вплинуть на всі форки.) clone_helper=Потрібна допомога у клонуванні? Відвідайте сторінку <a target="_blank" rel="noopener" href="%s">Допомога</a>. fork_repo=Форкнути репозиторій fork_from=Форк з -fork_visibility_helper=Неможливо змінити видимість форкнутого репозиторію. +fork_visibility_helper=Неможливо змінити видимість розгалуженого сховища. all_branches=Усі гілки view_all_branches=Переглянути всі гілки use_template=Застосувати цей шаблон @@ -904,64 +903,64 @@ open_with_editor=Відкрити в %s download_zip=Завантажити ZIP download_tar=Завантажити TAR.GZ download_bundle=Завантажити BUNDLE -generate_repo=Згенерувати репозиторій -generate_from=Генерувати з +generate_repo=Створити сховище +generate_from=Створити з repo_desc=Опис -repo_desc_helper=Введіть короткий опис (опціонально) +repo_desc_helper=Введіть короткий опис (необов'язково) repo_no_desc=Немає опису repo_lang=Мови -repo_gitignore_helper=Виберіть шаблон .gitignore. -repo_gitignore_helper_desc=Оберіть з списку мовних шаблонів файли, які не будуть відстежуватись. Типові артефакти, які генеруються за допомогою інструментів побудови кожної мови, за замовчуванням включені до .gitignor. +repo_gitignore_helper=Обрати шаблон .gitignore. +repo_gitignore_helper_desc=Оберіть з списку мовних шаблонів файли, які не слід відстежувати. Типові артефакти, що генеруються інструментами збірки кожної мови, за замовчуванням включені до .gitignor. issue_labels=Мітки задачі -issue_labels_helper=Вибрати мітку для задачі. +issue_labels_helper=Виберіть набір міток задачі. license=Ліцензія -license_helper=Виберіть ліцензійний файл. -license_helper_desc=Ліцензія регулює те, що інші можуть і не можуть робити з вашим кодом. Не впевнені, що саме підходить для вашого проєкту? Дивіться <a target="_blank" rel="noopener noreferrer" href="%s">Виберіть ліцензію.</a> +license_helper=Обрати файл ліцензії. +license_helper_desc=Ліцензія визначає, що інші можуть робити з вашим кодом, а що ні. Не впевнені, яка підходить для вашого проєкту? Дивіться <a target="_blank" rel="noopener noreferrer" href="%s">Вибір ліцензії.</a> multiple_licenses=Кілька ліцензій object_format=Формат об'єкту readme=README readme_helper=Виберіть шаблон README. -readme_helper_desc=Це місце, де ви можете написати повний опис вашого проєкту. -auto_init=Ініціалізувати репозиторій (Додає .gitignore, LICENSE та README) +readme_helper_desc=Тут ви можете повністю описати ваш проєкт. +auto_init=Ініціалізувати сховище (додає файли .gitignore, ліцензію та README) trust_model_helper=Виберіть модель довіри для підтвердження підпису. Можливі варіанти: -trust_model_helper_collaborator=Співавтор: підписи довіри від співавторів -trust_model_helper_committer=Учасник: довірені підписи участників -trust_model_helper_collaborator_committer=Співавтор+Комітер: довірчі підписи від співавторів, які відповідають комітеру -trust_model_helper_default=За замовчуванням: використовувати стандартну модель довіри для цієї установки -create_repo=Створити репозиторій +trust_model_helper_collaborator=Співавтор: довіряти підписам співавторів +trust_model_helper_committer=Комітер: довіряти підписам, які відповідають комітерам +trust_model_helper_collaborator_committer=Співавтор+Комітер: довіряти підписам співавторів, які відповідають комітеру +trust_model_helper_default=Типово: використовувати стандартну модель довіри для цієї установки +create_repo=Створити сховище default_branch=Головна гілка default_branch_label=типово -default_branch_helper=Гілка за замовчуванням є базовою гілкою для запитів на злиття та комітів коду. +default_branch_helper=Типова гілка є базовою гілкою для запитів на злиття та комітів. mirror_prune=Очистити -mirror_prune_desc=Видалення застарілих посилань які ви відслідковуєте -mirror_interval_invalid=Інтервал дзеркалювання є неприпустимим. +mirror_prune_desc=Видалити застарілі посилання на віддалені відстеження +mirror_interval_invalid=Інтервал дзеркалювання недійсний. mirror_sync=синхронізовано mirror_sync_on_commit=Синхронізувати, коли надсилаються коміти -mirror_address=Клонування з URL-адреси -mirror_address_desc=Помістіть будь-які необхідні облікові дані у розділі Авторизація. -mirror_lfs=Склад великих файлів (LFS) -mirror_lfs_desc=Активувати дзеркальне відображення даних LFS. +mirror_address=Клонувати з URL-адреси +mirror_address_desc=Введіть необхідні облікові дані в розділі Авторизація. +mirror_lfs=Сховище великих файлів (LFS) +mirror_lfs_desc=Активувати дзеркалювання даних LFS. mirror_lfs_endpoint=Кінцева точка LFS -mirror_lfs_endpoint_desc=Синхронізація спробує використовувати url для клону щоб <a target="_blank" rel="noopener noreferrer" href="%s">визначити LFS-сервер</a>. Ви також можете вказати кінцеву точку користувача, якщо дані репозиторію LFS зберігаються в іншому місці. +mirror_lfs_endpoint_desc=Синхронізація спробує використати URL-адресу клону для <a target="_blank" rel="noopener noreferrer" href="%s">визначення сервера LFS</a>. Ви також можете вказати власну кінцеву точку, якщо дані LFS сховища зберігаються в іншому місці. mirror_last_synced=Остання синхронізація mirror_password_placeholder=(без змін) -mirror_password_blank_placeholder=(відключено) +mirror_password_blank_placeholder=(Не встановлено) mirror_password_help=Змініть ім'я користувача, щоб видалити збережений пароль. watchers=Спостерігачі -stargazers=Зацікавлені +stargazers=Шанувальники stars_remove_warning=Це видалить усі зірки з цього сховища. forks=Форки stars=Зірки -reactions_more=додати %d більше -unit_disabled=Адміністратор сайту вимкнув цей розділ репозиторію. +reactions_more=і ще %d +unit_disabled=Адміністратор сайту вимкнув цей розділ сховища. language_other=Інші -adopt_search=Введіть ім'я користувача для пошуку неприйнятних репозиторіїв... (залиште порожнім, щоб знайти всі) -adopt_preexisting_label=Прийняті файли -adopt_preexisting=Прийняти вже існуючі файли -adopt_preexisting_content=Створити репозиторій з %s -adopt_preexisting_success=Прийняти файли та створити репозиторій з %s +adopt_search=Введіть ім'я користувача для пошуку неприйнятих сховищ... (залиште порожнім, щоб знайти всі) +adopt_preexisting_label=Прийняти файли +adopt_preexisting=Прийняти попередньо створені файли +adopt_preexisting_content=Створити сховище з %s +adopt_preexisting_success=Прийняти файли та створити сховище з %s delete_preexisting_label=Видалити -delete_preexisting=Видалити існуючі файли +delete_preexisting=Видалити попередньо створені файли delete_preexisting_content=Видалити файли з %s delete_preexisting_success=Видалено неприйняті файли в %s blame_prior=Переглянути анотацію, що передує цій зміні @@ -969,9 +968,9 @@ user_search_tooltip=Показує не більше 30 користувачів tree_path_not_found=Шлях %[1]s не існує в %[2]s -transfer.accept=Дозволити трансфер +transfer.accept=Дозволити переміщення transfer.accept_desc=`Перемістити до "%s"` -transfer.reject=Відхилити трансфер +transfer.reject=Відхилити переміщення transfer.reject_desc=`Скасувати переміщення до "%s"` desc.private=Приватний @@ -979,34 +978,34 @@ desc.public=Публічний desc.public_access=Публічний доступ desc.template=Шаблон desc.internal=Внутрішній -desc.archived=Архівний +desc.archived=Архівований desc.sha256=SHA256 template.items=Елементи шаблону template.git_content=Вміст Git (типова гілка) -template.git_hooks=Перехоплювачі Git -template.webhooks=Webhook'и +template.git_hooks=Хуки Git +template.webhooks=Веб-хуки template.topics=Теми template.avatar=Аватар template.issue_labels=Мітки задачі template.one_item=Слід обрати хоча б один елемент шаблону -template.invalid=Слід обрати шаблонний репозиторій +template.invalid=Слід обрати шаблонне сховище -archive.issue.nocomment=Цей репозиторій архівовано. Ви не можете коментувати задачі. -archive.pull.nocomment=Це архівний репозитарій. Ви не можете коментувати пулл-реквести. +archive.issue.nocomment=Це сховище архівовано. Ви не можете коментувати задачі. +archive.pull.nocomment=Це сховище архівовано. Ви не можете коментувати запити на злиття. -form.reach_limit_of_creation_1=Ви вже досягли ліміту в %d репозиторіїв. -form.reach_limit_of_creation_n=Ви досягли максимальної кількості %d створених репозиторіїв. +form.reach_limit_of_creation_1=Ви досягли максимальної кількості %d сховища. +form.reach_limit_of_creation_n=Ви досягли максимальної кількості %d сховищ. need_auth=Авторизація migrate_options=Параметри міграції migrate_service=Сервіс міграції migrate_options_mirror_helper=Це сховище буде дзеркалом -migrate_options_lfs=Перенесення LFS файлів +migrate_options_lfs=Перенесення файлів LFS migrate_options_lfs_endpoint.label=Кінцева точка LFS -migrate_options_lfs_endpoint.description=Міграція буде намагатися використовувати ваш Git віддалено, щоб <a target="_blank" rel="noopener noreferrer" href="%s">визначати LFS сервер</a>. Ви також можете вказати свою кінцеву точку, якщо дані репозиторію LFS зберігаються в іншому місці. -migrate_options_lfs_endpoint.description.local=Також підтримуються шляхи на локальному сервері. -migrate_items=Деталі міграції +migrate_options_lfs_endpoint.description=Міграція спробує використати ваш Git віддалено, щоб <a target="_blank" rel="noopener noreferrer" href="%s">визначати сервер LFS</a>. Ви також можете вказати власну кінцеву точку, якщо дані сховища LFS зберігаються в іншому місці. +migrate_options_lfs_endpoint.description.local=Також підтримується шлях до локального сервера. +migrate_items=Елементи міграції migrate_items_wiki=Вікі migrate_items_milestones=Етапи migrate_items_labels=Мітки @@ -1016,21 +1015,21 @@ migrate_items_merge_requests=Запити на злиття migrate_items_releases=Релізи migrate_repo=Перенести репозиторій migrate.clone_address=Міграція / клонувати з URL-адреси -migrate.clone_address_desc=URL-адреса HTTP(S) або Git "clone" існуючого репозиторія +migrate.clone_address_desc=URL-адреса HTTP(S) або Git "clone" існуючого сховища migrate.clone_local_path=або шлях до локального серверу migrate.permission_denied=Вам не дозволено імпортувати локальні репозиторії. migrate.permission_denied_blocked=Ви не можете імпортувати з заборонених вузлів, будь ласка, попросіть адміністратора перевірити налаштування ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. -migrate.invalid_lfs_endpoint=Помилкова кінцева точка LFS. +migrate.invalid_lfs_endpoint=Кінцева точка LFS недійсна. migrate.failed=Міграція не вдалася: %v migrate.migrate_items_options=Для перенесення додаткових елементів потрібен токен доступу migrated_from=Перенесено з <a href="%[1]s">%[2]s</a> migrated_from_fake=Перенесено з %[1]s -migrate.migrate=Міграція з %s +migrate.migrate=Мігрувати з %s migrate.migrating=Міграція із <b>%s</b>... migrate.migrating_failed=Міграція із <b>%s</b> не вдалася. migrate.migrating_failed.error=Не вдалося перенести: %s migrate.migrating_failed_no_addr=Міграція не вдалася. -migrate.git.description=Перенесення лише репозиторію з будь-якої служби Git. +migrate.git.description=Перенести сховище з будь-якого сервісу Git'у. migrate.gitlab.description=Перенести дані з gitlab.com та інших екземплярів GitLab. migrate.gitea.description=Перенести дані з gitea.com та інших екземплярів Gitea. migrate.gogs.description=Перенести дані з notabug.org та інших екземплярів Gogs. @@ -1051,12 +1050,12 @@ migrating_status=Cтатус міграції mirror_from=дзеркало forked_from=форк від generated_from=згенеровано з -fork_from_self=Ви не можете форкнути репозиторій, так як ви його власник. +fork_from_self=Ви не можете форкнути власне сховище. fork_guest_user=Увійдіть, щоб зробити форк репозитарію. watch_guest_user=Увійдіть, щоб слідкувати за цим репозиторієм. star_guest_user=Увійдіть, щоб додати в обране цей репозиторій. unwatch=Не стежити -watch=Слідкувати +watch=Стежити unstar=Видалити із обраних star=В обрані fork=Форк @@ -1068,7 +1067,7 @@ clone_this_repo=Кнонувати цей репозиторій cite_this_repo=Послатися на це сховище create_new_repo_command=Створити новий репозиторій з командного рядка push_exist_repo=Опублікувати існуючий репозиторій з командного рядка -empty_message=Цей репозиторій порожній. +empty_message=Це сховище порожнє. code=Код code.desc=Доступ до коду, файлів, комітів та гілок. @@ -1088,20 +1087,19 @@ org_labels_desc=Мітки рівня організації можуть вик org_labels_desc_manage=керувати milestone=Етап -milestones=Етап +milestones=Етапи commits=Коміти commit=Коміт release=Реліз releases=Релізи tag=Тег -released_this=випущені релізи +released_this=випустив(-ла) file_raw=Неформатований file_history=Історія file_view_source=Переглянути вихідний код -file_view_rendered=Переглянути відрендерено file_view_raw=Перегляд Raw file_permalink=Постійне посилання -file_too_large=Цей файл завеликий щоб бути показаним. +file_too_large=Файл занадто великий для відображення. file_is_empty=Файл порожній. code_preview_line_from_to=Рядки від %[1]d до %[2]d в %[3]s code_preview_line_in=Рядок %[1]d в %[2]s @@ -1120,10 +1118,10 @@ generated=Створено commit_graph=Графік комітів commit_graph.select=Виберіть гілки commit_graph.hide_pr_refs=Приховати запити на злиття -commit_graph.monochrome=Монохром +commit_graph.monochrome=Монохромний commit_graph.color=Колір commit.contained_in=Цей коміт міститься в: -blame=Звинувачення +blame=Анотація download_file=Завантажити файл normal_view=Звичайний вигляд line=рядок @@ -1133,41 +1131,40 @@ from_comment=(коментар) editor.add_file=Додати файл editor.new_file=Новий файл editor.upload_file=Завантажити файл -editor.edit_file=Редагування файлу +editor.edit_file=Редагувати файл editor.preview_changes=Попередній перегляд змін editor.cannot_edit_lfs_files=Файли LFS не можна редагувати в веб-інтерфейсі. editor.cannot_edit_non_text_files=Бінарні файли не можливо редагувати у веб-інтерфейсі. editor.edit_this_file=Редагувати файл editor.this_file_locked=Файл заблоковано -editor.must_be_on_a_branch=Ви повинні бути у гілці щоб зробити, або запропонувати зміни до цього файлу. -editor.fork_before_edit=Необхідно зробити форк цього репозиторій, щоб внести або запропонувати зміни в цей файл. +editor.must_be_on_a_branch=Ви повинні бути у гілці щоб робити або пропонувати зміни до цього файлу. +editor.fork_before_edit=Необхідно зробити форк цього сховища, щоб внести або запропонувати зміни в цей файл. editor.delete_this_file=Видалити файл editor.must_have_write_access=Ви повинні мати доступ на запис щоб запропонувати зміни до цього файлу. editor.file_delete_success=Файл "%s" видалено. -editor.name_your_file=Дайте назву файлу… -editor.filename_help=Щоб додати каталог, наберіть його назву, а потім - косу риску ('/'). Щоб видалити каталог, перейдіть до початку поля і натисніть backspace. +editor.name_your_file=Назвіть файл… +editor.filename_help=Щоб додати каталог, наберіть його назву, а потім - прямий слеш ('/'). Щоб видалити каталог, перейдіть до початку поля і натисніть видалити ліворуч. editor.or=або editor.cancel_lower=Скасувати editor.commit_signed_changes=Внести підписані зміни -editor.commit_changes=Закомітити зміни editor.add_tmpl=Додати '{filename}' editor.add=Додати %s editor.update=Оновити %s editor.delete=Видалити %s editor.commit_message_desc=Додати необов'язковий розширений опис… -editor.signoff_desc=Додатиь Signed-off-by комітом в конці повідомлення журналу комітів. -editor.commit_directly_to_this_branch=Зробіть коміт прямо в гілку <strong class="branch-name">%s</strong>. +editor.signoff_desc=Додати «Підписано комітером» в кінці повідомлення коміту. +editor.commit_directly_to_this_branch=Зробити коміт безпосередньо в гілку <strong class="branch-name">%s</strong>. editor.create_new_branch=Створити <strong>нову гілку</strong> для цього коміту та відкрити запит на злиття. editor.create_new_branch_np=Створити <strong>нову гілку</strong> для цього коміту. editor.propose_file_change=Запропонувати зміну файлу -editor.new_branch_name_desc=Ім'я нової гілки… +editor.new_branch_name_desc=Назва нової гілки… editor.cancel=Відмінити -editor.filename_cannot_be_empty=Ім'я файлу не може бути порожнім. +editor.filename_cannot_be_empty=Назва файлу не може бути порожньою. editor.invalid_commit_email=Адреса електронної пошти для коміту недійсна. editor.file_changed_while_editing=Зміст файлу змінився з моменту початку редагування. <a target="_blank" rel="noopener" href="%s"> Натисніть тут </a>, щоб переглянути що було змінено, або <strong>закомітьте зміни ще раз</strong>, щоб переписати їх. editor.commit_empty_file_header=Закомітити порожній файл -editor.commit_empty_file_text=Файл, в комміті порожній. Продовжити? -editor.no_changes_to_show=Нема змін для показу. +editor.commit_empty_file_text=Файл, який ви збираєтеся закомітити, порожній. Продовжувати? +editor.no_changes_to_show=Немає змін. editor.fail_to_update_file=Не вдалося оновити/створити файл "%s". editor.fail_to_update_file_summary=Помилка: editor.push_rejected_no_message=Зміну відхилено сервером без повідомлення. Будь ласка, перевірте Git-хуки. @@ -1177,7 +1174,6 @@ editor.add_subdir=Додати каталог… editor.upload_file_is_locked=Файл "%s" заблоковано %s. editor.upload_files_to_dir=`Завантажити файли до "%s"` editor.no_commit_to_branch=Не вдалося внести коміт безпосередньо до гілки, тому що: -editor.user_no_push_to_branch=Користувач не може здійснити пуш до гілки editor.require_signed_commit=Гілка вимагає підписаного коміту commits.desc=Переглянути історію зміни коду. @@ -1188,11 +1184,11 @@ commits.search_all=Усі гілки commits.author=Автор commits.message=Повідомлення commits.date=Дата -commits.older=Давніше -commits.newer=Новіше +commits.older=Старіші +commits.newer=Новіші commits.signed_by=Підписано commits.signed_by_untrusted_user=Підписаний недовіреним користувачем -commits.signed_by_untrusted_user_unmatched=Підписаний недовіреним користувачем, який не відповідає комітеру +commits.signed_by_untrusted_user_unmatched=Підписано недовіреним користувачем, який не відповідає комітеру commits.gpg_key_id=Ідентифікатор GPG ключа commits.ssh_key_fingerprint=Відбиток ключа SSH commits.view_file_diff=Переглянути зміни до цього файлу в цьому коміті @@ -1212,20 +1208,20 @@ projects.description_placeholder=Опис projects.create=Створити проєкт projects.title=Назва projects.new=Новий проєкт -projects.new_subheader=Координуйте, відстежуйте та оновлюйте інформацію про виконувану роботу в одному місці, аби проєкти залишалися прозорими та за розкладом. +projects.new_subheader=Координуйте, відстежуйте та оновлюйте свою роботу в одному місці, щоб проєкти залишалися прозорими та виконувалися за графіком. projects.create_success=Проєкт "%s" створено. projects.deletion=Видалити проєкт projects.deletion_desc=Видалення проєкту видаляє його з усіх пов'язаних задач. Продовжити? projects.deletion_success=Проєкт видалено. -projects.edit=Редагувати проєкти +projects.edit=Редагувати проєкт projects.edit_subheader=Проєкти організовують задачі та відстежують прогрес. -projects.modify=Оновити проєкт +projects.modify=Редагувати проєкт projects.edit_success=Проєкт "%s" оновлено. projects.type.none=Відсутній projects.type.basic_kanban=Спрощений канбан projects.type.bug_triage=Сортування помилок projects.template.desc=Шаблон проєкту -projects.template.desc_helper=Оберіть шаблон проєкту, аби почати +projects.template.desc_helper=Оберіть шаблон проєкту, щоб розпочати роботу projects.column.edit=Редагувати стовпець projects.column.edit_title=Назва projects.column.new_title=Назва @@ -1236,7 +1232,7 @@ projects.column.delete=Видалити стовпець projects.column.color=Колір projects.open=Відкрити projects.close=Закрити -projects.card_type.desc=Попередній перегляд карток +projects.card_type.desc=Попередні перегляди картки projects.card_type.images_and_text=Зображення і текст projects.card_type.text_only=Лише текст @@ -1249,7 +1245,7 @@ issues.filter_reviewers=Фільтр рецензентів issues.filter_no_results=Немає результатів issues.filter_no_results_placeholder=Спробуйте налаштувати свої фільтри пошуку. issues.new=Нова задача -issues.new.title_empty=Заголовок не може бути пустим +issues.new.title_empty=Заголовок не може бути порожнім issues.new.labels=Мітки issues.new.no_label=Без мітки issues.new.clear_labels=Очистити мітки @@ -1260,16 +1256,16 @@ issues.new.open_projects=Відкриті проєкти issues.new.closed_projects=Закриті проєкти issues.new.no_items=Немає елементів issues.new.milestone=Етап -issues.new.no_milestone=Етап відсутній +issues.new.no_milestone=Етапи відсутні issues.new.clear_milestone=Очистити етап issues.new.assignees=Виконавці issues.new.clear_assignees=Прибрати виконавців -issues.new.no_assignees=Немає виконавця +issues.new.no_assignees=Немає виконавців issues.new.no_reviewers=Немає рецензентів -issues.choose.get_started=Початок роботи +issues.choose.get_started=Розпочати issues.choose.open_external_link=Відкрити issues.choose.blank=Типово -issues.choose.blank_about=Створити задачу із шаблону за замовчуванням. +issues.choose.blank_about=Створити задачу із стандартного шаблону. issues.choose.ignore_invalid_templates=Недійсні шаблони проігноровано issues.choose.invalid_templates=Знайдено %v недійсний(х) шаблон(ів) issues.choose.invalid_config=Конфігурація задачі містить помилки: @@ -1282,7 +1278,7 @@ issues.create_label=Створити мітку issues.label_templates.title=Завантажити визначений набір міток issues.label_templates.info=Ще немає міток. Натисніть 'Нова мітка' або використовуйте попередньо визначений набір міток: issues.label_templates.helper=Оберіть набір міток -issues.label_templates.use=Використовувати набір міток +issues.label_templates.use=Використати набір міток issues.add_label=додано %s з міткою %s issues.add_labels=додано %s з мітками %s issues.remove_label=видалено %s з міткою %s diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 1a07c2f4c0..989802b9db 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -262,7 +262,7 @@ host=数据库主机 user=用户名 password=数据库用户密码 db_name=数据库名称 -db_schema=Schema +db_schema=架构 db_schema_helper=留空则数据库中默认值为("public")。 ssl_mode=SSL path=数据库文件路径 @@ -752,7 +752,7 @@ cancel=取消操作 language=界面语言 ui=主题 hidden_comment_types=隐藏的评论类型 -hidden_comment_types_description=此处选中的注释类型不会显示在工单页面中。比如,勾选「标签」删除所有「{user} 添加/删除的 {label}」注释。 +hidden_comment_types_description=此处选中的注释类型不会显示在工单页面中。比如,勾选「标记」删除所有「{user} 添加/删除的 {label}」注释。 hidden_comment_types.ref_tooltip=注释此问题在何处被提及过,如另一个问题、代码提交等 hidden_comment_types.issue_ref_tooltip=注释用户在何处更改了与此问题相关联的分支/标签 comment_type_group_reference=引用 @@ -1066,10 +1066,10 @@ download_tar=下载 TAR.GZ download_bundle=下载 BUNDLE generate_repo=生成仓库 generate_from=生成自 -repo_desc=仓库描述 +repo_desc=描述 repo_desc_helper=输入简要描述 (可选) repo_no_desc=无详细信息 -repo_lang=编程语言 +repo_lang=语言 repo_gitignore_helper=选择 .gitignore 模板。 repo_gitignore_helper_desc=从常见语言的模板列表中选择忽略跟踪的文件。默认情况下,由开发或构建工具生成的特殊文件都包含在 .gitignore 中。 issue_labels=工单标签 @@ -1161,15 +1161,15 @@ template.issue_labels=工单标签 template.one_item=必须至少选择一个模板项 template.invalid=必须选择一个模板仓库 -archive.title=该仓库已被归档。您可以查看文件和克隆它,但不能推送、创建工单或合并请求。 -archive.title_date=该仓库已于 %s 归档。您可以查看文件或克隆它,但不能推送、创建工单或合并请求。 +archive.title=该仓库已被归档。您可以查看文件和克隆它,但不能推送、打开工单或合并请求。 +archive.title_date=该仓库已于 %s 归档。您可以查看文件或克隆它,但不能推送、打开工单或合并请求。 archive.issue.nocomment=此仓库已存档,您不能在此工单添加评论。 archive.pull.nocomment=此仓库已存档,您不能在此合并请求添加评论。 form.reach_limit_of_creation_1=您已经达到了 %d 仓库的上限。 form.reach_limit_of_creation_n=您已经达到了 %d 个仓库的上限。 form.name_reserved=仓库名称 %s 是保留的。 -form.name_pattern_not_allowed=仓库名称中不允许使用 %s 格式。 +form.name_pattern_not_allowed=仓库名中不允许使用「%s」格式。 need_auth=授权 migrate_options=迁移选项 @@ -1261,7 +1261,7 @@ branch=分支 tree=目录树 clear_ref=`清除当前引用` filter_branch_and_tag=过滤分支或标签 -find_tag=查找 Git 标签 +find_tag=查找标签 branches=分支列表 tags=标签列表 issues=工单 @@ -1279,7 +1279,7 @@ commits=提交 commit=提交 release=发布 releases=发布 -tag=Git 标签 +tag=标签 released_this=发布 tagged_this=已标记 file.title=%s 位于 %s @@ -1336,7 +1336,7 @@ editor.cannot_edit_non_text_files=网页不能编辑二进制文件。 editor.edit_this_file=编辑文件 editor.this_file_locked=文件已锁定 editor.must_be_on_a_branch=您必须在某个分支上才能对此文件进行修改操作。 -editor.fork_before_edit=您必须在派生这个仓库才能对此文件进行修改操作 +editor.fork_before_edit=您必须派生这个仓库才能对此文件进行修改操作。 editor.delete_this_file=删除文件 editor.must_have_write_access=您必须具有写权限才能对此文件进行修改操作。 editor.file_delete_success=文件「%s」已删除。 @@ -1359,7 +1359,7 @@ editor.signoff_desc=在提交日志消息末尾添加签署人信息。 editor.commit_directly_to_this_branch=直接提交至 <strong class="branch-name">%s</strong> 分支。 editor.create_new_branch=为此提交创建一个 <strong>新的分支</strong> 并发起合并请求。 editor.create_new_branch_np=为此提交创建 <strong>新分支</strong>。 -editor.propose_file_change=提议文件更改 +editor.propose_file_change=建议文件更改 editor.new_branch_name=为这次提交的新分支命名 editor.new_branch_name_desc=新的分支名称... editor.cancel=取消 @@ -1613,8 +1613,8 @@ issues.close=关闭工单 issues.comment_pull_merged_at=已合并提交 %[1]s 到 %[2]s %[3]s issues.comment_manually_pull_merged_at=手动合并提交 %[1]s 到 %[2]s %[3]s issues.close_comment_issue=评论并关闭 -issues.reopen_issue=重新开启 -issues.reopen_comment_issue=评论并重新开启 +issues.reopen_issue=重新打开 +issues.reopen_comment_issue=评论并重新打开 issues.create_comment=评论 issues.comment.blocked_user=无法创建或编辑评论,因为您已被仓库所有者或工单创建者屏蔽。 issues.closed_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 关闭此工单` @@ -1650,19 +1650,19 @@ issues.edit=编辑 issues.cancel=取消 issues.save=保存 issues.label_title=标签名称 -issues.label_description=标签描述 -issues.label_color=标签颜色 +issues.label_description=描述 +issues.label_color=颜色 issues.label_color_invalid=无效的颜色 -issues.label_exclusive=互斥标签 +issues.label_exclusive=互斥 issues.label_archive=归档标签 -issues.label_archived_filter=显示存档标签 -issues.label_archive_tooltip=在标签搜索时,默认情况下存档标签将被排除在外。 +issues.label_archived_filter=显示已归档标签 +issues.label_archive_tooltip=在标签搜索时,默认情况下已归档标签将被排除在外。 issues.label_exclusive_desc=命名标签为 <code>scope/item</code> 以使其与其他以 <code>scope/</code> 开头的标签互斥。 issues.label_exclusive_warning=在编辑工单或合并请求的标签时,任何冲突的范围标签都将被删除。 issues.label_exclusive_order=排序顺序 issues.label_exclusive_order_tooltip=在同一个范围内的互斥标签将按照这个数字进行排序 issues.label_count=%d 个标签 -issues.label_open_issues=%d 个开启的工单 +issues.label_open_issues=%d 个已打开工单/合并请求 issues.label_edit=编辑 issues.label_delete=删除 issues.label_modify=编辑标签 @@ -1993,7 +1993,7 @@ milestones.new=新的里程碑 milestones.closed=于 %s关闭 milestones.update_ago=已更新 %s milestones.no_due_date=暂无截止日期 -milestones.open=开启 +milestones.open=打开 milestones.close=关闭 milestones.new_subheader=里程碑可以帮助您组织工单并跟踪其进度。 milestones.completeness=<strong>%d%%</strong> 已完成 @@ -2068,22 +2068,22 @@ activity=活动 activity.navbar.pulse=活动 activity.navbar.code_frequency=代码频率 activity.navbar.contributors=贡献者 -activity.navbar.recent_commits=最近的提交 +activity.navbar.recent_commits=最近提交 activity.period.filter_label=周期: activity.period.daily=1 天 activity.period.halfweekly=3 天 -activity.period.weekly=1周 -activity.period.monthly=1 个月 -activity.period.quarterly=3个月 -activity.period.semiyearly=6 个月 -activity.period.yearly=1年 +activity.period.weekly=1 周 +activity.period.monthly=1 月 +activity.period.quarterly=3 月 +activity.period.semiyearly=6 月 +activity.period.yearly=1 年 activity.overview=概览 activity.active_prs_count_1=<strong>%d</strong> 个合并请求 activity.active_prs_count_n=<strong>%d</strong> 个合并请求 -activity.merged_prs_count_1=合并请求 -activity.merged_prs_count_n=合并请求 -activity.opened_prs_count_1=新合并请求 -activity.opened_prs_count_n=新合并请求 +activity.merged_prs_count_1=已合并的合并请求 +activity.merged_prs_count_n=已合并的合并请求 +activity.opened_prs_count_1=创建的合并请求 +activity.opened_prs_count_n=创建的合并请求 activity.title.user_1=%d 位用户 activity.title.user_n=%d 位用户 activity.title.prs_1=%d 个合并请求 @@ -2101,8 +2101,8 @@ activity.title.issues_n=%d 张工单 activity.title.issues_closed_from=%s 从 %s 关闭 activity.title.issues_created_by=%[2]s 创建了 %[1]s activity.closed_issue_label=已关闭 -activity.new_issues_count_1=创建工单 -activity.new_issues_count_n=创建工单 +activity.new_issues_count_1=已打开的工单 +activity.new_issues_count_n=已打开的工单 activity.new_issue_label=打开的 activity.title.unresolved_conv_1=%d 未解决的会话 activity.title.unresolved_conv_n=%d 未解决的会话 @@ -2113,11 +2113,11 @@ activity.title.releases_n=%d 个发布 activity.title.releases_published_by=%[2]s 发布了 %[1]s activity.published_release_label=已发布 activity.no_git_activity=在此期间没有任何提交活动。 -activity.git_stats_exclude_merges=排除合并, +activity.git_stats_exclude_merges=排除合并后, activity.git_stats_author_1=%d 位作者 activity.git_stats_author_n=%d 位作者 -activity.git_stats_pushed_1=已经推送 -activity.git_stats_pushed_n=已经推送 +activity.git_stats_pushed_1=已推送 +activity.git_stats_pushed_n=已推送 activity.git_stats_commit_1=%d 次提交 activity.git_stats_commit_n=%d 次提交 activity.git_stats_push_to_branch=到 %s 和 @@ -2125,14 +2125,14 @@ activity.git_stats_push_to_all_branches=到所有分支。 activity.git_stats_on_default_branch=在 %s 上, activity.git_stats_file_1=%d 个文件 activity.git_stats_file_n=%d 个文件 -activity.git_stats_files_changed_1=已经改变 -activity.git_stats_files_changed_n=已经改变 -activity.git_stats_additions=而且 -activity.git_stats_addition_1=新增 %d 行 -activity.git_stats_addition_n=新增 %d 行 +activity.git_stats_files_changed_1=已修改 +activity.git_stats_files_changed_n=已修改 +activity.git_stats_additions=并且已有 +activity.git_stats_addition_1=%d 行新增 +activity.git_stats_addition_n=%d 行新增 activity.git_stats_and_deletions=和 -activity.git_stats_deletion_1=删除 %d 行 -activity.git_stats_deletion_n=删除 %d 行 +activity.git_stats_deletion_1=%d 行删除 +activity.git_stats_deletion_n=%d 行删除 contributors.contribution_type.filter_label=贡献类型: contributors.contribution_type.commits=提交 @@ -2211,7 +2211,7 @@ settings.tracker_issue_style=外部工单管理系统的编号格式 settings.tracker_issue_style.numeric=纯数字形式 settings.tracker_issue_style.alphanumeric=英文字母数字组合形式 settings.tracker_issue_style.regexp=正则表达式 -settings.tracker_issue_style.regexp_pattern=正则表达式模式 +settings.tracker_issue_style.regexp_pattern=正则表达式 settings.tracker_issue_style.regexp_pattern_desc=第一个被捕获的组将取代 <code>{index}</code>。 settings.tracker_url_format_desc=使用占位符 <code>{user}</code>, <code>{repo}</code> 和 <code>{index}</code> 作为用户名、仓库名和工单索引。 settings.enable_timetracker=启用时间跟踪 @@ -2221,7 +2221,7 @@ settings.pulls.ignore_whitespace=忽略空白冲突 settings.pulls.enable_autodetect_manual_merge=启用自动检测手动合并 (注意:在某些特殊情况下可能发生错误判断) settings.pulls.allow_rebase_update=允许通过变基更新合并请求分支 settings.pulls.default_delete_branch_after_merge=默认合并后删除合并请求分支 -settings.pulls.default_allow_edits_from_maintainers=默认开启允许维护者编辑 +settings.pulls.default_allow_edits_from_maintainers=默认允许维护者编辑 settings.releases_desc=启用仓库发布 settings.packages_desc=启用仓库软件包注册中心 settings.projects_desc=启用项目 @@ -2386,7 +2386,7 @@ settings.event_pull_request=合并请求 settings.event_pull_request_desc=合并请求已打开、关闭、重新打开或编辑。 settings.event_pull_request_assign=合并请求已指派 settings.event_pull_request_assign_desc=合并请求已指派或取消指派。 -settings.event_pull_request_label=合并请求已贴上标签 +settings.event_pull_request_label=合并请求增删标签 settings.event_pull_request_label_desc=合并请求的标签已更新或清除。 settings.event_pull_request_milestone=合并请求已记录于里程碑中 settings.event_pull_request_milestone_desc=合并请求已记录或取消记录于里程碑中。 @@ -2406,7 +2406,7 @@ settings.event_workflow_job_desc=Gitea 工作流队列中、等待中、正在 settings.event_package=软件包 settings.event_package_desc=软件包在仓库中已创建或删除。 settings.branch_filter=分支过滤 -settings.branch_filter_desc=推送、创建,删除分支事件的分支白名单,使用 glob 模式匹配指定。若为空或 <code>*</code>,则将报告所有分支的事件。语法文档见 <a href="%[1]s">%[2]s</a>。示例:<code>master</code>,<code>{master,release*}</code>。 +settings.branch_filter_desc=推送、创建,删除分支事件的分支白名单,使用 glob 表达式匹配指定。若为空或 <code>*</code>,则会报告所有分支的事件。语法文档见 <a href="%[1]s">%[2]s</a>。示例:<code>master</code>,<code>{master,release*}</code>。 settings.authorization_header=授权标头 settings.authorization_header_desc=当存在时将被作为授权标头包含在内。例如: %s。 settings.active=激活 @@ -2486,13 +2486,13 @@ settings.protect_merge_whitelist_committers_desc=仅允许白名单用户或团 settings.protect_merge_whitelist_users=合并白名单用户: settings.protect_merge_whitelist_teams=合并白名单团队: settings.protect_check_status_contexts=启用状态检查 -settings.protect_status_check_patterns=状态检查模式: -settings.protect_status_check_patterns_desc=输入模式,指定哪些状态检查必须通过,才能将分支合并到符合此规则的分支中去。每一行指定一个模式,模式不能为空。 +settings.protect_status_check_patterns=状态检查表达式: +settings.protect_status_check_patterns_desc=输入表达式以指定在分支合并到匹配此规则的分支之前必须通过哪些状态检查。每一行指定一个表达式且表达式不能为空。 settings.protect_check_status_contexts_desc=要求状态检查通过才能合并。如果启用,提交必须先推送到另一个分支,然后再合并或推送到匹配这些保护规则的分支。如果没有选择具体的状态检查上下文,则所有的状态检查都通过才能合并。 settings.protect_check_status_contexts_list=此仓库上周进行过的状态检查 settings.protect_status_check_matched=匹配 -settings.protect_invalid_status_check_pattern=无效的状态检查规则:「%s」。 -settings.protect_no_valid_status_check_patterns=没有有效的状态检查规则。 +settings.protect_invalid_status_check_pattern=无效的状态检查表达式:「%s」。 +settings.protect_no_valid_status_check_patterns=没有有效的状态检查表达式。 settings.protect_required_approvals=所需的批准: settings.protect_required_approvals_desc=只允许合并有足够审核的合并请求。要求的审核必须来自白名单或者有权限的用户或团队。 settings.protect_approvals_whitelist_enabled=仅列入白名单的用户或团队才可批准 @@ -2505,13 +2505,13 @@ settings.ignore_stale_approvals=忽略过期批准 settings.ignore_stale_approvals_desc=对旧提交(过期审核)的批准将不计入 PR 的批准数。如果过期审查已被驳回,则与此无关。 settings.require_signed_commits=需要签名提交 settings.require_signed_commits_desc=拒绝推送未签名或无法验证的提交到分支 -settings.protect_branch_name_pattern=受保护的分支名称模式 -settings.protect_branch_name_pattern_desc=分支保护的名称匹配规则。语法请参阅 <a href="%s">文档</a> 。如:main, release/** -settings.protect_patterns=规则 -settings.protect_protected_file_patterns=受保护的文件模式(使用分号 ';' 分隔): -settings.protect_protected_file_patterns_desc=即使用户有权添加、编辑或删除此分支中的文件,也不允许直接更改受保护的文件。 可以使用分号 (';') 分隔多个模式。 见<a href='%[1]s'>%[2]s</a>文档了解模式语法。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code> -settings.protect_unprotected_file_patterns=不受保护的文件模式(使用分号 ';' 分隔): -settings.protect_unprotected_file_patterns_desc=如果用户有写权限,则允许直接更改的不受保护的文件,以绕过推送限制。可以使用分号分隔多个模式 (';')。 见 <a href='%[1]s'>%[2]s</a> 文档了解模式语法。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code> +settings.protect_branch_name_pattern=受保护的分支名称表达式 +settings.protect_branch_name_pattern_desc=分支保护的名称匹配表达式。语法请参阅 <a href="%s">文档</a> 。如:main, release/** +settings.protect_patterns=表达式 +settings.protect_protected_file_patterns=受保护的文件表达式(使用分号「;」分隔): +settings.protect_protected_file_patterns_desc=即使用户有权添加、编辑或删除此分支中的文件,也不允许直接更改受保护的文件。 可以使用分号「;」分隔多个表达式。 见<a href='%[1]s'>%[2]s</a>文档了解表达式语法。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。 +settings.protect_unprotected_file_patterns=不受保护的文件表达式(使用分号「;」分隔): +settings.protect_unprotected_file_patterns_desc=如果用户有写权限,则允许直接更改的不受保护的文件,以绕过推送限制。可以使用分号分隔多个表达式「;」。 见 <a href='%[1]s'>%[2]s</a> 文档了解表达式语法。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。 settings.add_protected_branch=启用保护 settings.delete_protected_branch=禁用保护 settings.update_protect_branch_success=分支保护规则「%s」更新成功。 @@ -2537,15 +2537,15 @@ settings.protected_branch_required_rule_name=必须填写规则名称 settings.protected_branch_duplicate_rule_name=规则名称已存在 settings.protected_branch_required_approvals_min=所需的审批数不能为负数。 settings.tags=标签 -settings.tags.protection=Git 标签保护 -settings.tags.protection.pattern=Git 标签表达式 +settings.tags.protection=标签保护 +settings.tags.protection.pattern=标签表达式 settings.tags.protection.allowed=允许列表 settings.tags.protection.allowed.users=允许的账号 settings.tags.protection.allowed.teams=允许的团队 settings.tags.protection.allowed.noone=无 -settings.tags.protection.create=保护 Git 标签 -settings.tags.protection.none=没有受保护的 Git 标签。 -settings.tags.protection.pattern.description=您可以使用单个名称或 glob 模式匹配或正则表达式来匹配多个标签。了解详情请访问 <a target="_blank" rel="noopener" href="%s">保护 Git 标签指南</a>。 +settings.tags.protection.create=保护标签 +settings.tags.protection.none=没有受保护的标签。 +settings.tags.protection.pattern.description=您可以使用单个名称或 glob 表达式匹配或正则表达式来匹配多个标签。了解详情请访问 <a target="_blank" rel="noopener" href="%s">保护标签指南</a>。 settings.bot_token=Bot 令牌 settings.chat_id=聊天 ID settings.thread_id=线程 ID @@ -2571,7 +2571,7 @@ settings.archive.success=仓库已成功归档。 settings.archive.error=仓库在归档时出现异常。请通过日志获取详细信息。 settings.archive.error_ismirror=请不要对镜像仓库归档,谢谢! settings.archive.branchsettings_unavailable=已归档仓库无法进行分支设置。 -settings.archive.tagsettings_unavailable=已归档仓库的 Git 标签设置不可用。 +settings.archive.tagsettings_unavailable=已归档仓库的标签设置不可用。 settings.archive.mirrors_unavailable=如果仓库已归档,镜像将不可用。 settings.unarchive.button=撤销仓库归档 settings.unarchive.header=撤销此仓库归档 @@ -2672,7 +2672,7 @@ diff.submodule_updated=子模块 %[1]s 已更新:%[2]s releases.desc=跟踪项目版本和下载。 release.releases=发布 release.detail=发布详情 -release.tags=Git 标签 +release.tags=标签 release.new_release=发布新版 release.draft=草稿 release.prerelease=预发布 @@ -2701,16 +2701,16 @@ release.publish=发布版本 release.save_draft=保存草稿 release.edit_release=保存此次发布 release.delete_release=删除发布 -release.delete_tag=删除 Git 标签 +release.delete_tag=删除标签 release.deletion=删除发布 -release.deletion_desc=删除版本发布只会从 Gitea 中移除。这不会影响 Git 的标签以及您仓库的内容和历史。是否继续? +release.deletion_desc=删除发布只会从 Gitea 中移除发布。这不会影响 Git 的标签以及您仓库的内容和历史。是否继续? release.deletion_success=该发布已删除。 -release.deletion_tag_desc=将从仓库中删除此 Git 标签。仓库内容和历史记录保持不变。继续吗? -release.deletion_tag_success=该 Git 标签已删除。 +release.deletion_tag_desc=将从仓库中删除此标签。仓库内容和历史记录保持不变。继续吗? +release.deletion_tag_success=该标签已删除。 release.tag_name_already_exist=使用此标签名称的发布已经存在。 release.tag_name_invalid=标签名称无效。 -release.tag_name_protected=Git 标签名称已受保护。 -release.tag_already_exist=此 Git 标签名称已存在。 +release.tag_name_protected=标签名已受保护。 +release.tag_already_exist=此标签名已存在。 release.downloads=下载附件 release.download_count=下载:%s release.add_tag_msg=使用发布的标题和内容作为标签消息。 @@ -2809,7 +2809,7 @@ team_unit_desc=允许访问仓库单元 team_unit_disabled=(已禁用) form.name_reserved=组织名称「%s」是保留的。 -form.name_pattern_not_allowed=仓库名称中不允许使用「%s」。 +form.name_pattern_not_allowed=组织名中不允许使用「%s」格式。 form.create_org_not_allowed=此账号禁止创建组织 settings=组织设置 @@ -3305,7 +3305,7 @@ config.db_type=类型 config.db_host=主机 config.db_name=数据库名称 config.db_user=用户名 -config.db_schema=架构模式 +config.db_schema=架构 config.db_ssl_mode=SSL config.db_path=数据库路径 @@ -3476,10 +3476,10 @@ rename_repo=重命名仓库 <code>%[1]s</code> 为 <a href="%[2]s">%[3]s</a> commit_repo=推送到了仓库 <a href="%[1]s">%[4]s</a> 的 <a href="%[2]s">%[3]s</a> 分支 create_issue=`创建了工单 <a href="%[1]s">%[3]s#%[2]s</a>` close_issue=`关闭了工单 <a href="%[1]s">%[3]s#%[2]s</a>` -reopen_issue=`重新开启了工单 <a href="%[1]s">%[3]s#%[2]s</a>` +reopen_issue=`重新打开了工单 <a href="%[1]s">%[3]s#%[2]s</a>` create_pull_request=`创建了合并请求 <a href="%[1]s">%[3]s#%[2]s</a>` close_pull_request=`关闭了合并请求 <a href="%[1]s">%[3]s#%[2]s</a>` -reopen_pull_request=`重新开启了合并请求 <a href="%[1]s">%[3]s#%[2]s</a>` +reopen_pull_request=`重新打开了合并请求 <a href="%[1]s">%[3]s#%[2]s</a>` comment_issue=`评论了工单 <a href="%[1]s">%[3]s#%[2]s</a>` comment_pull=`评论了合并请求 <a href="%[1]s">%[3]s#%[2]s</a>` merge_pull_request=`合并了合并请求 <a href="%[1]s">%[3]s#%[2]s</a>` @@ -3704,7 +3704,7 @@ owner.settings.cleanuprules.preview=清理规则预览 owner.settings.cleanuprules.preview.overview=%d 个软件包计划被删除。 owner.settings.cleanuprules.preview.none=清理规则与任何软件包都不匹配。 owner.settings.cleanuprules.enabled=启用 -owner.settings.cleanuprules.pattern_full_match=应用规则到完整软件包名称 +owner.settings.cleanuprules.pattern_full_match=应用表达式到完整软件包名称 owner.settings.cleanuprules.keep.title=与这些规则相匹配的版本即使与下面的删除规则相匹配,也将予以保留。 owner.settings.cleanuprules.keep.count=保留最新的 owner.settings.cleanuprules.keep.count.1=每个软件包1个版本 diff --git a/package-lock.json b/package-lock.json index 8a2f3e0db7..59ce5b33e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -117,7 +117,7 @@ "vue-tsc": "2.2.10" }, "engines": { - "node": ">= 18.0.0" + "node": ">= 20.0.0" } }, "node_modules/@alloc/quick-lru": { diff --git a/package.json b/package.json index 96ade30f37..4faf34900a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "type": "module", "engines": { - "node": ">= 18.0.0" + "node": ">= 20.0.0" }, "dependencies": { "@citation-js/core": "0.7.18", diff --git a/routers/web/misc/misc.go b/routers/web/misc/misc.go index d42afafe9e..59b97c1717 100644 --- a/routers/web/misc/misc.go +++ b/routers/web/misc/misc.go @@ -20,7 +20,7 @@ func SSHInfo(rw http.ResponseWriter, req *http.Request) { return } rw.Header().Set("content-type", "text/json;charset=UTF-8") - _, err := rw.Write([]byte(`{"type":"gitea","version":1}`)) + _, err := rw.Write([]byte(`{"type":"agit","version":1}`)) if err != nil { log.Error("fail to write result: err: %v", err) rw.WriteHeader(http.StatusInternalServerError) diff --git a/routers/web/repo/view_file.go b/routers/web/repo/view_file.go index 3df6051975..f43433fb0d 100644 --- a/routers/web/repo/view_file.go +++ b/routers/web/repo/view_file.go @@ -140,13 +140,6 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["LFSLockHint"] = ctx.Tr("repo.editor.this_file_locked") } - // Assume file is not editable first. - if fInfo.isLFSFile { - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_lfs_files") - } else if !isRepresentableAsText { - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files") - } - // read all needed attributes which will be used later // there should be no performance different between reading 2 or 4 here attrsMap, err := attribute.CheckAttributes(ctx, ctx.Repo.GitRepo, ctx.Repo.CommitID, attribute.CheckAttributeOpts{ @@ -243,21 +236,6 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["FileContent"] = fileContent ctx.Data["LineEscapeStatus"] = statuses } - if !fInfo.isLFSFile { - if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { - if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID { - ctx.Data["CanEditFile"] = false - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.this_file_locked") - } else { - ctx.Data["CanEditFile"] = true - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file") - } - } else if !ctx.Repo.RefFullName.IsBranch() { - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch") - } else if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) { - ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit") - } - } case fInfo.st.IsPDF(): ctx.Data["IsPDFFile"] = true @@ -307,17 +285,49 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) { } } - if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { - if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID { - ctx.Data["CanDeleteFile"] = false - ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.this_file_locked") - } else { - ctx.Data["CanDeleteFile"] = true - ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file") - } - } else if !ctx.Repo.RefFullName.IsBranch() { + prepareToRenderButtons(ctx, fInfo.isLFSFile, isRepresentableAsText, lfsLock) +} + +func prepareToRenderButtons(ctx *context.Context, isLFSFile, isRepresentableAsText bool, lfsLock *git_model.LFSLock) { + // archived or mirror repository, the buttons should not be shown + if ctx.Repo.Repository.IsArchived || !ctx.Repo.Repository.CanEnableEditor() { + return + } + + // The buttons should not be shown if it's not a branch + if !ctx.Repo.RefFullName.IsBranch() { + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch") ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch") - } else if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) { + return + } + + if isLFSFile { + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_lfs_files") + } else if !isRepresentableAsText { + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files") + } + + if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) { + if !isLFSFile { // lfs file cannot be edited after fork + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit") + } ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access") + return + } + + // it's a lfs file and the user is not the owner of the lock + if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID { + ctx.Data["CanEditFile"] = false + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.this_file_locked") + ctx.Data["CanDeleteFile"] = false + ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.this_file_locked") + return + } + + if !isLFSFile { // lfs file cannot be edited + ctx.Data["CanEditFile"] = true + ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file") } + ctx.Data["CanDeleteFile"] = true + ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file") } diff --git a/services/context/repo.go b/services/context/repo.go index dafa398dc8..32d54c88ff 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -123,7 +123,8 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use sign, keyID, _, err := asymkey_service.SignCRUDAction(ctx, r.Repository.RepoPath(), doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName) - canCommit := r.CanEnableEditor(ctx, doer) && userCanPush + canEnableEditor := r.CanEnableEditor(ctx, doer) + canCommit := canEnableEditor && userCanPush if requireSigned { canCommit = canCommit && sign } @@ -139,7 +140,7 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use return CanCommitToBranchResults{ CanCommitToBranch: canCommit, - EditorEnabled: r.CanEnableEditor(ctx, doer), + EditorEnabled: canEnableEditor, UserCanPush: userCanPush, RequireSigned: requireSigned, WillSign: sign, diff --git a/web_src/fomantic/build/components/dropdown.js b/web_src/fomantic/build/components/dropdown.js index 9d0e07b33b..85530c7991 100644 --- a/web_src/fomantic/build/components/dropdown.js +++ b/web_src/fomantic/build/components/dropdown.js @@ -525,6 +525,7 @@ $.fn.dropdown = function(parameters) { return true; } if(settings.onShow.call(element) !== false) { + settings.onAfterFiltered.call(element); // GITEA-PATCH: callback to correctly handle the filtered items module.animate.show(function() { if( module.can.click() ) { module.bind.intent(); diff --git a/web_src/js/features/common-fetch-action.ts b/web_src/js/features/common-fetch-action.ts index 2da481e521..a4a69540a8 100644 --- a/web_src/js/features/common-fetch-action.ts +++ b/web_src/js/features/common-fetch-action.ts @@ -56,8 +56,12 @@ async function fetchActionDoRequest(actionElem: HTMLElement, url: string, opt: R actionElem.classList.remove('is-loading', 'loading-icon-2px'); } -async function formFetchAction(formEl: HTMLFormElement, e: SubmitEvent) { +async function onFormFetchActionSubmit(formEl: HTMLFormElement, e: SubmitEvent) { e.preventDefault(); + await submitFormFetchAction(formEl, submitEventSubmitter(e)); +} + +export async function submitFormFetchAction(formEl: HTMLFormElement, formSubmitter?: HTMLElement) { if (formEl.classList.contains('is-loading')) return; formEl.classList.add('is-loading'); @@ -68,7 +72,6 @@ async function formFetchAction(formEl: HTMLFormElement, e: SubmitEvent) { const formMethod = formEl.getAttribute('method') || 'get'; const formActionUrl = formEl.getAttribute('action'); const formData = new FormData(formEl); - const formSubmitter = submitEventSubmitter(e); const [submitterName, submitterValue] = [formSubmitter?.getAttribute('name'), formSubmitter?.getAttribute('value')]; if (submitterName) { formData.append(submitterName, submitterValue || ''); @@ -96,7 +99,7 @@ async function formFetchAction(formEl: HTMLFormElement, e: SubmitEvent) { await fetchActionDoRequest(formEl, reqUrl, reqOpt); } -async function linkAction(el: HTMLElement, e: Event) { +async function onLinkActionClick(el: HTMLElement, e: Event) { // A "link-action" can post AJAX request to its "data-url" // Then the browser is redirected to: the "redirect" in response, or "data-redirect" attribute, or current URL by reloading. // If the "link-action" has "data-modal-confirm" attribute, a confirm modal dialog will be shown before taking action. @@ -126,6 +129,6 @@ async function linkAction(el: HTMLElement, e: Event) { } export function initGlobalFetchAction() { - addDelegatedEventListener(document, 'submit', '.form-fetch-action', formFetchAction); - addDelegatedEventListener(document, 'click', '.link-action', linkAction); + addDelegatedEventListener(document, 'submit', '.form-fetch-action', onFormFetchActionSubmit); + addDelegatedEventListener(document, 'click', '.link-action', onLinkActionClick); } diff --git a/web_src/js/features/comp/LabelEdit.ts b/web_src/js/features/comp/LabelEdit.ts index a5bb750cdb..141c5eecfe 100644 --- a/web_src/js/features/comp/LabelEdit.ts +++ b/web_src/js/features/comp/LabelEdit.ts @@ -1,5 +1,6 @@ import {toggleElem} from '../../utils/dom.ts'; import {fomanticQuery} from '../../modules/fomantic/base.ts'; +import {submitFormFetchAction} from '../common-fetch-action.ts'; function nameHasScope(name: string): boolean { return /.*[^/]\/[^/].*/.test(name); @@ -70,7 +71,7 @@ export function initCompLabelEdit(pageSelector: string) { form.reportValidity(); return false; } - form.dispatchEvent(new Event('submit', {bubbles: true})); + submitFormFetchAction(form); }, }).modal('show'); }; |