mirror of
https://github.com/go-gitea/gitea
synced 2025-02-28 07:44:19 +00:00
Merge branch 'main' into Badge
This commit is contained in:
commit
401d2572f9
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "Gitea DevContainer",
|
"name": "Gitea DevContainer",
|
||||||
"image": "mcr.microsoft.com/devcontainers/go:1.22-bullseye",
|
"image": "mcr.microsoft.com/devcontainers/go:1.23-bookworm",
|
||||||
"features": {
|
"features": {
|
||||||
// installs nodejs into container
|
// installs nodejs into container
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "20"
|
"version": "20"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/git-lfs:1.1.0": {},
|
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {},
|
||||||
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
|
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
|
||||||
"ghcr.io/devcontainers/features/python:1": {
|
"ghcr.io/devcontainers/features/python:1": {
|
||||||
"version": "3.12"
|
"version": "3.12"
|
||||||
|
3
.github/labeler.yml
vendored
3
.github/labeler.yml
vendored
@ -70,10 +70,11 @@ modifies/go:
|
|||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
|
|
||||||
modifies/js:
|
modifies/frontend:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
- "**/*.js"
|
- "**/*.js"
|
||||||
|
- "**/*.ts"
|
||||||
- "**/*.vue"
|
- "**/*.vue"
|
||||||
|
|
||||||
docs-update-needed:
|
docs-update-needed:
|
||||||
|
4
.github/workflows/pull-db-tests.yml
vendored
4
.github/workflows/pull-db-tests.yml
vendored
@ -198,7 +198,9 @@ jobs:
|
|||||||
test-mssql:
|
test-mssql:
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
# specifying the version of ubuntu in use as mssql fails on newer kernels
|
||||||
|
# pending resolution from vendor
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
services:
|
services:
|
||||||
mssql:
|
mssql:
|
||||||
image: mcr.microsoft.com/mssql/server:2017-latest
|
image: mcr.microsoft.com/mssql/server:2017-latest
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Build stage
|
# Build stage
|
||||||
FROM docker.io/library/golang:1.22-alpine3.20 AS build-env
|
FROM docker.io/library/golang:1.23-alpine3.20 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY=${GOPROXY:-direct}
|
ENV GOPROXY=${GOPROXY:-direct}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Build stage
|
# Build stage
|
||||||
FROM docker.io/library/golang:1.22-alpine3.20 AS build-env
|
FROM docker.io/library/golang:1.23-alpine3.20 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY=${GOPROXY:-direct}
|
ENV GOPROXY=${GOPROXY:-direct}
|
||||||
|
8
Makefile
8
Makefile
@ -23,12 +23,12 @@ SHASUM ?= shasum -a 256
|
|||||||
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
|
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
|
||||||
COMMA := ,
|
COMMA := ,
|
||||||
|
|
||||||
XGO_VERSION := go-1.22.x
|
XGO_VERSION := go-1.23.x
|
||||||
|
|
||||||
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
||||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
|
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
|
||||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0
|
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
|
||||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.59.0
|
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.3
|
||||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
|
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
|
||||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
|
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
|
||||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
|
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
|
||||||
@ -179,6 +179,7 @@ TEST_PGSQL_DBNAME ?= testgitea
|
|||||||
TEST_PGSQL_USERNAME ?= postgres
|
TEST_PGSQL_USERNAME ?= postgres
|
||||||
TEST_PGSQL_PASSWORD ?= postgres
|
TEST_PGSQL_PASSWORD ?= postgres
|
||||||
TEST_PGSQL_SCHEMA ?= gtestschema
|
TEST_PGSQL_SCHEMA ?= gtestschema
|
||||||
|
TEST_MINIO_ENDPOINT ?= minio:9000
|
||||||
TEST_MSSQL_HOST ?= mssql:1433
|
TEST_MSSQL_HOST ?= mssql:1433
|
||||||
TEST_MSSQL_DBNAME ?= gitea
|
TEST_MSSQL_DBNAME ?= gitea
|
||||||
TEST_MSSQL_USERNAME ?= sa
|
TEST_MSSQL_USERNAME ?= sa
|
||||||
@ -574,6 +575,7 @@ generate-ini-pgsql:
|
|||||||
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
|
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
|
||||||
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
||||||
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
|
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
|
||||||
|
-e 's|{{TEST_MINIO_ENDPOINT}}|${TEST_MINIO_ENDPOINT}|g' \
|
||||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||||
|
79
assets/go-licenses.json
generated
79
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
@ -1,3 +1,6 @@
|
|||||||
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//go:build ignore
|
//go:build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
@ -5,6 +8,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -15,6 +20,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/build/license"
|
||||||
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -77,7 +84,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tr := tar.NewReader(gz)
|
tr := tar.NewReader(gz)
|
||||||
|
aliasesFiles := make(map[string][]string)
|
||||||
for {
|
for {
|
||||||
hdr, err := tr.Next()
|
hdr, err := tr.Next()
|
||||||
|
|
||||||
@ -97,26 +104,73 @@ func main() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(filepath.Base(hdr.Name), "README") {
|
fileBaseName := filepath.Base(hdr.Name)
|
||||||
|
licenseName := strings.TrimSuffix(fileBaseName, ".txt")
|
||||||
|
|
||||||
|
if strings.HasPrefix(fileBaseName, "README") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(filepath.Base(hdr.Name), "deprecated_") {
|
if strings.HasPrefix(fileBaseName, "deprecated_") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out, err := os.Create(path.Join(destination, strings.TrimSuffix(filepath.Base(hdr.Name), ".txt")))
|
out, err := os.Create(path.Join(destination, licenseName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to create new file. %s", err)
|
log.Fatalf("Failed to create new file. %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
|
|
||||||
if _, err := io.Copy(out, tr); err != nil {
|
// some license files have same content, so we need to detect these files and create a convert map into a json file
|
||||||
|
// Later we use this convert map to avoid adding same license content with different license name
|
||||||
|
h := md5.New()
|
||||||
|
// calculate md5 and write file in the same time
|
||||||
|
r := io.TeeReader(tr, h)
|
||||||
|
if _, err := io.Copy(out, r); err != nil {
|
||||||
log.Fatalf("Failed to write new file. %s", err)
|
log.Fatalf("Failed to write new file. %s", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Written %s\n", out.Name())
|
fmt.Printf("Written %s\n", out.Name())
|
||||||
|
|
||||||
|
md5 := hex.EncodeToString(h.Sum(nil))
|
||||||
|
aliasesFiles[md5] = append(aliasesFiles[md5], licenseName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate convert license name map
|
||||||
|
licenseAliases := make(map[string]string)
|
||||||
|
for _, fileNames := range aliasesFiles {
|
||||||
|
if len(fileNames) > 1 {
|
||||||
|
licenseName := license.GetLicenseNameFromAliases(fileNames)
|
||||||
|
if licenseName == "" {
|
||||||
|
// license name should not be empty as expected
|
||||||
|
// if it is empty, we need to rewrite the logic of GetLicenseNameFromAliases
|
||||||
|
log.Fatalf("GetLicenseNameFromAliases: license name is empty")
|
||||||
|
}
|
||||||
|
for _, fileName := range fileNames {
|
||||||
|
licenseAliases[fileName] = licenseName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// save convert license name map to file
|
||||||
|
b, err := json.Marshal(licenseAliases)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create json bytes. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
licenseAliasesDestination := filepath.Join(destination, "etc", "license-aliases.json")
|
||||||
|
if err := os.MkdirAll(filepath.Dir(licenseAliasesDestination), 0o755); err != nil {
|
||||||
|
log.Fatalf("Failed to create directory for license aliases json file. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(licenseAliasesDestination)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create license aliases json file. %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
if _, err = f.Write(b); err != nil {
|
||||||
|
log.Fatalf("Failed to write license aliases json file. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("Done")
|
fmt.Println("Done")
|
||||||
}
|
}
|
||||||
|
41
build/license/aliasgenerator.go
Normal file
41
build/license/aliasgenerator.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package license
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func GetLicenseNameFromAliases(fnl []string) string {
|
||||||
|
if len(fnl) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
shortestItem := func(list []string) string {
|
||||||
|
s := list[0]
|
||||||
|
for _, l := range list[1:] {
|
||||||
|
if len(l) < len(s) {
|
||||||
|
s = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
allHasPrefix := func(list []string, s string) bool {
|
||||||
|
for _, l := range list {
|
||||||
|
if !strings.HasPrefix(l, s) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sl := shortestItem(fnl)
|
||||||
|
slv := strings.Split(sl, "-")
|
||||||
|
var result string
|
||||||
|
for i := len(slv); i >= 0; i-- {
|
||||||
|
result = strings.Join(slv[:i], "-")
|
||||||
|
if allHasPrefix(fnl, result) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
39
build/license/aliasgenerator_test.go
Normal file
39
build/license/aliasgenerator_test.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package license
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetLicenseNameFromAliases(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
target string
|
||||||
|
inputs []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// real case which you can find in license-aliases.json
|
||||||
|
target: "AGPL-1.0",
|
||||||
|
inputs: []string{
|
||||||
|
"AGPL-1.0-only",
|
||||||
|
"AGPL-1.0-or-late",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "",
|
||||||
|
inputs: []string{
|
||||||
|
"APSL-1.0",
|
||||||
|
"AGPL-1.0-only",
|
||||||
|
"AGPL-1.0-or-late",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
result := GetLicenseNameFromAliases(tt.inputs)
|
||||||
|
assert.Equal(t, result, tt.target)
|
||||||
|
}
|
||||||
|
}
|
@ -158,7 +158,7 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
IsRestricted: restricted,
|
IsRestricted: restricted,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil {
|
if err := user_model.CreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
|
||||||
return fmt.Errorf("CreateUser: %w", err)
|
return fmt.Errorf("CreateUser: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,14 +542,14 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
|
|
||||||
index := bytes.IndexByte(rs.Data, byte(0))
|
index := bytes.IndexByte(rs.Data, byte(0))
|
||||||
if index >= len(rs.Data) {
|
if index >= len(rs.Data) {
|
||||||
return fail(ctx, "Protocol: format error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
return fail(ctx, "Protocol: format error", "pkt-line: format error %s", rs.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
if len(rs.Data) == 10 && rs.Data[9] == '\n' {
|
if len(rs.Data) == 10 && rs.Data[9] == '\n' {
|
||||||
index = 9
|
index = 9
|
||||||
} else {
|
} else {
|
||||||
return fail(ctx, "Protocol: format error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
return fail(ctx, "Protocol: format error", "pkt-line: format error %s", rs.Data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
140
cmd/serv.go
140
cmd/serv.go
@ -20,8 +20,10 @@ import (
|
|||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
"code.gitea.io/gitea/modules/lfstransfer"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/pprof"
|
"code.gitea.io/gitea/modules/pprof"
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
@ -36,7 +38,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
lfsAuthenticateVerb = "git-lfs-authenticate"
|
verbUploadPack = "git-upload-pack"
|
||||||
|
verbUploadArchive = "git-upload-archive"
|
||||||
|
verbReceivePack = "git-receive-pack"
|
||||||
|
verbLfsAuthenticate = "git-lfs-authenticate"
|
||||||
|
verbLfsTransfer = "git-lfs-transfer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdServ represents the available serv sub-command.
|
// CmdServ represents the available serv sub-command.
|
||||||
@ -73,12 +79,18 @@ func setup(ctx context.Context, debug bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
allowedCommands = map[string]perm.AccessMode{
|
// keep getAccessMode() in sync
|
||||||
"git-upload-pack": perm.AccessModeRead,
|
allowedCommands = container.SetOf(
|
||||||
"git-upload-archive": perm.AccessModeRead,
|
verbUploadPack,
|
||||||
"git-receive-pack": perm.AccessModeWrite,
|
verbUploadArchive,
|
||||||
lfsAuthenticateVerb: perm.AccessModeNone,
|
verbReceivePack,
|
||||||
}
|
verbLfsAuthenticate,
|
||||||
|
verbLfsTransfer,
|
||||||
|
)
|
||||||
|
allowedCommandsLfs = container.SetOf(
|
||||||
|
verbLfsAuthenticate,
|
||||||
|
verbLfsTransfer,
|
||||||
|
)
|
||||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -124,6 +136,45 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAccessMode(verb, lfsVerb string) perm.AccessMode {
|
||||||
|
switch verb {
|
||||||
|
case verbUploadPack, verbUploadArchive:
|
||||||
|
return perm.AccessModeRead
|
||||||
|
case verbReceivePack:
|
||||||
|
return perm.AccessModeWrite
|
||||||
|
case verbLfsAuthenticate, verbLfsTransfer:
|
||||||
|
switch lfsVerb {
|
||||||
|
case "upload":
|
||||||
|
return perm.AccessModeWrite
|
||||||
|
case "download":
|
||||||
|
return perm.AccessModeRead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// should be unreachable
|
||||||
|
return perm.AccessModeNone
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLFSAuthToken(ctx context.Context, lfsVerb string, results *private.ServCommandResults) (string, error) {
|
||||||
|
now := time.Now()
|
||||||
|
claims := lfs.Claims{
|
||||||
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
|
ExpiresAt: jwt.NewNumericDate(now.Add(setting.LFS.HTTPAuthExpiry)),
|
||||||
|
NotBefore: jwt.NewNumericDate(now),
|
||||||
|
},
|
||||||
|
RepoID: results.RepoID,
|
||||||
|
Op: lfsVerb,
|
||||||
|
UserID: results.UserID,
|
||||||
|
}
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
|
||||||
|
// Sign and get the complete encoded token as a string using the secret
|
||||||
|
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", fail(ctx, "Failed to sign JWT Token", "Failed to sign JWT token: %v", err)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Bearer %s", tokenString), nil
|
||||||
|
}
|
||||||
|
|
||||||
func runServ(c *cli.Context) error {
|
func runServ(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -143,6 +194,12 @@ func runServ(c *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
_ = fail(ctx, "Internal Server Error", "Panic: %v\n%s", err, log.Stack(2))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
keys := strings.Split(c.Args().First(), "-")
|
keys := strings.Split(c.Args().First(), "-")
|
||||||
if len(keys) != 2 || keys[0] != "key" {
|
if len(keys) != 2 || keys[0] != "key" {
|
||||||
return fail(ctx, "Key ID format error", "Invalid key argument: %s", c.Args().First())
|
return fail(ctx, "Key ID format error", "Invalid key argument: %s", c.Args().First())
|
||||||
@ -189,21 +246,9 @@ func runServ(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
verb := words[0]
|
verb := words[0]
|
||||||
repoPath := words[1]
|
repoPath := strings.TrimPrefix(words[1], "/")
|
||||||
if repoPath[0] == '/' {
|
|
||||||
repoPath = repoPath[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
var lfsVerb string
|
var lfsVerb string
|
||||||
if verb == lfsAuthenticateVerb {
|
|
||||||
if !setting.LFS.StartServer {
|
|
||||||
return fail(ctx, "Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(words) > 2 {
|
|
||||||
lfsVerb = words[2]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rr := strings.SplitN(repoPath, "/", 2)
|
rr := strings.SplitN(repoPath, "/", 2)
|
||||||
if len(rr) != 2 {
|
if len(rr) != 2 {
|
||||||
@ -240,53 +285,52 @@ func runServ(c *cli.Context) error {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedMode, has := allowedCommands[verb]
|
if allowedCommands.Contains(verb) {
|
||||||
if !has {
|
if allowedCommandsLfs.Contains(verb) {
|
||||||
|
if !setting.LFS.StartServer {
|
||||||
|
return fail(ctx, "Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
|
||||||
|
}
|
||||||
|
if verb == verbLfsTransfer && !setting.LFS.AllowPureSSH {
|
||||||
|
return fail(ctx, "Unknown git command", "LFS SSH transfer connection denied, pure SSH protocol is disabled")
|
||||||
|
}
|
||||||
|
if len(words) > 2 {
|
||||||
|
lfsVerb = words[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return fail(ctx, "Unknown git command", "Unknown git command %s", verb)
|
return fail(ctx, "Unknown git command", "Unknown git command %s", verb)
|
||||||
}
|
}
|
||||||
|
|
||||||
if verb == lfsAuthenticateVerb {
|
requestedMode := getAccessMode(verb, lfsVerb)
|
||||||
if lfsVerb == "upload" {
|
|
||||||
requestedMode = perm.AccessModeWrite
|
|
||||||
} else if lfsVerb == "download" {
|
|
||||||
requestedMode = perm.AccessModeRead
|
|
||||||
} else {
|
|
||||||
return fail(ctx, "Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
|
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
|
||||||
if extra.HasError() {
|
if extra.HasError() {
|
||||||
return fail(ctx, extra.UserMsg, "ServCommand failed: %s", extra.Error)
|
return fail(ctx, extra.UserMsg, "ServCommand failed: %s", extra.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LFS SSH protocol
|
||||||
|
if verb == verbLfsTransfer {
|
||||||
|
token, err := getLFSAuthToken(ctx, lfsVerb, results)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return lfstransfer.Main(ctx, repoPath, lfsVerb, token)
|
||||||
|
}
|
||||||
|
|
||||||
// LFS token authentication
|
// LFS token authentication
|
||||||
if verb == lfsAuthenticateVerb {
|
if verb == verbLfsAuthenticate {
|
||||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
||||||
|
|
||||||
now := time.Now()
|
token, err := getLFSAuthToken(ctx, lfsVerb, results)
|
||||||
claims := lfs.Claims{
|
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
|
||||||
ExpiresAt: jwt.NewNumericDate(now.Add(setting.LFS.HTTPAuthExpiry)),
|
|
||||||
NotBefore: jwt.NewNumericDate(now),
|
|
||||||
},
|
|
||||||
RepoID: results.RepoID,
|
|
||||||
Op: lfsVerb,
|
|
||||||
UserID: results.UserID,
|
|
||||||
}
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
||||||
|
|
||||||
// Sign and get the complete encoded token as a string using the secret
|
|
||||||
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fail(ctx, "Failed to sign JWT Token", "Failed to sign JWT token: %v", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenAuthentication := &git_model.LFSTokenResponse{
|
tokenAuthentication := &git_model.LFSTokenResponse{
|
||||||
Header: make(map[string]string),
|
Header: make(map[string]string),
|
||||||
Href: url,
|
Href: url,
|
||||||
}
|
}
|
||||||
tokenAuthentication.Header["Authorization"] = fmt.Sprintf("Bearer %s", tokenString)
|
tokenAuthentication.Header["Authorization"] = token
|
||||||
|
|
||||||
enc := json.NewEncoder(os.Stdout)
|
enc := json.NewEncoder(os.Stdout)
|
||||||
err = enc.Encode(tokenAuthentication)
|
err = enc.Encode(tokenAuthentication)
|
||||||
|
@ -306,6 +306,8 @@ RUN_USER = ; git
|
|||||||
;; Enables git-lfs support. true or false, default is false.
|
;; Enables git-lfs support. true or false, default is false.
|
||||||
;LFS_START_SERVER = false
|
;LFS_START_SERVER = false
|
||||||
;;
|
;;
|
||||||
|
;; Enables git-lfs SSH protocol support. true or false, default is false.
|
||||||
|
;LFS_ALLOW_PURE_SSH = false
|
||||||
;;
|
;;
|
||||||
;; LFS authentication secret, change this yourself
|
;; LFS authentication secret, change this yourself
|
||||||
;LFS_JWT_SECRET =
|
;LFS_JWT_SECRET =
|
||||||
@ -507,6 +509,9 @@ INTERNAL_TOKEN =
|
|||||||
;; stemming from cached/logged plain-text API tokens.
|
;; stemming from cached/logged plain-text API tokens.
|
||||||
;; In future releases, this will become the default behavior
|
;; In future releases, this will become the default behavior
|
||||||
;DISABLE_QUERY_AUTH_TOKEN = false
|
;DISABLE_QUERY_AUTH_TOKEN = false
|
||||||
|
;;
|
||||||
|
;; On user registration, record the IP address and user agent of the user to help identify potential abuse.
|
||||||
|
;; RECORD_USER_SIGNUP_METADATA = false
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@ -523,7 +528,8 @@ INTERNAL_TOKEN =
|
|||||||
;; HMAC to encode urls with, it **is required** if camo is enabled.
|
;; HMAC to encode urls with, it **is required** if camo is enabled.
|
||||||
;HMAC_KEY =
|
;HMAC_KEY =
|
||||||
;; Set to true to use camo for https too lese only non https urls are proxyed
|
;; Set to true to use camo for https too lese only non https urls are proxyed
|
||||||
;ALLWAYS = false
|
;; ALLWAYS is deprecated and will be removed in the future
|
||||||
|
;ALWAYS = false
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@ -1501,6 +1507,8 @@ LEVEL = Info
|
|||||||
;; - manage_gpg_keys: a user cannot configure gpg keys
|
;; - manage_gpg_keys: a user cannot configure gpg keys
|
||||||
;; - manage_mfa: a user cannot configure mfa devices
|
;; - manage_mfa: a user cannot configure mfa devices
|
||||||
;; - manage_credentials: a user cannot configure emails, passwords, or openid
|
;; - manage_credentials: a user cannot configure emails, passwords, or openid
|
||||||
|
;; - change_username: a user cannot change their username
|
||||||
|
;; - change_full_name: a user cannot change their full name
|
||||||
;;EXTERNAL_USER_DISABLE_FEATURES =
|
;;EXTERNAL_USER_DISABLE_FEATURES =
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@ -2692,7 +2700,7 @@ LEVEL = Info
|
|||||||
;; It's always recommended to use compression when using local disk as log storage if CPU or memory is not a bottleneck.
|
;; It's always recommended to use compression when using local disk as log storage if CPU or memory is not a bottleneck.
|
||||||
;; And for object storage services like S3, which is billed for requests, it would cause extra 2 times of get requests for each log view.
|
;; And for object storage services like S3, which is billed for requests, it would cause extra 2 times of get requests for each log view.
|
||||||
;; But it will save storage space and network bandwidth, so it's still recommended to use compression.
|
;; But it will save storage space and network bandwidth, so it's still recommended to use compression.
|
||||||
;LOG_COMPRESSION = none
|
;LOG_COMPRESSION = zstd
|
||||||
;; Default artifact retention time in days. Artifacts could have their own retention periods by setting the `retention-days` option in `actions/upload-artifact` step.
|
;; Default artifact retention time in days. Artifacts could have their own retention periods by setting the `retention-days` option in `actions/upload-artifact` step.
|
||||||
;ARTIFACT_RETENTION_DAYS = 90
|
;ARTIFACT_RETENTION_DAYS = 90
|
||||||
;; Timeout to stop the task which have running status, but haven't been updated for a long time
|
;; Timeout to stop the task which have running status, but haven't been updated for a long time
|
||||||
|
44
go.mod
44
go.mod
@ -1,6 +1,11 @@
|
|||||||
module code.gitea.io/gitea
|
module code.gitea.io/gitea
|
||||||
|
|
||||||
go 1.22
|
go 1.23
|
||||||
|
|
||||||
|
// rfc5280 said: "The serial number is an integer assigned by the CA to each certificate."
|
||||||
|
// But some CAs use negative serial number, just relax the check. related:
|
||||||
|
// Default TLS cert uses negative serial number #895 https://github.com/microsoft/mssql-docker/issues/895
|
||||||
|
godebug x509negativeserial=1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.gitea.io/actions-proto-go v0.4.0
|
code.gitea.io/actions-proto-go v0.4.0
|
||||||
@ -23,10 +28,14 @@ require (
|
|||||||
github.com/PuerkitoBio/goquery v1.9.2
|
github.com/PuerkitoBio/goquery v1.9.2
|
||||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
|
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
|
||||||
github.com/alecthomas/chroma/v2 v2.14.0
|
github.com/alecthomas/chroma/v2 v2.14.0
|
||||||
|
github.com/aws/aws-sdk-go v1.43.21
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.30
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/codecommit v1.25.1
|
||||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||||
github.com/blevesearch/bleve/v2 v2.4.2
|
github.com/blevesearch/bleve/v2 v2.4.2
|
||||||
github.com/buildkite/terminal-to-html/v3 v3.12.1
|
github.com/buildkite/terminal-to-html/v3 v3.12.1
|
||||||
github.com/caddyserver/certmagic v0.21.3
|
github.com/caddyserver/certmagic v0.21.3
|
||||||
|
github.com/charmbracelet/git-lfs-transfer v0.2.0
|
||||||
github.com/chi-middleware/proxy v1.1.1
|
github.com/chi-middleware/proxy v1.1.1
|
||||||
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
||||||
github.com/djherbis/buffer v1.2.0
|
github.com/djherbis/buffer v1.2.0
|
||||||
@ -59,6 +68,7 @@ require (
|
|||||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/google/go-github/v61 v61.0.0
|
github.com/google/go-github/v61 v61.0.0
|
||||||
|
github.com/google/licenseclassifier/v2 v2.0.0
|
||||||
github.com/google/pprof v0.0.0-20240618054019-d3b898a103f8
|
github.com/google/pprof v0.0.0-20240618054019-d3b898a103f8
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/feeds v1.2.0
|
github.com/gorilla/feeds v1.2.0
|
||||||
@ -82,7 +92,7 @@ require (
|
|||||||
github.com/mholt/archiver/v3 v3.5.1
|
github.com/mholt/archiver/v3 v3.5.1
|
||||||
github.com/microcosm-cc/bluemonday v1.0.26
|
github.com/microcosm-cc/bluemonday v1.0.26
|
||||||
github.com/microsoft/go-mssqldb v1.7.2
|
github.com/microsoft/go-mssqldb v1.7.2
|
||||||
github.com/minio/minio-go/v7 v7.0.71
|
github.com/minio/minio-go/v7 v7.0.77
|
||||||
github.com/msteinert/pam v1.2.0
|
github.com/msteinert/pam v1.2.0
|
||||||
github.com/nektos/act v0.2.63
|
github.com/nektos/act v0.2.63
|
||||||
github.com/niklasfasching/go-org v1.7.0
|
github.com/niklasfasching/go-org v1.7.0
|
||||||
@ -112,11 +122,11 @@ require (
|
|||||||
github.com/yuin/goldmark-meta v1.1.0
|
github.com/yuin/goldmark-meta v1.1.0
|
||||||
golang.org/x/crypto v0.26.0
|
golang.org/x/crypto v0.26.0
|
||||||
golang.org/x/image v0.18.0
|
golang.org/x/image v0.18.0
|
||||||
golang.org/x/net v0.26.0
|
golang.org/x/net v0.28.0
|
||||||
golang.org/x/oauth2 v0.21.0
|
golang.org/x/oauth2 v0.21.0
|
||||||
golang.org/x/sys v0.23.0
|
golang.org/x/sys v0.24.0
|
||||||
golang.org/x/text v0.17.0
|
golang.org/x/text v0.17.0
|
||||||
golang.org/x/tools v0.22.0
|
golang.org/x/tools v0.24.0
|
||||||
google.golang.org/grpc v1.62.1
|
google.golang.org/grpc v1.62.1
|
||||||
google.golang.org/protobuf v1.34.2
|
google.golang.org/protobuf v1.34.2
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
@ -146,6 +156,10 @@ require (
|
|||||||
github.com/andybalholm/cascadia v1.3.2 // indirect
|
github.com/andybalholm/cascadia v1.3.2 // indirect
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect
|
||||||
|
github.com/aws/smithy-go v1.20.4 // indirect
|
||||||
github.com/aymerick/douceur v0.2.0 // indirect
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||||
@ -185,6 +199,7 @@ require (
|
|||||||
github.com/fatih/color v1.17.0 // indirect
|
github.com/fatih/color v1.17.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
||||||
|
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
|
||||||
github.com/go-ap/errors v0.0.0-20240304112515-6077fa9c17b0 // indirect
|
github.com/go-ap/errors v0.0.0-20240304112515-6077fa9c17b0 // indirect
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||||
@ -192,6 +207,7 @@ require (
|
|||||||
github.com/go-faster/errors v0.7.1 // indirect
|
github.com/go-faster/errors v0.7.1 // indirect
|
||||||
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
|
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-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/analysis v0.23.0 // indirect
|
||||||
github.com/go-openapi/errors v0.22.0 // indirect
|
github.com/go-openapi/errors v0.22.0 // indirect
|
||||||
github.com/go-openapi/inflect v0.21.0 // indirect
|
github.com/go-openapi/inflect v0.21.0 // indirect
|
||||||
@ -249,22 +265,24 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||||
github.com/mschoch/smat v0.2.0 // 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/nwaples/rardecode v1.1.3 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||||
|
github.com/onsi/gomega v1.33.1 // indirect
|
||||||
github.com/paulmach/orb v0.11.1 // indirect
|
github.com/paulmach/orb v0.11.1 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_model v0.6.0 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.50.0 // indirect
|
github.com/prometheus/common v0.55.0 // indirect
|
||||||
github.com/prometheus/procfs v0.13.0 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/rhysd/actionlint v1.7.1 // indirect
|
github.com/rhysd/actionlint v1.7.1 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
github.com/rs/xid v1.5.0 // indirect
|
github.com/rs/xid v1.6.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
@ -300,7 +318,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.27.0 // indirect
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
||||||
golang.org/x/mod v0.18.0 // indirect
|
golang.org/x/mod v0.20.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
|
||||||
@ -313,7 +331,9 @@ 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/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
|
||||||
|
|
||||||
replace github.com/nektos/act => gitea.com/gitea/act v0.259.1
|
replace github.com/nektos/act => gitea.com/gitea/act v0.261.3
|
||||||
|
|
||||||
|
replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0
|
||||||
|
|
||||||
// TODO: This could be removed after https://github.com/mholt/archiver/pull/396 merged
|
// TODO: This could be removed after https://github.com/mholt/archiver/pull/396 merged
|
||||||
replace github.com/mholt/archiver/v3 => github.com/anchore/archiver/v3 v3.5.2
|
replace github.com/mholt/archiver/v3 => github.com/anchore/archiver/v3 v3.5.2
|
||||||
|
78
go.sum
78
go.sum
@ -16,8 +16,10 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
|||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
|
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
|
||||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
|
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
|
||||||
gitea.com/gitea/act v0.259.1 h1:8GG1o/xtUHl3qjn5f0h/2FXrT5ubBn05TJOM5ry+FBw=
|
gitea.com/gitea/act v0.261.3 h1:BhiYpGJQKGq0XMYYICCYAN4KnsEWHyLbA6dxhZwFcV4=
|
||||||
gitea.com/gitea/act v0.259.1/go.mod h1:UxZWRYqQG2Yj4+4OqfGWW5a3HELwejyWFQyU7F1jUD8=
|
gitea.com/gitea/act v0.261.3/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
|
||||||
|
gitea.com/gitea/git-lfs-transfer v0.2.0 h1:baHaNoBSRaeq/xKayEXwiDQtlIjps4Ac/Ll4KqLMB40=
|
||||||
|
gitea.com/gitea/git-lfs-transfer v0.2.0/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits=
|
||||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso=
|
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso=
|
||||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw=
|
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw=
|
||||||
gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
|
gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
|
||||||
@ -109,6 +111,20 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
|
|||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
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 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
|
github.com/aws/aws-sdk-go v1.43.21 h1:E4S2eX3d2gKJyI/ISrcIrSwXwqjIvCK85gtBMt4sAPE=
|
||||||
|
github.com/aws/aws-sdk-go v1.43.21/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.30 h1:aau/oYFtibVovr2rDt8FHlU17BTicFEMAi29V1U+L5Q=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.30/go.mod h1:BPJ/yXV92ZVq6G8uYvbU0gSl8q94UB63nMT5ctNO38g=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/codecommit v1.25.1 h1:mOOALIM4JzhYkq3voCBbmZqmyEVEhHsfasMTbVxLkNs=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/codecommit v1.25.1/go.mod h1:6zf5j3mIUXKM0s2iz5ttR2Qwq+o47D0jotpAyaKgZRA=
|
||||||
|
github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
|
||||||
|
github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
@ -277,6 +293,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
|||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
|
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
|
||||||
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||||
|
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
|
||||||
|
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
|
||||||
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||||
@ -316,6 +334,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
|
|||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||||
|
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.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
|
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
|
||||||
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
|
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
|
||||||
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
|
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
|
||||||
@ -421,6 +441,8 @@ github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47
|
|||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/licenseclassifier/v2 v2.0.0 h1:1Y57HHILNf4m0ABuMVb6xk4vAJYEUO0gDxNpog0pyeA=
|
||||||
|
github.com/google/licenseclassifier/v2 v2.0.0/go.mod h1:cOjbdH0kyC9R22sdQbYsFkto4NGCAc+ZSwbeThazEtM=
|
||||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
github.com/google/pprof v0.0.0-20240618054019-d3b898a103f8 h1:ASJ/LAqdCHOyMYI+dwNxn7Rd8FscNkMyTr1KZU1JI/M=
|
github.com/google/pprof v0.0.0-20240618054019-d3b898a103f8 h1:ASJ/LAqdCHOyMYI+dwNxn7Rd8FscNkMyTr1KZU1JI/M=
|
||||||
github.com/google/pprof v0.0.0-20240618054019-d3b898a103f8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
github.com/google/pprof v0.0.0-20240618054019-d3b898a103f8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||||
@ -504,6 +526,9 @@ github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LF
|
|||||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||||
github.com/jhillyerd/enmime v1.2.0 h1:dIu1IPEymQgoT2dzuB//ttA/xcV40NMPpQtmd4wslHk=
|
github.com/jhillyerd/enmime v1.2.0 h1:dIu1IPEymQgoT2dzuB//ttA/xcV40NMPpQtmd4wslHk=
|
||||||
github.com/jhillyerd/enmime v1.2.0/go.mod h1:FRFuUPCLh8PByQv+8xRcLO9QHqaqTqreYhopv5eyk4I=
|
github.com/jhillyerd/enmime v1.2.0/go.mod h1:FRFuUPCLh8PByQv+8xRcLO9QHqaqTqreYhopv5eyk4I=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
@ -586,8 +611,8 @@ github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
|
|||||||
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
|
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
|
||||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
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/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||||
github.com/minio/minio-go/v7 v7.0.71 h1:No9XfOKTYi6i0GnBj+WZwD8WP5GZfL7n7GOjRqCdAjA=
|
github.com/minio/minio-go/v7 v7.0.77 h1:GaGghJRg9nwDVlNbwYjSDJT1rqltQkBFDsypWX1v3Bw=
|
||||||
github.com/minio/minio-go/v7 v7.0.71/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo=
|
github.com/minio/minio-go/v7 v7.0.77/go.mod h1:AVM3IUN6WwKzmwBxVdjzhH8xq+f57JSbbvzqvUzR6eg=
|
||||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
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/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
@ -611,6 +636,8 @@ github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
|||||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||||
github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE=
|
github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE=
|
||||||
github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
|
github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||||
github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek=
|
github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek=
|
||||||
@ -635,8 +662,8 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042
|
|||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
||||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||||
@ -646,8 +673,8 @@ github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
|
|||||||
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
|
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
|
||||||
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
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.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||||
@ -666,12 +693,12 @@ github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
|||||||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||||
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ=
|
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||||
github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ=
|
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||||
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/quasoft/websspi v1.1.2 h1:/mA4w0LxWlE3novvsoEL6BBA1WnjJATbjkh1kFrTidw=
|
github.com/quasoft/websspi v1.1.2 h1:/mA4w0LxWlE3novvsoEL6BBA1WnjJATbjkh1kFrTidw=
|
||||||
github.com/quasoft/websspi v1.1.2/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
|
github.com/quasoft/websspi v1.1.2/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
@ -694,8 +721,8 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4
|
|||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
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 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 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
@ -710,6 +737,7 @@ github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jN
|
|||||||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||||
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
|
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
|
||||||
|
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 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
@ -752,6 +780,7 @@ github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
@ -880,8 +909,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@ -891,6 +920,7 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
|
|||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
@ -899,8 +929,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -948,8 +978,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
@ -985,8 +1015,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
|
|||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -452,13 +452,10 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
|
|||||||
|
|
||||||
actions := make([]*Action, 0, opts.PageSize)
|
actions := make([]*Action, 0, opts.PageSize)
|
||||||
var count int64
|
var count int64
|
||||||
|
opts.SetDefaultValues()
|
||||||
|
|
||||||
if opts.Page < 10 { // TODO: why it's 10 but other values? It's an experience value.
|
if opts.Page < 10 { // TODO: why it's 10 but other values? It's an experience value.
|
||||||
sess := db.GetEngine(ctx).Where(cond).
|
sess := db.GetEngine(ctx).Where(cond)
|
||||||
Select("`action`.*"). // this line will avoid select other joined table's columns
|
|
||||||
Join("INNER", "repository", "`repository`.id = `action`.repo_id")
|
|
||||||
|
|
||||||
opts.SetDefaultValues()
|
|
||||||
sess = db.SetSessionPagination(sess, &opts)
|
sess = db.SetSessionPagination(sess, &opts)
|
||||||
|
|
||||||
count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
|
count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
|
||||||
@ -467,11 +464,7 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// First, only query which IDs are necessary, and only then query all actions to speed up the overall query
|
// First, only query which IDs are necessary, and only then query all actions to speed up the overall query
|
||||||
sess := db.GetEngine(ctx).Where(cond).
|
sess := db.GetEngine(ctx).Where(cond).Select("`action`.id")
|
||||||
Select("`action`.id").
|
|
||||||
Join("INNER", "repository", "`repository`.id = `action`.repo_id")
|
|
||||||
|
|
||||||
opts.SetDefaultValues()
|
|
||||||
sess = db.SetSessionPagination(sess, &opts)
|
sess = db.SetSessionPagination(sess, &opts)
|
||||||
|
|
||||||
actionIDs := make([]int64, 0, opts.PageSize)
|
actionIDs := make([]int64, 0, opts.PageSize)
|
||||||
@ -481,8 +474,7 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
|
|||||||
|
|
||||||
count, err = db.GetEngine(ctx).Where(cond).
|
count, err = db.GetEngine(ctx).Where(cond).
|
||||||
Table("action").
|
Table("action").
|
||||||
Cols("`action`.id").
|
Cols("`action`.id").Count()
|
||||||
Join("INNER", "repository", "`repository`.id = `action`.repo_id").Count()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, fmt.Errorf("Count: %w", err)
|
return nil, 0, fmt.Errorf("Count: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -228,6 +228,8 @@ func TestNotifyWatchers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetFeedsCorrupted(t *testing.T) {
|
func TestGetFeedsCorrupted(t *testing.T) {
|
||||||
|
// Now we will not check for corrupted data in the feeds
|
||||||
|
// users should run doctor to fix their data
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||||
@ -241,8 +243,8 @@ func TestGetFeedsCorrupted(t *testing.T) {
|
|||||||
IncludePrivate: true,
|
IncludePrivate: true,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, actions, 0)
|
assert.Len(t, actions, 1)
|
||||||
assert.Equal(t, int64(0), count)
|
assert.Equal(t, int64(1), count)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConsistencyUpdateAction(t *testing.T) {
|
func TestConsistencyUpdateAction(t *testing.T) {
|
||||||
|
@ -34,6 +34,7 @@ type ActivityStats struct {
|
|||||||
OpenedPRAuthorCount int64
|
OpenedPRAuthorCount int64
|
||||||
MergedPRs issues_model.PullRequestList
|
MergedPRs issues_model.PullRequestList
|
||||||
MergedPRAuthorCount int64
|
MergedPRAuthorCount int64
|
||||||
|
ActiveIssues issues_model.IssueList
|
||||||
OpenedIssues issues_model.IssueList
|
OpenedIssues issues_model.IssueList
|
||||||
OpenedIssueAuthorCount int64
|
OpenedIssueAuthorCount int64
|
||||||
ClosedIssues issues_model.IssueList
|
ClosedIssues issues_model.IssueList
|
||||||
@ -172,7 +173,7 @@ func (stats *ActivityStats) MergedPRPerc() int {
|
|||||||
|
|
||||||
// ActiveIssueCount returns total active issue count
|
// ActiveIssueCount returns total active issue count
|
||||||
func (stats *ActivityStats) ActiveIssueCount() int {
|
func (stats *ActivityStats) ActiveIssueCount() int {
|
||||||
return stats.OpenedIssueCount() + stats.ClosedIssueCount()
|
return len(stats.ActiveIssues)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenedIssueCount returns open issue count
|
// OpenedIssueCount returns open issue count
|
||||||
@ -285,13 +286,21 @@ func (stats *ActivityStats) FillIssues(ctx context.Context, repoID int64, fromTi
|
|||||||
stats.ClosedIssueAuthorCount = count
|
stats.ClosedIssueAuthorCount = count
|
||||||
|
|
||||||
// New issues
|
// New issues
|
||||||
sess = issuesForActivityStatement(ctx, repoID, fromTime, false, false)
|
sess = newlyCreatedIssues(ctx, repoID, fromTime)
|
||||||
sess.OrderBy("issue.created_unix ASC")
|
sess.OrderBy("issue.created_unix ASC")
|
||||||
stats.OpenedIssues = make(issues_model.IssueList, 0)
|
stats.OpenedIssues = make(issues_model.IssueList, 0)
|
||||||
if err = sess.Find(&stats.OpenedIssues); err != nil {
|
if err = sess.Find(&stats.OpenedIssues); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Active issues
|
||||||
|
sess = activeIssues(ctx, repoID, fromTime)
|
||||||
|
sess.OrderBy("issue.created_unix ASC")
|
||||||
|
stats.ActiveIssues = make(issues_model.IssueList, 0)
|
||||||
|
if err = sess.Find(&stats.ActiveIssues); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Opened issue authors
|
// Opened issue authors
|
||||||
sess = issuesForActivityStatement(ctx, repoID, fromTime, false, false)
|
sess = issuesForActivityStatement(ctx, repoID, fromTime, false, false)
|
||||||
if _, err = sess.Select("count(distinct issue.poster_id) as `count`").Table("issue").Get(&count); err != nil {
|
if _, err = sess.Select("count(distinct issue.poster_id) as `count`").Table("issue").Get(&count); err != nil {
|
||||||
@ -317,6 +326,23 @@ func (stats *ActivityStats) FillUnresolvedIssues(ctx context.Context, repoID int
|
|||||||
return sess.Find(&stats.UnresolvedIssues)
|
return sess.Find(&stats.UnresolvedIssues)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newlyCreatedIssues(ctx context.Context, repoID int64, fromTime time.Time) *xorm.Session {
|
||||||
|
sess := db.GetEngine(ctx).Where("issue.repo_id = ?", repoID).
|
||||||
|
And("issue.is_pull = ?", false). // Retain the is_pull check to exclude pull requests
|
||||||
|
And("issue.created_unix >= ?", fromTime.Unix()) // Include all issues created after fromTime
|
||||||
|
|
||||||
|
return sess
|
||||||
|
}
|
||||||
|
|
||||||
|
func activeIssues(ctx context.Context, repoID int64, fromTime time.Time) *xorm.Session {
|
||||||
|
sess := db.GetEngine(ctx).Where("issue.repo_id = ?", repoID).
|
||||||
|
And("issue.is_pull = ?", false).
|
||||||
|
And("issue.created_unix >= ?", fromTime.Unix()).
|
||||||
|
Or("issue.closed_unix >= ?", fromTime.Unix())
|
||||||
|
|
||||||
|
return sess
|
||||||
|
}
|
||||||
|
|
||||||
func issuesForActivityStatement(ctx context.Context, repoID int64, fromTime time.Time, closed, unresolved bool) *xorm.Session {
|
func issuesForActivityStatement(ctx context.Context, repoID int64, fromTime time.Time, closed, unresolved bool) *xorm.Session {
|
||||||
sess := db.GetEngine(ctx).Where("issue.repo_id = ?", repoID).
|
sess := db.GetEngine(ctx).Where("issue.repo_id = ?", repoID).
|
||||||
And("issue.is_closed = ?", closed)
|
And("issue.is_closed = ?", closed)
|
||||||
|
@ -179,27 +179,6 @@ func GetMigratingTask(ctx context.Context, repoID int64) (*Task, error) {
|
|||||||
return &task, nil
|
return &task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMigratingTaskByID returns the migrating task by repo's id
|
|
||||||
func GetMigratingTaskByID(ctx context.Context, id, doerID int64) (*Task, *migration.MigrateOptions, error) {
|
|
||||||
task := Task{
|
|
||||||
ID: id,
|
|
||||||
DoerID: doerID,
|
|
||||||
Type: structs.TaskTypeMigrateRepo,
|
|
||||||
}
|
|
||||||
has, err := db.GetEngine(ctx).Get(&task)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
} else if !has {
|
|
||||||
return nil, nil, ErrTaskDoesNotExist{id, 0, task.Type}
|
|
||||||
}
|
|
||||||
|
|
||||||
var opts migration.MigrateOptions
|
|
||||||
if err := json.Unmarshal([]byte(task.PayloadContent), &opts); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return &task, &opts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateTask creates a task on database
|
// CreateTask creates a task on database
|
||||||
func CreateTask(ctx context.Context, task *Task) error {
|
func CreateTask(ctx context.Context, task *Task) error {
|
||||||
return db.Insert(ctx, task)
|
return db.Insert(ctx, task)
|
||||||
|
@ -114,7 +114,7 @@ func readArmoredSign(r io.Reader) (body io.Reader, err error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if block.Type != openpgp.SignatureType {
|
if block.Type != openpgp.SignatureType {
|
||||||
return nil, fmt.Errorf("expected '" + openpgp.SignatureType + "', got: " + block.Type)
|
return nil, fmt.Errorf("expected '%s', got: %s", openpgp.SignatureType, block.Type)
|
||||||
}
|
}
|
||||||
return block.Body, nil
|
return block.Body, nil
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ func tryGetKeyIDFromSignature(sig *packet.Signature) string {
|
|||||||
if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
|
if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
|
||||||
return fmt.Sprintf("%016X", *sig.IssuerKeyId)
|
return fmt.Sprintf("%016X", *sig.IssuerKeyId)
|
||||||
}
|
}
|
||||||
if sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) > 0 {
|
if len(sig.IssuerFingerprint) > 0 {
|
||||||
return fmt.Sprintf("%016X", sig.IssuerFingerprint[12:20])
|
return fmt.Sprintf("%016X", sig.IssuerFingerprint[12:20])
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
|
@ -39,7 +39,7 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) {
|
|||||||
|
|
||||||
// golangci lint is incorrect here - there is no benefit to using driver.ExecerContext here
|
// golangci lint is incorrect here - there is no benefit to using driver.ExecerContext here
|
||||||
// and in any case pq does not implement it
|
// and in any case pq does not implement it
|
||||||
if execer, ok := conn.(driver.Execer); ok { //nolint
|
if execer, ok := conn.(driver.Execer); ok { //nolint:staticcheck
|
||||||
_, err := execer.Exec(`SELECT set_config(
|
_, err := execer.Exec(`SELECT set_config(
|
||||||
'search_path',
|
'search_path',
|
||||||
$1 || ',' || current_setting('search_path'),
|
$1 || ',' || current_setting('search_path'),
|
||||||
@ -64,7 +64,7 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) {
|
|||||||
// driver.String.ConvertValue will never return err for string
|
// driver.String.ConvertValue will never return err for string
|
||||||
|
|
||||||
// golangci lint is incorrect here - there is no benefit to using stmt.ExecWithContext here
|
// golangci lint is incorrect here - there is no benefit to using stmt.ExecWithContext here
|
||||||
_, err = stmt.Exec([]driver.Value{schemaValue}) //nolint
|
_, err = stmt.Exec([]driver.Value{schemaValue}) //nolint:staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -83,3 +83,22 @@
|
|||||||
issue_id: 2 # in repo_id 1
|
issue_id: 2 # in repo_id 1
|
||||||
review_id: 20
|
review_id: 20
|
||||||
created_unix: 946684810
|
created_unix: 946684810
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 10
|
||||||
|
type: 22 # review
|
||||||
|
poster_id: 5
|
||||||
|
issue_id: 3 # in repo_id 1
|
||||||
|
content: "reviewed by user5"
|
||||||
|
review_id: 21
|
||||||
|
created_unix: 946684816
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 11
|
||||||
|
type: 27 # review request
|
||||||
|
poster_id: 2
|
||||||
|
issue_id: 3 # in repo_id 1
|
||||||
|
content: "review request for user5"
|
||||||
|
review_id: 22
|
||||||
|
assignee_id: 5
|
||||||
|
created_unix: 946684817
|
||||||
|
1
models/fixtures/repo_license.yml
Normal file
1
models/fixtures/repo_license.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
[] # empty
|
@ -26,7 +26,7 @@
|
|||||||
fork_id: 0
|
fork_id: 0
|
||||||
is_template: false
|
is_template: false
|
||||||
template_id: 0
|
template_id: 0
|
||||||
size: 7597
|
size: 8478
|
||||||
is_fsck_enabled: true
|
is_fsck_enabled: true
|
||||||
close_issues_via_commit_in_any_branch: false
|
close_issues_via_commit_in_any_branch: false
|
||||||
|
|
||||||
|
@ -179,3 +179,22 @@
|
|||||||
content: "Review Comment"
|
content: "Review Comment"
|
||||||
updated_unix: 946684810
|
updated_unix: 946684810
|
||||||
created_unix: 946684810
|
created_unix: 946684810
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 21
|
||||||
|
type: 2
|
||||||
|
reviewer_id: 5
|
||||||
|
issue_id: 3
|
||||||
|
content: "reviewed by user5"
|
||||||
|
commit_id: 4a357436d925b5c974181ff12a994538ddc5a269
|
||||||
|
updated_unix: 946684816
|
||||||
|
created_unix: 946684816
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 22
|
||||||
|
type: 4
|
||||||
|
reviewer_id: 5
|
||||||
|
issue_id: 3
|
||||||
|
content: "review request for user5"
|
||||||
|
updated_unix: 946684817
|
||||||
|
created_unix: 946684817
|
||||||
|
@ -48,12 +48,12 @@ func (issue *Issue) ProjectColumnID(ctx context.Context) int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadIssuesFromColumn load issues assigned to this column
|
// LoadIssuesFromColumn load issues assigned to this column
|
||||||
func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column) (IssueList, error) {
|
func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, opts *IssuesOptions) (IssueList, error) {
|
||||||
issueList, err := Issues(ctx, &IssuesOptions{
|
issueList, err := Issues(ctx, opts.Copy(func(o *IssuesOptions) {
|
||||||
ProjectColumnID: b.ID,
|
o.ProjectColumnID = b.ID
|
||||||
ProjectID: b.ProjectID,
|
o.ProjectID = b.ProjectID
|
||||||
SortType: "project-column-sorting",
|
o.SortType = "project-column-sorting"
|
||||||
})
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -78,10 +78,10 @@ func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column) (IssueLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadIssuesFromColumnList load issues assigned to the columns
|
// LoadIssuesFromColumnList load issues assigned to the columns
|
||||||
func LoadIssuesFromColumnList(ctx context.Context, bs project_model.ColumnList) (map[int64]IssueList, error) {
|
func LoadIssuesFromColumnList(ctx context.Context, bs project_model.ColumnList, opts *IssuesOptions) (map[int64]IssueList, error) {
|
||||||
issuesMap := make(map[int64]IssueList, len(bs))
|
issuesMap := make(map[int64]IssueList, len(bs))
|
||||||
for i := range bs {
|
for i := range bs {
|
||||||
il, err := LoadIssuesFromColumn(ctx, bs[i])
|
il, err := LoadIssuesFromColumn(ctx, bs[i], opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,19 @@ type IssuesOptions struct { //nolint
|
|||||||
User *user_model.User // issues permission scope
|
User *user_model.User // issues permission scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the options.
|
||||||
|
// Be careful, it's not a deep copy, so `IssuesOptions.RepoIDs = {...}` is OK while `IssuesOptions.RepoIDs[0] = ...` is not.
|
||||||
|
func (o *IssuesOptions) Copy(edit ...func(options *IssuesOptions)) *IssuesOptions {
|
||||||
|
if o == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
v := *o
|
||||||
|
for _, e := range edit {
|
||||||
|
e(&v)
|
||||||
|
}
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
// applySorts sort an issues-related session based on the provided
|
// applySorts sort an issues-related session based on the provided
|
||||||
// sortType string
|
// sortType string
|
||||||
func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) {
|
func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) {
|
||||||
|
@ -268,6 +268,10 @@ func (pr *PullRequest) LoadAttributes(ctx context.Context) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pr *PullRequest) IsAgitFlow() bool {
|
||||||
|
return pr.Flow == PullRequestFlowAGit
|
||||||
|
}
|
||||||
|
|
||||||
// LoadHeadRepo loads the head repository, pr.HeadRepo will remain nil if it does not exist
|
// LoadHeadRepo loads the head repository, pr.HeadRepo will remain nil if it does not exist
|
||||||
// and thus ErrRepoNotExist will never be returned
|
// and thus ErrRepoNotExist will never be returned
|
||||||
func (pr *PullRequest) LoadHeadRepo(ctx context.Context) (err error) {
|
func (pr *PullRequest) LoadHeadRepo(ctx context.Context) (err error) {
|
||||||
@ -410,7 +414,7 @@ func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer)
|
|||||||
|
|
||||||
// Note: This doesn't page as we only expect a very limited number of reviews
|
// Note: This doesn't page as we only expect a very limited number of reviews
|
||||||
reviews, err := FindLatestReviews(ctx, FindReviewOptions{
|
reviews, err := FindLatestReviews(ctx, FindReviewOptions{
|
||||||
Type: ReviewTypeApprove,
|
Types: []ReviewType{ReviewTypeApprove},
|
||||||
IssueID: pr.IssueID,
|
IssueID: pr.IssueID,
|
||||||
OfficialOnly: setting.Repository.PullRequest.DefaultMergeMessageOfficialApproversOnly,
|
OfficialOnly: setting.Repository.PullRequest.DefaultMergeMessageOfficialApproversOnly,
|
||||||
})
|
})
|
||||||
|
@ -26,6 +26,7 @@ type PullRequestsOptions struct {
|
|||||||
SortType string
|
SortType string
|
||||||
Labels []int64
|
Labels []int64
|
||||||
MilestoneID int64
|
MilestoneID int64
|
||||||
|
PosterID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullRequestsOptions) *xorm.Session {
|
func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullRequestsOptions) *xorm.Session {
|
||||||
@ -46,6 +47,10 @@ func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullR
|
|||||||
sess.And("issue.milestone_id=?", opts.MilestoneID)
|
sess.And("issue.milestone_id=?", opts.MilestoneID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.PosterID > 0 {
|
||||||
|
sess.And("issue.poster_id=?", opts.PosterID)
|
||||||
|
}
|
||||||
|
|
||||||
return sess
|
return sess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ func (r *Review) TooltipContent() string {
|
|||||||
}
|
}
|
||||||
return "repo.issues.review.official"
|
return "repo.issues.review.official"
|
||||||
case ReviewTypeComment:
|
case ReviewTypeComment:
|
||||||
return "repo.issues.review.comment"
|
return "repo.issues.review.commented"
|
||||||
case ReviewTypeReject:
|
case ReviewTypeReject:
|
||||||
return "repo.issues.review.rejected"
|
return "repo.issues.review.rejected"
|
||||||
case ReviewTypeRequest:
|
case ReviewTypeRequest:
|
||||||
@ -389,7 +389,7 @@ func GetCurrentReview(ctx context.Context, reviewer *user_model.User, issue *Iss
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
reviews, err := FindReviews(ctx, FindReviewOptions{
|
reviews, err := FindReviews(ctx, FindReviewOptions{
|
||||||
Type: ReviewTypePending,
|
Types: []ReviewType{ReviewTypePending},
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
ReviewerID: reviewer.ID,
|
ReviewerID: reviewer.ID,
|
||||||
})
|
})
|
||||||
|
@ -92,7 +92,7 @@ func (reviews ReviewList) LoadIssues(ctx context.Context) error {
|
|||||||
// FindReviewOptions represent possible filters to find reviews
|
// FindReviewOptions represent possible filters to find reviews
|
||||||
type FindReviewOptions struct {
|
type FindReviewOptions struct {
|
||||||
db.ListOptions
|
db.ListOptions
|
||||||
Type ReviewType
|
Types []ReviewType
|
||||||
IssueID int64
|
IssueID int64
|
||||||
ReviewerID int64
|
ReviewerID int64
|
||||||
OfficialOnly bool
|
OfficialOnly bool
|
||||||
@ -107,8 +107,8 @@ func (opts *FindReviewOptions) toCond() builder.Cond {
|
|||||||
if opts.ReviewerID > 0 {
|
if opts.ReviewerID > 0 {
|
||||||
cond = cond.And(builder.Eq{"reviewer_id": opts.ReviewerID})
|
cond = cond.And(builder.Eq{"reviewer_id": opts.ReviewerID})
|
||||||
}
|
}
|
||||||
if opts.Type != ReviewTypeUnknown {
|
if len(opts.Types) > 0 {
|
||||||
cond = cond.And(builder.Eq{"type": opts.Type})
|
cond = cond.And(builder.In("type", opts.Types))
|
||||||
}
|
}
|
||||||
if opts.OfficialOnly {
|
if opts.OfficialOnly {
|
||||||
cond = cond.And(builder.Eq{"official": true})
|
cond = cond.And(builder.Eq{"official": true})
|
||||||
|
@ -63,7 +63,7 @@ func TestReviewType_Icon(t *testing.T) {
|
|||||||
func TestFindReviews(t *testing.T) {
|
func TestFindReviews(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
reviews, err := issues_model.FindReviews(db.DefaultContext, issues_model.FindReviewOptions{
|
reviews, err := issues_model.FindReviews(db.DefaultContext, issues_model.FindReviewOptions{
|
||||||
Type: issues_model.ReviewTypeApprove,
|
Types: []issues_model.ReviewType{issues_model.ReviewTypeApprove},
|
||||||
IssueID: 2,
|
IssueID: 2,
|
||||||
ReviewerID: 1,
|
ReviewerID: 1,
|
||||||
})
|
})
|
||||||
@ -75,7 +75,7 @@ func TestFindReviews(t *testing.T) {
|
|||||||
func TestFindLatestReviews(t *testing.T) {
|
func TestFindLatestReviews(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
reviews, err := issues_model.FindLatestReviews(db.DefaultContext, issues_model.FindReviewOptions{
|
reviews, err := issues_model.FindLatestReviews(db.DefaultContext, issues_model.FindReviewOptions{
|
||||||
Type: issues_model.ReviewTypeApprove,
|
Types: []issues_model.ReviewType{issues_model.ReviewTypeApprove},
|
||||||
IssueID: 11,
|
IssueID: 11,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -500,7 +500,7 @@ var migrations = []Migration{
|
|||||||
// v259 -> v260
|
// v259 -> v260
|
||||||
NewMigration("Convert scoped access tokens", v1_20.ConvertScopedAccessTokens),
|
NewMigration("Convert scoped access tokens", v1_20.ConvertScopedAccessTokens),
|
||||||
|
|
||||||
// Gitea 1.20.0 ends at 260
|
// Gitea 1.20.0 ends at v260
|
||||||
|
|
||||||
// v260 -> v261
|
// v260 -> v261
|
||||||
NewMigration("Drop custom_labels column of action_runner table", v1_21.DropCustomLabelsColumnOfActionRunner),
|
NewMigration("Drop custom_labels column of action_runner table", v1_21.DropCustomLabelsColumnOfActionRunner),
|
||||||
@ -601,6 +601,8 @@ var migrations = []Migration{
|
|||||||
NewMigration("Add metadata column for comment table", v1_23.AddCommentMetaDataColumn),
|
NewMigration("Add metadata column for comment table", v1_23.AddCommentMetaDataColumn),
|
||||||
// v304 -> v305
|
// v304 -> v305
|
||||||
NewMigration("Add index for release sha1", v1_23.AddIndexForReleaseSha1),
|
NewMigration("Add index for release sha1", v1_23.AddIndexForReleaseSha1),
|
||||||
|
// v305 -> v306
|
||||||
|
NewMigration("Add Repository Licenses", v1_23.AddRepositoryLicenses),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
@ -83,7 +83,7 @@ func UnwrapLDAPSourceCfg(x *xorm.Engine) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to unmarshal %s: %w", source.Cfg, err)
|
return fmt.Errorf("failed to unmarshal %s: %w", source.Cfg, err)
|
||||||
}
|
}
|
||||||
if wrapped.Source != nil && len(wrapped.Source) > 0 {
|
if len(wrapped.Source) > 0 {
|
||||||
bs, err := json.Marshal(wrapped.Source)
|
bs, err := json.Marshal(wrapped.Source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
23
models/migrations/v1_23/v305.go
Normal file
23
models/migrations/v1_23/v305.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_23 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddRepositoryLicenses(x *xorm.Engine) error {
|
||||||
|
type RepoLicense struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
RepoID int64 `xorm:"UNIQUE(s) NOT NULL"`
|
||||||
|
CommitID string
|
||||||
|
License string `xorm:"VARCHAR(255) UNIQUE(s) NOT NULL"`
|
||||||
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
||||||
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX UPDATED"`
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.Sync(new(RepoLicense))
|
||||||
|
}
|
@ -9,7 +9,9 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
|
"code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
@ -112,6 +114,49 @@ func IsUserOrgOwner(ctx context.Context, users user_model.UserList, orgID int64)
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOrgAssignees returns all users that have write access and can be assigned to issues
|
||||||
|
// of the any repository in the organization.
|
||||||
|
func GetOrgAssignees(ctx context.Context, orgID int64) (_ []*user_model.User, err error) {
|
||||||
|
e := db.GetEngine(ctx)
|
||||||
|
userIDs := make([]int64, 0, 10)
|
||||||
|
if err = e.Table("access").
|
||||||
|
Join("INNER", "repository", "`repository`.id = `access`.repo_id").
|
||||||
|
Where("`repository`.owner_id = ? AND `access`.mode >= ?", orgID, perm.AccessModeWrite).
|
||||||
|
Select("user_id").
|
||||||
|
Find(&userIDs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
additionalUserIDs := make([]int64, 0, 10)
|
||||||
|
if err = e.Table("team_user").
|
||||||
|
Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id").
|
||||||
|
Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id").
|
||||||
|
Join("INNER", "repository", "`repository`.id = `team_repo`.repo_id").
|
||||||
|
Where("`repository`.owner_id = ? AND (`team_unit`.access_mode >= ? OR (`team_unit`.access_mode = ? AND `team_unit`.`type` = ?))",
|
||||||
|
orgID, perm.AccessModeWrite, perm.AccessModeRead, unit.TypePullRequests).
|
||||||
|
Distinct("`team_user`.uid").
|
||||||
|
Select("`team_user`.uid").
|
||||||
|
Find(&additionalUserIDs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueUserIDs := make(container.Set[int64])
|
||||||
|
uniqueUserIDs.AddMultiple(userIDs...)
|
||||||
|
uniqueUserIDs.AddMultiple(additionalUserIDs...)
|
||||||
|
|
||||||
|
users := make([]*user_model.User, 0, len(uniqueUserIDs))
|
||||||
|
if len(userIDs) > 0 {
|
||||||
|
if err = e.In("id", uniqueUserIDs.Values()).
|
||||||
|
Where(builder.Eq{"`user`.is_active": true}).
|
||||||
|
OrderBy(user_model.GetOrderByName()).
|
||||||
|
Find(&users); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
func loadOrganizationOwners(ctx context.Context, users user_model.UserList, orgID int64) (map[int64]*TeamUser, error) {
|
func loadOrganizationOwners(ctx context.Context, users user_model.UserList, orgID int64) (map[int64]*TeamUser, error) {
|
||||||
if len(users) == 0 {
|
if len(users) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
120
models/repo/license.go
Normal file
120
models/repo/license.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
db.RegisterModel(new(RepoLicense))
|
||||||
|
}
|
||||||
|
|
||||||
|
type RepoLicense struct { //revive:disable-line:exported
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
RepoID int64 `xorm:"UNIQUE(s) NOT NULL"`
|
||||||
|
CommitID string
|
||||||
|
License string `xorm:"VARCHAR(255) UNIQUE(s) NOT NULL"`
|
||||||
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
||||||
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX UPDATED"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepoLicenseList defines a list of repo licenses
|
||||||
|
type RepoLicenseList []*RepoLicense //revive:disable-line:exported
|
||||||
|
|
||||||
|
func (rll RepoLicenseList) StringList() []string {
|
||||||
|
var licenses []string
|
||||||
|
for _, rl := range rll {
|
||||||
|
licenses = append(licenses, rl.License)
|
||||||
|
}
|
||||||
|
return licenses
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepoLicenses returns the license statistics for a repository
|
||||||
|
func GetRepoLicenses(ctx context.Context, repo *Repository) (RepoLicenseList, error) {
|
||||||
|
licenses := make(RepoLicenseList, 0)
|
||||||
|
if err := db.GetEngine(ctx).Where("`repo_id` = ?", repo.ID).Asc("`license`").Find(&licenses); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return licenses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRepoLicenses updates the license statistics for repository
|
||||||
|
func UpdateRepoLicenses(ctx context.Context, repo *Repository, commitID string, licenses []string) error {
|
||||||
|
oldLicenses, err := GetRepoLicenses(ctx, repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, license := range licenses {
|
||||||
|
upd := false
|
||||||
|
for _, o := range oldLicenses {
|
||||||
|
// Update already existing license
|
||||||
|
if o.License == license {
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.ID).Cols("`commit_id`").Update(o); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
upd = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Insert new license
|
||||||
|
if !upd {
|
||||||
|
if err := db.Insert(ctx, &RepoLicense{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
CommitID: commitID,
|
||||||
|
License: license,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Delete old licenses
|
||||||
|
licenseToDelete := make([]int64, 0, len(oldLicenses))
|
||||||
|
for _, o := range oldLicenses {
|
||||||
|
if o.CommitID != commitID {
|
||||||
|
licenseToDelete = append(licenseToDelete, o.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(licenseToDelete) > 0 {
|
||||||
|
if _, err := db.GetEngine(ctx).In("`id`", licenseToDelete).Delete(&RepoLicense{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyLicense Copy originalRepo license information to destRepo (use for forked repo)
|
||||||
|
func CopyLicense(ctx context.Context, originalRepo, destRepo *Repository) error {
|
||||||
|
repoLicenses, err := GetRepoLicenses(ctx, originalRepo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(repoLicenses) > 0 {
|
||||||
|
newRepoLicenses := make(RepoLicenseList, 0, len(repoLicenses))
|
||||||
|
|
||||||
|
for _, rl := range repoLicenses {
|
||||||
|
newRepoLicense := &RepoLicense{
|
||||||
|
RepoID: destRepo.ID,
|
||||||
|
CommitID: rl.CommitID,
|
||||||
|
License: rl.License,
|
||||||
|
}
|
||||||
|
newRepoLicenses = append(newRepoLicenses, newRepoLicense)
|
||||||
|
}
|
||||||
|
if err := db.Insert(ctx, &newRepoLicenses); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanRepoLicenses will remove all license record of the repo
|
||||||
|
func CleanRepoLicenses(ctx context.Context, repo *Repository) error {
|
||||||
|
return db.DeleteBeans(ctx, &RepoLicense{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
})
|
||||||
|
}
|
@ -234,6 +234,7 @@ type FindReleasesOptions struct {
|
|||||||
IsDraft optional.Option[bool]
|
IsDraft optional.Option[bool]
|
||||||
TagNames []string
|
TagNames []string
|
||||||
HasSha1 optional.Option[bool] // useful to find draft releases which are created with existing tags
|
HasSha1 optional.Option[bool] // useful to find draft releases which are created with existing tags
|
||||||
|
NamePattern optional.Option[string]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts FindReleasesOptions) ToConds() builder.Cond {
|
func (opts FindReleasesOptions) ToConds() builder.Cond {
|
||||||
@ -261,6 +262,11 @@ func (opts FindReleasesOptions) ToConds() builder.Cond {
|
|||||||
cond = cond.And(builder.Eq{"sha1": ""})
|
cond = cond.And(builder.Eq{"sha1": ""})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.NamePattern.Has() && opts.NamePattern.Value() != "" {
|
||||||
|
cond = cond.And(builder.Like{"lower_tag_name", strings.ToLower(opts.NamePattern.Value())})
|
||||||
|
}
|
||||||
|
|
||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,7 +749,7 @@ func GetUserRepositories(ctx context.Context, opts *SearchRepoOptions) (Reposito
|
|||||||
cond = cond.And(builder.Eq{"is_private": false})
|
cond = cond.And(builder.Eq{"is_private": false})
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.LowerNames != nil && len(opts.LowerNames) > 0 {
|
if len(opts.LowerNames) > 0 {
|
||||||
cond = cond.And(builder.In("lower_name", opts.LowerNames))
|
cond = cond.And(builder.In("lower_name", opts.LowerNames))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,19 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
|
|||||||
builder.Like{"LOWER(full_name)", lowerKeyword},
|
builder.Like{"LOWER(full_name)", lowerKeyword},
|
||||||
)
|
)
|
||||||
if opts.SearchByEmail {
|
if opts.SearchByEmail {
|
||||||
keywordCond = keywordCond.Or(builder.Like{"LOWER(email)", lowerKeyword})
|
var emailCond builder.Cond
|
||||||
|
emailCond = builder.Like{"LOWER(email)", lowerKeyword}
|
||||||
|
if opts.Actor == nil {
|
||||||
|
emailCond = emailCond.And(builder.Eq{"keep_email_private": false})
|
||||||
|
} else if !opts.Actor.IsAdmin {
|
||||||
|
emailCond = emailCond.And(
|
||||||
|
builder.Or(
|
||||||
|
builder.Eq{"keep_email_private": false},
|
||||||
|
builder.Eq{"id": opts.Actor.ID},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
keywordCond = keywordCond.Or(emailCond)
|
||||||
}
|
}
|
||||||
|
|
||||||
cond = cond.And(keywordCond)
|
cond = cond.And(keywordCond)
|
||||||
|
@ -14,4 +14,8 @@ const (
|
|||||||
UserActivityPubPrivPem = "activitypub.priv_pem"
|
UserActivityPubPrivPem = "activitypub.priv_pem"
|
||||||
// UserActivityPubPubPem is user's public key
|
// UserActivityPubPubPem is user's public key
|
||||||
UserActivityPubPubPem = "activitypub.pub_pem"
|
UserActivityPubPubPem = "activitypub.pub_pem"
|
||||||
|
// SignupIP is the IP address that the user signed up with
|
||||||
|
SignupIP = "signup.ip"
|
||||||
|
// SignupUserAgent is the user agent that the user signed up with
|
||||||
|
SignupUserAgent = "signup.user_agent"
|
||||||
)
|
)
|
||||||
|
@ -150,6 +150,14 @@ type User struct {
|
|||||||
KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"`
|
KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Meta defines the meta information of a user, to be stored in the K/V table
|
||||||
|
type Meta struct {
|
||||||
|
// Store the initial registration of the user, to aid in spam prevention
|
||||||
|
// Ensure that one IP isn't creating many accounts (following mediawiki approach)
|
||||||
|
InitialIP string
|
||||||
|
InitialUserAgent string
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
db.RegisterModel(new(User))
|
db.RegisterModel(new(User))
|
||||||
}
|
}
|
||||||
@ -400,6 +408,10 @@ func (u *User) IsIndividual() bool {
|
|||||||
return u.Type == UserTypeIndividual
|
return u.Type == UserTypeIndividual
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) IsUser() bool {
|
||||||
|
return u.Type == UserTypeIndividual || u.Type == UserTypeBot
|
||||||
|
}
|
||||||
|
|
||||||
// IsBot returns whether or not the user is of type bot
|
// IsBot returns whether or not the user is of type bot
|
||||||
func (u *User) IsBot() bool {
|
func (u *User) IsBot() bool {
|
||||||
return u.Type == UserTypeBot
|
return u.Type == UserTypeBot
|
||||||
@ -615,17 +627,17 @@ type CreateUserOverwriteOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser creates record of a new user.
|
// CreateUser creates record of a new user.
|
||||||
func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
|
func CreateUser(ctx context.Context, u *User, meta *Meta, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
|
||||||
return createUser(ctx, u, false, overwriteDefault...)
|
return createUser(ctx, u, meta, false, overwriteDefault...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AdminCreateUser is used by admins to manually create users
|
// AdminCreateUser is used by admins to manually create users
|
||||||
func AdminCreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
|
func AdminCreateUser(ctx context.Context, u *User, meta *Meta, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
|
||||||
return createUser(ctx, u, true, overwriteDefault...)
|
return createUser(ctx, u, meta, true, overwriteDefault...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createUser creates record of a new user.
|
// createUser creates record of a new user.
|
||||||
func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
|
func createUser(ctx context.Context, u *User, meta *Meta, createdByAdmin bool, overwriteDefault ...*CreateUserOverwriteOptions) (err error) {
|
||||||
if err = IsUsableUsername(u.Name); err != nil {
|
if err = IsUsableUsername(u.Name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -745,6 +757,22 @@ func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefa
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if setting.RecordUserSignupMetadata {
|
||||||
|
// insert initial IP and UserAgent
|
||||||
|
if err = SetUserSetting(ctx, u.ID, SignupIP, meta.InitialIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim user agent string to a reasonable length, if necessary
|
||||||
|
userAgent := strings.TrimSpace(meta.InitialUserAgent)
|
||||||
|
if len(userAgent) > 255 {
|
||||||
|
userAgent = userAgent[:255]
|
||||||
|
}
|
||||||
|
if err = SetUserSetting(ctx, u.ID, SignupUserAgent, userAgent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// insert email address
|
// insert email address
|
||||||
if err := db.Insert(ctx, &EmailAddress{
|
if err := db.Insert(ctx, &EmailAddress{
|
||||||
UID: u.ID,
|
UID: u.ID,
|
||||||
|
@ -227,7 +227,7 @@ func TestCreateUserInvalidEmail(t *testing.T) {
|
|||||||
MustChangePassword: false,
|
MustChangePassword: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := user_model.CreateUser(db.DefaultContext, user)
|
err := user_model.CreateUser(db.DefaultContext, user, &user_model.Meta{})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.True(t, user_model.IsErrEmailCharIsNotSupported(err))
|
assert.True(t, user_model.IsErrEmailCharIsNotSupported(err))
|
||||||
}
|
}
|
||||||
@ -241,7 +241,7 @@ func TestCreateUserEmailAlreadyUsed(t *testing.T) {
|
|||||||
user.Name = "testuser"
|
user.Name = "testuser"
|
||||||
user.LowerName = strings.ToLower(user.Name)
|
user.LowerName = strings.ToLower(user.Name)
|
||||||
user.ID = 0
|
user.ID = 0
|
||||||
err := user_model.CreateUser(db.DefaultContext, user)
|
err := user_model.CreateUser(db.DefaultContext, user, &user_model.Meta{})
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.True(t, user_model.IsErrEmailAlreadyUsed(err))
|
assert.True(t, user_model.IsErrEmailAlreadyUsed(err))
|
||||||
}
|
}
|
||||||
@ -258,7 +258,7 @@ func TestCreateUserCustomTimestamps(t *testing.T) {
|
|||||||
user.ID = 0
|
user.ID = 0
|
||||||
user.Email = "unique@example.com"
|
user.Email = "unique@example.com"
|
||||||
user.CreatedUnix = creationTimestamp
|
user.CreatedUnix = creationTimestamp
|
||||||
err := user_model.CreateUser(db.DefaultContext, user)
|
err := user_model.CreateUser(db.DefaultContext, user, &user_model.Meta{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
fetched, err := user_model.GetUserByID(context.Background(), user.ID)
|
fetched, err := user_model.GetUserByID(context.Background(), user.ID)
|
||||||
@ -283,7 +283,7 @@ func TestCreateUserWithoutCustomTimestamps(t *testing.T) {
|
|||||||
user.Email = "unique@example.com"
|
user.Email = "unique@example.com"
|
||||||
user.CreatedUnix = 0
|
user.CreatedUnix = 0
|
||||||
user.UpdatedUnix = 0
|
user.UpdatedUnix = 0
|
||||||
err := user_model.CreateUser(db.DefaultContext, user)
|
err := user_model.CreateUser(db.DefaultContext, user, &user_model.Meta{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
timestampEnd := time.Now().Unix()
|
timestampEnd := time.Now().Unix()
|
||||||
|
@ -18,8 +18,32 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
|
|||||||
return fullStepsOfEmptySteps(task)
|
return fullStepsOfEmptySteps(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
firstStep := task.Steps[0]
|
// firstStep is the first step that has run or running, not include preStep.
|
||||||
|
// For example,
|
||||||
|
// 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): firstStep is step1.
|
||||||
|
// 2. preStep(Success) -> step1(Skipped) -> step2(Success) -> postStep(Success): firstStep is step2.
|
||||||
|
// 3. preStep(Success) -> step1(Running) -> step2(Waiting) -> postStep(Waiting): firstStep is step1.
|
||||||
|
// 4. preStep(Success) -> step1(Skipped) -> step2(Skipped) -> postStep(Skipped): firstStep is nil.
|
||||||
|
// 5. preStep(Success) -> step1(Cancelled) -> step2(Cancelled) -> postStep(Cancelled): firstStep is nil.
|
||||||
|
var firstStep *actions_model.ActionTaskStep
|
||||||
|
// lastHasRunStep is the last step that has run.
|
||||||
|
// For example,
|
||||||
|
// 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1.
|
||||||
|
// 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3.
|
||||||
|
// 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2.
|
||||||
|
// So its Stopped is the Started of postStep when there are no more steps to run.
|
||||||
|
var lastHasRunStep *actions_model.ActionTaskStep
|
||||||
|
|
||||||
var logIndex int64
|
var logIndex int64
|
||||||
|
for _, step := range task.Steps {
|
||||||
|
if firstStep == nil && (step.Status.HasRun() || step.Status.IsRunning()) {
|
||||||
|
firstStep = step
|
||||||
|
}
|
||||||
|
if step.Status.HasRun() {
|
||||||
|
lastHasRunStep = step
|
||||||
|
}
|
||||||
|
logIndex += step.LogLength
|
||||||
|
}
|
||||||
|
|
||||||
preStep := &actions_model.ActionTaskStep{
|
preStep := &actions_model.ActionTaskStep{
|
||||||
Name: preStepName,
|
Name: preStepName,
|
||||||
@ -28,32 +52,17 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
|
|||||||
Status: actions_model.StatusRunning,
|
Status: actions_model.StatusRunning,
|
||||||
}
|
}
|
||||||
|
|
||||||
if firstStep.Status.HasRun() || firstStep.Status.IsRunning() {
|
// No step has run or is running, so preStep is equal to the task
|
||||||
|
if firstStep == nil {
|
||||||
|
preStep.Stopped = task.Stopped
|
||||||
|
preStep.Status = task.Status
|
||||||
|
} else {
|
||||||
preStep.LogLength = firstStep.LogIndex
|
preStep.LogLength = firstStep.LogIndex
|
||||||
preStep.Stopped = firstStep.Started
|
preStep.Stopped = firstStep.Started
|
||||||
preStep.Status = actions_model.StatusSuccess
|
preStep.Status = actions_model.StatusSuccess
|
||||||
} else if task.Status.IsDone() {
|
|
||||||
preStep.Stopped = task.Stopped
|
|
||||||
preStep.Status = actions_model.StatusFailure
|
|
||||||
if task.Status.IsSkipped() {
|
|
||||||
preStep.Status = actions_model.StatusSkipped
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
logIndex += preStep.LogLength
|
logIndex += preStep.LogLength
|
||||||
|
|
||||||
// lastHasRunStep is the last step that has run.
|
|
||||||
// For example,
|
|
||||||
// 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1.
|
|
||||||
// 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3.
|
|
||||||
// 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2.
|
|
||||||
// So its Stopped is the Started of postStep when there are no more steps to run.
|
|
||||||
var lastHasRunStep *actions_model.ActionTaskStep
|
|
||||||
for _, step := range task.Steps {
|
|
||||||
if step.Status.HasRun() {
|
|
||||||
lastHasRunStep = step
|
|
||||||
}
|
|
||||||
logIndex += step.LogLength
|
|
||||||
}
|
|
||||||
if lastHasRunStep == nil {
|
if lastHasRunStep == nil {
|
||||||
lastHasRunStep = preStep
|
lastHasRunStep = preStep
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,25 @@ func TestFullSteps(t *testing.T) {
|
|||||||
{Name: postStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
|
{Name: postStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "first step is skipped",
|
||||||
|
task: &actions_model.ActionTask{
|
||||||
|
Steps: []*actions_model.ActionTaskStep{
|
||||||
|
{Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
|
||||||
|
{Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090},
|
||||||
|
},
|
||||||
|
Status: actions_model.StatusSuccess,
|
||||||
|
Started: 10000,
|
||||||
|
Stopped: 10100,
|
||||||
|
LogLength: 100,
|
||||||
|
},
|
||||||
|
want: []*actions_model.ActionTaskStep{
|
||||||
|
{Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010},
|
||||||
|
{Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
|
||||||
|
{Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090},
|
||||||
|
{Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 10100},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
15
modules/cache/context.go
vendored
15
modules/cache/context.go
vendored
@ -63,9 +63,9 @@ func (cc *cacheContext) isDiscard() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// cacheContextLifetime is the max lifetime of cacheContext.
|
// cacheContextLifetime is the max lifetime of cacheContext.
|
||||||
// Since cacheContext is used to cache data in a request level context, 10s is enough.
|
// Since cacheContext is used to cache data in a request level context, 5 minutes is enough.
|
||||||
// If a cacheContext is used more than 10s, it's probably misuse.
|
// If a cacheContext is used more than 5 minutes, it's probably misuse.
|
||||||
const cacheContextLifetime = 10 * time.Second
|
const cacheContextLifetime = 5 * time.Minute
|
||||||
|
|
||||||
var timeNow = time.Now
|
var timeNow = time.Now
|
||||||
|
|
||||||
@ -109,7 +109,8 @@ func WithCacheContext(ctx context.Context) context.Context {
|
|||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return context.WithValue(ctx, cacheContextKey, &cacheContext{
|
// FIXME: review the use of this nolint directive
|
||||||
|
return context.WithValue(ctx, cacheContextKey, &cacheContext{ //nolint:staticcheck
|
||||||
data: make(map[any]map[any]any),
|
data: make(map[any]map[any]any),
|
||||||
created: timeNow(),
|
created: timeNow(),
|
||||||
})
|
})
|
||||||
@ -131,7 +132,7 @@ func GetContextData(ctx context.Context, tp, key any) any {
|
|||||||
if c.Expired() {
|
if c.Expired() {
|
||||||
// The warning means that the cache context is misused for long-life task,
|
// The warning means that the cache context is misused for long-life task,
|
||||||
// it can be resolved with WithNoCacheContext(ctx).
|
// it can be resolved with WithNoCacheContext(ctx).
|
||||||
log.Warn("cache context is expired, may be misused for long-life tasks: %v", c)
|
log.Warn("cache context is expired, is highly likely to be misused for long-life tasks: %v", c)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return c.Get(tp, key)
|
return c.Get(tp, key)
|
||||||
@ -144,7 +145,7 @@ func SetContextData(ctx context.Context, tp, key, value any) {
|
|||||||
if c.Expired() {
|
if c.Expired() {
|
||||||
// The warning means that the cache context is misused for long-life task,
|
// The warning means that the cache context is misused for long-life task,
|
||||||
// it can be resolved with WithNoCacheContext(ctx).
|
// it can be resolved with WithNoCacheContext(ctx).
|
||||||
log.Warn("cache context is expired, may be misused for long-life tasks: %v", c)
|
log.Warn("cache context is expired, is highly likely to be misused for long-life tasks: %v", c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Put(tp, key, value)
|
c.Put(tp, key, value)
|
||||||
@ -157,7 +158,7 @@ func RemoveContextData(ctx context.Context, tp, key any) {
|
|||||||
if c.Expired() {
|
if c.Expired() {
|
||||||
// The warning means that the cache context is misused for long-life task,
|
// The warning means that the cache context is misused for long-life task,
|
||||||
// it can be resolved with WithNoCacheContext(ctx).
|
// it can be resolved with WithNoCacheContext(ctx).
|
||||||
log.Warn("cache context is expired, may be misused for long-life tasks: %v", c)
|
log.Warn("cache context is expired, is highly likely to be misused for long-life tasks: %v", c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Delete(tp, key)
|
c.Delete(tp, key)
|
||||||
|
2
modules/cache/context_test.go
vendored
2
modules/cache/context_test.go
vendored
@ -45,7 +45,7 @@ func TestWithCacheContext(t *testing.T) {
|
|||||||
timeNow = now
|
timeNow = now
|
||||||
}()
|
}()
|
||||||
timeNow = func() time.Time {
|
timeNow = func() time.Time {
|
||||||
return now().Add(10 * time.Second)
|
return now().Add(5 * time.Minute)
|
||||||
}
|
}
|
||||||
v = GetContextData(ctx, field, "my_config1")
|
v = GetContextData(ctx, field, "my_config1")
|
||||||
assert.Nil(t, v)
|
assert.Nil(t, v)
|
||||||
|
@ -114,7 +114,7 @@ type LogNameStatusCommitData struct {
|
|||||||
// Next returns the next LogStatusCommitData
|
// Next returns the next LogStatusCommitData
|
||||||
func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int, changed []bool, maxpathlen int) (*LogNameStatusCommitData, error) {
|
func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int, changed []bool, maxpathlen int) (*LogNameStatusCommitData, error) {
|
||||||
var err error
|
var err error
|
||||||
if g.next == nil || len(g.next) == 0 {
|
if len(g.next) == 0 {
|
||||||
g.buffull = false
|
g.buffull = false
|
||||||
g.next, err = g.rd.ReadSlice('\x00')
|
g.next, err = g.rd.ReadSlice('\x00')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -13,11 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NewDialContext returns a DialContext for Transport, the DialContext will do allow/block list check
|
// NewDialContext returns a DialContext for Transport, the DialContext will do allow/block list check
|
||||||
func NewDialContext(usage string, allowList, blockList *HostMatchList) func(ctx context.Context, network, addr string) (net.Conn, error) {
|
func NewDialContext(usage string, allowList, blockList *HostMatchList, proxy *url.URL) func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
return NewDialContextWithProxy(usage, allowList, blockList, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDialContextWithProxy(usage string, allowList, blockList *HostMatchList, proxy *url.URL) func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
// How Go HTTP Client works with redirection:
|
// How Go HTTP Client works with redirection:
|
||||||
// transport.RoundTrip URL=http://domain.com, Host=domain.com
|
// transport.RoundTrip URL=http://domain.com, Host=domain.com
|
||||||
// transport.DialContext addrOrHost=domain.com:80
|
// transport.DialContext addrOrHost=domain.com:80
|
||||||
|
@ -75,7 +75,8 @@ func HandleGenericETagTimeCache(req *http.Request, w http.ResponseWriter, etag s
|
|||||||
w.Header().Set("Etag", etag)
|
w.Header().Set("Etag", etag)
|
||||||
}
|
}
|
||||||
if lastModified != nil && !lastModified.IsZero() {
|
if lastModified != nil && !lastModified.IsZero() {
|
||||||
w.Header().Set("Last-Modified", lastModified.Format(http.TimeFormat))
|
// http.TimeFormat required a UTC time, refer to https://pkg.go.dev/net/http#TimeFormat
|
||||||
|
w.Header().Set("Last-Modified", lastModified.UTC().Format(http.TimeFormat))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(etag) > 0 {
|
if len(etag) > 0 {
|
||||||
|
@ -79,6 +79,7 @@ func ServeSetHeaders(w http.ResponseWriter, opts *ServeHeaderOptions) {
|
|||||||
httpcache.SetCacheControlInHeader(header, duration)
|
httpcache.SetCacheControlInHeader(header, duration)
|
||||||
|
|
||||||
if !opts.LastModified.IsZero() {
|
if !opts.LastModified.IsZero() {
|
||||||
|
// http.TimeFormat required a UTC time, refer to https://pkg.go.dev/net/http#TimeFormat
|
||||||
header.Set("Last-Modified", opts.LastModified.UTC().Format(http.TimeFormat))
|
header.Set("Last-Modified", opts.LastModified.UTC().Format(http.TimeFormat))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,6 @@ func getRequestScheme(req *http.Request) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func getForwardedHost(req *http.Request) string {
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host
|
|
||||||
return req.Header.Get("X-Forwarded-Host")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GuessCurrentAppURL tries to guess the current full app URL (with sub-path) by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
|
// GuessCurrentAppURL tries to guess the current full app URL (with sub-path) by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
|
||||||
func GuessCurrentAppURL(ctx context.Context) string {
|
func GuessCurrentAppURL(ctx context.Context) string {
|
||||||
return GuessCurrentHostURL(ctx) + setting.AppSubURL + "/"
|
return GuessCurrentHostURL(ctx) + setting.AppSubURL + "/"
|
||||||
@ -81,11 +76,9 @@ func GuessCurrentHostURL(ctx context.Context) string {
|
|||||||
if reqScheme == "" {
|
if reqScheme == "" {
|
||||||
return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
|
return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
|
||||||
}
|
}
|
||||||
reqHost := getForwardedHost(req)
|
// X-Forwarded-Host has many problems: non-standard, not well-defined (X-Forwarded-Port or not), conflicts with Host header.
|
||||||
if reqHost == "" {
|
// So do not use X-Forwarded-Host, just use Host header directly.
|
||||||
reqHost = req.Host
|
return reqScheme + "://" + req.Host
|
||||||
}
|
|
||||||
return reqScheme + "://" + reqHost
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeAbsoluteURL tries to make a link to an absolute URL:
|
// MakeAbsoluteURL tries to make a link to an absolute URL:
|
||||||
|
@ -70,7 +70,7 @@ func TestMakeAbsoluteURL(t *testing.T) {
|
|||||||
"X-Forwarded-Proto": {"https"},
|
"X-Forwarded-Proto": {"https"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
assert.Equal(t, "https://forwarded-host/foo", MakeAbsoluteURL(ctx, "/foo"))
|
assert.Equal(t, "https://user-host/foo", MakeAbsoluteURL(ctx, "/foo"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsCurrentGiteaSiteURL(t *testing.T) {
|
func TestIsCurrentGiteaSiteURL(t *testing.T) {
|
||||||
@ -119,5 +119,6 @@ func TestIsCurrentGiteaSiteURL(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
assert.True(t, IsCurrentGiteaSiteURL(ctx, "http://localhost:3000"))
|
assert.True(t, IsCurrentGiteaSiteURL(ctx, "http://localhost:3000"))
|
||||||
assert.True(t, IsCurrentGiteaSiteURL(ctx, "https://forwarded-host"))
|
assert.True(t, IsCurrentGiteaSiteURL(ctx, "https://user-host"))
|
||||||
|
assert.False(t, IsCurrentGiteaSiteURL(ctx, "https://forwarded-host"))
|
||||||
}
|
}
|
||||||
|
@ -284,6 +284,8 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
|
|||||||
searchRequest.AddFacet("languages", bleve.NewFacetRequest("Language", 10))
|
searchRequest.AddFacet("languages", bleve.NewFacetRequest("Language", 10))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
searchRequest.SortBy([]string{"-_score", "UpdatedAt"})
|
||||||
|
|
||||||
result, err := b.inner.Indexer.SearchInContext(ctx, searchRequest)
|
result, err := b.inner.Indexer.SearchInContext(ctx, searchRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, nil, err
|
return 0, nil, nil, err
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
|
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
|
||||||
inner_elasticsearch "code.gitea.io/gitea/modules/indexer/internal/elasticsearch"
|
inner_elasticsearch "code.gitea.io/gitea/modules/indexer/internal/elasticsearch"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/typesniffer"
|
"code.gitea.io/gitea/modules/typesniffer"
|
||||||
@ -197,8 +198,33 @@ func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha st
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes indexes by ids
|
// Delete entries by repoId
|
||||||
func (b *Indexer) Delete(ctx context.Context, repoID int64) error {
|
func (b *Indexer) Delete(ctx context.Context, repoID int64) error {
|
||||||
|
if err := b.doDelete(ctx, repoID); err != nil {
|
||||||
|
// Maybe there is a conflict during the delete operation, so we should retry after a refresh
|
||||||
|
log.Warn("Deletion of entries of repo %v within index %v was erroneus. Trying to refresh index before trying again", repoID, b.inner.VersionedIndexName(), err)
|
||||||
|
if err := b.refreshIndex(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := b.doDelete(ctx, repoID); err != nil {
|
||||||
|
log.Error("Could not delete entries of repo %v within index %v", repoID, b.inner.VersionedIndexName())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Indexer) refreshIndex(ctx context.Context) error {
|
||||||
|
if _, err := b.inner.Client.Refresh(b.inner.VersionedIndexName()).Do(ctx); err != nil {
|
||||||
|
log.Error("Error while trying to refresh index %v", b.inner.VersionedIndexName(), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete entries by repoId
|
||||||
|
func (b *Indexer) doDelete(ctx context.Context, repoID int64) error {
|
||||||
_, err := b.inner.Client.DeleteByQuery(b.inner.VersionedIndexName()).
|
_, err := b.inner.Client.DeleteByQuery(b.inner.VersionedIndexName()).
|
||||||
Query(elastic.NewTermsQuery("repo_id", repoID)).
|
Query(elastic.NewTermsQuery("repo_id", repoID)).
|
||||||
Do(ctx)
|
Do(ctx)
|
||||||
@ -318,7 +344,8 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
|
|||||||
NumOfFragments(0). // return all highting content on fragments
|
NumOfFragments(0). // return all highting content on fragments
|
||||||
HighlighterType("fvh"),
|
HighlighterType("fvh"),
|
||||||
).
|
).
|
||||||
Sort("repo_id", true).
|
Sort("_score", false).
|
||||||
|
Sort("updated_at", true).
|
||||||
From(start).Size(pageSize).
|
From(start).Size(pageSize).
|
||||||
Do(ctx)
|
Do(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -349,7 +376,8 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
|
|||||||
NumOfFragments(0). // return all highting content on fragments
|
NumOfFragments(0). // return all highting content on fragments
|
||||||
HighlighterType("fvh"),
|
HighlighterType("fvh"),
|
||||||
).
|
).
|
||||||
Sort("repo_id", true).
|
Sort("_score", false).
|
||||||
|
Sort("updated_at", true).
|
||||||
From(start).Size(pageSize).
|
From(start).Size(pageSize).
|
||||||
Do(ctx)
|
Do(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -401,7 +401,7 @@ func (f *valuedField) Render() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *valuedField) Value() string {
|
func (f *valuedField) Value() string {
|
||||||
return strings.TrimSpace(f.Get(fmt.Sprintf("form-field-" + f.ID)))
|
return strings.TrimSpace(f.Get(fmt.Sprintf("form-field-%s", f.ID)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *valuedField) Options() []*valuedOption {
|
func (f *valuedField) Options() []*valuedOption {
|
||||||
|
301
modules/lfstransfer/backend/backend.go
Normal file
301
modules/lfstransfer/backend/backend.go
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
"code.gitea.io/gitea/modules/lfs"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/git-lfs-transfer/transfer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version is the git-lfs-transfer protocol version number.
|
||||||
|
const Version = "1"
|
||||||
|
|
||||||
|
// Capabilities is a list of Git LFS capabilities supported by this package.
|
||||||
|
var Capabilities = []string{
|
||||||
|
"version=" + Version,
|
||||||
|
"locking",
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ transfer.Backend = &GiteaBackend{}
|
||||||
|
|
||||||
|
// GiteaBackend is an adapter between git-lfs-transfer library and Gitea's internal LFS API
|
||||||
|
type GiteaBackend struct {
|
||||||
|
ctx context.Context
|
||||||
|
server *url.URL
|
||||||
|
op string
|
||||||
|
token string
|
||||||
|
itoken string
|
||||||
|
logger transfer.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, repo, op, token string, logger transfer.Logger) (transfer.Backend, error) {
|
||||||
|
// runServ guarantees repo will be in form [owner]/[name].git
|
||||||
|
server, err := url.Parse(setting.LocalURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
server = server.JoinPath("api/internal/repo", repo, "info/lfs")
|
||||||
|
return &GiteaBackend{ctx: ctx, server: server, op: op, token: token, itoken: fmt.Sprintf("Bearer %s", setting.InternalToken), logger: logger}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch implements transfer.Backend
|
||||||
|
func (g *GiteaBackend) Batch(_ string, pointers []transfer.BatchItem, args transfer.Args) ([]transfer.BatchItem, error) {
|
||||||
|
reqBody := lfs.BatchRequest{Operation: g.op}
|
||||||
|
if transfer, ok := args[argTransfer]; ok {
|
||||||
|
reqBody.Transfers = []string{transfer}
|
||||||
|
}
|
||||||
|
if ref, ok := args[argRefname]; ok {
|
||||||
|
reqBody.Ref = &lfs.Reference{Name: ref}
|
||||||
|
}
|
||||||
|
reqBody.Objects = make([]lfs.Pointer, len(pointers))
|
||||||
|
for i := range pointers {
|
||||||
|
reqBody.Objects[i].Oid = pointers[i].Oid
|
||||||
|
reqBody.Objects[i].Size = pointers[i].Size
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyBytes, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json marshal error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
url := g.server.JoinPath("objects/batch").String()
|
||||||
|
headers := map[string]string{
|
||||||
|
headerAuthorisation: g.itoken,
|
||||||
|
headerAuthX: g.token,
|
||||||
|
headerAccept: mimeGitLFS,
|
||||||
|
headerContentType: mimeGitLFS,
|
||||||
|
}
|
||||||
|
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("http request error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
g.logger.Log("http statuscode error", resp.StatusCode, statusCodeToErr(resp.StatusCode))
|
||||||
|
return nil, statusCodeToErr(resp.StatusCode)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
respBytes, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("http read error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var respBody lfs.BatchResponse
|
||||||
|
err = json.Unmarshal(respBytes, &respBody)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json umarshal error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// rebuild slice, we can't rely on order in resp being the same as req
|
||||||
|
pointers = pointers[:0]
|
||||||
|
opNum := opMap[g.op]
|
||||||
|
for _, obj := range respBody.Objects {
|
||||||
|
pointer := transfer.Pointer{Oid: obj.Pointer.Oid, Size: obj.Pointer.Size}
|
||||||
|
item := transfer.BatchItem{Pointer: pointer, Args: map[string]string{}}
|
||||||
|
switch opNum {
|
||||||
|
case opDownload:
|
||||||
|
if action, ok := obj.Actions[actionDownload]; ok {
|
||||||
|
item.Present = true
|
||||||
|
idMap := obj.Actions
|
||||||
|
idMapBytes, err := json.Marshal(idMap)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json marshal error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
idMapStr := base64.StdEncoding.EncodeToString(idMapBytes)
|
||||||
|
item.Args[argID] = idMapStr
|
||||||
|
if authHeader, ok := action.Header[headerAuthorisation]; ok {
|
||||||
|
authHeaderB64 := base64.StdEncoding.EncodeToString([]byte(authHeader))
|
||||||
|
item.Args[argToken] = authHeaderB64
|
||||||
|
}
|
||||||
|
if action.ExpiresAt != nil {
|
||||||
|
item.Args[argExpiresAt] = action.ExpiresAt.String()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// must be an error, but the SSH protocol can't propagate individual errors
|
||||||
|
g.logger.Log("object not found", obj.Pointer.Oid, obj.Pointer.Size)
|
||||||
|
item.Present = false
|
||||||
|
}
|
||||||
|
case opUpload:
|
||||||
|
if action, ok := obj.Actions[actionUpload]; ok {
|
||||||
|
item.Present = false
|
||||||
|
idMap := obj.Actions
|
||||||
|
idMapBytes, err := json.Marshal(idMap)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json marshal error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
idMapStr := base64.StdEncoding.EncodeToString(idMapBytes)
|
||||||
|
item.Args[argID] = idMapStr
|
||||||
|
if authHeader, ok := action.Header[headerAuthorisation]; ok {
|
||||||
|
authHeaderB64 := base64.StdEncoding.EncodeToString([]byte(authHeader))
|
||||||
|
item.Args[argToken] = authHeaderB64
|
||||||
|
}
|
||||||
|
if action.ExpiresAt != nil {
|
||||||
|
item.Args[argExpiresAt] = action.ExpiresAt.String()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
item.Present = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pointers = append(pointers, item)
|
||||||
|
}
|
||||||
|
return pointers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download implements transfer.Backend. The returned reader must be closed by the
|
||||||
|
// caller.
|
||||||
|
func (g *GiteaBackend) Download(oid string, args transfer.Args) (io.ReadCloser, int64, error) {
|
||||||
|
idMapStr, exists := args[argID]
|
||||||
|
if !exists {
|
||||||
|
return nil, 0, ErrMissingID
|
||||||
|
}
|
||||||
|
idMapBytes, err := base64.StdEncoding.DecodeString(idMapStr)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("base64 decode error", err)
|
||||||
|
return nil, 0, transfer.ErrCorruptData
|
||||||
|
}
|
||||||
|
idMap := map[string]*lfs.Link{}
|
||||||
|
err = json.Unmarshal(idMapBytes, &idMap)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json unmarshal error", err)
|
||||||
|
return nil, 0, transfer.ErrCorruptData
|
||||||
|
}
|
||||||
|
action, exists := idMap[actionDownload]
|
||||||
|
if !exists {
|
||||||
|
g.logger.Log("argument id incorrect")
|
||||||
|
return nil, 0, transfer.ErrCorruptData
|
||||||
|
}
|
||||||
|
url := action.Href
|
||||||
|
headers := map[string]string{
|
||||||
|
headerAuthorisation: g.itoken,
|
||||||
|
headerAuthX: g.token,
|
||||||
|
headerAccept: mimeOctetStream,
|
||||||
|
}
|
||||||
|
req := newInternalRequest(g.ctx, url, http.MethodGet, headers, nil)
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, 0, statusCodeToErr(resp.StatusCode)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
respBytes, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
respSize := int64(len(respBytes))
|
||||||
|
respBuf := io.NopCloser(bytes.NewBuffer(respBytes))
|
||||||
|
return respBuf, respSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartUpload implements transfer.Backend.
|
||||||
|
func (g *GiteaBackend) Upload(oid string, size int64, r io.Reader, args transfer.Args) error {
|
||||||
|
idMapStr, exists := args[argID]
|
||||||
|
if !exists {
|
||||||
|
return ErrMissingID
|
||||||
|
}
|
||||||
|
idMapBytes, err := base64.StdEncoding.DecodeString(idMapStr)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("base64 decode error", err)
|
||||||
|
return transfer.ErrCorruptData
|
||||||
|
}
|
||||||
|
idMap := map[string]*lfs.Link{}
|
||||||
|
err = json.Unmarshal(idMapBytes, &idMap)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json unmarshal error", err)
|
||||||
|
return transfer.ErrCorruptData
|
||||||
|
}
|
||||||
|
action, exists := idMap[actionUpload]
|
||||||
|
if !exists {
|
||||||
|
g.logger.Log("argument id incorrect")
|
||||||
|
return transfer.ErrCorruptData
|
||||||
|
}
|
||||||
|
url := action.Href
|
||||||
|
headers := map[string]string{
|
||||||
|
headerAuthorisation: g.itoken,
|
||||||
|
headerAuthX: g.token,
|
||||||
|
headerContentType: mimeOctetStream,
|
||||||
|
headerContentLength: strconv.FormatInt(size, 10),
|
||||||
|
}
|
||||||
|
reqBytes, err := io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req := newInternalRequest(g.ctx, url, http.MethodPut, headers, reqBytes)
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return statusCodeToErr(resp.StatusCode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify implements transfer.Backend.
|
||||||
|
func (g *GiteaBackend) Verify(oid string, size int64, args transfer.Args) (transfer.Status, error) {
|
||||||
|
reqBody := lfs.Pointer{Oid: oid, Size: size}
|
||||||
|
|
||||||
|
bodyBytes, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
return transfer.NewStatus(transfer.StatusInternalServerError), err
|
||||||
|
}
|
||||||
|
idMapStr, exists := args[argID]
|
||||||
|
if !exists {
|
||||||
|
return transfer.NewStatus(transfer.StatusBadRequest, "missing argument: id"), ErrMissingID
|
||||||
|
}
|
||||||
|
idMapBytes, err := base64.StdEncoding.DecodeString(idMapStr)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("base64 decode error", err)
|
||||||
|
return transfer.NewStatus(transfer.StatusBadRequest, "corrupt argument: id"), transfer.ErrCorruptData
|
||||||
|
}
|
||||||
|
idMap := map[string]*lfs.Link{}
|
||||||
|
err = json.Unmarshal(idMapBytes, &idMap)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json unmarshal error", err)
|
||||||
|
return transfer.NewStatus(transfer.StatusBadRequest, "corrupt argument: id"), transfer.ErrCorruptData
|
||||||
|
}
|
||||||
|
action, exists := idMap[actionVerify]
|
||||||
|
if !exists {
|
||||||
|
// the server sent no verify action
|
||||||
|
return transfer.SuccessStatus(), nil
|
||||||
|
}
|
||||||
|
url := action.Href
|
||||||
|
headers := map[string]string{
|
||||||
|
headerAuthorisation: g.itoken,
|
||||||
|
headerAuthX: g.token,
|
||||||
|
headerAccept: mimeGitLFS,
|
||||||
|
headerContentType: mimeGitLFS,
|
||||||
|
}
|
||||||
|
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
return transfer.NewStatus(transfer.StatusInternalServerError), err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return transfer.NewStatus(uint32(resp.StatusCode), http.StatusText(resp.StatusCode)), statusCodeToErr(resp.StatusCode)
|
||||||
|
}
|
||||||
|
return transfer.SuccessStatus(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LockBackend implements transfer.Backend.
|
||||||
|
func (g *GiteaBackend) LockBackend(_ transfer.Args) transfer.LockBackend {
|
||||||
|
return newGiteaLockBackend(g)
|
||||||
|
}
|
296
modules/lfstransfer/backend/lock.go
Normal file
296
modules/lfstransfer/backend/lock.go
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
lfslock "code.gitea.io/gitea/modules/structs"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/git-lfs-transfer/transfer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ transfer.LockBackend = &giteaLockBackend{}
|
||||||
|
|
||||||
|
type giteaLockBackend struct {
|
||||||
|
ctx context.Context
|
||||||
|
g *GiteaBackend
|
||||||
|
server *url.URL
|
||||||
|
token string
|
||||||
|
itoken string
|
||||||
|
logger transfer.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGiteaLockBackend(g *GiteaBackend) transfer.LockBackend {
|
||||||
|
server := g.server.JoinPath("locks")
|
||||||
|
return &giteaLockBackend{ctx: g.ctx, g: g, server: server, token: g.token, itoken: g.itoken, logger: g.logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create implements transfer.LockBackend
|
||||||
|
func (g *giteaLockBackend) Create(path, refname string) (transfer.Lock, error) {
|
||||||
|
reqBody := lfslock.LFSLockRequest{Path: path}
|
||||||
|
|
||||||
|
bodyBytes, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json marshal error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
url := g.server.String()
|
||||||
|
headers := map[string]string{
|
||||||
|
headerAuthorisation: g.itoken,
|
||||||
|
headerAuthX: g.token,
|
||||||
|
headerAccept: mimeGitLFS,
|
||||||
|
headerContentType: mimeGitLFS,
|
||||||
|
}
|
||||||
|
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("http request error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
respBytes, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("http read error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusCreated {
|
||||||
|
g.logger.Log("http statuscode error", resp.StatusCode, statusCodeToErr(resp.StatusCode))
|
||||||
|
return nil, statusCodeToErr(resp.StatusCode)
|
||||||
|
}
|
||||||
|
var respBody lfslock.LFSLockResponse
|
||||||
|
err = json.Unmarshal(respBytes, &respBody)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json umarshal error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if respBody.Lock == nil {
|
||||||
|
g.logger.Log("api returned nil lock")
|
||||||
|
return nil, fmt.Errorf("api returned nil lock")
|
||||||
|
}
|
||||||
|
respLock := respBody.Lock
|
||||||
|
owner := userUnknown
|
||||||
|
if respLock.Owner != nil {
|
||||||
|
owner = respLock.Owner.Name
|
||||||
|
}
|
||||||
|
lock := newGiteaLock(g, respLock.ID, respLock.Path, respLock.LockedAt, owner)
|
||||||
|
return lock, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock implements transfer.LockBackend
|
||||||
|
func (g *giteaLockBackend) Unlock(lock transfer.Lock) error {
|
||||||
|
reqBody := lfslock.LFSLockDeleteRequest{}
|
||||||
|
|
||||||
|
bodyBytes, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json marshal error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
url := g.server.JoinPath(lock.ID(), "unlock").String()
|
||||||
|
headers := map[string]string{
|
||||||
|
headerAuthorisation: g.itoken,
|
||||||
|
headerAuthX: g.token,
|
||||||
|
headerAccept: mimeGitLFS,
|
||||||
|
headerContentType: mimeGitLFS,
|
||||||
|
}
|
||||||
|
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("http request error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
g.logger.Log("http statuscode error", resp.StatusCode, statusCodeToErr(resp.StatusCode))
|
||||||
|
return statusCodeToErr(resp.StatusCode)
|
||||||
|
}
|
||||||
|
// no need to read response
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPath implements transfer.LockBackend
|
||||||
|
func (g *giteaLockBackend) FromPath(path string) (transfer.Lock, error) {
|
||||||
|
v := url.Values{
|
||||||
|
argPath: []string{path},
|
||||||
|
}
|
||||||
|
|
||||||
|
respLocks, _, err := g.queryLocks(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(respLocks) == 0 {
|
||||||
|
return nil, transfer.ErrNotFound
|
||||||
|
}
|
||||||
|
return respLocks[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromID implements transfer.LockBackend
|
||||||
|
func (g *giteaLockBackend) FromID(id string) (transfer.Lock, error) {
|
||||||
|
v := url.Values{
|
||||||
|
argID: []string{id},
|
||||||
|
}
|
||||||
|
|
||||||
|
respLocks, _, err := g.queryLocks(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(respLocks) == 0 {
|
||||||
|
return nil, transfer.ErrNotFound
|
||||||
|
}
|
||||||
|
return respLocks[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range implements transfer.LockBackend
|
||||||
|
func (g *giteaLockBackend) Range(cursor string, limit int, iter func(transfer.Lock) error) (string, error) {
|
||||||
|
v := url.Values{
|
||||||
|
argLimit: []string{strconv.FormatInt(int64(limit), 10)},
|
||||||
|
}
|
||||||
|
if cursor != "" {
|
||||||
|
v[argCursor] = []string{cursor}
|
||||||
|
}
|
||||||
|
|
||||||
|
respLocks, cursor, err := g.queryLocks(v)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, lock := range respLocks {
|
||||||
|
err := iter(lock)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cursor, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *giteaLockBackend) queryLocks(v url.Values) ([]transfer.Lock, string, error) {
|
||||||
|
urlq := g.server.JoinPath() // get a copy
|
||||||
|
urlq.RawQuery = v.Encode()
|
||||||
|
url := urlq.String()
|
||||||
|
headers := map[string]string{
|
||||||
|
headerAuthorisation: g.itoken,
|
||||||
|
headerAuthX: g.token,
|
||||||
|
headerAccept: mimeGitLFS,
|
||||||
|
headerContentType: mimeGitLFS,
|
||||||
|
}
|
||||||
|
req := newInternalRequest(g.ctx, url, http.MethodGet, headers, nil)
|
||||||
|
resp, err := req.Response()
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("http request error", err)
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
respBytes, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("http read error", err)
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
g.logger.Log("http statuscode error", resp.StatusCode, statusCodeToErr(resp.StatusCode))
|
||||||
|
return nil, "", statusCodeToErr(resp.StatusCode)
|
||||||
|
}
|
||||||
|
var respBody lfslock.LFSLockList
|
||||||
|
err = json.Unmarshal(respBytes, &respBody)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Log("json umarshal error", err)
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
respLocks := make([]transfer.Lock, 0, len(respBody.Locks))
|
||||||
|
for _, respLock := range respBody.Locks {
|
||||||
|
owner := userUnknown
|
||||||
|
if respLock.Owner != nil {
|
||||||
|
owner = respLock.Owner.Name
|
||||||
|
}
|
||||||
|
lock := newGiteaLock(g, respLock.ID, respLock.Path, respLock.LockedAt, owner)
|
||||||
|
respLocks = append(respLocks, lock)
|
||||||
|
}
|
||||||
|
return respLocks, respBody.Next, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ transfer.Lock = &giteaLock{}
|
||||||
|
|
||||||
|
type giteaLock struct {
|
||||||
|
g *giteaLockBackend
|
||||||
|
id string
|
||||||
|
path string
|
||||||
|
lockedAt time.Time
|
||||||
|
owner string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGiteaLock(g *giteaLockBackend, id, path string, lockedAt time.Time, owner string) transfer.Lock {
|
||||||
|
return &giteaLock{g: g, id: id, path: path, lockedAt: lockedAt, owner: owner}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock implements transfer.Lock
|
||||||
|
func (g *giteaLock) Unlock() error {
|
||||||
|
return g.g.Unlock(g)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID implements transfer.Lock
|
||||||
|
func (g *giteaLock) ID() string {
|
||||||
|
return g.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path implements transfer.Lock
|
||||||
|
func (g *giteaLock) Path() string {
|
||||||
|
return g.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormattedTimestamp implements transfer.Lock
|
||||||
|
func (g *giteaLock) FormattedTimestamp() string {
|
||||||
|
return g.lockedAt.UTC().Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OwnerName implements transfer.Lock
|
||||||
|
func (g *giteaLock) OwnerName() string {
|
||||||
|
return g.owner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *giteaLock) CurrentUser() (string, error) {
|
||||||
|
return userSelf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsLockSpec implements transfer.Lock
|
||||||
|
func (g *giteaLock) AsLockSpec(ownerID bool) ([]string, error) {
|
||||||
|
msgs := []string{
|
||||||
|
fmt.Sprintf("lock %s", g.ID()),
|
||||||
|
fmt.Sprintf("path %s %s", g.ID(), g.Path()),
|
||||||
|
fmt.Sprintf("locked-at %s %s", g.ID(), g.FormattedTimestamp()),
|
||||||
|
fmt.Sprintf("ownername %s %s", g.ID(), g.OwnerName()),
|
||||||
|
}
|
||||||
|
if ownerID {
|
||||||
|
user, err := g.CurrentUser()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting current user: %w", err)
|
||||||
|
}
|
||||||
|
who := "theirs"
|
||||||
|
if user == g.OwnerName() {
|
||||||
|
who = "ours"
|
||||||
|
}
|
||||||
|
msgs = append(msgs, fmt.Sprintf("owner %s %s", g.ID(), who))
|
||||||
|
}
|
||||||
|
return msgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsArguments implements transfer.Lock
|
||||||
|
func (g *giteaLock) AsArguments() []string {
|
||||||
|
return []string{
|
||||||
|
fmt.Sprintf("id=%s", g.ID()),
|
||||||
|
fmt.Sprintf("path=%s", g.Path()),
|
||||||
|
fmt.Sprintf("locked-at=%s", g.FormattedTimestamp()),
|
||||||
|
fmt.Sprintf("ownername=%s", g.OwnerName()),
|
||||||
|
}
|
||||||
|
}
|
141
modules/lfstransfer/backend/util.go
Normal file
141
modules/lfstransfer/backend/util.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
|
"code.gitea.io/gitea/modules/proxyprotocol"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/git-lfs-transfer/transfer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTP headers
|
||||||
|
const (
|
||||||
|
headerAccept = "Accept"
|
||||||
|
headerAuthorisation = "Authorization"
|
||||||
|
headerAuthX = "X-Auth"
|
||||||
|
headerContentType = "Content-Type"
|
||||||
|
headerContentLength = "Content-Length"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MIME types
|
||||||
|
const (
|
||||||
|
mimeGitLFS = "application/vnd.git-lfs+json"
|
||||||
|
mimeOctetStream = "application/octet-stream"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SSH protocol action keys
|
||||||
|
const (
|
||||||
|
actionDownload = "download"
|
||||||
|
actionUpload = "upload"
|
||||||
|
actionVerify = "verify"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SSH protocol argument keys
|
||||||
|
const (
|
||||||
|
argCursor = "cursor"
|
||||||
|
argExpiresAt = "expires-at"
|
||||||
|
argID = "id"
|
||||||
|
argLimit = "limit"
|
||||||
|
argPath = "path"
|
||||||
|
argRefname = "refname"
|
||||||
|
argToken = "token"
|
||||||
|
argTransfer = "transfer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default username constants
|
||||||
|
const (
|
||||||
|
userSelf = "(self)"
|
||||||
|
userUnknown = "(unknown)"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Operations enum
|
||||||
|
const (
|
||||||
|
opNone = iota
|
||||||
|
opDownload
|
||||||
|
opUpload
|
||||||
|
)
|
||||||
|
|
||||||
|
var opMap = map[string]int{
|
||||||
|
"download": opDownload,
|
||||||
|
"upload": opUpload,
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrMissingID = fmt.Errorf("%w: missing id arg", transfer.ErrMissingData)
|
||||||
|
|
||||||
|
func statusCodeToErr(code int) error {
|
||||||
|
switch code {
|
||||||
|
case http.StatusBadRequest:
|
||||||
|
return transfer.ErrParseError
|
||||||
|
case http.StatusConflict:
|
||||||
|
return transfer.ErrConflict
|
||||||
|
case http.StatusForbidden:
|
||||||
|
return transfer.ErrForbidden
|
||||||
|
case http.StatusNotFound:
|
||||||
|
return transfer.ErrNotFound
|
||||||
|
case http.StatusUnauthorized:
|
||||||
|
return transfer.ErrUnauthorized
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("server returned status %v: %v", code, http.StatusText(code))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInternalRequest(ctx context.Context, url, method string, headers map[string]string, body []byte) *httplib.Request {
|
||||||
|
req := httplib.NewRequest(url, method).
|
||||||
|
SetContext(ctx).
|
||||||
|
SetTimeout(10*time.Second, 60*time.Second).
|
||||||
|
SetTLSClientConfig(&tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if setting.Protocol == setting.HTTPUnix {
|
||||||
|
req.SetTransport(&http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
var d net.Dialer
|
||||||
|
conn, err := d.DialContext(ctx, "unix", setting.HTTPAddr)
|
||||||
|
if err != nil {
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
if setting.LocalUseProxyProtocol {
|
||||||
|
if err = proxyprotocol.WriteLocalHeader(conn); err != nil {
|
||||||
|
_ = conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conn, err
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else if setting.LocalUseProxyProtocol {
|
||||||
|
req.SetTransport(&http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
var d net.Dialer
|
||||||
|
conn, err := d.DialContext(ctx, network, address)
|
||||||
|
if err != nil {
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
if err = proxyprotocol.WriteLocalHeader(conn); err != nil {
|
||||||
|
_ = conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, err
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range headers {
|
||||||
|
req.Header(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Body(body)
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
21
modules/lfstransfer/logger.go
Normal file
21
modules/lfstransfer/logger.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package lfstransfer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/charmbracelet/git-lfs-transfer/transfer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ transfer.Logger = (*GiteaLogger)(nil)
|
||||||
|
|
||||||
|
// noop logger for passing into transfer
|
||||||
|
type GiteaLogger struct{}
|
||||||
|
|
||||||
|
func newLogger() transfer.Logger {
|
||||||
|
return &GiteaLogger{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log implements transfer.Logger
|
||||||
|
func (g *GiteaLogger) Log(msg string, itms ...any) {
|
||||||
|
}
|
42
modules/lfstransfer/main.go
Normal file
42
modules/lfstransfer/main.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package lfstransfer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/lfstransfer/backend"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/git-lfs-transfer/transfer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Main(ctx context.Context, repo, verb, token string) error {
|
||||||
|
logger := newLogger()
|
||||||
|
pktline := transfer.NewPktline(os.Stdin, os.Stdout, logger)
|
||||||
|
giteaBackend, err := backend.New(ctx, repo, verb, token, logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cap := range backend.Capabilities {
|
||||||
|
if err := pktline.WritePacketText(cap); err != nil {
|
||||||
|
logger.Log("error sending capability due to error:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := pktline.WriteFlush(); err != nil {
|
||||||
|
logger.Log("error flushing capabilities:", err)
|
||||||
|
}
|
||||||
|
p := transfer.NewProcessor(pktline, giteaBackend, logger)
|
||||||
|
defer logger.Log("done processing commands")
|
||||||
|
switch verb {
|
||||||
|
case "upload":
|
||||||
|
return p.ProcessCommands(transfer.UploadOperation)
|
||||||
|
case "download":
|
||||||
|
return p.ProcessCommands(transfer.DownloadOperation)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown operation %q", verb)
|
||||||
|
}
|
||||||
|
}
|
@ -38,7 +38,7 @@ func camoHandleLink(link string) string {
|
|||||||
if setting.Camo.Enabled {
|
if setting.Camo.Enabled {
|
||||||
lnkURL, err := url.Parse(link)
|
lnkURL, err := url.Parse(link)
|
||||||
if err == nil && lnkURL.IsAbs() && !strings.HasPrefix(link, setting.AppURL) &&
|
if err == nil && lnkURL.IsAbs() && !strings.HasPrefix(link, setting.AppURL) &&
|
||||||
(setting.Camo.Allways || lnkURL.Scheme != "https") {
|
(setting.Camo.Always || lnkURL.Scheme != "https") {
|
||||||
return CamoEncode(link)
|
return CamoEncode(link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ func TestCamoHandleLink(t *testing.T) {
|
|||||||
"https://image.proxy/eivin43gJwGVIjR9MiYYtFIk0mw/aHR0cDovL3Rlc3RpbWFnZXMub3JnL2ltZy5qcGc",
|
"https://image.proxy/eivin43gJwGVIjR9MiYYtFIk0mw/aHR0cDovL3Rlc3RpbWFnZXMub3JnL2ltZy5qcGc",
|
||||||
camoHandleLink("http://testimages.org/img.jpg"))
|
camoHandleLink("http://testimages.org/img.jpg"))
|
||||||
|
|
||||||
setting.Camo.Allways = true
|
setting.Camo.Always = true
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
"https://gitea.com/img.jpg",
|
"https://gitea.com/img.jpg",
|
||||||
camoHandleLink("https://gitea.com/img.jpg"))
|
camoHandleLink("https://gitea.com/img.jpg"))
|
||||||
|
@ -38,4 +38,7 @@ type MigrateOptions struct {
|
|||||||
ReleaseAssets bool
|
ReleaseAssets bool
|
||||||
MigrateToRepoID int64
|
MigrateToRepoID int64
|
||||||
MirrorInterval string `json:"mirror_interval"`
|
MirrorInterval string `json:"mirror_interval"`
|
||||||
|
|
||||||
|
AWSAccessKeyID string
|
||||||
|
AWSSecretAccessKey string
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ type Metadata struct {
|
|||||||
Homepage string `json:"homepage,omitempty"`
|
Homepage string `json:"homepage,omitempty"`
|
||||||
License Licenses `json:"license,omitempty"`
|
License Licenses `json:"license,omitempty"`
|
||||||
Authors []Author `json:"authors,omitempty"`
|
Authors []Author `json:"authors,omitempty"`
|
||||||
|
Bin []string `json:"bin,omitempty"`
|
||||||
Autoload map[string]any `json:"autoload,omitempty"`
|
Autoload map[string]any `json:"autoload,omitempty"`
|
||||||
AutoloadDev map[string]any `json:"autoload-dev,omitempty"`
|
AutoloadDev map[string]any `json:"autoload-dev,omitempty"`
|
||||||
Extra map[string]any `json:"extra,omitempty"`
|
Extra map[string]any `json:"extra,omitempty"`
|
||||||
|
@ -120,7 +120,7 @@ func (q *baseChannel) RemoveAll(ctx context.Context) error {
|
|||||||
q.mu.Lock()
|
q.mu.Lock()
|
||||||
defer q.mu.Unlock()
|
defer q.mu.Unlock()
|
||||||
|
|
||||||
for q.c != nil && len(q.c) > 0 {
|
for len(q.c) > 0 {
|
||||||
<-q.c
|
<-q.c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ type LicenseValues struct {
|
|||||||
func GetLicense(name string, values *LicenseValues) ([]byte, error) {
|
func GetLicense(name string, values *LicenseValues) ([]byte, error) {
|
||||||
data, err := options.License(name)
|
data, err := options.License(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("GetRepoInitFile[%s]: %w", name, err)
|
return nil, fmt.Errorf("GetLicense[%s]: %w", name, err)
|
||||||
}
|
}
|
||||||
return fillLicensePlaceholder(name, values, data), nil
|
return fillLicensePlaceholder(name, values, data), nil
|
||||||
}
|
}
|
||||||
|
@ -62,11 +62,11 @@ func (c logCompression) IsValid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c logCompression) IsNone() bool {
|
func (c logCompression) IsNone() bool {
|
||||||
return c == "" || strings.ToLower(string(c)) == "none"
|
return strings.ToLower(string(c)) == "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c logCompression) IsZstd() bool {
|
func (c logCompression) IsZstd() bool {
|
||||||
return strings.ToLower(string(c)) == "zstd"
|
return c == "" || strings.ToLower(string(c)) == "zstd"
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadActionsFrom(rootCfg ConfigProvider) error {
|
func loadActionsFrom(rootCfg ConfigProvider) error {
|
||||||
|
@ -29,4 +29,6 @@ const (
|
|||||||
UserFeatureManageGPGKeys = "manage_gpg_keys"
|
UserFeatureManageGPGKeys = "manage_gpg_keys"
|
||||||
UserFeatureManageMFA = "manage_mfa"
|
UserFeatureManageMFA = "manage_mfa"
|
||||||
UserFeatureManageCredentials = "manage_credentials"
|
UserFeatureManageCredentials = "manage_credentials"
|
||||||
|
UserFeatureChangeUsername = "change_username"
|
||||||
|
UserFeatureChangeFullName = "change_full_name"
|
||||||
)
|
)
|
||||||
|
@ -3,18 +3,28 @@
|
|||||||
|
|
||||||
package setting
|
package setting
|
||||||
|
|
||||||
import "code.gitea.io/gitea/modules/log"
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
var Camo = struct {
|
var Camo = struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
ServerURL string `ini:"SERVER_URL"`
|
ServerURL string `ini:"SERVER_URL"`
|
||||||
HMACKey string `ini:"HMAC_KEY"`
|
HMACKey string `ini:"HMAC_KEY"`
|
||||||
Allways bool
|
Always bool
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
func loadCamoFrom(rootCfg ConfigProvider) {
|
func loadCamoFrom(rootCfg ConfigProvider) {
|
||||||
mustMapSetting(rootCfg, "camo", &Camo)
|
mustMapSetting(rootCfg, "camo", &Camo)
|
||||||
if Camo.Enabled {
|
if Camo.Enabled {
|
||||||
|
oldValue := rootCfg.Section("camo").Key("ALLWAYS").MustString("")
|
||||||
|
if oldValue != "" {
|
||||||
|
log.Warn("camo.ALLWAYS is deprecated, use camo.ALWAYS instead")
|
||||||
|
Camo.Always, _ = strconv.ParseBool(oldValue)
|
||||||
|
}
|
||||||
|
|
||||||
if Camo.ServerURL == "" || Camo.HMACKey == "" {
|
if Camo.ServerURL == "" || Camo.HMACKey == "" {
|
||||||
log.Fatal(`Camo settings require "SERVER_URL" and HMAC_KEY`)
|
log.Fatal(`Camo settings require "SERVER_URL" and HMAC_KEY`)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
// LFS represents the configuration for Git LFS
|
// LFS represents the configuration for Git LFS
|
||||||
var LFS = struct {
|
var LFS = struct {
|
||||||
StartServer bool `ini:"LFS_START_SERVER"`
|
StartServer bool `ini:"LFS_START_SERVER"`
|
||||||
|
AllowPureSSH bool `ini:"LFS_ALLOW_PURE_SSH"`
|
||||||
JWTSecretBytes []byte `ini:"-"`
|
JWTSecretBytes []byte `ini:"-"`
|
||||||
HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"`
|
HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"`
|
||||||
MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"`
|
MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"`
|
||||||
|
@ -37,6 +37,7 @@ var (
|
|||||||
DisableQueryAuthToken bool
|
DisableQueryAuthToken bool
|
||||||
CSRFCookieName = "_csrf"
|
CSRFCookieName = "_csrf"
|
||||||
CSRFCookieHTTPOnly = true
|
CSRFCookieHTTPOnly = true
|
||||||
|
RecordUserSignupMetadata = false
|
||||||
)
|
)
|
||||||
|
|
||||||
// loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set
|
// loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set
|
||||||
@ -164,6 +165,8 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
|
|||||||
// TODO: default value should be true in future releases
|
// TODO: default value should be true in future releases
|
||||||
DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false)
|
DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false)
|
||||||
|
|
||||||
|
RecordUserSignupMetadata = sec.Key("RECORD_USER_SIGNUP_METADATA").MustBool(false)
|
||||||
|
|
||||||
// warn if the setting is set to false explicitly
|
// warn if the setting is set to false explicitly
|
||||||
if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken {
|
if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken {
|
||||||
log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.")
|
log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.")
|
||||||
|
@ -114,7 +114,7 @@ func convertAzureBlobErr(err error) error {
|
|||||||
if !errors.As(err, &respErr) {
|
if !errors.As(err, &respErr) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return fmt.Errorf(respErr.ErrorCode)
|
return fmt.Errorf("%s", respErr.ErrorCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAzureBlobStorage returns a azure blob storage
|
// NewAzureBlobStorage returns a azure blob storage
|
||||||
|
@ -114,6 +114,7 @@ type Repository struct {
|
|||||||
MirrorUpdated time.Time `json:"mirror_updated,omitempty"`
|
MirrorUpdated time.Time `json:"mirror_updated,omitempty"`
|
||||||
RepoTransfer *RepoTransfer `json:"repo_transfer"`
|
RepoTransfer *RepoTransfer `json:"repo_transfer"`
|
||||||
Topics []string `json:"topics"`
|
Topics []string `json:"topics"`
|
||||||
|
Licenses []string `json:"licenses"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRepoOption options when creating repository
|
// CreateRepoOption options when creating repository
|
||||||
@ -300,6 +301,7 @@ const (
|
|||||||
OneDevService // 6 onedev service
|
OneDevService // 6 onedev service
|
||||||
GitBucketService // 7 gitbucket service
|
GitBucketService // 7 gitbucket service
|
||||||
CodebaseService // 8 codebase service
|
CodebaseService // 8 codebase service
|
||||||
|
CodeCommitService // 9 codecommit service
|
||||||
)
|
)
|
||||||
|
|
||||||
// Name represents the service type's name
|
// Name represents the service type's name
|
||||||
@ -325,6 +327,8 @@ func (gt GitServiceType) Title() string {
|
|||||||
return "GitBucket"
|
return "GitBucket"
|
||||||
case CodebaseService:
|
case CodebaseService:
|
||||||
return "Codebase"
|
return "Codebase"
|
||||||
|
case CodeCommitService:
|
||||||
|
return "CodeCommit"
|
||||||
case PlainGitService:
|
case PlainGitService:
|
||||||
return "Git"
|
return "Git"
|
||||||
}
|
}
|
||||||
@ -361,6 +365,9 @@ type MigrateRepoOptions struct {
|
|||||||
PullRequests bool `json:"pull_requests"`
|
PullRequests bool `json:"pull_requests"`
|
||||||
Releases bool `json:"releases"`
|
Releases bool `json:"releases"`
|
||||||
MirrorInterval string `json:"mirror_interval"`
|
MirrorInterval string `json:"mirror_interval"`
|
||||||
|
|
||||||
|
AWSAccessKeyID string `json:"aws_access_key_id"`
|
||||||
|
AWSSecretAccessKey string `json:"aws_secret_access_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenAuth represents whether a service type supports token-based auth
|
// TokenAuth represents whether a service type supports token-based auth
|
||||||
@ -382,6 +389,7 @@ var SupportedFullGitService = []GitServiceType{
|
|||||||
OneDevService,
|
OneDevService,
|
||||||
GitBucketService,
|
GitBucketService,
|
||||||
CodebaseService,
|
CodebaseService,
|
||||||
|
CodeCommitService,
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepoTransfer represents a pending repo transfer
|
// RepoTransfer represents a pending repo transfer
|
||||||
|
@ -34,7 +34,7 @@ func AvatarHTML(src string, size int, class, name string) template.HTML {
|
|||||||
name = "avatar"
|
name = "avatar"
|
||||||
}
|
}
|
||||||
|
|
||||||
return template.HTML(`<img class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
|
return template.HTML(`<img loading="lazy" class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avatar renders user avatars. args: user, size (int), class (string)
|
// Avatar renders user avatars. args: user, size (int), class (string)
|
||||||
|
14
options/gitignore/Hexo
Normal file
14
options/gitignore/Hexo
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# gitignore template for Hexo sites
|
||||||
|
# website: https://hexo.io/
|
||||||
|
# Recommended: Node.gitignore
|
||||||
|
|
||||||
|
# Ignore generated directory
|
||||||
|
public/
|
||||||
|
|
||||||
|
# Ignore temp files
|
||||||
|
tmp/
|
||||||
|
.tmp*
|
||||||
|
|
||||||
|
# additional files
|
||||||
|
db.json
|
||||||
|
.deploy*/
|
@ -16,6 +16,8 @@ _autosave-*
|
|||||||
*-save.pro
|
*-save.pro
|
||||||
*-save.kicad_pcb
|
*-save.kicad_pcb
|
||||||
fp-info-cache
|
fp-info-cache
|
||||||
|
~*.lck
|
||||||
|
\#auto_saved_files#
|
||||||
|
|
||||||
# Netlist files (exported from Eeschema)
|
# Netlist files (exported from Eeschema)
|
||||||
*.net
|
*.net
|
||||||
|
3
options/gitignore/ReScript
Normal file
3
options/gitignore/ReScript
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/node_modules/
|
||||||
|
/lib/
|
||||||
|
.bsb.lock
|
3
options/gitignore/Terragrunt
Normal file
3
options/gitignore/Terragrunt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Ignore the default terragrunt cache directory
|
||||||
|
# https://terragrunt.gruntwork.io/docs/features/caching/
|
||||||
|
.terragrunt-cache
|
2
options/gitignore/Zig
Normal file
2
options/gitignore/Zig
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.zig-cache/
|
||||||
|
zig-out/
|
14
options/license/Boehm-GC-without-fee
Normal file
14
options/license/Boehm-GC-without-fee
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Copyright (c) 2000
|
||||||
|
SWsoft company
|
||||||
|
|
||||||
|
Modifications copyright (c) 2001, 2013. Oracle and/or its affiliates.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
This material is provided "as is", with absolutely no warranty expressed
|
||||||
|
or implied. Any use is at your own risk.
|
||||||
|
|
||||||
|
Permission to use or copy this software for any purpose is hereby granted
|
||||||
|
without fee, provided the above notices are retained on all copies.
|
||||||
|
Permission to modify the code and to distribute modified code is granted,
|
||||||
|
provided the above notices are retained, and a notice that the code was
|
||||||
|
modified is included with the above copyright notice.
|
13
options/license/DocBook-Stylesheet
Normal file
13
options/license/DocBook-Stylesheet
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Copyright 2005 Norman Walsh, Sun Microsystems,
|
||||||
|
Inc., and the Organization for the Advancement
|
||||||
|
of Structured Information Standards (OASIS).
|
||||||
|
|
||||||
|
Release: $Id: db4-upgrade.xsl 8905 2010-09-12 11:47:07Z bobstayton $
|
||||||
|
|
||||||
|
Permission to use, copy, modify and distribute this stylesheet
|
||||||
|
and its accompanying documentation for any purpose and
|
||||||
|
without fee is hereby granted in perpetuity, provided that
|
||||||
|
the above copyright notice and this paragraph appear in
|
||||||
|
all copies. The copyright holders make no representation
|
||||||
|
about the suitability of the schema for any purpose. It
|
||||||
|
is provided "as is" without expressed or implied warranty.
|
10
options/license/GPL-3.0-389-ds-base-exception
Normal file
10
options/license/GPL-3.0-389-ds-base-exception
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Additional permission under GPLv3 section 7:
|
||||||
|
|
||||||
|
If you modify this Program, or any covered work, by
|
||||||
|
linking or combining it with OpenSSL, or a modified
|
||||||
|
version of OpenSSL licensed under the OpenSSL license
|
||||||
|
(https://www.openssl.org/source/license.html), the licensors of this
|
||||||
|
Program grant you additional permission to convey the resulting work.
|
||||||
|
Corresponding Source for a non-source form of such a combination
|
||||||
|
shall include the source code for the parts that are licensed
|
||||||
|
under the OpenSSL license as well as that of the covered work.
|
30
options/license/MIT-Click
Normal file
30
options/license/MIT-Click
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
Portions of this software are subject to the license below. The relevant
|
||||||
|
source files are clearly marked; they refer to this file using the phrase
|
||||||
|
"the Click LICENSE file". This license is an MIT license, plus a clause
|
||||||
|
(taken from the W3C license) requiring prior written permission to use our
|
||||||
|
names in publicity.
|
||||||
|
|
||||||
|
===========================================================================
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
The name and trademarks of copyright holders may NOT be used in advertising
|
||||||
|
or publicity pertaining to the Software without specific, written prior
|
||||||
|
permission. Title to copyright in this Software and any associated
|
||||||
|
documentation will at all times remain with copyright holders.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
75
options/license/Sendmail-Open-Source-1.1
Normal file
75
options/license/Sendmail-Open-Source-1.1
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
SENDMAIL OPEN SOURCE LICENSE
|
||||||
|
|
||||||
|
The following license terms and conditions apply to this open source
|
||||||
|
software ("Software"), unless a different license is obtained directly
|
||||||
|
from Sendmail, Inc. ("Sendmail") located at 6475 Christie Ave, Suite 350,
|
||||||
|
Emeryville, CA 94608, USA.
|
||||||
|
|
||||||
|
Use, modification and redistribution (including distribution of any
|
||||||
|
modified or derived work) of the Software in source and binary forms is
|
||||||
|
permitted only if each of the following conditions of 1-6 are met:
|
||||||
|
|
||||||
|
1. Redistributions of the Software qualify as "freeware" or "open
|
||||||
|
source software" under one of the following terms:
|
||||||
|
|
||||||
|
(a) Redistributions are made at no charge beyond the reasonable
|
||||||
|
cost of materials and delivery; or
|
||||||
|
|
||||||
|
(b) Redistributions are accompanied by a copy of the modified
|
||||||
|
Source Code (on an acceptable machine-readable medium) or by an
|
||||||
|
irrevocable offer to provide a copy of the modified Source Code
|
||||||
|
(on an acceptable machine-readable medium) for up to three years
|
||||||
|
at the cost of materials and delivery. Such redistributions must
|
||||||
|
allow further use, modification, and redistribution of the Source
|
||||||
|
Code under substantially the same terms as this license. For
|
||||||
|
the purposes of redistribution "Source Code" means the complete
|
||||||
|
human-readable, compilable, linkable, and operational source
|
||||||
|
code of the redistributed module(s) including all modifications.
|
||||||
|
|
||||||
|
2. Redistributions of the Software Source Code must retain the
|
||||||
|
copyright notices as they appear in each Source Code file, these
|
||||||
|
license terms and conditions, and the disclaimer/limitation of
|
||||||
|
liability set forth in paragraph 6 below. Redistributions of the
|
||||||
|
Software Source Code must also comply with the copyright notices
|
||||||
|
and/or license terms and conditions imposed by contributors on
|
||||||
|
embedded code. The contributors' license terms and conditions
|
||||||
|
and/or copyright notices are contained in the Source Code
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
3. Redistributions of the Software in binary form must reproduce the
|
||||||
|
Copyright Notice described below, these license terms and conditions,
|
||||||
|
and the disclaimer/limitation of liability set forth in paragraph
|
||||||
|
6 below, in the documentation and/or other materials provided with
|
||||||
|
the binary distribution. For the purposes of binary distribution,
|
||||||
|
"Copyright Notice" refers to the following language: "Copyright (c)
|
||||||
|
1998-2009 Sendmail, Inc. All rights reserved."
|
||||||
|
|
||||||
|
4. Neither the name, trademark or logo of Sendmail, Inc. (including
|
||||||
|
without limitation its subsidiaries or affiliates) or its contributors
|
||||||
|
may be used to endorse or promote products, or software or services
|
||||||
|
derived from this Software without specific prior written permission.
|
||||||
|
The name "sendmail" is a registered trademark and service mark of
|
||||||
|
Sendmail, Inc.
|
||||||
|
|
||||||
|
5. We reserve the right to cancel this license if you do not comply with
|
||||||
|
the terms. This license is governed by California law and both of us
|
||||||
|
agree that for any dispute arising out of or relating to this Software,
|
||||||
|
that jurisdiction and venue is proper in San Francisco or Alameda
|
||||||
|
counties. These license terms and conditions reflect the complete
|
||||||
|
agreement for the license of the Software (which means this supercedes
|
||||||
|
prior or contemporaneous agreements or representations). If any term
|
||||||
|
or condition under this license is found to be invalid, the remaining
|
||||||
|
terms and conditions still apply.
|
||||||
|
|
||||||
|
6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY
|
||||||
|
SENDMAIL AND ITS CONTRIBUTORS "AS IS" WITHOUT WARRANTY OF ANY KIND
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT AND FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE ARE EXPRESSLY DISCLAIMED. IN NO EVENT SHALL SENDMAIL
|
||||||
|
OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||||
|
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||||
|
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
WITHOUT LIMITATION NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||||
|
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
58
options/license/TrustedQSL
Normal file
58
options/license/TrustedQSL
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
Copyright (C) 2001-2015 American Radio Relay League, Inc. All rights
|
||||||
|
reserved.
|
||||||
|
|
||||||
|
Portions (C) 2003-2023 The TrustedQSL Developers. Please see the AUTHORS.txt
|
||||||
|
file for contributors.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
1. Any redistribution of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the disclaimer shown in
|
||||||
|
Paragraph 5 (below).
|
||||||
|
|
||||||
|
2. Redistribution in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the disclaimer shown in
|
||||||
|
Paragraph 5 (below) in the documentation and/or other materials
|
||||||
|
provided with the distribution.
|
||||||
|
|
||||||
|
3. Products derived from or including this software may not use
|
||||||
|
"Logbook of the World" or "LoTW" or any other American Radio Relay
|
||||||
|
League, Incorporated trademarks or servicemarks in their names
|
||||||
|
without prior written permission of the ARRL. See Paragraph 6
|
||||||
|
(below) for contact information.
|
||||||
|
|
||||||
|
4. Use of this software does not imply endorsement by ARRL of
|
||||||
|
products derived from or including this software and vendors may not
|
||||||
|
claim such endorsement.
|
||||||
|
|
||||||
|
5. Disclaimer: This software is provided "as-is" without
|
||||||
|
representation, guarantee or warranty of any kind, either express or
|
||||||
|
implied, including but not limited to the implied warranties of
|
||||||
|
merchantability or of fitness for a particular purpose. The entire
|
||||||
|
risk as to the quality and performance of the software is solely
|
||||||
|
with you. Should the software prove defective, you (and not the
|
||||||
|
American Radio Relay League, its officers, directors, employees or
|
||||||
|
agents) assume the entire cost of all necessary servicing, repair or
|
||||||
|
correction. In no event will ARRL be liable to you or to any third
|
||||||
|
party for any damages, whether direct or indirect, including lost
|
||||||
|
profits, lost savings, or other incidental or consequential damages
|
||||||
|
arising out of the use or inability to use such software, regardless
|
||||||
|
of whether ARRL has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
6. Contact information:
|
||||||
|
|
||||||
|
American Radio Relay League, Inc.
|
||||||
|
Attn: Logbook of the World Manager
|
||||||
|
225 Main St
|
||||||
|
Newington, CT 06111
|
||||||
|
voice: 860-594-0200
|
||||||
|
fax: 860-594-0259
|
||||||
|
email: logbook@arrl.org
|
||||||
|
Worldwide Web: www.arrl.org
|
||||||
|
|
||||||
|
This software consists of voluntary contributions made by many
|
||||||
|
individuals on behalf of the ARRL. More information on the "Logbook
|
||||||
|
of The World" project and the ARRL is available from the ARRL Web
|
||||||
|
site at www.arrl.org.
|
1
options/license/etc/license-aliases.json
Normal file
1
options/license/etc/license-aliases.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"AGPL-1.0-only":"AGPL-1.0","AGPL-1.0-or-later":"AGPL-1.0","AGPL-3.0-only":"AGPL-3.0","AGPL-3.0-or-later":"AGPL-3.0","CAL-1.0":"CAL-1.0","CAL-1.0-Combined-Work-Exception":"CAL-1.0","GFDL-1.1-invariants-only":"GFDL-1.1","GFDL-1.1-invariants-or-later":"GFDL-1.1","GFDL-1.1-no-invariants-only":"GFDL-1.1","GFDL-1.1-no-invariants-or-later":"GFDL-1.1","GFDL-1.1-only":"GFDL-1.1","GFDL-1.1-or-later":"GFDL-1.1","GFDL-1.2-invariants-only":"GFDL-1.2","GFDL-1.2-invariants-or-later":"GFDL-1.2","GFDL-1.2-no-invariants-only":"GFDL-1.2","GFDL-1.2-no-invariants-or-later":"GFDL-1.2","GFDL-1.2-only":"GFDL-1.2","GFDL-1.2-or-later":"GFDL-1.2","GFDL-1.3-invariants-only":"GFDL-1.3","GFDL-1.3-invariants-or-later":"GFDL-1.3","GFDL-1.3-no-invariants-only":"GFDL-1.3","GFDL-1.3-no-invariants-or-later":"GFDL-1.3","GFDL-1.3-only":"GFDL-1.3","GFDL-1.3-or-later":"GFDL-1.3","GPL-1.0-only":"GPL-1.0","GPL-1.0-or-later":"GPL-1.0","GPL-2.0-only":"GPL-2.0","GPL-2.0-or-later":"GPL-2.0","GPL-3.0-only":"GPL-3.0","GPL-3.0-or-later":"GPL-3.0","LGPL-2.0-only":"LGPL-2.0","LGPL-2.0-or-later":"LGPL-2.0","LGPL-2.1-only":"LGPL-2.1","LGPL-2.1-or-later":"LGPL-2.1","LGPL-3.0-only":"LGPL-3.0","LGPL-3.0-or-later":"LGPL-3.0","MPL-2.0":"MPL-2.0","MPL-2.0-no-copyleft-exception":"MPL-2.0","OFL-1.0":"OFL-1.0","OFL-1.0-RFN":"OFL-1.0","OFL-1.0-no-RFN":"OFL-1.0","OFL-1.1":"OFL-1.1","OFL-1.1-RFN":"OFL-1.1","OFL-1.1-no-RFN":"OFL-1.1"}
|
23
options/license/harbour-exception
Normal file
23
options/license/harbour-exception
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
As a special exception, the Harbour Project gives permission for
|
||||||
|
additional uses of the text contained in its release of Harbour.
|
||||||
|
|
||||||
|
The exception is that, if you link the Harbour libraries with other
|
||||||
|
files to produce an executable, this does not by itself cause the
|
||||||
|
resulting executable to be covered by the GNU General Public License.
|
||||||
|
Your use of that executable is in no way restricted on account of
|
||||||
|
linking the Harbour library code into it.
|
||||||
|
|
||||||
|
This exception does not however invalidate any other reasons why
|
||||||
|
the executable file might be covered by the GNU General Public License.
|
||||||
|
|
||||||
|
This exception applies only to the code released by the Harbour
|
||||||
|
Project under the name Harbour. If you copy code from other
|
||||||
|
Harbour Project or Free Software Foundation releases into a copy of
|
||||||
|
Harbour, as the General Public License permits, the exception does
|
||||||
|
not apply to the code that you add in this way. To avoid misleading
|
||||||
|
anyone as to the status of such modified files, you must delete
|
||||||
|
this exception notice from them.
|
||||||
|
|
||||||
|
If you write modifications of your own for Harbour, it is your choice
|
||||||
|
whether to permit this exception to apply to your modifications.
|
||||||
|
If you do not wish that, delete this exception notice.
|
@ -218,8 +218,6 @@ string.desc=Z – A
|
|||||||
|
|
||||||
[error]
|
[error]
|
||||||
occurred=Došlo k chybě
|
occurred=Došlo k chybě
|
||||||
missing_csrf=Špatný požadavek: Neexistuje CSRF token
|
|
||||||
invalid_csrf=Špatný požadavek: Neplatný CSRF token
|
|
||||||
not_found=Cíl nebyl nalezen.
|
not_found=Cíl nebyl nalezen.
|
||||||
network_error=Chyba sítě
|
network_error=Chyba sítě
|
||||||
|
|
||||||
@ -691,7 +689,6 @@ public_profile=Veřejný profil
|
|||||||
biography_placeholder=Řekněte nám něco o sobě! (Můžete použít Markdown)
|
biography_placeholder=Řekněte nám něco o sobě! (Můžete použít Markdown)
|
||||||
location_placeholder=Sdílejte svou přibližnou polohu s ostatními
|
location_placeholder=Sdílejte svou přibližnou polohu s ostatními
|
||||||
profile_desc=Nastavte, jak bude váš profil zobrazen ostatním uživatelům. Vaše hlavní e-mailová adresa bude použita pro oznámení, obnovení hesla a operace Git.
|
profile_desc=Nastavte, jak bude váš profil zobrazen ostatním uživatelům. Vaše hlavní e-mailová adresa bude použita pro oznámení, obnovení hesla a operace Git.
|
||||||
password_username_disabled=Externí uživatelé nemohou měnit svoje uživatelské jméno. Kontaktujte prosím svého administrátora pro více detailů.
|
|
||||||
full_name=Celé jméno
|
full_name=Celé jméno
|
||||||
website=Web
|
website=Web
|
||||||
location=Místo
|
location=Místo
|
||||||
@ -1701,7 +1698,6 @@ issues.dependency.add_error_dep_not_same_repo=Oba úkoly musí být ve stejném
|
|||||||
issues.review.self.approval=Nemůžete schválit svůj pull request.
|
issues.review.self.approval=Nemůžete schválit svůj pull request.
|
||||||
issues.review.self.rejection=Nemůžete požadovat změny ve svém vlastním pull requestu.
|
issues.review.self.rejection=Nemůžete požadovat změny ve svém vlastním pull requestu.
|
||||||
issues.review.approve=schválil tyto změny %s
|
issues.review.approve=schválil tyto změny %s
|
||||||
issues.review.comment=Okomentovat
|
|
||||||
issues.review.dismissed=zamítl/a posouzení od %s %s
|
issues.review.dismissed=zamítl/a posouzení od %s %s
|
||||||
issues.review.dismissed_label=Zamítnuto
|
issues.review.dismissed_label=Zamítnuto
|
||||||
issues.review.left_comment=zanechal komentář
|
issues.review.left_comment=zanechal komentář
|
||||||
@ -1726,6 +1722,7 @@ issues.review.hide_resolved=Skrýt vyřešené
|
|||||||
issues.review.resolve_conversation=Vyřešit konverzaci
|
issues.review.resolve_conversation=Vyřešit konverzaci
|
||||||
issues.review.un_resolve_conversation=Nevyřešit konverzaci
|
issues.review.un_resolve_conversation=Nevyřešit konverzaci
|
||||||
issues.review.resolved_by=označil tuto konverzaci jako vyřešenou
|
issues.review.resolved_by=označil tuto konverzaci jako vyřešenou
|
||||||
|
issues.review.commented=Okomentovat
|
||||||
issues.assignee.error=Ne všichni zpracovatelé byli přidáni z důvodu neočekávané chyby.
|
issues.assignee.error=Ne všichni zpracovatelé byli přidáni z důvodu neočekávané chyby.
|
||||||
issues.reference_issue.body=Tělo zprávy
|
issues.reference_issue.body=Tělo zprávy
|
||||||
issues.content_history.deleted=vymazáno
|
issues.content_history.deleted=vymazáno
|
||||||
|
@ -213,8 +213,6 @@ string.desc=Z–A
|
|||||||
|
|
||||||
[error]
|
[error]
|
||||||
occurred=Ein Fehler ist aufgetreten
|
occurred=Ein Fehler ist aufgetreten
|
||||||
missing_csrf=Fehlerhafte Anfrage: Kein CSRF Token verfügbar
|
|
||||||
invalid_csrf=Fehlerhafte Anfrage: Ungültiger CSRF Token
|
|
||||||
not_found=Das Ziel konnte nicht gefunden werden.
|
not_found=Das Ziel konnte nicht gefunden werden.
|
||||||
network_error=Netzwerkfehler
|
network_error=Netzwerkfehler
|
||||||
|
|
||||||
@ -685,7 +683,6 @@ public_profile=Öffentliches Profil
|
|||||||
biography_placeholder=Erzähle uns ein wenig über Dich selbst! (Du kannst Markdown verwenden)
|
biography_placeholder=Erzähle uns ein wenig über Dich selbst! (Du kannst Markdown verwenden)
|
||||||
location_placeholder=Teile Deinen ungefähren Standort mit anderen
|
location_placeholder=Teile Deinen ungefähren Standort mit anderen
|
||||||
profile_desc=Lege fest, wie dein Profil anderen Benutzern angezeigt wird. Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und webbasierte Git-Operationen verwendet.
|
profile_desc=Lege fest, wie dein Profil anderen Benutzern angezeigt wird. Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und webbasierte Git-Operationen verwendet.
|
||||||
password_username_disabled=Benutzer, die nicht von Gitea verwaltet werden können ihren Benutzernamen nicht ändern. Bitte kontaktiere deinen Administrator für mehr Details.
|
|
||||||
full_name=Vollständiger Name
|
full_name=Vollständiger Name
|
||||||
website=Webseite
|
website=Webseite
|
||||||
location=Standort
|
location=Standort
|
||||||
@ -1681,7 +1678,6 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe
|
|||||||
issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen.
|
issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen.
|
||||||
issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen.
|
issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen.
|
||||||
issues.review.approve=hat die Änderungen %s genehmigt
|
issues.review.approve=hat die Änderungen %s genehmigt
|
||||||
issues.review.comment=Kommentieren
|
|
||||||
issues.review.dismissed=verwarf %ss Review %s
|
issues.review.dismissed=verwarf %ss Review %s
|
||||||
issues.review.dismissed_label=Verworfen
|
issues.review.dismissed_label=Verworfen
|
||||||
issues.review.left_comment=hat einen Kommentar hinterlassen
|
issues.review.left_comment=hat einen Kommentar hinterlassen
|
||||||
@ -1706,6 +1702,7 @@ issues.review.hide_resolved=Gelöste ausblenden
|
|||||||
issues.review.resolve_conversation=Diskussion als "erledigt" markieren
|
issues.review.resolve_conversation=Diskussion als "erledigt" markieren
|
||||||
issues.review.un_resolve_conversation=Diskussion als "nicht-erledigt" markieren
|
issues.review.un_resolve_conversation=Diskussion als "nicht-erledigt" markieren
|
||||||
issues.review.resolved_by=markierte diese Unterhaltung als gelöst
|
issues.review.resolved_by=markierte diese Unterhaltung als gelöst
|
||||||
|
issues.review.commented=Kommentieren
|
||||||
issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden.
|
issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden.
|
||||||
issues.reference_issue.body=Beschreibung
|
issues.reference_issue.body=Beschreibung
|
||||||
issues.content_history.deleted=gelöscht
|
issues.content_history.deleted=gelöscht
|
||||||
|
@ -184,8 +184,6 @@ string.desc=Z - A
|
|||||||
|
|
||||||
[error]
|
[error]
|
||||||
occurred=Παρουσιάστηκε ένα σφάλμα
|
occurred=Παρουσιάστηκε ένα σφάλμα
|
||||||
missing_csrf=Bad Request: δεν υπάρχει διακριτικό CSRF
|
|
||||||
invalid_csrf=Λάθος Αίτημα: μη έγκυρο διακριτικό CSRF
|
|
||||||
not_found=Ο προορισμός δεν βρέθηκε.
|
not_found=Ο προορισμός δεν βρέθηκε.
|
||||||
network_error=Σφάλμα δικτύου
|
network_error=Σφάλμα δικτύου
|
||||||
|
|
||||||
@ -622,7 +620,6 @@ public_profile=Δημόσιο Προφίλ
|
|||||||
biography_placeholder=Πείτε μας λίγο για τον εαυτό σας! (Μπορείτε να γράψετε με Markdown)
|
biography_placeholder=Πείτε μας λίγο για τον εαυτό σας! (Μπορείτε να γράψετε με Markdown)
|
||||||
location_placeholder=Μοιραστείτε την κατά προσέγγιση τοποθεσία σας με άλλους
|
location_placeholder=Μοιραστείτε την κατά προσέγγιση τοποθεσία σας με άλλους
|
||||||
profile_desc=Ελέγξτε πώς εμφανίζεται το προφίλ σας σε άλλους χρήστες. Η κύρια διεύθυνση email σας θα χρησιμοποιηθεί για ειδοποιήσεις, ανάκτηση κωδικού πρόσβασης και λειτουργίες Git που βασίζονται στο web.
|
profile_desc=Ελέγξτε πώς εμφανίζεται το προφίλ σας σε άλλους χρήστες. Η κύρια διεύθυνση email σας θα χρησιμοποιηθεί για ειδοποιήσεις, ανάκτηση κωδικού πρόσβασης και λειτουργίες Git που βασίζονται στο web.
|
||||||
password_username_disabled=Οι μη τοπικοί χρήστες δεν επιτρέπεται να αλλάξουν το όνομα χρήστη τους. Επικοινωνήστε με το διαχειριστή σας για περισσότερες λεπτομέρειες.
|
|
||||||
full_name=Πλήρες Όνομα
|
full_name=Πλήρες Όνομα
|
||||||
website=Ιστοσελίδα
|
website=Ιστοσελίδα
|
||||||
location=Τοποθεσία
|
location=Τοποθεσία
|
||||||
@ -1603,7 +1600,6 @@ issues.dependency.add_error_dep_not_same_repo=Και τα δύο ζητήματ
|
|||||||
issues.review.self.approval=Δεν μπορείτε να εγκρίνετε το δικό σας pull request.
|
issues.review.self.approval=Δεν μπορείτε να εγκρίνετε το δικό σας pull request.
|
||||||
issues.review.self.rejection=Δεν μπορείτε να ζητήσετε αλλαγές στο δικό σας pull request.
|
issues.review.self.rejection=Δεν μπορείτε να ζητήσετε αλλαγές στο δικό σας pull request.
|
||||||
issues.review.approve=ενέκρινε αυτές τις αλλαγές %s
|
issues.review.approve=ενέκρινε αυτές τις αλλαγές %s
|
||||||
issues.review.comment=Σχόλιο
|
|
||||||
issues.review.dismissed=απέρριψε την αξιολόγηση %s %s
|
issues.review.dismissed=απέρριψε την αξιολόγηση %s %s
|
||||||
issues.review.dismissed_label=Απορρίφθηκε
|
issues.review.dismissed_label=Απορρίφθηκε
|
||||||
issues.review.left_comment=άφησε ένα σχόλιο
|
issues.review.left_comment=άφησε ένα σχόλιο
|
||||||
@ -1628,6 +1624,7 @@ issues.review.hide_resolved=Απόκρυψη επιλυμένων
|
|||||||
issues.review.resolve_conversation=Επίλυση συνομιλίας
|
issues.review.resolve_conversation=Επίλυση συνομιλίας
|
||||||
issues.review.un_resolve_conversation=Ανεπίλυτη συνομιλία
|
issues.review.un_resolve_conversation=Ανεπίλυτη συνομιλία
|
||||||
issues.review.resolved_by=σημείωση αυτή την συνομιλία ως επιλυμένη
|
issues.review.resolved_by=σημείωση αυτή την συνομιλία ως επιλυμένη
|
||||||
|
issues.review.commented=Σχόλιο
|
||||||
issues.assignee.error=Δεν προστέθηκαν όλοι οι παραλήπτες λόγω απροσδόκητου σφάλματος.
|
issues.assignee.error=Δεν προστέθηκαν όλοι οι παραλήπτες λόγω απροσδόκητου σφάλματος.
|
||||||
issues.reference_issue.body=Σώμα
|
issues.reference_issue.body=Σώμα
|
||||||
issues.content_history.deleted=διαγράφηκε
|
issues.content_history.deleted=διαγράφηκε
|
||||||
|
@ -159,6 +159,7 @@ filter.public = Public
|
|||||||
filter.private = Private
|
filter.private = Private
|
||||||
|
|
||||||
no_results_found = No results found.
|
no_results_found = No results found.
|
||||||
|
internal_error_skipped = Internal error occurred but is skipped: %s
|
||||||
|
|
||||||
[search]
|
[search]
|
||||||
search = Search...
|
search = Search...
|
||||||
@ -178,6 +179,8 @@ code_search_by_git_grep = Current code search results are provided by "git grep"
|
|||||||
package_kind = Search packages...
|
package_kind = Search packages...
|
||||||
project_kind = Search projects...
|
project_kind = Search projects...
|
||||||
branch_kind = Search branches...
|
branch_kind = Search branches...
|
||||||
|
tag_kind = Search tags...
|
||||||
|
tag_tooltip = Search for matching tags. Use '%' to match any sequence of numbers.
|
||||||
commit_kind = Search commits...
|
commit_kind = Search commits...
|
||||||
runner_kind = Search runners...
|
runner_kind = Search runners...
|
||||||
no_results = No matching results found.
|
no_results = No matching results found.
|
||||||
@ -220,8 +223,6 @@ string.desc = Z - A
|
|||||||
[error]
|
[error]
|
||||||
occurred = An error occurred
|
occurred = An error occurred
|
||||||
report_message = If you believe that this is a Gitea bug, please search for issues on <a href="%s" target="_blank">GitHub</a> or open a new issue if necessary.
|
report_message = If you believe that this is a Gitea bug, please search for issues on <a href="%s" target="_blank">GitHub</a> or open a new issue if necessary.
|
||||||
missing_csrf = Bad Request: no CSRF token present
|
|
||||||
invalid_csrf = Bad Request: invalid CSRF token
|
|
||||||
not_found = The target couldn't be found.
|
not_found = The target couldn't be found.
|
||||||
network_error = Network error
|
network_error = Network error
|
||||||
|
|
||||||
@ -583,6 +584,8 @@ invalid_slug_error = `Please provide a valid slug.`
|
|||||||
|
|
||||||
username_been_taken = The username is already taken.
|
username_been_taken = The username is already taken.
|
||||||
username_change_not_local_user = Non-local users are not allowed to change their username.
|
username_change_not_local_user = Non-local users are not allowed to change their username.
|
||||||
|
change_username_disabled = Changing username is disabled.
|
||||||
|
change_full_name_disabled = Changing full name is disabled.
|
||||||
username_has_not_been_changed = Username has not been changed
|
username_has_not_been_changed = Username has not been changed
|
||||||
slug_been_taken = The slug is already taken.
|
slug_been_taken = The slug is already taken.
|
||||||
repo_name_been_taken = The repository name is already used.
|
repo_name_been_taken = The repository name is already used.
|
||||||
@ -709,7 +712,8 @@ public_profile = Public Profile
|
|||||||
biography_placeholder = Tell us a little bit about yourself! (You can use Markdown)
|
biography_placeholder = Tell us a little bit about yourself! (You can use Markdown)
|
||||||
location_placeholder = Share your approximate location with others
|
location_placeholder = Share your approximate location with others
|
||||||
profile_desc = Control how your profile is show to other users. Your primary email address will be used for notifications, password recovery and web-based Git operations.
|
profile_desc = Control how your profile is show to other users. Your primary email address will be used for notifications, password recovery and web-based Git operations.
|
||||||
password_username_disabled = Non-local users are not allowed to change their username. Please contact your site administrator for more details.
|
password_username_disabled = You are not allowed to change their username. Please contact your site administrator for more details.
|
||||||
|
password_full_name_disabled = You are not allowed to change their full name. Please contact your site administrator for more details.
|
||||||
full_name = Full Name
|
full_name = Full Name
|
||||||
website = Website
|
website = Website
|
||||||
location = Location
|
location = Location
|
||||||
@ -1044,6 +1048,7 @@ issue_labels_helper = Select an issue label set.
|
|||||||
license = License
|
license = License
|
||||||
license_helper = Select a license file.
|
license_helper = Select a license file.
|
||||||
license_helper_desc = A license governs what others can and can't do with your code. Not sure which one is right for your project? See <a target="_blank" rel="noopener noreferrer" href="%s">Choose a license.</a>
|
license_helper_desc = A license governs what others can and can't do with your code. Not sure which one is right for your project? See <a target="_blank" rel="noopener noreferrer" href="%s">Choose a license.</a>
|
||||||
|
multiple_licenses = Multiple Licenses
|
||||||
object_format = Object Format
|
object_format = Object Format
|
||||||
object_format_helper = Object format of the repository. Cannot be changed later. SHA1 is most compatible.
|
object_format_helper = Object format of the repository. Cannot be changed later. SHA1 is most compatible.
|
||||||
readme = README
|
readme = README
|
||||||
@ -1181,6 +1186,11 @@ migrate.gogs.description = Migrate data from notabug.org or other Gogs instances
|
|||||||
migrate.onedev.description = Migrate data from code.onedev.io or other OneDev instances.
|
migrate.onedev.description = Migrate data from code.onedev.io or other OneDev instances.
|
||||||
migrate.codebase.description = Migrate data from codebasehq.com.
|
migrate.codebase.description = Migrate data from codebasehq.com.
|
||||||
migrate.gitbucket.description = Migrate data from GitBucket instances.
|
migrate.gitbucket.description = Migrate data from GitBucket instances.
|
||||||
|
migrate.codecommit.description = Migrate data from AWS CodeCommit.
|
||||||
|
migrate.codecommit.aws_access_key_id = AWS Access Key ID
|
||||||
|
migrate.codecommit.aws_secret_access_key = AWS Secret Access Key
|
||||||
|
migrate.codecommit.https_git_credentials_username = HTTPS Git Credentials Username
|
||||||
|
migrate.codecommit.https_git_credentials_password = HTTPS Git Credentials Password
|
||||||
migrate.migrating_git = Migrating Git Data
|
migrate.migrating_git = Migrating Git Data
|
||||||
migrate.migrating_topics = Migrating Topics
|
migrate.migrating_topics = Migrating Topics
|
||||||
migrate.migrating_milestones = Migrating Milestones
|
migrate.migrating_milestones = Migrating Milestones
|
||||||
@ -1279,7 +1289,6 @@ commit_graph.color = Color
|
|||||||
commit.contained_in = This commit is contained in:
|
commit.contained_in = This commit is contained in:
|
||||||
commit.contained_in_default_branch = This commit is part of the default branch
|
commit.contained_in_default_branch = This commit is part of the default branch
|
||||||
commit.load_referencing_branches_and_tags = Load branches and tags referencing this commit
|
commit.load_referencing_branches_and_tags = Load branches and tags referencing this commit
|
||||||
commit.load_tags_failed = Load tags failed because of internal error
|
|
||||||
blame = Blame
|
blame = Blame
|
||||||
download_file = Download file
|
download_file = Download file
|
||||||
normal_view = Normal View
|
normal_view = Normal View
|
||||||
@ -1757,7 +1766,7 @@ issues.review.hide_resolved = Hide resolved
|
|||||||
issues.review.resolve_conversation = Resolve conversation
|
issues.review.resolve_conversation = Resolve conversation
|
||||||
issues.review.un_resolve_conversation = Unresolve conversation
|
issues.review.un_resolve_conversation = Unresolve conversation
|
||||||
issues.review.resolved_by = marked this conversation as resolved
|
issues.review.resolved_by = marked this conversation as resolved
|
||||||
issues.review.comment = Comment
|
issues.review.commented = Comment
|
||||||
issues.review.official = Approved
|
issues.review.official = Approved
|
||||||
issues.review.requested = Review pending
|
issues.review.requested = Review pending
|
||||||
issues.review.rejected = Changes requested
|
issues.review.rejected = Changes requested
|
||||||
@ -1927,6 +1936,7 @@ pulls.delete.text = Do you really want to delete this pull request? (This will p
|
|||||||
pulls.recently_pushed_new_branches = You pushed on branch <strong>%[1]s</strong> %[2]s
|
pulls.recently_pushed_new_branches = You pushed on branch <strong>%[1]s</strong> %[2]s
|
||||||
|
|
||||||
pull.deleted_branch = (deleted):%s
|
pull.deleted_branch = (deleted):%s
|
||||||
|
pull.agit_documentation = Review documentation about AGit
|
||||||
|
|
||||||
comments.edit.already_changed = Unable to save changes to the comment. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes
|
comments.edit.already_changed = Unable to save changes to the comment. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes
|
||||||
|
|
||||||
@ -2942,6 +2952,7 @@ dashboard.start_schedule_tasks = Start actions schedule tasks
|
|||||||
dashboard.sync_branch.started = Branches Sync started
|
dashboard.sync_branch.started = Branches Sync started
|
||||||
dashboard.sync_tag.started = Tags Sync started
|
dashboard.sync_tag.started = Tags Sync started
|
||||||
dashboard.rebuild_issue_indexer = Rebuild issue indexer
|
dashboard.rebuild_issue_indexer = Rebuild issue indexer
|
||||||
|
dashboard.sync_repo_licenses = Sync repo licenses
|
||||||
|
|
||||||
users.user_manage_panel = User Account Management
|
users.user_manage_panel = User Account Management
|
||||||
users.new_account = Create User Account
|
users.new_account = Create User Account
|
||||||
|
@ -182,8 +182,6 @@ string.desc=Z - A
|
|||||||
|
|
||||||
[error]
|
[error]
|
||||||
occurred=Ha ocurrido un error
|
occurred=Ha ocurrido un error
|
||||||
missing_csrf=Solicitud incorrecta: sin token CSRF
|
|
||||||
invalid_csrf=Solicitud incorrecta: el token CSRF no es válido
|
|
||||||
not_found=El objetivo no pudo ser encontrado.
|
not_found=El objetivo no pudo ser encontrado.
|
||||||
network_error=Error de red
|
network_error=Error de red
|
||||||
|
|
||||||
@ -619,7 +617,6 @@ public_profile=Perfil público
|
|||||||
biography_placeholder=¡Cuéntanos un poco sobre ti mismo! (Puedes usar Markdown)
|
biography_placeholder=¡Cuéntanos un poco sobre ti mismo! (Puedes usar Markdown)
|
||||||
location_placeholder=Comparte tu ubicación aproximada con otros
|
location_placeholder=Comparte tu ubicación aproximada con otros
|
||||||
profile_desc=Controla cómo se muestra su perfil a otros usuarios. Tu dirección de correo electrónico principal se utilizará para notificaciones, recuperación de contraseña y operaciones de Git basadas en la web.
|
profile_desc=Controla cómo se muestra su perfil a otros usuarios. Tu dirección de correo electrónico principal se utilizará para notificaciones, recuperación de contraseña y operaciones de Git basadas en la web.
|
||||||
password_username_disabled=Usuarios no locales no tienen permitido cambiar su nombre de usuario. Por favor, contacta con el administrador del sistema para más detalles.
|
|
||||||
full_name=Nombre completo
|
full_name=Nombre completo
|
||||||
website=Página web
|
website=Página web
|
||||||
location=Localización
|
location=Localización
|
||||||
@ -1593,7 +1590,6 @@ issues.dependency.add_error_dep_not_same_repo=Ambas incidencias deben estar en e
|
|||||||
issues.review.self.approval=No puede aprobar su propio pull request.
|
issues.review.self.approval=No puede aprobar su propio pull request.
|
||||||
issues.review.self.rejection=No puede sugerir cambios en su propio pull request.
|
issues.review.self.rejection=No puede sugerir cambios en su propio pull request.
|
||||||
issues.review.approve=aprobado estos cambios %s
|
issues.review.approve=aprobado estos cambios %s
|
||||||
issues.review.comment=Comentario
|
|
||||||
issues.review.dismissed=descartó la revisión de %s %s
|
issues.review.dismissed=descartó la revisión de %s %s
|
||||||
issues.review.dismissed_label=Descartado
|
issues.review.dismissed_label=Descartado
|
||||||
issues.review.left_comment=dejó un comentario
|
issues.review.left_comment=dejó un comentario
|
||||||
@ -1618,6 +1614,7 @@ issues.review.hide_resolved=Ocultar resueltos
|
|||||||
issues.review.resolve_conversation=Resolver conversación
|
issues.review.resolve_conversation=Resolver conversación
|
||||||
issues.review.un_resolve_conversation=Marcar conversación sin resolver
|
issues.review.un_resolve_conversation=Marcar conversación sin resolver
|
||||||
issues.review.resolved_by=ha marcado esta conversación como resuelta
|
issues.review.resolved_by=ha marcado esta conversación como resuelta
|
||||||
|
issues.review.commented=Comentario
|
||||||
issues.assignee.error=No todos los asignados fueron añadidos debido a un error inesperado.
|
issues.assignee.error=No todos los asignados fueron añadidos debido a un error inesperado.
|
||||||
issues.reference_issue.body=Cuerpo
|
issues.reference_issue.body=Cuerpo
|
||||||
issues.content_history.deleted=borrado
|
issues.content_history.deleted=borrado
|
||||||
|
@ -118,7 +118,6 @@ filter.private=خصوصی
|
|||||||
[filter]
|
[filter]
|
||||||
|
|
||||||
[error]
|
[error]
|
||||||
missing_csrf=درخواست بد: بلیط CSRF ندارد
|
|
||||||
|
|
||||||
[startpage]
|
[startpage]
|
||||||
app_desc=یک سرویس گیت بیدرد سر و راحت
|
app_desc=یک سرویس گیت بیدرد سر و راحت
|
||||||
@ -492,7 +491,6 @@ account_link=حسابهای مرتبط
|
|||||||
organization=سازمان ها
|
organization=سازمان ها
|
||||||
|
|
||||||
public_profile=نمایه عمومی
|
public_profile=نمایه عمومی
|
||||||
password_username_disabled=حسابهای غیر محلی مجاز به تغییر نام کاربری نیستند. لطفا با مدیر سایت در ارتباط باشید.
|
|
||||||
full_name=نام کامل
|
full_name=نام کامل
|
||||||
website=تارنما
|
website=تارنما
|
||||||
location=موقعیت مکانی
|
location=موقعیت مکانی
|
||||||
@ -1235,7 +1233,6 @@ issues.dependency.add_error_dep_not_same_repo=هر دو موضوع باید از
|
|||||||
issues.review.self.approval=شما نمیتوانید تقاضای واکشی خود را تایید کنید.
|
issues.review.self.approval=شما نمیتوانید تقاضای واکشی خود را تایید کنید.
|
||||||
issues.review.self.rejection=شما نمیتوانید تقاضا تغییرات تقاضای واکشی خود را تغییر دهید.
|
issues.review.self.rejection=شما نمیتوانید تقاضا تغییرات تقاضای واکشی خود را تغییر دهید.
|
||||||
issues.review.approve=این تغییرات را تایید شدند %s
|
issues.review.approve=این تغییرات را تایید شدند %s
|
||||||
issues.review.comment=دیدگاه
|
|
||||||
issues.review.dismissed=بررسی %s %s را رد شده
|
issues.review.dismissed=بررسی %s %s را رد شده
|
||||||
issues.review.dismissed_label=رها شده
|
issues.review.dismissed_label=رها شده
|
||||||
issues.review.left_comment=یک نظر ثبت کرد
|
issues.review.left_comment=یک نظر ثبت کرد
|
||||||
@ -1256,6 +1253,7 @@ issues.review.hide_resolved=مخفی کردن حل شده ها
|
|||||||
issues.review.resolve_conversation=مکالمه را بعنوان حل شده علامت گذاری کردن
|
issues.review.resolve_conversation=مکالمه را بعنوان حل شده علامت گذاری کردن
|
||||||
issues.review.un_resolve_conversation=مکالمه را بعنوان حل نشده علامت گذاری کردن
|
issues.review.un_resolve_conversation=مکالمه را بعنوان حل نشده علامت گذاری کردن
|
||||||
issues.review.resolved_by=علامت گذاری این مکالمه بعنوان حل شده
|
issues.review.resolved_by=علامت گذاری این مکالمه بعنوان حل شده
|
||||||
|
issues.review.commented=دیدگاه
|
||||||
issues.assignee.error=به دلیل خطای غیرمنتظره همه تکالیف اضافه نشد.
|
issues.assignee.error=به دلیل خطای غیرمنتظره همه تکالیف اضافه نشد.
|
||||||
issues.reference_issue.body=Body
|
issues.reference_issue.body=Body
|
||||||
issues.content_history.deleted=حذف شده
|
issues.content_history.deleted=حذف شده
|
||||||
|
@ -133,8 +133,6 @@ filter.private=Yksityinen
|
|||||||
|
|
||||||
[error]
|
[error]
|
||||||
occurred=Virhe tapahtui
|
occurred=Virhe tapahtui
|
||||||
missing_csrf=Virheellinen pyyntö: CSRF-tunnusta ei ole olemassa
|
|
||||||
invalid_csrf=Virheellinen pyyntö: Virheellinen CSRF-tunniste
|
|
||||||
not_found=Kohdetta ei löytynyt.
|
not_found=Kohdetta ei löytynyt.
|
||||||
network_error=Verkkovirhe
|
network_error=Verkkovirhe
|
||||||
|
|
||||||
@ -453,7 +451,6 @@ account_link=Linkitetyt tilit
|
|||||||
organization=Organisaatiot
|
organization=Organisaatiot
|
||||||
|
|
||||||
public_profile=Julkinen profiili
|
public_profile=Julkinen profiili
|
||||||
password_username_disabled=Ei-paikalliset käyttäjät eivät voi muuttaa käyttäjätunnustaan. Ole hyvä ja ota yhteyttä sivuston ylläpitäjään saadaksesi lisätietoa.
|
|
||||||
full_name=Kokonimi
|
full_name=Kokonimi
|
||||||
website=Nettisivut
|
website=Nettisivut
|
||||||
location=Sijainti
|
location=Sijainti
|
||||||
@ -955,6 +952,7 @@ issues.review.left_comment=jätti kommentin
|
|||||||
issues.review.pending=Odottaa
|
issues.review.pending=Odottaa
|
||||||
issues.review.show_resolved=Näytä ratkaisu
|
issues.review.show_resolved=Näytä ratkaisu
|
||||||
issues.review.hide_resolved=Piilota ratkaisu
|
issues.review.hide_resolved=Piilota ratkaisu
|
||||||
|
issues.review.commented=Kommentoi
|
||||||
issues.reference_issue.body=Kuvaus
|
issues.reference_issue.body=Kuvaus
|
||||||
issues.content_history.deleted=poistettu
|
issues.content_history.deleted=poistettu
|
||||||
issues.content_history.edited=muokattu
|
issues.content_history.edited=muokattu
|
||||||
|
@ -31,6 +31,7 @@ username=Nom d'utilisateur
|
|||||||
email=Courriel
|
email=Courriel
|
||||||
password=Mot de passe
|
password=Mot de passe
|
||||||
access_token=Jeton d’accès
|
access_token=Jeton d’accès
|
||||||
|
re_type=Confirmez le mot de passe
|
||||||
captcha=CAPTCHA
|
captcha=CAPTCHA
|
||||||
twofa=Authentification à deux facteurs
|
twofa=Authentification à deux facteurs
|
||||||
twofa_scratch=Code de secours pour l'authentification à deux facteurs
|
twofa_scratch=Code de secours pour l'authentification à deux facteurs
|
||||||
@ -158,6 +159,7 @@ filter.public=Public
|
|||||||
filter.private=Privé
|
filter.private=Privé
|
||||||
|
|
||||||
no_results_found=Aucun résultat trouvé.
|
no_results_found=Aucun résultat trouvé.
|
||||||
|
internal_error_skipped=Une erreur interne est survenue, mais ignorée : %s
|
||||||
|
|
||||||
[search]
|
[search]
|
||||||
search=Rechercher…
|
search=Rechercher…
|
||||||
@ -176,6 +178,8 @@ code_search_by_git_grep=Les résultats de recherche de code actuels sont fournis
|
|||||||
package_kind=Chercher des paquets…
|
package_kind=Chercher des paquets…
|
||||||
project_kind=Chercher des projets…
|
project_kind=Chercher des projets…
|
||||||
branch_kind=Chercher des branches…
|
branch_kind=Chercher des branches…
|
||||||
|
tag_kind=Chercher des étiquettes…
|
||||||
|
tag_tooltip=Cherchez des étiquettes correspondantes. Utilisez « % » pour rechercher n’importe quelle suite de nombres.
|
||||||
commit_kind=Chercher des révisions…
|
commit_kind=Chercher des révisions…
|
||||||
runner_kind=Chercher des exécuteurs…
|
runner_kind=Chercher des exécuteurs…
|
||||||
no_results=Aucun résultat correspondant trouvé.
|
no_results=Aucun résultat correspondant trouvé.
|
||||||
@ -217,18 +221,20 @@ string.desc=Z - A
|
|||||||
|
|
||||||
[error]
|
[error]
|
||||||
occurred=Une erreur s’est produite
|
occurred=Une erreur s’est produite
|
||||||
missing_csrf=Requête incorrecte: aucun jeton CSRF présent
|
report_message=Si vous pensez qu’il s’agit d’un bug Gitea, veuillez consulter notre board <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> ou ouvrir un nouveau ticket si nécessaire.
|
||||||
invalid_csrf=Requête incorrecte : jeton CSRF invalide
|
|
||||||
not_found=La cible n'a pu être trouvée.
|
not_found=La cible n'a pu être trouvée.
|
||||||
network_error=Erreur réseau
|
network_error=Erreur réseau
|
||||||
|
|
||||||
[startpage]
|
[startpage]
|
||||||
app_desc=Un service Git auto-hébergé sans prise de tête
|
app_desc=Un service Git auto-hébergé sans prise de tête
|
||||||
install=Facile à installer
|
install=Facile à installer
|
||||||
|
install_desc=Il suffit de <a target="_blank" rel="noopener noreferrer" href="%[1]s">lancer l’exécutable</a> adapté à votre plateforme, le déployer avec <a target="_blank" rel="noopener noreferrer" href="%[2]s">Docker</a> ou de l’installer depuis un <a target="_blank" rel="noopener noreferrer" href="%[3]s">gestionnaire de paquet</a>.
|
||||||
platform=Multi-plateforme
|
platform=Multi-plateforme
|
||||||
|
platform_desc=Gitea tourne partout où <a target="_blank" rel="noopener noreferrer" href="%s">Go</a> peut être compilé : Windows, macOS, Linux, ARM, etc. Choisissez votre préféré !
|
||||||
lightweight=Léger
|
lightweight=Léger
|
||||||
lightweight_desc=Gitea utilise peu de ressources. Il peut même tourner sur un Raspberry Pi très bon marché. Économisez l'énergie de vos serveurs !
|
lightweight_desc=Gitea utilise peu de ressources. Il peut même tourner sur un Raspberry Pi très bon marché. Économisez l'énergie de vos serveurs !
|
||||||
license=Open Source
|
license=Open Source
|
||||||
|
license_desc=Venez récupérer <a target="_blank" rel="noopener noreferrer" href="%[1]s">%[2]s</a> ! Rejoignez-nous en <a target="_blank" rel="noopener noreferrer" href="%[3]s">contribuant</a> à rendre ce projet encore meilleur !
|
||||||
|
|
||||||
[install]
|
[install]
|
||||||
install=Installation
|
install=Installation
|
||||||
@ -312,6 +318,7 @@ admin_setting_desc=La création d'un compte administrateur est facultative. Le p
|
|||||||
admin_title=Paramètres de compte administrateur
|
admin_title=Paramètres de compte administrateur
|
||||||
admin_name=Nom d’utilisateur administrateur
|
admin_name=Nom d’utilisateur administrateur
|
||||||
admin_password=Mot de passe
|
admin_password=Mot de passe
|
||||||
|
confirm_password=Confirmez le mot de passe
|
||||||
admin_email=Courriel
|
admin_email=Courriel
|
||||||
install_btn_confirm=Installer Gitea
|
install_btn_confirm=Installer Gitea
|
||||||
test_git_failed=Le test de la commande "git" a échoué : %v
|
test_git_failed=Le test de la commande "git" a échoué : %v
|
||||||
@ -450,6 +457,7 @@ authorize_title=Autoriser "%s" à accéder à votre compte ?
|
|||||||
authorization_failed=L’autorisation a échoué
|
authorization_failed=L’autorisation a échoué
|
||||||
authorization_failed_desc=L'autorisation a échoué car nous avons détecté une demande incorrecte. Veuillez contacter le responsable de l'application que vous avez essayé d'autoriser.
|
authorization_failed_desc=L'autorisation a échoué car nous avons détecté une demande incorrecte. Veuillez contacter le responsable de l'application que vous avez essayé d'autoriser.
|
||||||
sspi_auth_failed=Échec de l'authentification SSPI
|
sspi_auth_failed=Échec de l'authentification SSPI
|
||||||
|
password_pwned=Le mot de passe que vous avez choisi <a target="_blank" rel="noopener noreferrer" href="%s">fait partit des mots de passe ayant fuité</a> sur internet. Veuillez réessayer avec un mot de passe différent et considérez remplacer ce mot de passe si vous l’utilisez ailleurs.
|
||||||
password_pwned_err=Impossible d'envoyer la demande à HaveIBeenPwned
|
password_pwned_err=Impossible d'envoyer la demande à HaveIBeenPwned
|
||||||
last_admin=Vous ne pouvez pas supprimer ce compte car au moins un administrateur est requis.
|
last_admin=Vous ne pouvez pas supprimer ce compte car au moins un administrateur est requis.
|
||||||
signin_passkey=Se connecter avec une clé d’identification (passkey)
|
signin_passkey=Se connecter avec une clé d’identification (passkey)
|
||||||
@ -533,6 +541,7 @@ UserName=Nom d'utilisateur
|
|||||||
RepoName=Nom du dépôt
|
RepoName=Nom du dépôt
|
||||||
Email=Courriel
|
Email=Courriel
|
||||||
Password=Mot de passe
|
Password=Mot de passe
|
||||||
|
Retype=Confirmez le mot de passe
|
||||||
SSHTitle=Nom de la clé SSH
|
SSHTitle=Nom de la clé SSH
|
||||||
HttpsUrl=URL HTTPS
|
HttpsUrl=URL HTTPS
|
||||||
PayloadUrl=URL des données utiles
|
PayloadUrl=URL des données utiles
|
||||||
@ -686,15 +695,16 @@ applications=Applications
|
|||||||
orgs=Gérer les organisations
|
orgs=Gérer les organisations
|
||||||
repos=Dépôts
|
repos=Dépôts
|
||||||
delete=Supprimer le compte
|
delete=Supprimer le compte
|
||||||
|
twofa=Authentification à deux facteurs (TOTP)
|
||||||
account_link=Comptes liés
|
account_link=Comptes liés
|
||||||
organization=Organisations
|
organization=Organisations
|
||||||
uid=UID
|
uid=UID
|
||||||
|
webauthn=Authentification à deux facteurs (Clés de sécurité)
|
||||||
|
|
||||||
public_profile=Profil public
|
public_profile=Profil public
|
||||||
biography_placeholder=Parlez-nous un peu de vous ! (Vous pouvez utiliser Markdown)
|
biography_placeholder=Parlez-nous un peu de vous ! (Vous pouvez utiliser Markdown)
|
||||||
location_placeholder=Partagez votre position approximative avec d'autres personnes
|
location_placeholder=Partagez votre position approximative avec d'autres personnes
|
||||||
profile_desc=Contrôlez comment votre profil est affiché aux autres utilisateurs. Votre adresse courriel principale sera utilisée pour les notifications, la récupération de mot de passe et les opérations Git basées sur le Web.
|
profile_desc=Contrôlez comment votre profil est affiché aux autres utilisateurs. Votre adresse courriel principale sera utilisée pour les notifications, la récupération de mot de passe et les opérations Git basées sur le Web.
|
||||||
password_username_disabled=Les utilisateurs externes ne sont pas autorisés à modifier leur nom d'utilisateur. Veuillez contacter l'administrateur de votre site pour plus de détails.
|
|
||||||
full_name=Nom complet
|
full_name=Nom complet
|
||||||
website=Site Web
|
website=Site Web
|
||||||
location=Localisation
|
location=Localisation
|
||||||
@ -786,6 +796,7 @@ add_email_success=La nouvelle adresse e-mail a été ajoutée.
|
|||||||
email_preference_set_success=L'e-mail de préférence a été défini avec succès.
|
email_preference_set_success=L'e-mail de préférence a été défini avec succès.
|
||||||
add_openid_success=La nouvelle adresse OpenID a été ajoutée.
|
add_openid_success=La nouvelle adresse OpenID a été ajoutée.
|
||||||
keep_email_private=Cacher l'adresse e-mail
|
keep_email_private=Cacher l'adresse e-mail
|
||||||
|
keep_email_private_popup=Ceci masquera votre adresse e-mail de votre profil, de vos demandes d’ajout et des fichiers modifiés depuis l'interface Web. Les révisions déjà soumises ne seront pas modifiés. Utilisez %s dans les révisions pour les associer à votre compte.
|
||||||
openid_desc=OpenID vous permet de confier l'authentification à une tierce partie.
|
openid_desc=OpenID vous permet de confier l'authentification à une tierce partie.
|
||||||
|
|
||||||
manage_ssh_keys=Gérer les clés SSH
|
manage_ssh_keys=Gérer les clés SSH
|
||||||
@ -924,20 +935,26 @@ revoke_oauth2_grant=Révoquer l'accès
|
|||||||
revoke_oauth2_grant_description=La révocation de l'accès à cette application tierce l'empêchera d'accéder à vos données. Vous êtes sûr ?
|
revoke_oauth2_grant_description=La révocation de l'accès à cette application tierce l'empêchera d'accéder à vos données. Vous êtes sûr ?
|
||||||
revoke_oauth2_grant_success=Accès révoqué avec succès.
|
revoke_oauth2_grant_success=Accès révoqué avec succès.
|
||||||
|
|
||||||
|
twofa_desc=Pour protéger votre compte contre les vols de mot de passes, vous pouvez utiliser un smartphone ou autres appareils pour recevoir un code temporaire à usage unique (TOTP).
|
||||||
twofa_recovery_tip=Si vous perdez votre appareil, vous pourrez utiliser une clé de récupération à usage unique pour obtenir l’accès à votre compte.
|
twofa_recovery_tip=Si vous perdez votre appareil, vous pourrez utiliser une clé de récupération à usage unique pour obtenir l’accès à votre compte.
|
||||||
twofa_is_enrolled=Votre compte est <strong>inscrit</strong> à l'authentification à deux facteurs.
|
twofa_is_enrolled=Votre compte est <strong>inscrit</strong> à l'authentification à deux facteurs.
|
||||||
twofa_not_enrolled=Votre compte n'est pas inscrit à l'authentification à deux facteurs.
|
twofa_not_enrolled=Votre compte n'est pas inscrit à l'authentification à deux facteurs.
|
||||||
twofa_disable=Désactiver l'authentification à deux facteurs
|
twofa_disable=Désactiver l'authentification à deux facteurs
|
||||||
|
twofa_scratch_token_regenerate=Régénérer une clé de secours à usage unique
|
||||||
|
twofa_scratch_token_regenerated=Votre clé de secours à usage unique est désormais « %s ». Stockez-la dans un endroit sûr, elle ne sera plus jamais affichée.
|
||||||
twofa_enroll=Activer l'authentification à deux facteurs
|
twofa_enroll=Activer l'authentification à deux facteurs
|
||||||
twofa_disable_note=Vous pouvez désactiver l'authentification à deux facteurs si nécessaire.
|
twofa_disable_note=Vous pouvez désactiver l'authentification à deux facteurs si nécessaire.
|
||||||
twofa_disable_desc=Désactiver l'authentification à deux facteurs rendra votre compte plus vulnérable. Confirmer ?
|
twofa_disable_desc=Désactiver l'authentification à deux facteurs rendra votre compte plus vulnérable. Confirmer ?
|
||||||
|
regenerate_scratch_token_desc=Si vous avez égaré votre clé de secours ou avez dû l’utiliser pour vous authentifier, vous pouvez la régénérer.
|
||||||
twofa_disabled=L'authentification à deux facteurs a été désactivée.
|
twofa_disabled=L'authentification à deux facteurs a été désactivée.
|
||||||
scan_this_image=Scannez cette image avec votre application d'authentification :
|
scan_this_image=Scannez cette image avec votre application d'authentification :
|
||||||
or_enter_secret=Ou saisissez le code %s
|
or_enter_secret=Ou saisissez le code %s
|
||||||
then_enter_passcode=Et entrez le code de passe s'affichant dans l'application :
|
then_enter_passcode=Et entrez le code de passe s'affichant dans l'application :
|
||||||
passcode_invalid=Le mot de passe est invalide. Réessayez.
|
passcode_invalid=Le mot de passe est invalide. Réessayez.
|
||||||
|
twofa_enrolled=L’authentification à deux facteurs a été activée pour votre compte. Gardez votre clé de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois.
|
||||||
twofa_failed_get_secret=Impossible d'obtenir le secret.
|
twofa_failed_get_secret=Impossible d'obtenir le secret.
|
||||||
|
|
||||||
|
webauthn_desc=Les clefs de sécurité sont des dispositifs matériels contenant des clefs cryptographiques. Elles peuvent être utilisées pour l’authentification à deux facteurs. La clef de sécurité doit supporter le standard <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>.
|
||||||
webauthn_register_key=Ajouter une clé de sécurité
|
webauthn_register_key=Ajouter une clé de sécurité
|
||||||
webauthn_nickname=Pseudonyme
|
webauthn_nickname=Pseudonyme
|
||||||
webauthn_delete_key=Retirer la clé de sécurité
|
webauthn_delete_key=Retirer la clé de sécurité
|
||||||
@ -1082,7 +1099,9 @@ tree_path_not_found_branch=Le chemin %[1]s n’existe pas dans la branche %[2]s.
|
|||||||
tree_path_not_found_tag=Le chemin %[1]s n’existe pas dans l’étiquette %[2]s.
|
tree_path_not_found_tag=Le chemin %[1]s n’existe pas dans l’étiquette %[2]s.
|
||||||
|
|
||||||
transfer.accept=Accepter le transfert
|
transfer.accept=Accepter le transfert
|
||||||
|
transfer.accept_desc=Transférer à « %s »
|
||||||
transfer.reject=Refuser le transfert
|
transfer.reject=Refuser le transfert
|
||||||
|
transfer.reject_desc=Annuler le transfert à « %s »
|
||||||
transfer.no_permission_to_accept=Vous n’êtes pas autorisé à accepter ce transfert.
|
transfer.no_permission_to_accept=Vous n’êtes pas autorisé à accepter ce transfert.
|
||||||
transfer.no_permission_to_reject=Vous n’êtes pas autorisé à rejeter ce transfert.
|
transfer.no_permission_to_reject=Vous n’êtes pas autorisé à rejeter ce transfert.
|
||||||
|
|
||||||
@ -1157,6 +1176,11 @@ migrate.gogs.description=Migrer les données depuis notabug.org ou d’autres in
|
|||||||
migrate.onedev.description=Migrer les données depuis code.onedev.io ou d’autre instance de OneDev.
|
migrate.onedev.description=Migrer les données depuis code.onedev.io ou d’autre instance de OneDev.
|
||||||
migrate.codebase.description=Migrer les données depuis codebasehq.com.
|
migrate.codebase.description=Migrer les données depuis codebasehq.com.
|
||||||
migrate.gitbucket.description=Migrer les données depuis des instances GitBucket.
|
migrate.gitbucket.description=Migrer les données depuis des instances GitBucket.
|
||||||
|
migrate.codecommit.description=Migrer les données depuis AWS CodeCommit.
|
||||||
|
migrate.codecommit.aws_access_key_id=ID de la clé d’accès AWS
|
||||||
|
migrate.codecommit.aws_secret_access_key=Clé d’accès secrète AWS
|
||||||
|
migrate.codecommit.https_git_credentials_username=Nom d’utilisateur Git HTTPS
|
||||||
|
migrate.codecommit.https_git_credentials_password=Mot de passe Git HTTPS
|
||||||
migrate.migrating_git=Migration des données Git
|
migrate.migrating_git=Migration des données Git
|
||||||
migrate.migrating_topics=Migration des sujets
|
migrate.migrating_topics=Migration des sujets
|
||||||
migrate.migrating_milestones=Migration des jalons
|
migrate.migrating_milestones=Migration des jalons
|
||||||
@ -1217,6 +1241,7 @@ releases=Publications
|
|||||||
tag=Étiquette
|
tag=Étiquette
|
||||||
released_this=a publié ceci
|
released_this=a publié ceci
|
||||||
tagged_this=a étiqueté
|
tagged_this=a étiqueté
|
||||||
|
file.title=%s sur %s
|
||||||
file_raw=Brut
|
file_raw=Brut
|
||||||
file_history=Historique
|
file_history=Historique
|
||||||
file_view_source=Voir le code source
|
file_view_source=Voir le code source
|
||||||
@ -1254,7 +1279,6 @@ commit_graph.color=Couleur
|
|||||||
commit.contained_in=Cette révision appartient à :
|
commit.contained_in=Cette révision appartient à :
|
||||||
commit.contained_in_default_branch=Cette révision appartient à la branche par défaut
|
commit.contained_in_default_branch=Cette révision appartient à la branche par défaut
|
||||||
commit.load_referencing_branches_and_tags=Charger les branches et étiquettes référençant cette révision
|
commit.load_referencing_branches_and_tags=Charger les branches et étiquettes référençant cette révision
|
||||||
commit.load_tags_failed=Le chargement des étiquettes a échoué à cause d’une erreur interne
|
|
||||||
blame=Annotations
|
blame=Annotations
|
||||||
download_file=Télécharger le fichier
|
download_file=Télécharger le fichier
|
||||||
normal_view=Vue normale
|
normal_view=Vue normale
|
||||||
@ -1457,6 +1481,7 @@ issues.remove_labels=a supprimé les labels %s %s.
|
|||||||
issues.add_remove_labels=a ajouté le label %s et supprimé %s %s.
|
issues.add_remove_labels=a ajouté le label %s et supprimé %s %s.
|
||||||
issues.add_milestone_at=`a ajouté ça au jalon <b>%s</b> %s.`
|
issues.add_milestone_at=`a ajouté ça au jalon <b>%s</b> %s.`
|
||||||
issues.add_project_at=`a ajouté ça au projet <b>%s</b> %s.`
|
issues.add_project_at=`a ajouté ça au projet <b>%s</b> %s.`
|
||||||
|
issues.move_to_column_of_project=`a déplacé ça vers %s dans %s sur %s`
|
||||||
issues.change_milestone_at=`a remplacé le jalon <b><strike>%s</strike></b> par <b>%s</b> %s.`
|
issues.change_milestone_at=`a remplacé le jalon <b><strike>%s</strike></b> par <b>%s</b> %s.`
|
||||||
issues.change_project_at=`a remplacé le projet <b><strike>%s</strike></b> par <b>%s</b> %s.`
|
issues.change_project_at=`a remplacé le projet <b><strike>%s</strike></b> par <b>%s</b> %s.`
|
||||||
issues.remove_milestone_at=`a supprimé ça du jalon <b>%s</b> %s.`
|
issues.remove_milestone_at=`a supprimé ça du jalon <b>%s</b> %s.`
|
||||||
@ -1705,10 +1730,10 @@ issues.dependency.add_error_dep_not_same_repo=Les deux tickets doivent être dan
|
|||||||
issues.review.self.approval=Vous ne pouvez approuver vos propres demandes d'ajout.
|
issues.review.self.approval=Vous ne pouvez approuver vos propres demandes d'ajout.
|
||||||
issues.review.self.rejection=Vous ne pouvez demander de changements sur vos propres demandes de changement.
|
issues.review.self.rejection=Vous ne pouvez demander de changements sur vos propres demandes de changement.
|
||||||
issues.review.approve=a approuvé ces modifications %s.
|
issues.review.approve=a approuvé ces modifications %s.
|
||||||
issues.review.comment=Commenter
|
issues.review.comment=a évalué %s
|
||||||
issues.review.dismissed=a révoqué l’évaluation de %s %s.
|
issues.review.dismissed=a révoqué l’évaluation de %s %s.
|
||||||
issues.review.dismissed_label=Révoquée
|
issues.review.dismissed_label=Révoquée
|
||||||
issues.review.left_comment=laisser un commentaire
|
issues.review.left_comment=à laissé un commentaire
|
||||||
issues.review.content.empty=Vous devez laisser un commentaire indiquant le(s) changement(s) demandé(s).
|
issues.review.content.empty=Vous devez laisser un commentaire indiquant le(s) changement(s) demandé(s).
|
||||||
issues.review.reject=a requis les changements %s
|
issues.review.reject=a requis les changements %s
|
||||||
issues.review.wait=a été sollicité pour évaluer cette demande d’ajout %s.
|
issues.review.wait=a été sollicité pour évaluer cette demande d’ajout %s.
|
||||||
@ -1730,6 +1755,12 @@ issues.review.hide_resolved=Réduire
|
|||||||
issues.review.resolve_conversation=Clore la conversation
|
issues.review.resolve_conversation=Clore la conversation
|
||||||
issues.review.un_resolve_conversation=Rouvrir la conversation
|
issues.review.un_resolve_conversation=Rouvrir la conversation
|
||||||
issues.review.resolved_by=a marqué cette conversation comme résolue.
|
issues.review.resolved_by=a marqué cette conversation comme résolue.
|
||||||
|
issues.review.commented=À commenté
|
||||||
|
issues.review.official=Approuvée
|
||||||
|
issues.review.requested=Évaluation en attente
|
||||||
|
issues.review.rejected=Changements demandées
|
||||||
|
issues.review.stale=Modifiée depuis la dernière approbation
|
||||||
|
issues.review.unofficial=Approbation non comptabilisée
|
||||||
issues.assignee.error=Tous les assignés n'ont pas été ajoutés en raison d'une erreur inattendue.
|
issues.assignee.error=Tous les assignés n'ont pas été ajoutés en raison d'une erreur inattendue.
|
||||||
issues.reference_issue.body=Corps
|
issues.reference_issue.body=Corps
|
||||||
issues.content_history.deleted=a supprimé
|
issues.content_history.deleted=a supprimé
|
||||||
@ -1803,6 +1834,8 @@ pulls.is_empty=Les changements sur cette branche sont déjà sur la branche cibl
|
|||||||
pulls.required_status_check_failed=Certains contrôles requis n'ont pas réussi.
|
pulls.required_status_check_failed=Certains contrôles requis n'ont pas réussi.
|
||||||
pulls.required_status_check_missing=Certains contrôles requis sont manquants.
|
pulls.required_status_check_missing=Certains contrôles requis sont manquants.
|
||||||
pulls.required_status_check_administrator=En tant qu'administrateur, vous pouvez toujours fusionner cette requête de pull.
|
pulls.required_status_check_administrator=En tant qu'administrateur, vous pouvez toujours fusionner cette requête de pull.
|
||||||
|
pulls.blocked_by_approvals=Cette demande d'ajout n’est pas suffisamment approuvée. %d approbations obtenues sur %d.
|
||||||
|
pulls.blocked_by_approvals_whitelisted=Cette demande d’ajout n’a pas encore assez d’approbations. %d sur %d approbations de la part des utilisateurs ou équipes sur la liste autorisée.
|
||||||
pulls.blocked_by_rejection=Cette demande d’ajout nécessite des corrections sollicitées par un évaluateur officiel.
|
pulls.blocked_by_rejection=Cette demande d’ajout nécessite des corrections sollicitées par un évaluateur officiel.
|
||||||
pulls.blocked_by_official_review_requests=Cette demande d’ajout a des sollicitations officielles d’évaluation.
|
pulls.blocked_by_official_review_requests=Cette demande d’ajout a des sollicitations officielles d’évaluation.
|
||||||
pulls.blocked_by_outdated_branch=Cette demande d’ajout est bloquée car elle est obsolète.
|
pulls.blocked_by_outdated_branch=Cette demande d’ajout est bloquée car elle est obsolète.
|
||||||
@ -1844,7 +1877,9 @@ pulls.unrelated_histories=Échec de la fusion: La tête de fusion et la base ne
|
|||||||
pulls.merge_out_of_date=Échec de la fusion: La base a été mise à jour en cours de fusion. Indice : Réessayez.
|
pulls.merge_out_of_date=Échec de la fusion: La base a été mise à jour en cours de fusion. Indice : Réessayez.
|
||||||
pulls.head_out_of_date=Échec de la fusion : L’en-tête a été mis à jour pendant la fusion. Conseil : réessayez.
|
pulls.head_out_of_date=Échec de la fusion : L’en-tête a été mis à jour pendant la fusion. Conseil : réessayez.
|
||||||
pulls.has_merged=Échec : La demande d’ajout est déjà fusionnée, vous ne pouvez plus la fusionner, ni modifier sa branche cible.
|
pulls.has_merged=Échec : La demande d’ajout est déjà fusionnée, vous ne pouvez plus la fusionner, ni modifier sa branche cible.
|
||||||
|
pulls.push_rejected=Échec de la fusion : la soumission a été rejetée. Revoyez les déclencheurs Git pour ce dépôt.
|
||||||
pulls.push_rejected_summary=Message de rejet complet
|
pulls.push_rejected_summary=Message de rejet complet
|
||||||
|
pulls.push_rejected_no_message=Échec de la fusion : la soumission a été rejetée sans raison. Revoyez les déclencheurs Git pour ce dépôt.
|
||||||
pulls.open_unmerged_pull_exists=`Vous ne pouvez pas rouvrir ceci car la demande d’ajout #%d, en attente, a des propriétés identiques.`
|
pulls.open_unmerged_pull_exists=`Vous ne pouvez pas rouvrir ceci car la demande d’ajout #%d, en attente, a des propriétés identiques.`
|
||||||
pulls.status_checking=Certains contrôles sont en attente
|
pulls.status_checking=Certains contrôles sont en attente
|
||||||
pulls.status_checks_success=Tous les contrôles ont réussi
|
pulls.status_checks_success=Tous les contrôles ont réussi
|
||||||
@ -1868,6 +1903,7 @@ pulls.cmd_instruction_checkout_title=Basculer
|
|||||||
pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications.
|
pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications.
|
||||||
pulls.cmd_instruction_merge_title=Fusionner
|
pulls.cmd_instruction_merge_title=Fusionner
|
||||||
pulls.cmd_instruction_merge_desc=Fusionner les modifications et mettre à jour sur Gitea.
|
pulls.cmd_instruction_merge_desc=Fusionner les modifications et mettre à jour sur Gitea.
|
||||||
|
pulls.cmd_instruction_merge_warning=Attention : cette opération ne peut pas fusionner la demande d’ajout car la « détection automatique de fusion manuelle » n’a pas été activée
|
||||||
pulls.clear_merge_message=Effacer le message de fusion
|
pulls.clear_merge_message=Effacer le message de fusion
|
||||||
pulls.clear_merge_message_hint=Effacer le message de fusion ne supprimera que le message de la révision, mais pas les pieds de révision générés tels que "Co-Authored-By:".
|
pulls.clear_merge_message_hint=Effacer le message de fusion ne supprimera que le message de la révision, mais pas les pieds de révision générés tels que "Co-Authored-By:".
|
||||||
|
|
||||||
@ -1889,6 +1925,7 @@ pulls.delete.text=Voulez-vous vraiment supprimer cet demande d'ajout ? (Cela sup
|
|||||||
pulls.recently_pushed_new_branches=Vous avez soumis sur la branche <strong>%[1]s</strong> %[2]s
|
pulls.recently_pushed_new_branches=Vous avez soumis sur la branche <strong>%[1]s</strong> %[2]s
|
||||||
|
|
||||||
pull.deleted_branch=(supprimé) : %s
|
pull.deleted_branch=(supprimé) : %s
|
||||||
|
pull.agit_documentation=Voir la documentation sur AGit
|
||||||
|
|
||||||
comments.edit.already_changed=Impossible d’enregistrer ce commentaire. Il semble que le contenu ait été modifié par un autre utilisateur. Veuillez rafraîchir la page et réessayer afin d’éviter d’écraser leurs modifications.
|
comments.edit.already_changed=Impossible d’enregistrer ce commentaire. Il semble que le contenu ait été modifié par un autre utilisateur. Veuillez rafraîchir la page et réessayer afin d’éviter d’écraser leurs modifications.
|
||||||
|
|
||||||
@ -1899,6 +1936,7 @@ milestones.no_due_date=Aucune date d'échéance
|
|||||||
milestones.open=Ouvrir
|
milestones.open=Ouvrir
|
||||||
milestones.close=Fermer
|
milestones.close=Fermer
|
||||||
milestones.new_subheader=Les jalons peuvent vous aider à organiser vos tickets et à suivre leurs progrès.
|
milestones.new_subheader=Les jalons peuvent vous aider à organiser vos tickets et à suivre leurs progrès.
|
||||||
|
milestones.completeness=<strong>%d%%</strong> complété
|
||||||
milestones.create=Créer un Jalon
|
milestones.create=Créer un Jalon
|
||||||
milestones.title=Titre
|
milestones.title=Titre
|
||||||
milestones.desc=Description
|
milestones.desc=Description
|
||||||
@ -2083,7 +2121,8 @@ settings.push_mirror_sync_in_progress=Versement des changements vers le miroir d
|
|||||||
settings.site=Site Web
|
settings.site=Site Web
|
||||||
settings.update_settings=Appliquer
|
settings.update_settings=Appliquer
|
||||||
settings.update_mirror_settings=Mettre à jour les paramètres du miroir
|
settings.update_mirror_settings=Mettre à jour les paramètres du miroir
|
||||||
settings.branches.update_default_branch=Changer la Branche par Défaut
|
settings.branches.switch_default_branch=Changer la branche par défaut
|
||||||
|
settings.branches.update_default_branch=Changer la branche par défaut
|
||||||
settings.branches.add_new_rule=Ajouter une nouvelle règle
|
settings.branches.add_new_rule=Ajouter une nouvelle règle
|
||||||
settings.advanced_settings=Paramètres avancés
|
settings.advanced_settings=Paramètres avancés
|
||||||
settings.wiki_desc=Activer le wiki du dépôt
|
settings.wiki_desc=Activer le wiki du dépôt
|
||||||
@ -2120,6 +2159,7 @@ settings.pulls.default_delete_branch_after_merge=Supprimer la branche après la
|
|||||||
settings.pulls.default_allow_edits_from_maintainers=Autoriser les modifications par les mainteneurs par défaut
|
settings.pulls.default_allow_edits_from_maintainers=Autoriser les modifications par les mainteneurs par défaut
|
||||||
settings.releases_desc=Activer les publications du dépôt
|
settings.releases_desc=Activer les publications du dépôt
|
||||||
settings.packages_desc=Activer le registre des paquets du dépôt
|
settings.packages_desc=Activer le registre des paquets du dépôt
|
||||||
|
settings.projects_desc=Activer les projets de dépôt
|
||||||
settings.projects_mode_desc=Mode Projets (type de projets à afficher)
|
settings.projects_mode_desc=Mode Projets (type de projets à afficher)
|
||||||
settings.projects_mode_repo=Projets de dépôt uniquement
|
settings.projects_mode_repo=Projets de dépôt uniquement
|
||||||
settings.projects_mode_owner=Projets d’utilisateur ou d’organisation uniquement
|
settings.projects_mode_owner=Projets d’utilisateur ou d’organisation uniquement
|
||||||
@ -2159,6 +2199,7 @@ settings.transfer_in_progress=Il y a actuellement un transfert en cours. Veuille
|
|||||||
settings.transfer_notices_1=- Vous perdrez l'accès à ce dépôt si vous le transférez à un autre utilisateur.
|
settings.transfer_notices_1=- Vous perdrez l'accès à ce dépôt si vous le transférez à un autre utilisateur.
|
||||||
settings.transfer_notices_2=- Vous conserverez l'accès à ce dépôt si vous le transférez à une organisation dont vous êtes (co-)propriétaire.
|
settings.transfer_notices_2=- Vous conserverez l'accès à ce dépôt si vous le transférez à une organisation dont vous êtes (co-)propriétaire.
|
||||||
settings.transfer_notices_3=- Si le dépôt est privé et est transféré à un utilisateur individuel, cette action s'assure que l'utilisateur a au moins la permission de lire (et modifie les permissions si nécessaire).
|
settings.transfer_notices_3=- Si le dépôt est privé et est transféré à un utilisateur individuel, cette action s'assure que l'utilisateur a au moins la permission de lire (et modifie les permissions si nécessaire).
|
||||||
|
settings.transfer_notices_4=- Si le dépôt appartient à une organisation et que vous le transférez à une autre organisation ou personne, vous perdrez les liens entre les tickets du dépôt et le tableau de projet de l’organisation.
|
||||||
settings.transfer_owner=Nouveau propriétaire
|
settings.transfer_owner=Nouveau propriétaire
|
||||||
settings.transfer_perform=Effectuer le transfert
|
settings.transfer_perform=Effectuer le transfert
|
||||||
settings.transfer_started=`Ce dépôt a été marqué pour le transfert et attend la confirmation de "%s"`
|
settings.transfer_started=`Ce dépôt a été marqué pour le transfert et attend la confirmation de "%s"`
|
||||||
@ -2295,6 +2336,7 @@ settings.event_pull_request_merge=Fusion de demande d'ajout
|
|||||||
settings.event_package=Paquet
|
settings.event_package=Paquet
|
||||||
settings.event_package_desc=Paquet créé ou supprimé.
|
settings.event_package_desc=Paquet créé ou supprimé.
|
||||||
settings.branch_filter=Filtre de branche
|
settings.branch_filter=Filtre de branche
|
||||||
|
settings.branch_filter_desc=Liste de branches et motifs globs autorisant la soumission, la création et suppression de branches. Laisser vide ou utiliser <code>*</code> englobent toutes les branches. Voir la <a href="%[1]s">%[2]s</a>. Exemples : <code>master</code>, <code>{master,release*}</code>.
|
||||||
settings.authorization_header=En-tête « Authorization »
|
settings.authorization_header=En-tête « Authorization »
|
||||||
settings.authorization_header_desc=Si présent, sera ajouté aux requêtes comme en-tête d’authentification. Exemples : %s.
|
settings.authorization_header_desc=Si présent, sera ajouté aux requêtes comme en-tête d’authentification. Exemples : %s.
|
||||||
settings.active=Actif
|
settings.active=Actif
|
||||||
@ -2340,9 +2382,13 @@ settings.deploy_key_deletion=Supprimer une clef de déploiement
|
|||||||
settings.deploy_key_deletion_desc=La suppression d'une clef de déploiement révoque son accès à ce dépôt. Continuer ?
|
settings.deploy_key_deletion_desc=La suppression d'une clef de déploiement révoque son accès à ce dépôt. Continuer ?
|
||||||
settings.deploy_key_deletion_success=La clé de déploiement a été supprimée.
|
settings.deploy_key_deletion_success=La clé de déploiement a été supprimée.
|
||||||
settings.branches=Branches
|
settings.branches=Branches
|
||||||
|
settings.protected_branch=Protection de branche
|
||||||
settings.protected_branch.save_rule=Enregistrer la règle
|
settings.protected_branch.save_rule=Enregistrer la règle
|
||||||
settings.protected_branch.delete_rule=Supprimer la règle
|
settings.protected_branch.delete_rule=Supprimer la règle
|
||||||
settings.protected_branch_can_push=Autoriser la soumission ?
|
settings.protected_branch_can_push=Autoriser la soumission ?
|
||||||
|
settings.protected_branch_can_push_yes=Vous pouvez soumettre
|
||||||
|
settings.protected_branch_can_push_no=Vous ne pouvez pas soumettre
|
||||||
|
settings.branch_protection=Paramètres de protection de branches pour la branche <b>%s</b>
|
||||||
settings.protect_this_branch=Activer la protection de branche
|
settings.protect_this_branch=Activer la protection de branche
|
||||||
settings.protect_this_branch_desc=Empêche les suppressions et limite les poussées et fusions sur cette branche.
|
settings.protect_this_branch_desc=Empêche les suppressions et limite les poussées et fusions sur cette branche.
|
||||||
settings.protect_disable_push=Désactiver la soumission
|
settings.protect_disable_push=Désactiver la soumission
|
||||||
@ -2372,11 +2418,13 @@ settings.protect_merge_whitelist_teams=Équipes autorisées à fusionner :
|
|||||||
settings.protect_check_status_contexts=Activer le Contrôle Qualité
|
settings.protect_check_status_contexts=Activer le Contrôle Qualité
|
||||||
settings.protect_status_check_patterns=Motifs de vérification des statuts :
|
settings.protect_status_check_patterns=Motifs de vérification des statuts :
|
||||||
settings.protect_status_check_patterns_desc=Entrez des motifs pour spécifier quelles vérifications doivent réussir avant que des branches puissent être fusionnées. Un motif par ligne. Un motif ne peut être vide.
|
settings.protect_status_check_patterns_desc=Entrez des motifs pour spécifier quelles vérifications doivent réussir avant que des branches puissent être fusionnées. Un motif par ligne. Un motif ne peut être vide.
|
||||||
|
settings.protect_check_status_contexts_desc=Exiger le status « succès » avant de fusionner. Quand activée, une branche protégée ne peux accepter que des soumissions ou des fusions ayant le status « succès ». Lorsqu'il n’y a pas de contexte, la dernière révision fait foi.
|
||||||
settings.protect_check_status_contexts_list=Contrôles qualité trouvés au cours de la semaine dernière pour ce dépôt
|
settings.protect_check_status_contexts_list=Contrôles qualité trouvés au cours de la semaine dernière pour ce dépôt
|
||||||
settings.protect_status_check_matched=Correspondant
|
settings.protect_status_check_matched=Correspondant
|
||||||
settings.protect_invalid_status_check_pattern=Motif de vérification des statuts incorrect : « %s ».
|
settings.protect_invalid_status_check_pattern=Motif de vérification des statuts incorrect : « %s ».
|
||||||
settings.protect_no_valid_status_check_patterns=Aucun motif de vérification des statuts valide.
|
settings.protect_no_valid_status_check_patterns=Aucun motif de vérification des statuts valide.
|
||||||
settings.protect_required_approvals=Minimum d'approbations requis :
|
settings.protect_required_approvals=Minimum d'approbations requis :
|
||||||
|
settings.protect_required_approvals_desc=Permet de fusionner les demandes d’ajout lorsque suffisamment d’évaluation sont positives.
|
||||||
settings.protect_approvals_whitelist_enabled=Restreindre les approbations aux utilisateurs ou aux équipes sur liste d’autorisés
|
settings.protect_approvals_whitelist_enabled=Restreindre les approbations aux utilisateurs ou aux équipes sur liste d’autorisés
|
||||||
settings.protect_approvals_whitelist_enabled_desc=Seuls les évaluations des utilisateurs ou des équipes suivantes compteront dans les approbations requises. Si laissé vide, les évaluations de toute personne ayant un accès en écriture seront comptabilisées à la place.
|
settings.protect_approvals_whitelist_enabled_desc=Seuls les évaluations des utilisateurs ou des équipes suivantes compteront dans les approbations requises. Si laissé vide, les évaluations de toute personne ayant un accès en écriture seront comptabilisées à la place.
|
||||||
settings.protect_approvals_whitelist_users=Évaluateurs autorisés :
|
settings.protect_approvals_whitelist_users=Évaluateurs autorisés :
|
||||||
@ -2388,12 +2436,18 @@ settings.ignore_stale_approvals_desc=Ignorer les approbations d’anciennes rév
|
|||||||
settings.require_signed_commits=Exiger des révisions signées
|
settings.require_signed_commits=Exiger des révisions signées
|
||||||
settings.require_signed_commits_desc=Rejeter les soumissions sur cette branche lorsqu'ils ne sont pas signés ou vérifiables.
|
settings.require_signed_commits_desc=Rejeter les soumissions sur cette branche lorsqu'ils ne sont pas signés ou vérifiables.
|
||||||
settings.protect_branch_name_pattern=Motif de nom de branche protégé
|
settings.protect_branch_name_pattern=Motif de nom de branche protégé
|
||||||
|
settings.protect_branch_name_pattern_desc=Motifs de nom de branche protégé. Consultez la <a href="%s">documentation</a> pour la syntaxe du motif. Exemples : <code>main</code>, <code>release/**</code>
|
||||||
settings.protect_patterns=Motifs
|
settings.protect_patterns=Motifs
|
||||||
settings.protect_protected_file_patterns=Liste des fichiers et motifs protégés
|
settings.protect_protected_file_patterns=Liste des fichiers et motifs protégés
|
||||||
|
settings.protect_protected_file_patterns_desc=Liste de fichiers et de motifs, séparés par un point-virgule « ; », qui ne pourront pas être modifiés même si les utilisateurs disposent des droits sur la branche. Consultez la <a href='%[1]s'>%[2]s</a>. Exemples : <code>.drone.yml ; /docs/**/*.txt</code>.
|
||||||
settings.protect_unprotected_file_patterns=Liste des fichiers et motifs exclus
|
settings.protect_unprotected_file_patterns=Liste des fichiers et motifs exclus
|
||||||
|
settings.protect_unprotected_file_patterns_desc=Liste de fichiers et de motifs globs, séparés par un point-virgule « ; », qui pourront être modifiés malgré la protection de branche, par les utilisateurs autorisés. Voir la <a href='%[1]s'>%[2]s</a>. Exemples : <code>.drone.yml ; /docs/**/*.txt</code>.
|
||||||
|
settings.add_protected_branch=Activer la protection
|
||||||
|
settings.delete_protected_branch=Désactiver la protection
|
||||||
settings.update_protect_branch_success=La règle de protection de branche "%s" a été mise à jour.
|
settings.update_protect_branch_success=La règle de protection de branche "%s" a été mise à jour.
|
||||||
settings.remove_protected_branch_success=La règle de protection de branche "%s" a été retirée.
|
settings.remove_protected_branch_success=La règle de protection de branche "%s" a été retirée.
|
||||||
settings.remove_protected_branch_failed=Impossible de retirer la règle de protection de branche "%s".
|
settings.remove_protected_branch_failed=Impossible de retirer la règle de protection de branche "%s".
|
||||||
|
settings.protected_branch_deletion=Désactiver la protection de branche
|
||||||
settings.protected_branch_deletion_desc=Désactiver la protection de branche permet aux utilisateurs ayant accès en écriture de pousser des modifications sur la branche. Continuer ?
|
settings.protected_branch_deletion_desc=Désactiver la protection de branche permet aux utilisateurs ayant accès en écriture de pousser des modifications sur la branche. Continuer ?
|
||||||
settings.block_rejected_reviews=Bloquer la fusion en cas d’évaluations négatives
|
settings.block_rejected_reviews=Bloquer la fusion en cas d’évaluations négatives
|
||||||
settings.block_rejected_reviews_desc=La fusion ne sera pas possible lorsque des modifications sont demandées par les évaluateurs officiels, même s'il y a suffisamment d’approbations.
|
settings.block_rejected_reviews_desc=La fusion ne sera pas possible lorsque des modifications sont demandées par les évaluateurs officiels, même s'il y a suffisamment d’approbations.
|
||||||
@ -2403,6 +2457,7 @@ settings.block_outdated_branch=Bloquer la fusion si la demande d'ajout est obsol
|
|||||||
settings.block_outdated_branch_desc=La fusion ne sera pas possible lorsque la branche principale est derrière la branche de base.
|
settings.block_outdated_branch_desc=La fusion ne sera pas possible lorsque la branche principale est derrière la branche de base.
|
||||||
settings.default_branch_desc=Sélectionnez une branche par défaut pour les demandes de fusion et les révisions :
|
settings.default_branch_desc=Sélectionnez une branche par défaut pour les demandes de fusion et les révisions :
|
||||||
settings.merge_style_desc=Styles de fusion
|
settings.merge_style_desc=Styles de fusion
|
||||||
|
settings.default_merge_style_desc=Méthode de fusion par défaut
|
||||||
settings.choose_branch=Choisissez une branche…
|
settings.choose_branch=Choisissez une branche…
|
||||||
settings.no_protected_branch=Il n'y a pas de branche protégée.
|
settings.no_protected_branch=Il n'y a pas de branche protégée.
|
||||||
settings.edit_protected_branch=Éditer
|
settings.edit_protected_branch=Éditer
|
||||||
@ -2418,12 +2473,25 @@ settings.tags.protection.allowed.teams=Équipes autorisées
|
|||||||
settings.tags.protection.allowed.noone=Personne
|
settings.tags.protection.allowed.noone=Personne
|
||||||
settings.tags.protection.create=Protéger l'étiquette
|
settings.tags.protection.create=Protéger l'étiquette
|
||||||
settings.tags.protection.none=Il n'y a pas d'étiquettes protégées.
|
settings.tags.protection.none=Il n'y a pas d'étiquettes protégées.
|
||||||
|
settings.tags.protection.pattern.description=Vous pouvez utiliser au choix un nom unique, un motif de glob ou une expression régulière qui correspondra à plusieurs étiquettes. Pour plus d’informations, consultez le <a target="_blank" rel="noopener" href="%s">guide sur les étiquettes protégées</a>.
|
||||||
settings.bot_token=Jeton de Bot
|
settings.bot_token=Jeton de Bot
|
||||||
settings.chat_id=ID de conversation
|
settings.chat_id=ID de conversation
|
||||||
settings.thread_id=ID du fil
|
settings.thread_id=ID du fil
|
||||||
settings.matrix.homeserver_url=URL du serveur d'accueil
|
settings.matrix.homeserver_url=URL du serveur d'accueil
|
||||||
settings.matrix.room_id=ID de la salle
|
settings.matrix.room_id=ID de la salle
|
||||||
settings.matrix.message_type=Type de message
|
settings.matrix.message_type=Type de message
|
||||||
|
settings.visibility.private.button=Rendre privé
|
||||||
|
settings.visibility.private.text=Rendre le dépôt privé rendra non seulement le dépôt visible uniquement aux membres autorisés, mais peut également rompre la relation entre lui et ses bifurcations, observateurs, et favoris.
|
||||||
|
settings.visibility.private.bullet_title=<strong>Changer la visibilité en privé :</strong>
|
||||||
|
settings.visibility.private.bullet_one=Va rendre le dépôt visible uniquement par les membres autorisés
|
||||||
|
settings.visibility.private.bullet_two=Peut supprimer la relation avec <strong>ses bifurcations</strong>, <strong>ses observateurs</strong> et <strong>ses favoris</strong>
|
||||||
|
settings.visibility.public.button=Rendre public
|
||||||
|
settings.visibility.public.text=Rendre le dépôt public rendra le dépôt visible à tout le monde.
|
||||||
|
settings.visibility.public.bullet_title=<strong>Changer la visibilité en public va :</strong>
|
||||||
|
settings.visibility.public.bullet_one=Rendre le dépôt visible à tout le monde.
|
||||||
|
settings.visibility.success=Visibilité du dépôt changée.
|
||||||
|
settings.visibility.error=Une erreur s’est produite en essayant de changer la visibilité du dépôt.
|
||||||
|
settings.visibility.fork_error=Impossible de changer la visibilité d’un dépôt bifurqué.
|
||||||
settings.archive.button=Archiver ce dépôt
|
settings.archive.button=Archiver ce dépôt
|
||||||
settings.archive.header=Archiver ce dépôt
|
settings.archive.header=Archiver ce dépôt
|
||||||
settings.archive.text=Archiver un dépôt le place en lecture seule et le cache des tableaux de bord. Personne ne pourra faire de nouvelles révisions, d'ouvrir des tickets ou des demandes d'ajouts (pas même vous!).
|
settings.archive.text=Archiver un dépôt le place en lecture seule et le cache des tableaux de bord. Personne ne pourra faire de nouvelles révisions, d'ouvrir des tickets ou des demandes d'ajouts (pas même vous!).
|
||||||
@ -2620,6 +2688,7 @@ tag.create_success=L'étiquette "%s" a été créée.
|
|||||||
|
|
||||||
topic.manage_topics=Gérer les sujets
|
topic.manage_topics=Gérer les sujets
|
||||||
topic.done=Terminé
|
topic.done=Terminé
|
||||||
|
topic.count_prompt=Vous ne pouvez pas sélectionner plus de 25 sujets
|
||||||
topic.format_prompt=Les sujets doivent commencer par un caractère alphanumérique, peuvent inclure des traits d’union « - » et des points « . », et mesurer jusqu'à 35 caractères. Les lettres doivent être en minuscules.
|
topic.format_prompt=Les sujets doivent commencer par un caractère alphanumérique, peuvent inclure des traits d’union « - » et des points « . », et mesurer jusqu'à 35 caractères. Les lettres doivent être en minuscules.
|
||||||
|
|
||||||
find_file.go_to_file=Aller au fichier
|
find_file.go_to_file=Aller au fichier
|
||||||
@ -2786,6 +2855,7 @@ last_page=Dernière
|
|||||||
total=Total : %d
|
total=Total : %d
|
||||||
settings=Paramètres administrateur
|
settings=Paramètres administrateur
|
||||||
|
|
||||||
|
dashboard.new_version_hint=Gitea %s est maintenant disponible, vous utilisez %s. Consultez <a target="_blank" rel="noreferrer" href="%s">le blog</a> pour plus de détails.
|
||||||
dashboard.statistic=Résumé
|
dashboard.statistic=Résumé
|
||||||
dashboard.maintenance_operations=Opérations de maintenance
|
dashboard.maintenance_operations=Opérations de maintenance
|
||||||
dashboard.system_status=État du système
|
dashboard.system_status=État du système
|
||||||
@ -2828,6 +2898,7 @@ dashboard.reinit_missing_repos=Réinitialiser tous les dépôts Git manquants po
|
|||||||
dashboard.sync_external_users=Synchroniser les données de l’utilisateur externe
|
dashboard.sync_external_users=Synchroniser les données de l’utilisateur externe
|
||||||
dashboard.cleanup_hook_task_table=Nettoyer la table hook_task
|
dashboard.cleanup_hook_task_table=Nettoyer la table hook_task
|
||||||
dashboard.cleanup_packages=Nettoyer des paquets expirés
|
dashboard.cleanup_packages=Nettoyer des paquets expirés
|
||||||
|
dashboard.cleanup_actions=Nettoyer les reliquats des actions obsolètes
|
||||||
dashboard.server_uptime=Uptime du serveur
|
dashboard.server_uptime=Uptime du serveur
|
||||||
dashboard.current_goroutine=Goroutines actuelles
|
dashboard.current_goroutine=Goroutines actuelles
|
||||||
dashboard.current_memory_usage=Utilisation Mémoire actuelle
|
dashboard.current_memory_usage=Utilisation Mémoire actuelle
|
||||||
@ -2857,9 +2928,15 @@ dashboard.total_gc_time=Pause GC
|
|||||||
dashboard.total_gc_pause=Pause GC
|
dashboard.total_gc_pause=Pause GC
|
||||||
dashboard.last_gc_pause=Dernière Pause GC
|
dashboard.last_gc_pause=Dernière Pause GC
|
||||||
dashboard.gc_times=Nombres de GC
|
dashboard.gc_times=Nombres de GC
|
||||||
|
dashboard.delete_old_actions=Supprimer toutes les anciennes activités de la base de données
|
||||||
|
dashboard.delete_old_actions.started=La suppression des anciennes activités de la base de données a démarré.
|
||||||
dashboard.update_checker=Vérificateur de mise à jour
|
dashboard.update_checker=Vérificateur de mise à jour
|
||||||
dashboard.delete_old_system_notices=Supprimer toutes les anciennes observations de la base de données
|
dashboard.delete_old_system_notices=Supprimer toutes les anciennes observations de la base de données
|
||||||
dashboard.gc_lfs=Épousseter les métaobjets LFS
|
dashboard.gc_lfs=Épousseter les métaobjets LFS
|
||||||
|
dashboard.stop_zombie_tasks=Arrêter les tâches zombies
|
||||||
|
dashboard.stop_endless_tasks=Arrêter les tâches interminables
|
||||||
|
dashboard.cancel_abandoned_jobs=Annuler les travaux abandonnés
|
||||||
|
dashboard.start_schedule_tasks=Démarrer les tâches planifiées
|
||||||
dashboard.sync_branch.started=Début de la synchronisation des branches
|
dashboard.sync_branch.started=Début de la synchronisation des branches
|
||||||
dashboard.sync_tag.started=Synchronisation des étiquettes
|
dashboard.sync_tag.started=Synchronisation des étiquettes
|
||||||
dashboard.rebuild_issue_indexer=Reconstruire l’indexeur des tickets
|
dashboard.rebuild_issue_indexer=Reconstruire l’indexeur des tickets
|
||||||
@ -2970,10 +3047,12 @@ packages.size=Taille
|
|||||||
packages.published=Publiés
|
packages.published=Publiés
|
||||||
|
|
||||||
defaulthooks=Déclencheurs web par défaut
|
defaulthooks=Déclencheurs web par défaut
|
||||||
|
defaulthooks.desc=Les webhooks font automatiquement des requêtes POST HTTP à un serveur spécifié lorsque certains événements Gitea se déclenchent. Ceux créés ici sont par défaut copiés sur tous les nouveaux dépôts. Pour plus d'information, consultez le <a target="_blank" rel="noopener" href="%s">guide des webhooks</a>.
|
||||||
defaulthooks.add_webhook=Ajouter un déclencheur web par défaut
|
defaulthooks.add_webhook=Ajouter un déclencheur web par défaut
|
||||||
defaulthooks.update_webhook=Mettre à jour le déclencheur web par défaut
|
defaulthooks.update_webhook=Mettre à jour le déclencheur web par défaut
|
||||||
|
|
||||||
systemhooks=Webhooks système
|
systemhooks=Webhooks système
|
||||||
|
systemhooks.desc=Les webhooks font automatiquement des requêtes POST HTTP à un serveur spécifié lorsque certains événements Gitea se déclenchent. Ceux créé ici agiront sur tous les dépôts, ce qui peux impacter les performances du système. Pour plus d’information, consultez <a target="_blank" rel="noopener" href="%s">le guide des webhooks</a>.
|
||||||
systemhooks.add_webhook=Ajouter un rappel système
|
systemhooks.add_webhook=Ajouter un rappel système
|
||||||
systemhooks.update_webhook=Mettre à jour un rappel système
|
systemhooks.update_webhook=Mettre à jour un rappel système
|
||||||
|
|
||||||
@ -3068,8 +3147,18 @@ auths.tips=Conseils
|
|||||||
auths.tips.oauth2.general=Authentification OAuth2
|
auths.tips.oauth2.general=Authentification OAuth2
|
||||||
auths.tips.oauth2.general.tip=Lors de l'enregistrement d'une nouvelle authentification OAuth2, l'URL de rappel/redirection doit être :
|
auths.tips.oauth2.general.tip=Lors de l'enregistrement d'une nouvelle authentification OAuth2, l'URL de rappel/redirection doit être :
|
||||||
auths.tip.oauth2_provider=Fournisseur OAuth2
|
auths.tip.oauth2_provider=Fournisseur OAuth2
|
||||||
|
auths.tip.bitbucket=Créez un nouveau jeton OAuth sur %s et ajoutez la permission « Compte » → « Lecture ».
|
||||||
auths.tip.nextcloud=`Enregistrez un nouveau consommateur OAuth sur votre instance en utilisant le menu "Paramètres -> Sécurité -> Client OAuth 2.0"`
|
auths.tip.nextcloud=`Enregistrez un nouveau consommateur OAuth sur votre instance en utilisant le menu "Paramètres -> Sécurité -> Client OAuth 2.0"`
|
||||||
|
auths.tip.dropbox=Créez une nouvelle application sur %s
|
||||||
|
auths.tip.facebook=Enregistrez une nouvelle application sur%s et ajoutez le produit « Facebook Login ».
|
||||||
|
auths.tip.github=Créez une nouvelle application OAuth sur %s
|
||||||
|
auths.tip.gitlab_new=Enregistrez une nouvelle application sur %s
|
||||||
|
auths.tip.google_plus=Obtenez des identifiants OAuth2 sur la console API de Google (%s)
|
||||||
auths.tip.openid_connect=Utilisez l’URL de découverte OpenID « https://{server}/.well-known/openid-configuration » pour spécifier les points d'accès.
|
auths.tip.openid_connect=Utilisez l’URL de découverte OpenID « https://{server}/.well-known/openid-configuration » pour spécifier les points d'accès.
|
||||||
|
auths.tip.twitter=Rendez-vous sur %s, créez une application et assurez-vous que l’option « Autoriser l’application à être utilisée avec Twitter Connect » est activée.
|
||||||
|
auths.tip.discord=Enregistrer une nouvelle application sur %s
|
||||||
|
auths.tip.gitea=Enregistrez une nouvelle application OAuth2. Le guide peut être trouvé sur %s.
|
||||||
|
auths.tip.yandex=Créez une nouvelle application sur %s. Sélectionnez les autorisations suivantes dans la section « Yandex.Passport API » : « Accès à l’adresse e-mail », « Accès à l’avatar de l’utilisateur » et « Accès au nom d’utilisateur, prénom, surnom et genre ».
|
||||||
auths.tip.mastodon=Entrez une URL d'instance personnalisée pour l'instance mastodon avec laquelle vous voulez vous authentifier (ou utiliser celle par défaut)
|
auths.tip.mastodon=Entrez une URL d'instance personnalisée pour l'instance mastodon avec laquelle vous voulez vous authentifier (ou utiliser celle par défaut)
|
||||||
auths.edit=Mettre à jour la source d'authentification
|
auths.edit=Mettre à jour la source d'authentification
|
||||||
auths.activated=Cette source d'authentification est activée
|
auths.activated=Cette source d'authentification est activée
|
||||||
@ -3243,6 +3332,8 @@ monitor.start=Heure de démarrage
|
|||||||
monitor.execute_time=Heure d'Éxécution
|
monitor.execute_time=Heure d'Éxécution
|
||||||
monitor.last_execution_result=Résultat
|
monitor.last_execution_result=Résultat
|
||||||
monitor.process.cancel=Annuler le processus
|
monitor.process.cancel=Annuler le processus
|
||||||
|
monitor.process.cancel_desc=L’annulation d’un processus peut entraîner une perte de données.
|
||||||
|
monitor.process.cancel_notices=Annuler : <strong>%s</strong> ?
|
||||||
monitor.process.children=Enfant
|
monitor.process.children=Enfant
|
||||||
|
|
||||||
monitor.queues=Files d'attente
|
monitor.queues=Files d'attente
|
||||||
@ -3344,6 +3435,7 @@ raw_minutes=minutes
|
|||||||
|
|
||||||
[dropzone]
|
[dropzone]
|
||||||
default_message=Déposez les fichiers ou cliquez ici pour téléverser.
|
default_message=Déposez les fichiers ou cliquez ici pour téléverser.
|
||||||
|
invalid_input_type=Vous ne pouvez pas téléverser des fichiers de ce type.
|
||||||
file_too_big=La taille du fichier ({{filesize}} Mo) dépasse la taille maximale ({{maxFilesize}} Mo).
|
file_too_big=La taille du fichier ({{filesize}} Mo) dépasse la taille maximale ({{maxFilesize}} Mo).
|
||||||
remove_file=Supprimer le fichier
|
remove_file=Supprimer le fichier
|
||||||
|
|
||||||
@ -3615,6 +3707,7 @@ runs.no_workflows.quick_start=Vous découvrez les Actions Gitea ? Consultez <a t
|
|||||||
runs.no_workflows.documentation=Pour plus d’informations sur les actions Gitea, voir <a target="_blank" rel="noopener noreferrer" href="%s">la documentation</a>.
|
runs.no_workflows.documentation=Pour plus d’informations sur les actions Gitea, voir <a target="_blank" rel="noopener noreferrer" href="%s">la documentation</a>.
|
||||||
runs.no_runs=Le flux de travail n'a pas encore d'exécution.
|
runs.no_runs=Le flux de travail n'a pas encore d'exécution.
|
||||||
runs.empty_commit_message=(message de révision vide)
|
runs.empty_commit_message=(message de révision vide)
|
||||||
|
runs.expire_log_message=Les journaux ont été supprimés car ils étaient trop anciens.
|
||||||
|
|
||||||
workflow.disable=Désactiver le flux de travail
|
workflow.disable=Désactiver le flux de travail
|
||||||
workflow.disable_success=Le flux de travail « %s » a bien été désactivé.
|
workflow.disable_success=Le flux de travail « %s » a bien été désactivé.
|
||||||
@ -3646,6 +3739,7 @@ variables.update.failed=Impossible d’éditer la variable.
|
|||||||
variables.update.success=La variable a bien été modifiée.
|
variables.update.success=La variable a bien été modifiée.
|
||||||
|
|
||||||
[projects]
|
[projects]
|
||||||
|
deleted.display_name=Projet supprimé
|
||||||
type-1.display_name=Projet personnel
|
type-1.display_name=Projet personnel
|
||||||
type-2.display_name=Projet de dépôt
|
type-2.display_name=Projet de dépôt
|
||||||
type-3.display_name=Projet d’organisation
|
type-3.display_name=Projet d’organisation
|
||||||
|
2992
options/locale/locale_ga-IE.ini
Normal file
2992
options/locale/locale_ga-IE.ini
Normal file
File diff suppressed because it is too large
Load Diff
@ -397,7 +397,6 @@ account_link=Kapcsolt fiókok
|
|||||||
organization=Szervezetek
|
organization=Szervezetek
|
||||||
|
|
||||||
public_profile=Nyilvános profil
|
public_profile=Nyilvános profil
|
||||||
password_username_disabled=A nem helyi felhasználóknak nem engedélyezett, hogy megváltoztassák a felhasználói nevüket. Kérjük lépjen kapcsolatba a helyi rendszergazdájával további információkért.
|
|
||||||
full_name=Teljes név
|
full_name=Teljes név
|
||||||
website=Webhely
|
website=Webhely
|
||||||
location=Hely
|
location=Hely
|
||||||
@ -901,13 +900,13 @@ issues.dependency.add_error_dep_issue_not_exist=Függő hibajegy nem létezik.
|
|||||||
issues.dependency.add_error_dep_not_exist=A függőség nem létezik.
|
issues.dependency.add_error_dep_not_exist=A függőség nem létezik.
|
||||||
issues.dependency.add_error_dep_exists=A függőség már létezik.
|
issues.dependency.add_error_dep_exists=A függőség már létezik.
|
||||||
issues.dependency.add_error_dep_not_same_repo=Mindkét hibajegynek ugyanabban a tárolóban kell lennie.
|
issues.dependency.add_error_dep_not_same_repo=Mindkét hibajegynek ugyanabban a tárolóban kell lennie.
|
||||||
issues.review.comment=Hozzászólás
|
|
||||||
issues.review.reject=%s változtatások kérése
|
issues.review.reject=%s változtatások kérése
|
||||||
issues.review.pending=Függőben
|
issues.review.pending=Függőben
|
||||||
issues.review.review=Értékelés
|
issues.review.review=Értékelés
|
||||||
issues.review.reviewers=Véleményezők
|
issues.review.reviewers=Véleményezők
|
||||||
issues.review.show_outdated=Elavultak mutatása
|
issues.review.show_outdated=Elavultak mutatása
|
||||||
issues.review.hide_outdated=Elavultak elrejtése
|
issues.review.hide_outdated=Elavultak elrejtése
|
||||||
|
issues.review.commented=Hozzászólás
|
||||||
issues.assignee.error=Nem minden megbízott lett hozzáadva egy nem várt hiba miatt.
|
issues.assignee.error=Nem minden megbízott lett hozzáadva egy nem várt hiba miatt.
|
||||||
|
|
||||||
|
|
||||||
|
@ -317,7 +317,6 @@ account_link=Akun Tertaut
|
|||||||
organization=Organisasi
|
organization=Organisasi
|
||||||
|
|
||||||
public_profile=Profil Publik
|
public_profile=Profil Publik
|
||||||
password_username_disabled=Pengguna non-lokal tidak diizinkan untuk mengubah nama pengguna mereka. Silakan hubungi administrator sistem anda untuk lebih lanjut.
|
|
||||||
full_name=Nama Lengkap
|
full_name=Nama Lengkap
|
||||||
website=Situs Web
|
website=Situs Web
|
||||||
location=Lokasi
|
location=Lokasi
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user