mirror of
https://github.com/go-gitea/gitea
synced 2024-09-27 16:56:49 +02:00
Merge branch 'main' into lunny/fix_issue_comment_num
This commit is contained in:
commit
ee21b92588
@ -310,7 +310,7 @@ rules:
|
|||||||
jquery/no-merge: [2]
|
jquery/no-merge: [2]
|
||||||
jquery/no-param: [2]
|
jquery/no-param: [2]
|
||||||
jquery/no-parent: [0]
|
jquery/no-parent: [0]
|
||||||
jquery/no-parents: [0]
|
jquery/no-parents: [2]
|
||||||
jquery/no-parse-html: [2]
|
jquery/no-parse-html: [2]
|
||||||
jquery/no-prop: [2]
|
jquery/no-prop: [2]
|
||||||
jquery/no-proxy: [2]
|
jquery/no-proxy: [2]
|
||||||
@ -319,8 +319,8 @@ rules:
|
|||||||
jquery/no-show: [2]
|
jquery/no-show: [2]
|
||||||
jquery/no-size: [2]
|
jquery/no-size: [2]
|
||||||
jquery/no-sizzle: [2]
|
jquery/no-sizzle: [2]
|
||||||
jquery/no-slide: [0]
|
jquery/no-slide: [2]
|
||||||
jquery/no-submit: [0]
|
jquery/no-submit: [2]
|
||||||
jquery/no-text: [0]
|
jquery/no-text: [0]
|
||||||
jquery/no-toggle: [2]
|
jquery/no-toggle: [2]
|
||||||
jquery/no-trigger: [0]
|
jquery/no-trigger: [0]
|
||||||
@ -458,7 +458,7 @@ rules:
|
|||||||
no-jquery/no-other-utils: [2]
|
no-jquery/no-other-utils: [2]
|
||||||
no-jquery/no-param: [2]
|
no-jquery/no-param: [2]
|
||||||
no-jquery/no-parent: [0]
|
no-jquery/no-parent: [0]
|
||||||
no-jquery/no-parents: [0]
|
no-jquery/no-parents: [2]
|
||||||
no-jquery/no-parse-html-literal: [0]
|
no-jquery/no-parse-html-literal: [0]
|
||||||
no-jquery/no-parse-html: [2]
|
no-jquery/no-parse-html: [2]
|
||||||
no-jquery/no-parse-json: [2]
|
no-jquery/no-parse-json: [2]
|
||||||
|
8
.github/workflows/pull-compliance.yml
vendored
8
.github/workflows/pull-compliance.yml
vendored
@ -38,6 +38,8 @@ jobs:
|
|||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: pip install poetry
|
- run: pip install poetry
|
||||||
- run: make deps-py
|
- run: make deps-py
|
||||||
- run: make deps-frontend
|
- run: make deps-frontend
|
||||||
@ -65,6 +67,8 @@ jobs:
|
|||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend
|
- run: make deps-frontend
|
||||||
- run: make lint-swagger
|
- run: make lint-swagger
|
||||||
|
|
||||||
@ -134,6 +138,8 @@ jobs:
|
|||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend
|
- run: make deps-frontend
|
||||||
- run: make lint-frontend
|
- run: make lint-frontend
|
||||||
- run: make checks-frontend
|
- run: make checks-frontend
|
||||||
@ -181,6 +187,8 @@ jobs:
|
|||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend
|
- run: make deps-frontend
|
||||||
- run: make lint-md
|
- run: make lint-md
|
||||||
- run: make docs
|
- run: make docs
|
||||||
|
2
.github/workflows/pull-e2e-tests.yml
vendored
2
.github/workflows/pull-e2e-tests.yml
vendored
@ -24,6 +24,8 @@ jobs:
|
|||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend frontend deps-backend
|
- run: make deps-frontend frontend deps-backend
|
||||||
- run: npx playwright install --with-deps
|
- run: npx playwright install --with-deps
|
||||||
- run: make test-e2e-sqlite
|
- run: make test-e2e-sqlite
|
||||||
|
2
.github/workflows/release-nightly.yml
vendored
2
.github/workflows/release-nightly.yml
vendored
@ -25,6 +25,8 @@ jobs:
|
|||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend deps-backend
|
- run: make deps-frontend deps-backend
|
||||||
# xgo build
|
# xgo build
|
||||||
- run: make release
|
- run: make release
|
||||||
|
2
.github/workflows/release-tag-rc.yml
vendored
2
.github/workflows/release-tag-rc.yml
vendored
@ -24,6 +24,8 @@ jobs:
|
|||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend deps-backend
|
- run: make deps-frontend deps-backend
|
||||||
# xgo build
|
# xgo build
|
||||||
- run: make release
|
- run: make release
|
||||||
|
2
.github/workflows/release-tag-version.yml
vendored
2
.github/workflows/release-tag-version.yml
vendored
@ -26,6 +26,8 @@ jobs:
|
|||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
- run: make deps-frontend deps-backend
|
- run: make deps-frontend deps-backend
|
||||||
# xgo build
|
# xgo build
|
||||||
- run: make release
|
- run: make release
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
linters:
|
linters:
|
||||||
|
enable-all: false
|
||||||
|
disable-all: true
|
||||||
|
fast: false
|
||||||
enable:
|
enable:
|
||||||
- bidichk
|
- bidichk
|
||||||
# - deadcode # deprecated - https://github.com/golangci/golangci-lint/issues/1841
|
|
||||||
- depguard
|
- depguard
|
||||||
- dupl
|
- dupl
|
||||||
- errcheck
|
- errcheck
|
||||||
- forbidigo
|
- forbidigo
|
||||||
- gocritic
|
- gocritic
|
||||||
# - gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
|
||||||
- gofmt
|
- gofmt
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- gosimple
|
- gosimple
|
||||||
@ -17,20 +18,18 @@ linters:
|
|||||||
- nolintlint
|
- nolintlint
|
||||||
- revive
|
- revive
|
||||||
- staticcheck
|
- staticcheck
|
||||||
# - structcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841
|
|
||||||
- stylecheck
|
- stylecheck
|
||||||
- typecheck
|
- typecheck
|
||||||
- unconvert
|
- unconvert
|
||||||
- unused
|
- unused
|
||||||
# - varcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841
|
|
||||||
- wastedassign
|
- wastedassign
|
||||||
enable-all: false
|
|
||||||
disable-all: true
|
|
||||||
fast: false
|
|
||||||
|
|
||||||
run:
|
run:
|
||||||
timeout: 10m
|
timeout: 10m
|
||||||
|
|
||||||
|
output:
|
||||||
|
sort-results: true
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
stylecheck:
|
stylecheck:
|
||||||
checks: ["all", "-ST1005", "-ST1003"]
|
checks: ["all", "-ST1005", "-ST1003"]
|
||||||
@ -47,27 +46,37 @@ linters-settings:
|
|||||||
errorCode: 1
|
errorCode: 1
|
||||||
warningCode: 1
|
warningCode: 1
|
||||||
rules:
|
rules:
|
||||||
|
- name: atomic
|
||||||
|
- name: bare-return
|
||||||
- name: blank-imports
|
- name: blank-imports
|
||||||
|
- name: constant-logical-expr
|
||||||
- name: context-as-argument
|
- name: context-as-argument
|
||||||
- name: context-keys-type
|
- name: context-keys-type
|
||||||
- name: dot-imports
|
- name: dot-imports
|
||||||
|
- name: duplicated-imports
|
||||||
|
- name: empty-lines
|
||||||
|
- name: error-naming
|
||||||
- name: error-return
|
- name: error-return
|
||||||
- name: error-strings
|
- name: error-strings
|
||||||
- name: error-naming
|
- name: errorf
|
||||||
- name: exported
|
- name: exported
|
||||||
|
- name: identical-branches
|
||||||
- name: if-return
|
- name: if-return
|
||||||
- name: increment-decrement
|
- name: increment-decrement
|
||||||
- name: var-naming
|
- name: indent-error-flow
|
||||||
- name: var-declaration
|
- name: modifies-value-receiver
|
||||||
- name: package-comments
|
- name: package-comments
|
||||||
- name: range
|
- name: range
|
||||||
- name: receiver-naming
|
- name: receiver-naming
|
||||||
|
- name: redefines-builtin-id
|
||||||
|
- name: string-of-int
|
||||||
|
- name: superfluous-else
|
||||||
- name: time-naming
|
- name: time-naming
|
||||||
|
- name: unconditional-recursion
|
||||||
- name: unexported-return
|
- name: unexported-return
|
||||||
- name: indent-error-flow
|
- name: unreachable-code
|
||||||
- name: errorf
|
- name: var-declaration
|
||||||
- name: duplicated-imports
|
- name: var-naming
|
||||||
- name: modifies-value-receiver
|
|
||||||
gofumpt:
|
gofumpt:
|
||||||
extra-rules: true
|
extra-rules: true
|
||||||
depguard:
|
depguard:
|
||||||
@ -93,8 +102,8 @@ issues:
|
|||||||
max-issues-per-linter: 0
|
max-issues-per-linter: 0
|
||||||
max-same-issues: 0
|
max-same-issues: 0
|
||||||
exclude-dirs: [node_modules, public, web_src]
|
exclude-dirs: [node_modules, public, web_src]
|
||||||
|
exclude-case-sensitive: true
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
# Exclude some linters from running on tests files.
|
|
||||||
- path: _test\.go
|
- path: _test\.go
|
||||||
linters:
|
linters:
|
||||||
- gocyclo
|
- gocyclo
|
||||||
@ -112,19 +121,19 @@ issues:
|
|||||||
- path: cmd
|
- path: cmd
|
||||||
linters:
|
linters:
|
||||||
- forbidigo
|
- forbidigo
|
||||||
- linters:
|
- text: "webhook"
|
||||||
|
linters:
|
||||||
- dupl
|
- dupl
|
||||||
text: "webhook"
|
- text: "`ID' should not be capitalized"
|
||||||
- linters:
|
linters:
|
||||||
- gocritic
|
- gocritic
|
||||||
text: "`ID' should not be capitalized"
|
- text: "swagger"
|
||||||
- linters:
|
linters:
|
||||||
- unused
|
- unused
|
||||||
- deadcode
|
- deadcode
|
||||||
text: "swagger"
|
- text: "argument x is overwritten before first use"
|
||||||
- linters:
|
linters:
|
||||||
- staticcheck
|
- staticcheck
|
||||||
text: "argument x is overwritten before first use"
|
|
||||||
- text: "commentFormatting: put a space between `//` and comment text"
|
- text: "commentFormatting: put a space between `//` and comment text"
|
||||||
linters:
|
linters:
|
||||||
- gocritic
|
- gocritic
|
||||||
|
5223
CHANGELOG-archived.md
Normal file
5223
CHANGELOG-archived.md
Normal file
File diff suppressed because it is too large
Load Diff
5382
CHANGELOG.md
5382
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
4
Makefile
4
Makefile
@ -143,9 +143,9 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMAN
|
|||||||
GO_DIRS := build cmd models modules routers services tests
|
GO_DIRS := build cmd models modules routers services tests
|
||||||
WEB_DIRS := web_src/js web_src/css
|
WEB_DIRS := web_src/js web_src/css
|
||||||
|
|
||||||
ESLINT_FILES := web_src/js tools *.config.js tests/e2e
|
ESLINT_FILES := web_src/js tools *.js tests/e2e
|
||||||
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
|
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
|
||||||
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github
|
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml))
|
||||||
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
|
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
|
||||||
|
|
||||||
GO_SOURCES := $(wildcard *.go)
|
GO_SOURCES := $(wildcard *.go)
|
||||||
|
10
assets/go-licenses.json
generated
10
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
@ -4,6 +4,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
@ -91,7 +92,7 @@ func runListAuth(c *cli.Context) error {
|
|||||||
|
|
||||||
func runDeleteAuth(c *cli.Context) error {
|
func runDeleteAuth(c *cli.Context) error {
|
||||||
if !c.IsSet("id") {
|
if !c.IsSet("id") {
|
||||||
return fmt.Errorf("--id flag is missing")
|
return errors.New("--id flag is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
@ -193,7 +194,7 @@ func runAddOauth(c *cli.Context) error {
|
|||||||
|
|
||||||
func runUpdateOauth(c *cli.Context) error {
|
func runUpdateOauth(c *cli.Context) error {
|
||||||
if !c.IsSet("id") {
|
if !c.IsSet("id") {
|
||||||
return fmt.Errorf("--id flag is missing")
|
return errors.New("--id flag is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
|
@ -5,7 +5,6 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
@ -166,7 +165,7 @@ func runAddSMTP(c *cli.Context) error {
|
|||||||
|
|
||||||
func runUpdateSMTP(c *cli.Context) error {
|
func runUpdateSMTP(c *cli.Context) error {
|
||||||
if !c.IsSet("id") {
|
if !c.IsSet("id") {
|
||||||
return fmt.Errorf("--id flag is missing")
|
return errors.New("--id flag is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ var microcmdUserDelete = &cli.Command{
|
|||||||
|
|
||||||
func runDeleteUser(c *cli.Context) error {
|
func runDeleteUser(c *cli.Context) error {
|
||||||
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
|
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
|
||||||
return fmt.Errorf("You must provide the id, username or email of a user to delete")
|
return errors.New("You must provide the id, username or email of a user to delete")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
@ -42,7 +43,7 @@ var microcmdUserGenerateAccessToken = &cli.Command{
|
|||||||
|
|
||||||
func runGenerateAccessToken(c *cli.Context) error {
|
func runGenerateAccessToken(c *cli.Context) error {
|
||||||
if !c.IsSet("username") {
|
if !c.IsSet("username") {
|
||||||
return fmt.Errorf("You must provide a username to generate a token for")
|
return errors.New("You must provide a username to generate a token for")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
@ -68,7 +69,7 @@ func runGenerateAccessToken(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if exist {
|
if exist {
|
||||||
return fmt.Errorf("access token name has been used already")
|
return errors.New("access token name has been used already")
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure the scopes are valid
|
// make sure the scopes are valid
|
||||||
|
62
cmd/dump.go
62
cmd/dump.go
@ -87,6 +87,10 @@ var CmdDump = &cli.Command{
|
|||||||
Name: "skip-index",
|
Name: "skip-index",
|
||||||
Usage: "Skip bleve index data",
|
Usage: "Skip bleve index data",
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "skip-db",
|
||||||
|
Usage: "Skip database",
|
||||||
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "type",
|
Name: "type",
|
||||||
Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
|
Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
|
||||||
@ -185,35 +189,41 @@ func runDump(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir := ctx.String("tempdir")
|
if ctx.Bool("skip-db") {
|
||||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
|
||||||
fatal("Path does not exist: %s", tmpDir)
|
dumper.GlobalExcludeAbsPath(setting.Database.Path)
|
||||||
}
|
log.Info("Skipping database")
|
||||||
|
|
||||||
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
|
|
||||||
if err != nil {
|
|
||||||
fatal("Failed to create tmp file: %v", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = dbDump.Close()
|
|
||||||
if err := util.Remove(dbDump.Name()); err != nil {
|
|
||||||
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
targetDBType := ctx.String("database")
|
|
||||||
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
|
|
||||||
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
|
|
||||||
} else {
|
} else {
|
||||||
log.Info("Dumping database...")
|
tmpDir := ctx.String("tempdir")
|
||||||
}
|
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||||
|
fatal("Path does not exist: %s", tmpDir)
|
||||||
|
}
|
||||||
|
|
||||||
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
|
||||||
fatal("Failed to dump database: %v", err)
|
if err != nil {
|
||||||
}
|
fatal("Failed to create tmp file: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = dbDump.Close()
|
||||||
|
if err := util.Remove(dbDump.Name()); err != nil {
|
||||||
|
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
|
targetDBType := ctx.String("database")
|
||||||
fatal("Failed to include gitea-db.sql: %v", err)
|
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
|
||||||
|
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
|
||||||
|
} else {
|
||||||
|
log.Info("Dumping database...")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
||||||
|
fatal("Failed to dump database: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
|
||||||
|
fatal("Failed to include gitea-db.sql: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
||||||
|
@ -157,9 +157,9 @@ func runViewDo(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(matchedAssetFiles) == 0 {
|
if len(matchedAssetFiles) == 0 {
|
||||||
return fmt.Errorf("no files matched the given pattern")
|
return errors.New("no files matched the given pattern")
|
||||||
} else if len(matchedAssetFiles) > 1 {
|
} else if len(matchedAssetFiles) > 1 {
|
||||||
return fmt.Errorf("too many files matched the given pattern, try to be more specific")
|
return errors.New("too many files matched the given pattern, try to be more specific")
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := matchedAssetFiles[0].fs.ReadFile(matchedAssetFiles[0].name)
|
data, err := matchedAssetFiles[0].fs.ReadFile(matchedAssetFiles[0].name)
|
||||||
@ -180,7 +180,7 @@ func runExtractDo(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.NArg() == 0 {
|
if c.NArg() == 0 {
|
||||||
return fmt.Errorf("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
|
return errors.New("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
|
||||||
}
|
}
|
||||||
|
|
||||||
destdir := "."
|
destdir := "."
|
||||||
|
@ -465,7 +465,7 @@ func hookPrintResult(output, isCreate bool, branch, url string) {
|
|||||||
fmt.Fprintf(os.Stderr, " %s\n", url)
|
fmt.Fprintf(os.Stderr, " %s\n", url)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, "")
|
fmt.Fprintln(os.Stderr, "")
|
||||||
os.Stderr.Sync()
|
_ = os.Stderr.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
func pushOptions() map[string]string {
|
func pushOptions() map[string]string {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@ -249,7 +250,7 @@ func runAddFileLogger(c *cli.Context) error {
|
|||||||
if c.IsSet("filename") {
|
if c.IsSet("filename") {
|
||||||
vals["filename"] = c.String("filename")
|
vals["filename"] = c.String("filename")
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("filename must be set when creating a file logger")
|
return errors.New("filename must be set when creating a file logger")
|
||||||
}
|
}
|
||||||
if c.IsSet("rotate") {
|
if c.IsSet("rotate") {
|
||||||
vals["rotate"] = c.Bool("rotate")
|
vals["rotate"] = c.Bool("rotate")
|
||||||
|
@ -1231,7 +1231,8 @@ LEVEL = Info
|
|||||||
;DEFAULT_THEME = gitea-auto
|
;DEFAULT_THEME = gitea-auto
|
||||||
;;
|
;;
|
||||||
;; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`.
|
;; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`.
|
||||||
;THEMES = gitea-auto,gitea-light,gitea-dark
|
;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
|
||||||
|
;THEMES =
|
||||||
;;
|
;;
|
||||||
;; All available reactions users can choose on issues/prs and comments.
|
;; All available reactions users can choose on issues/prs and comments.
|
||||||
;; Values can be emoji alias (:smile:) or a unicode emoji.
|
;; Values can be emoji alias (:smile:) or a unicode emoji.
|
||||||
@ -2377,22 +2378,6 @@ LEVEL = Info
|
|||||||
;; Enable issue by repository metrics; default is false
|
;; Enable issue by repository metrics; default is false
|
||||||
;ENABLED_ISSUE_BY_REPOSITORY = false
|
;ENABLED_ISSUE_BY_REPOSITORY = false
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;[task]
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;
|
|
||||||
;; Task queue type, could be `channel` or `redis`.
|
|
||||||
;QUEUE_TYPE = channel
|
|
||||||
;;
|
|
||||||
;; Task queue length, available only when `QUEUE_TYPE` is `channel`.
|
|
||||||
;QUEUE_LENGTH = 1000
|
|
||||||
;;
|
|
||||||
;; Task queue connection string, available only when `QUEUE_TYPE` is `redis`.
|
|
||||||
;; If there is a password of redis, use `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for `redis-clsuter`.
|
|
||||||
;QUEUE_CONN_STR = "redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s"
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;[migrations]
|
;[migrations]
|
||||||
|
@ -214,10 +214,9 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||||||
- `SITEMAP_PAGING_NUM`: **20**: Number of items that are displayed in a single subsitemap.
|
- `SITEMAP_PAGING_NUM`: **20**: Number of items that are displayed in a single subsitemap.
|
||||||
- `GRAPH_MAX_COMMIT_NUM`: **100**: Number of maximum commits shown in the commit graph.
|
- `GRAPH_MAX_COMMIT_NUM`: **100**: Number of maximum commits shown in the commit graph.
|
||||||
- `CODE_COMMENT_LINES`: **4**: Number of line of codes shown for a code comment.
|
- `CODE_COMMENT_LINES`: **4**: Number of line of codes shown for a code comment.
|
||||||
- `DEFAULT_THEME`: **gitea-auto**: \[gitea-auto, gitea-light, gitea-dark\]: Set the default theme for the Gitea installation.
|
- `DEFAULT_THEME`: **gitea-auto**: Set the default theme for the Gitea installation, custom themes could be provided by "{CustomPath}/public/assets/css/theme-*.css".
|
||||||
- `SHOW_USER_EMAIL`: **true**: Whether the email of the user should be shown in the Explore Users page.
|
- `SHOW_USER_EMAIL`: **true**: Whether the email of the user should be shown in the Explore Users page.
|
||||||
- `THEMES`: **gitea-auto,gitea-light,gitea-dark**: All available themes. Allow users select personalized themes.
|
- `THEMES`: **_empty_**: All available themes by "{CustomPath}/public/assets/css/theme-*.css". Allow users select personalized themes.
|
||||||
regardless of the value of `DEFAULT_THEME`.
|
|
||||||
- `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB)
|
- `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB)
|
||||||
- `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI
|
- `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI
|
||||||
- `REACTIONS`: All available reactions users can choose on issues/prs and comments
|
- `REACTIONS`: All available reactions users can choose on issues/prs and comments
|
||||||
@ -1198,14 +1197,6 @@ in this mapping or the filetype using heuristics.
|
|||||||
|
|
||||||
- `DEFAULT_UI_LOCATION`: Default location of time on the UI, so that we can display correct user's time on UI. i.e. Asia/Shanghai
|
- `DEFAULT_UI_LOCATION`: Default location of time on the UI, so that we can display correct user's time on UI. i.e. Asia/Shanghai
|
||||||
|
|
||||||
## Task (`task`)
|
|
||||||
|
|
||||||
Task queue configuration has been moved to `queue.task`. However, the below configuration values are kept for backwards compatibility:
|
|
||||||
|
|
||||||
- `QUEUE_TYPE`: **channel**: Task queue type, could be `channel` or `redis`.
|
|
||||||
- `QUEUE_LENGTH`: **1000**: Task queue length, available only when `QUEUE_TYPE` is `channel`.
|
|
||||||
- `QUEUE_CONN_STR`: **redis://127.0.0.1:6379/0**: Task queue connection string, available only when `QUEUE_TYPE` is `redis`. If redis needs a password, use `redis://123@127.0.0.1:6379/0` or `redis+cluster://123@127.0.0.1:6379/0`.
|
|
||||||
|
|
||||||
## Migrations (`migrations`)
|
## Migrations (`migrations`)
|
||||||
|
|
||||||
- `MAX_ATTEMPTS`: **3**: Max attempts per http/https request on migrations.
|
- `MAX_ATTEMPTS`: **3**: Max attempts per http/https request on migrations.
|
||||||
|
@ -212,10 +212,9 @@ menu:
|
|||||||
- `SITEMAP_PAGING_NUM`: **20**: 在单个子SiteMap中显示的项数。
|
- `SITEMAP_PAGING_NUM`: **20**: 在单个子SiteMap中显示的项数。
|
||||||
- `GRAPH_MAX_COMMIT_NUM`: **100**: 提交图中显示的最大commit数量。
|
- `GRAPH_MAX_COMMIT_NUM`: **100**: 提交图中显示的最大commit数量。
|
||||||
- `CODE_COMMENT_LINES`: **4**: 在代码评论中能够显示的最大代码行数。
|
- `CODE_COMMENT_LINES`: **4**: 在代码评论中能够显示的最大代码行数。
|
||||||
- `DEFAULT_THEME`: **gitea-auto**: \[gitea-auto, gitea-light, gitea-dark\]: 在Gitea安装时候设置的默认主题。
|
- `DEFAULT_THEME`: **gitea-auto**: 在Gitea安装时候设置的默认主题,自定义的主题可以通过 "{CustomPath}/public/assets/css/theme-*.css" 提供。
|
||||||
- `SHOW_USER_EMAIL`: **true**: 用户的电子邮件是否应该显示在`Explore Users`页面中。
|
- `SHOW_USER_EMAIL`: **true**: 用户的电子邮件是否应该显示在`Explore Users`页面中。
|
||||||
- `THEMES`: **gitea-auto,gitea-light,gitea-dark**: 所有可用的主题。允许用户选择个性化的主题,
|
- `THEMES`: **_empty_**: 所有可用的主题(由 "{CustomPath}/public/assets/css/theme-*.css" 提供)。允许用户选择个性化的主题,
|
||||||
而不受DEFAULT_THEME 值的影响。
|
|
||||||
- `MAX_DISPLAY_FILE_SIZE`: **8388608**: 能够显示文件的最大大小(默认为8MiB)。
|
- `MAX_DISPLAY_FILE_SIZE`: **8388608**: 能够显示文件的最大大小(默认为8MiB)。
|
||||||
- `REACTIONS`: 用户可以在问题(Issue)、Pull Request(PR)以及评论中选择的所有可选的反应。
|
- `REACTIONS`: 用户可以在问题(Issue)、Pull Request(PR)以及评论中选择的所有可选的反应。
|
||||||
这些值可以是表情符号别名(例如::smile:)或Unicode表情符号。
|
这些值可以是表情符号别名(例如::smile:)或Unicode表情符号。
|
||||||
@ -1128,15 +1127,6 @@ ALLOW_DATA_URI_IMAGES = true
|
|||||||
|
|
||||||
- `DEFAULT_UI_LOCATION`:在 UI 上的默认时间位置,以便我们可以在 UI 上显示正确的用户时间。例如:Asia/Shanghai
|
- `DEFAULT_UI_LOCATION`:在 UI 上的默认时间位置,以便我们可以在 UI 上显示正确的用户时间。例如:Asia/Shanghai
|
||||||
|
|
||||||
## 任务 (`task`)
|
|
||||||
|
|
||||||
任务队列配置已移动到 `queue.task`。然而,以下配置值仍保留以确保向后兼容:
|
|
||||||
|
|
||||||
- `QUEUE_TYPE`:**channel**:任务队列类型,可以是 `channel` 或 `redis`。
|
|
||||||
- `QUEUE_LENGTH`:**1000**:任务队列长度,仅在 `QUEUE_TYPE` 为 `channel` 时可用。
|
|
||||||
- `QUEUE_CONN_STR`:**redis://127.0.0.1:6379/0**:任务队列连接字符串,仅在 `QUEUE_TYPE` 为 `redis` 时可用。
|
|
||||||
如果 redis 需要密码,使用 `redis://123@127.0.0.1:6379/0` 或 `redis+cluster://123@127.0.0.1:6379/0`。
|
|
||||||
|
|
||||||
## 迁移 (`migrations`)
|
## 迁移 (`migrations`)
|
||||||
|
|
||||||
- `MAX_ATTEMPTS`:**3**:每次 http/https 请求的最大尝试次数(用于迁移)。
|
- `MAX_ATTEMPTS`:**3**:每次 http/https 请求的最大尝试次数(用于迁移)。
|
||||||
|
@ -381,7 +381,7 @@ To make a custom theme available to all users:
|
|||||||
|
|
||||||
1. Add a CSS file to `$GITEA_CUSTOM/public/assets/css/theme-<theme-name>.css`.
|
1. Add a CSS file to `$GITEA_CUSTOM/public/assets/css/theme-<theme-name>.css`.
|
||||||
The value of `$GITEA_CUSTOM` of your instance can be queried by calling `gitea help` and looking up the value of "CustomPath".
|
The value of `$GITEA_CUSTOM` of your instance can be queried by calling `gitea help` and looking up the value of "CustomPath".
|
||||||
2. Add `<theme-name>` to the comma-separated list of setting `THEMES` in `app.ini`
|
2. Add `<theme-name>` to the comma-separated list of setting `THEMES` in `app.ini`, or leave `THEMES` empty to allow all themes.
|
||||||
|
|
||||||
Community themes are listed in [gitea/awesome-gitea#themes](https://gitea.com/gitea/awesome-gitea#themes).
|
Community themes are listed in [gitea/awesome-gitea#themes](https://gitea.com/gitea/awesome-gitea#themes).
|
||||||
|
|
||||||
|
@ -178,17 +178,6 @@ At some point, a customer or third party needs access to a specific repo and onl
|
|||||||
|
|
||||||
Use [Fail2Ban](administration/fail2ban-setup.md) to monitor and stop automated login attempts or other malicious behavior based on log patterns
|
Use [Fail2Ban](administration/fail2ban-setup.md) to monitor and stop automated login attempts or other malicious behavior based on log patterns
|
||||||
|
|
||||||
## How to add/use custom themes
|
|
||||||
|
|
||||||
Gitea supports three official themes right now, `gitea-light`, `gitea-dark`, and `gitea-auto` (automatically switches between the previous two depending on operating system settings).
|
|
||||||
To add your own theme, currently the only way is to provide a complete theme (not just color overrides)
|
|
||||||
|
|
||||||
As an example, let's say our theme is `arc-blue` (this is a real theme, and can be found [in this issue](https://github.com/go-gitea/gitea/issues/6011))
|
|
||||||
|
|
||||||
Name the `.css` file `theme-arc-blue.css` and add it to your custom folder in `custom/public/assets/css`
|
|
||||||
|
|
||||||
Allow users to use it by adding `arc-blue` to the list of `THEMES` in your `app.ini`
|
|
||||||
|
|
||||||
## SSHD vs built-in SSH
|
## SSHD vs built-in SSH
|
||||||
|
|
||||||
SSHD is the built-in SSH server on most Unix systems.
|
SSHD is the built-in SSH server on most Unix systems.
|
||||||
|
@ -182,17 +182,6 @@ Gitea不提供内置的Pages服务器。您需要一个专用的域名来提供
|
|||||||
|
|
||||||
使用 [Fail2Ban](administration/fail2ban-setup.md) 监视并阻止基于日志模式的自动登录尝试或其他恶意行为。
|
使用 [Fail2Ban](administration/fail2ban-setup.md) 监视并阻止基于日志模式的自动登录尝试或其他恶意行为。
|
||||||
|
|
||||||
## 如何添加/使用自定义主题
|
|
||||||
|
|
||||||
Gitea 目前支持三个官方主题,分别是 `gitea-light`、`gitea-dark` 和 `gitea-auto`(根据操作系统设置自动切换前两个主题)。
|
|
||||||
要添加自己的主题,目前唯一的方法是提供一个完整的主题(不仅仅是颜色覆盖)。
|
|
||||||
|
|
||||||
假设我们的主题是 `arc-blue`(这是一个真实的主题,可以在[此问题](https://github.com/go-gitea/gitea/issues/6011)中找到)
|
|
||||||
|
|
||||||
将`.css`文件命名为`theme-arc-blue.css`并将其添加到`custom/public/assets/css`文件夹中
|
|
||||||
|
|
||||||
通过将`arc-blue`添加到`app.ini`中的`THEMES`列表中,允许用户使用该主题
|
|
||||||
|
|
||||||
## SSHD vs 内建SSH
|
## SSHD vs 内建SSH
|
||||||
|
|
||||||
SSHD是大多数Unix系统上内建的SSH服务器。
|
SSHD是大多数Unix系统上内建的SSH服务器。
|
||||||
|
3
go.mod
3
go.mod
@ -16,7 +16,6 @@ require (
|
|||||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
||||||
github.com/NYTimes/gziphandler v1.1.1
|
|
||||||
github.com/PuerkitoBio/goquery v1.9.1
|
github.com/PuerkitoBio/goquery v1.9.1
|
||||||
github.com/alecthomas/chroma/v2 v2.13.0
|
github.com/alecthomas/chroma/v2 v2.13.0
|
||||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||||
@ -67,7 +66,7 @@ require (
|
|||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
|
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
|
||||||
github.com/klauspost/compress v1.17.7
|
github.com/klauspost/compress v1.17.8
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7
|
github.com/klauspost/cpuid/v2 v2.2.7
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
github.com/markbates/goth v1.79.0
|
github.com/markbates/goth v1.79.0
|
||||||
|
6
go.sum
6
go.sum
@ -70,8 +70,6 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
|
|||||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
|
||||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
|
||||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||||
github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
|
github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
|
||||||
@ -500,8 +498,8 @@ github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
|
|||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
|
@ -98,13 +98,10 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if run.Repo == nil {
|
if err := run.LoadRepo(ctx); err != nil {
|
||||||
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
|
return err
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
run.Repo = repo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := run.Repo.LoadAttributes(ctx); err != nil {
|
if err := run.Repo.LoadAttributes(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -120,6 +117,19 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (run *ActionRun) LoadRepo(ctx context.Context) error {
|
||||||
|
if run == nil || run.Repo != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
run.Repo = repo
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (run *ActionRun) Duration() time.Duration {
|
func (run *ActionRun) Duration() time.Duration {
|
||||||
return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration
|
return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration
|
||||||
}
|
}
|
||||||
|
@ -270,7 +270,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
|
|||||||
// Only affect action runners were a owner ID is set, as actions runners
|
// Only affect action runners were a owner ID is set, as actions runners
|
||||||
// could also be created on a repository.
|
// could also be created on a repository.
|
||||||
return db.GetEngine(ctx).Table("action_runner").
|
return db.GetEngine(ctx).Table("action_runner").
|
||||||
Join("LEFT", "user", "`action_runner`.owner_id = `user`.id").
|
Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id").
|
||||||
Where("`action_runner`.owner_id != ?", 0).
|
Where("`action_runner`.owner_id != ?", 0).
|
||||||
And(builder.IsNull{"`user`.id"}).
|
And(builder.IsNull{"`user`.id"}).
|
||||||
Count(new(ActionRunner))
|
Count(new(ActionRunner))
|
||||||
@ -279,7 +279,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
|
|||||||
func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
|
func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
|
||||||
subQuery := builder.Select("`action_runner`.id").
|
subQuery := builder.Select("`action_runner`.id").
|
||||||
From("`action_runner`").
|
From("`action_runner`").
|
||||||
Join("LEFT", "user", "`action_runner`.owner_id = `user`.id").
|
Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id").
|
||||||
Where(builder.Neq{"`action_runner`.owner_id": 0}).
|
Where(builder.Neq{"`action_runner`.owner_id": 0}).
|
||||||
And(builder.IsNull{"`user`.id"})
|
And(builder.IsNull{"`user`.id"})
|
||||||
b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
|
b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
|
||||||
@ -289,3 +289,25 @@ func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
|
|||||||
}
|
}
|
||||||
return res.RowsAffected()
|
return res.RowsAffected()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CountRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
|
||||||
|
return db.GetEngine(ctx).Table("action_runner").
|
||||||
|
Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id").
|
||||||
|
Where("`action_runner`.repo_id != ?", 0).
|
||||||
|
And(builder.IsNull{"`repository`.id"}).
|
||||||
|
Count(new(ActionRunner))
|
||||||
|
}
|
||||||
|
|
||||||
|
func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
|
||||||
|
subQuery := builder.Select("`action_runner`.id").
|
||||||
|
From("`action_runner`").
|
||||||
|
Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id").
|
||||||
|
Where(builder.Neq{"`action_runner`.repo_id": 0}).
|
||||||
|
And(builder.IsNull{"`repository`.id"})
|
||||||
|
b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
|
||||||
|
res, err := db.GetEngine(ctx).Exec(b)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return res.RowsAffected()
|
||||||
|
}
|
||||||
|
@ -92,6 +92,11 @@ func DeleteVariable(ctx context.Context, id int64) error {
|
|||||||
func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
|
func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
|
||||||
variables := map[string]string{}
|
variables := map[string]string{}
|
||||||
|
|
||||||
|
if err := run.LoadRepo(ctx); err != nil {
|
||||||
|
log.Error("LoadRepo: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Global
|
// Global
|
||||||
globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{})
|
globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -110,7 +110,6 @@ func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerific
|
|||||||
Reason: "gpg.error.no_committer_account",
|
Reason: "gpg.error.no_committer_account",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -294,7 +295,7 @@ func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOp
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if app.UID != opts.UserID {
|
if app.UID != opts.UserID {
|
||||||
return nil, fmt.Errorf("UID mismatch")
|
return nil, errors.New("UID mismatch")
|
||||||
}
|
}
|
||||||
builtinApps := BuiltinApplications()
|
builtinApps := BuiltinApplications()
|
||||||
if _, builtin := builtinApps[app.ClientID]; builtin {
|
if _, builtin := builtinApps[app.ClientID]; builtin {
|
||||||
|
@ -13,8 +13,6 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
//////////////////// Application
|
|
||||||
|
|
||||||
func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
|
func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||||
|
@ -228,7 +228,6 @@ func NamesToBean(names ...string) ([]any, error) {
|
|||||||
// Need to map provided names to beans...
|
// Need to map provided names to beans...
|
||||||
beanMap := make(map[string]any)
|
beanMap := make(map[string]any)
|
||||||
for _, bean := range tables {
|
for _, bean := range tables {
|
||||||
|
|
||||||
beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
|
beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
|
||||||
beanMap[strings.ToLower(x.TableName(bean))] = bean
|
beanMap[strings.ToLower(x.TableName(bean))] = bean
|
||||||
beanMap[strings.ToLower(x.TableName(bean, true))] = bean
|
beanMap[strings.ToLower(x.TableName(bean, true))] = bean
|
||||||
|
@ -5,7 +5,7 @@ package git
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repositor
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !force && u.ID != lock.OwnerID {
|
if !force && u.ID != lock.OwnerID {
|
||||||
return nil, fmt.Errorf("user doesn't own lock and force flag is not set")
|
return nil, errors.New("user doesn't own lock and force flag is not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil {
|
if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil {
|
||||||
|
@ -345,11 +345,9 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if opts.ReviewerTeam != nil {
|
} else if opts.ReviewerTeam != nil {
|
||||||
review.Type = ReviewTypeRequest
|
review.Type = ReviewTypeRequest
|
||||||
review.ReviewerTeamID = opts.ReviewerTeam.ID
|
review.ReviewerTeamID = opts.ReviewerTeam.ID
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("provide either reviewer or reviewer team")
|
return nil, fmt.Errorf("provide either reviewer or reviewer team")
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,6 @@ func RecreateTable(sess *xorm.Session, bean any) error {
|
|||||||
log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err)
|
log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case setting.Database.Type.IsMySQL():
|
case setting.Database.Type.IsMySQL():
|
||||||
// MySQL will drop all the constraints on the old table
|
// MySQL will drop all the constraints on the old table
|
||||||
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
|
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
|
||||||
@ -228,7 +227,6 @@ func RecreateTable(sess *xorm.Session, bean any) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sequenceMap[sequence] = sequenceData
|
sequenceMap[sequence] = sequenceData
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CASCADE causes postgres to drop all the constraints on the old table
|
// CASCADE causes postgres to drop all the constraints on the old table
|
||||||
@ -293,9 +291,7 @@ func RecreateTable(sess *xorm.Session, bean any) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case setting.Database.Type.IsMSSQL():
|
case setting.Database.Type.IsMSSQL():
|
||||||
// MSSQL will drop all the constraints on the old table
|
// MSSQL will drop all the constraints on the old table
|
||||||
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
|
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
|
||||||
@ -308,7 +304,6 @@ func RecreateTable(sess *xorm.Session, bean any) error {
|
|||||||
log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
|
log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Fatal("Unrecognized DB")
|
log.Fatal("Unrecognized DB")
|
||||||
}
|
}
|
||||||
|
@ -584,6 +584,8 @@ var migrations = []Migration{
|
|||||||
NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2),
|
NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2),
|
||||||
// v297 -> v298
|
// v297 -> v298
|
||||||
NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode),
|
NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode),
|
||||||
|
// v298 -> v299
|
||||||
|
NewMigration("Drop wrongly created table o_auth2_application", v1_23.DropWronglyCreatedTable),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
@ -262,7 +262,6 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
|
|||||||
for _, u := range units {
|
for _, u := range units {
|
||||||
var found bool
|
var found bool
|
||||||
for _, team := range teams {
|
for _, team := range teams {
|
||||||
|
|
||||||
var teamU []*TeamUnit
|
var teamU []*TeamUnit
|
||||||
var unitEnabled bool
|
var unitEnabled bool
|
||||||
err = sess.Where("team_id = ?", team.ID).Find(&teamU)
|
err = sess.Where("team_id = ?", team.ID).Find(&teamU)
|
||||||
@ -331,7 +330,6 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !protectedBranch.EnableApprovalsWhitelist {
|
if !protectedBranch.EnableApprovalsWhitelist {
|
||||||
|
|
||||||
perm, err := getUserRepoPermission(sess, baseRepo, reviewer)
|
perm, err := getUserRepoPermission(sess, baseRepo, reviewer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -9,9 +9,9 @@ import (
|
|||||||
|
|
||||||
// AddConfidentialColumnToOAuth2ApplicationTable: add ConfidentialClient column, setting existing rows to true
|
// AddConfidentialColumnToOAuth2ApplicationTable: add ConfidentialClient column, setting existing rows to true
|
||||||
func AddConfidentialClientColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
|
func AddConfidentialClientColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
|
||||||
type OAuth2Application struct {
|
type oauth2Application struct {
|
||||||
|
ID int64
|
||||||
ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
|
ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
|
||||||
}
|
}
|
||||||
|
return x.Sync(new(oauth2Application))
|
||||||
return x.Sync(new(OAuth2Application))
|
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,12 @@ import (
|
|||||||
|
|
||||||
func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
|
func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
|
||||||
// premigration
|
// premigration
|
||||||
type OAuth2Application struct {
|
type oauth2Application struct {
|
||||||
ID int64
|
ID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare and load the testing database
|
// Prepare and load the testing database
|
||||||
x, deferable := base.PrepareTestEnv(t, 0, new(OAuth2Application))
|
x, deferable := base.PrepareTestEnv(t, 0, new(oauth2Application))
|
||||||
defer deferable()
|
defer deferable()
|
||||||
if x == nil || t.Failed() {
|
if x == nil || t.Failed() {
|
||||||
return
|
return
|
||||||
@ -36,7 +36,7 @@ func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
got := []ExpectedOAuth2Application{}
|
got := []ExpectedOAuth2Application{}
|
||||||
if err := x.Table("o_auth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
|
if err := x.Table("oauth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ func ChangeContainerMetadataMultiArch(x *xorm.Engine) error {
|
|||||||
|
|
||||||
// Convert to new metadata format
|
// Convert to new metadata format
|
||||||
|
|
||||||
new := &MetadataNew{
|
newMetadata := &MetadataNew{
|
||||||
Type: old.Type,
|
Type: old.Type,
|
||||||
IsTagged: old.IsTagged,
|
IsTagged: old.IsTagged,
|
||||||
Platform: old.Platform,
|
Platform: old.Platform,
|
||||||
@ -119,7 +119,7 @@ func ChangeContainerMetadataMultiArch(x *xorm.Engine) error {
|
|||||||
Manifests: manifests,
|
Manifests: manifests,
|
||||||
}
|
}
|
||||||
|
|
||||||
metadataJSON, err := json.Marshal(new)
|
metadataJSON, err := json.Marshal(newMetadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
10
models/migrations/v1_23/v298.go
Normal file
10
models/migrations/v1_23/v298.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_23 //nolint
|
||||||
|
|
||||||
|
import "xorm.io/xorm"
|
||||||
|
|
||||||
|
func DropWronglyCreatedTable(x *xorm.Engine) error {
|
||||||
|
return x.DropTables("o_auth2_application")
|
||||||
|
}
|
@ -61,7 +61,6 @@ func AddScratchHash(x *xorm.Engine) error {
|
|||||||
if _, err := sess.ID(tfa.ID).Cols("scratch_salt, scratch_hash").Update(tfa); err != nil {
|
if _, err := sess.ID(tfa.ID).Cols("scratch_salt, scratch_hash").Update(tfa); err != nil {
|
||||||
return fmt.Errorf("couldn't add in scratch_hash and scratch_salt: %w", err)
|
return fmt.Errorf("couldn't add in scratch_hash and scratch_salt: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,6 @@ func HashAppToken(x *xorm.Engine) error {
|
|||||||
if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil {
|
if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil {
|
||||||
return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %w", err)
|
return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ func removeAllRepositories(ctx context.Context, t *organization.Team) (err error
|
|||||||
|
|
||||||
// Remove watches from all users and now unaccessible repos
|
// Remove watches from all users and now unaccessible repos
|
||||||
for _, user := range t.Members {
|
for _, user := range t.Members {
|
||||||
has, err := access_model.HasAccess(ctx, user.ID, repo)
|
has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has {
|
} else if has {
|
||||||
@ -544,7 +544,7 @@ func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Reposito
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ReconsiderWatches(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
func ReconsiderWatches(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
||||||
if has, err := access_model.HasAccess(ctx, user.ID, repo); err != nil || has {
|
if has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo); err != nil || has {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := repo_model.WatchRepo(ctx, user, repo, false); err != nil {
|
if err := repo_model.WatchRepo(ctx, user, repo, false); err != nil {
|
||||||
|
@ -226,9 +226,8 @@ func GetTeamIDsByNames(ctx context.Context, orgID int64, names []string, ignoreN
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if ignoreNonExistent {
|
if ignoreNonExistent {
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
ids = append(ids, u.ID)
|
ids = append(ids, u.ID)
|
||||||
}
|
}
|
||||||
|
@ -287,9 +287,10 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
|
|||||||
// SearchVersions gets all versions of packages matching the search options
|
// SearchVersions gets all versions of packages matching the search options
|
||||||
func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
|
func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
|
||||||
sess := db.GetEngine(ctx).
|
sess := db.GetEngine(ctx).
|
||||||
Where(opts.ToConds()).
|
Select("package_version.*").
|
||||||
Table("package_version").
|
Table("package_version").
|
||||||
Join("INNER", "package", "package.id = package_version.package_id")
|
Join("INNER", "package", "package.id = package_version.package_id").
|
||||||
|
Where(opts.ToConds())
|
||||||
|
|
||||||
opts.configureOrderBy(sess)
|
opts.configureOrderBy(sess)
|
||||||
|
|
||||||
@ -304,19 +305,18 @@ func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*Package
|
|||||||
|
|
||||||
// SearchLatestVersions gets the latest version of every package matching the search options
|
// SearchLatestVersions gets the latest version of every package matching the search options
|
||||||
func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
|
func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
|
||||||
cond := opts.ToConds().
|
in := builder.
|
||||||
And(builder.Expr("pv2.id IS NULL"))
|
Select("MAX(package_version.id)").
|
||||||
|
From("package_version").
|
||||||
joinCond := builder.Expr("package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))")
|
InnerJoin("package", "package.id = package_version.package_id").
|
||||||
if opts.IsInternal.Has() {
|
Where(opts.ToConds()).
|
||||||
joinCond = joinCond.And(builder.Eq{"pv2.is_internal": opts.IsInternal.Value()})
|
GroupBy("package_version.package_id")
|
||||||
}
|
|
||||||
|
|
||||||
sess := db.GetEngine(ctx).
|
sess := db.GetEngine(ctx).
|
||||||
|
Select("package_version.*").
|
||||||
Table("package_version").
|
Table("package_version").
|
||||||
Join("LEFT", "package_version pv2", joinCond).
|
|
||||||
Join("INNER", "package", "package.id = package_version.package_id").
|
Join("INNER", "package", "package.id = package_version.package_id").
|
||||||
Where(cond)
|
Where(builder.In("package_version.id", in))
|
||||||
|
|
||||||
opts.configureOrderBy(sess)
|
opts.configureOrderBy(sess)
|
||||||
|
|
||||||
|
@ -79,17 +79,17 @@ func TestHasAccess(t *testing.T) {
|
|||||||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
||||||
assert.True(t, repo2.IsPrivate)
|
assert.True(t, repo2.IsPrivate)
|
||||||
|
|
||||||
has, err := access_model.HasAccess(db.DefaultContext, user1.ID, repo1)
|
has, err := access_model.HasAnyUnitAccess(db.DefaultContext, user1.ID, repo1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.True(t, has)
|
assert.True(t, has)
|
||||||
|
|
||||||
_, err = access_model.HasAccess(db.DefaultContext, user1.ID, repo2)
|
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user1.ID, repo2)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
_, err = access_model.HasAccess(db.DefaultContext, user2.ID, repo1)
|
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user2.ID, repo1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
_, err = access_model.HasAccess(db.DefaultContext, user2.ID, repo2)
|
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user2.ID, repo2)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ type Permission struct {
|
|||||||
|
|
||||||
units []*repo_model.RepoUnit
|
units []*repo_model.RepoUnit
|
||||||
unitsMode map[unit.Type]perm_model.AccessMode
|
unitsMode map[unit.Type]perm_model.AccessMode
|
||||||
|
|
||||||
|
everyoneAccessMode map[unit.Type]perm_model.AccessMode
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsOwner returns true if current user is the owner of repository.
|
// IsOwner returns true if current user is the owner of repository.
|
||||||
@ -36,9 +38,24 @@ func (p *Permission) IsAdmin() bool {
|
|||||||
return p.AccessMode >= perm_model.AccessModeAdmin
|
return p.AccessMode >= perm_model.AccessModeAdmin
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAccess returns true if the current user might have at least read access to any unit of this repository
|
// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
|
||||||
func (p *Permission) HasAccess() bool {
|
// It doesn't count the "everyone access mode".
|
||||||
return len(p.unitsMode) > 0 || p.AccessMode >= perm_model.AccessModeRead
|
func (p *Permission) HasAnyUnitAccess() bool {
|
||||||
|
for _, v := range p.unitsMode {
|
||||||
|
if v >= perm_model.AccessModeRead {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.AccessMode >= perm_model.AccessModeRead
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
|
||||||
|
for _, v := range p.everyoneAccessMode {
|
||||||
|
if v >= perm_model.AccessModeRead {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.HasAnyUnitAccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasUnits returns true if the permission contains attached units
|
// HasUnits returns true if the permission contains attached units
|
||||||
@ -56,16 +73,16 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UnitAccessMode returns current user access mode to the specify unit of the repository
|
// UnitAccessMode returns current user access mode to the specify unit of the repository
|
||||||
|
// It also considers "everyone access mode"
|
||||||
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
|
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
|
||||||
if p.unitsMode != nil {
|
// if the units map contains the access mode, use it, but admin/owner mode could override it
|
||||||
// if the units map contains the access mode, use it, but admin/owner mode could override it
|
if m, ok := p.unitsMode[unitType]; ok {
|
||||||
if m, ok := p.unitsMode[unitType]; ok {
|
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
|
||||||
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// if the units map does not contain the access mode, return the default access mode if the unit exists
|
// if the units map does not contain the access mode, return the default access mode if the unit exists
|
||||||
|
unitDefaultAccessMode := max(p.AccessMode, p.everyoneAccessMode[unitType])
|
||||||
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
|
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
|
||||||
return util.Iif(hasUnit, p.AccessMode, perm_model.AccessModeNone)
|
return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Permission) SetUnitsWithDefaultAccessMode(units []*repo_model.RepoUnit, mode perm_model.AccessMode) {
|
func (p *Permission) SetUnitsWithDefaultAccessMode(units []*repo_model.RepoUnit, mode perm_model.AccessMode) {
|
||||||
@ -159,14 +176,15 @@ func (p *Permission) LogString() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
|
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
|
||||||
if user != nil && user.ID > 0 {
|
if user == nil || user.ID <= 0 {
|
||||||
for _, u := range perm.units {
|
return
|
||||||
if perm.unitsMode == nil {
|
}
|
||||||
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
|
for _, u := range perm.units {
|
||||||
}
|
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
|
||||||
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.unitsMode[u.Type] {
|
if perm.everyoneAccessMode == nil {
|
||||||
perm.unitsMode[u.Type] = u.EveryoneAccessMode
|
perm.everyoneAccessMode = make(map[unit.Type]perm_model.AccessMode)
|
||||||
}
|
}
|
||||||
|
perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -373,8 +391,8 @@ func CanBeAssigned(ctx context.Context, user *user_model.User, repo *repo_model.
|
|||||||
perm.CanAccessAny(perm_model.AccessModeRead, unit.TypePullRequests), nil
|
perm.CanAccessAny(perm_model.AccessModeRead, unit.TypePullRequests), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAccess returns true if user has access to repo
|
// HasAnyUnitAccess see the comment of "perm.HasAnyUnitAccess"
|
||||||
func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
|
func HasAnyUnitAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
|
||||||
var user *user_model.User
|
var user *user_model.User
|
||||||
var err error
|
var err error
|
||||||
if userID > 0 {
|
if userID > 0 {
|
||||||
@ -387,7 +405,7 @@ func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return perm.HasAccess(), nil
|
return perm.HasAnyUnitAccess(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getUsersWithAccessMode returns users that have at least given access mode to the repository.
|
// getUsersWithAccessMode returns users that have at least given access mode to the repository.
|
||||||
|
@ -14,16 +14,54 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestHasAnyUnitAccess(t *testing.T) {
|
||||||
|
perm := Permission{}
|
||||||
|
assert.False(t, perm.HasAnyUnitAccess())
|
||||||
|
|
||||||
|
perm = Permission{
|
||||||
|
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
|
||||||
|
}
|
||||||
|
assert.False(t, perm.HasAnyUnitAccess())
|
||||||
|
assert.False(t, perm.HasAnyUnitAccessOrEveryoneAccess())
|
||||||
|
|
||||||
|
perm = Permission{
|
||||||
|
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
|
||||||
|
everyoneAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
|
||||||
|
}
|
||||||
|
assert.False(t, perm.HasAnyUnitAccess())
|
||||||
|
assert.True(t, perm.HasAnyUnitAccessOrEveryoneAccess())
|
||||||
|
|
||||||
|
perm = Permission{
|
||||||
|
AccessMode: perm_model.AccessModeRead,
|
||||||
|
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
|
||||||
|
}
|
||||||
|
assert.True(t, perm.HasAnyUnitAccess())
|
||||||
|
|
||||||
|
perm = Permission{
|
||||||
|
unitsMode: map[unit.Type]perm_model.AccessMode{unit.TypeWiki: perm_model.AccessModeRead},
|
||||||
|
}
|
||||||
|
assert.True(t, perm.HasAnyUnitAccess())
|
||||||
|
}
|
||||||
|
|
||||||
func TestApplyEveryoneRepoPermission(t *testing.T) {
|
func TestApplyEveryoneRepoPermission(t *testing.T) {
|
||||||
perm := Permission{
|
perm := Permission{
|
||||||
AccessMode: perm_model.AccessModeNone,
|
AccessMode: perm_model.AccessModeNone,
|
||||||
units: []*repo_model.RepoUnit{
|
units: []*repo_model.RepoUnit{
|
||||||
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeNone},
|
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
applyEveryoneRepoPermission(nil, &perm)
|
applyEveryoneRepoPermission(nil, &perm)
|
||||||
assert.False(t, perm.CanRead(unit.TypeWiki))
|
assert.False(t, perm.CanRead(unit.TypeWiki))
|
||||||
|
|
||||||
|
perm = Permission{
|
||||||
|
AccessMode: perm_model.AccessModeNone,
|
||||||
|
units: []*repo_model.RepoUnit{
|
||||||
|
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
applyEveryoneRepoPermission(&user_model.User{ID: 0}, &perm)
|
||||||
|
assert.False(t, perm.CanRead(unit.TypeWiki))
|
||||||
|
|
||||||
perm = Permission{
|
perm = Permission{
|
||||||
AccessMode: perm_model.AccessModeNone,
|
AccessMode: perm_model.AccessModeNone,
|
||||||
units: []*repo_model.RepoUnit{
|
units: []*repo_model.RepoUnit{
|
||||||
@ -40,8 +78,8 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
|
||||||
assert.True(t, perm.CanRead(unit.TypeWiki))
|
// it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units
|
||||||
assert.False(t, perm.CanWrite(unit.TypeWiki)) // because there is no unit mode, so the everyone-mode is used as the unit's access mode
|
assert.True(t, perm.CanWrite(unit.TypeWiki))
|
||||||
|
|
||||||
perm = Permission{
|
perm = Permission{
|
||||||
units: []*repo_model.RepoUnit{
|
units: []*repo_model.RepoUnit{
|
||||||
|
@ -110,13 +110,11 @@ func createBoardsForProjectsType(ctx context.Context, project *Project) error {
|
|||||||
var items []string
|
var items []string
|
||||||
|
|
||||||
switch project.BoardType {
|
switch project.BoardType {
|
||||||
|
|
||||||
case BoardTypeBugTriage:
|
case BoardTypeBugTriage:
|
||||||
items = setting.Project.ProjectBoardBugTriageType
|
items = setting.Project.ProjectBoardBugTriageType
|
||||||
|
|
||||||
case BoardTypeBasicKanban:
|
case BoardTypeBasicKanban:
|
||||||
items = setting.Project.ProjectBoardBasicKanbanType
|
items = setting.Project.ProjectBoardBasicKanbanType
|
||||||
|
|
||||||
case BoardTypeNone:
|
case BoardTypeNone:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
|
@ -170,7 +170,6 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64)
|
|||||||
// the owner of a private repo needs to be explicitly added.
|
// the owner of a private repo needs to be explicitly added.
|
||||||
cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID})
|
cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID})
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// This is a "public" repository:
|
// This is a "public" repository:
|
||||||
// Any user that has read access, is a watcher or organization member can be requested to review
|
// Any user that has read access, is a watcher or organization member can be requested to review
|
||||||
|
@ -5,6 +5,7 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
@ -147,7 +148,7 @@ func DeleteRepositoryTransfer(ctx context.Context, repoID int64) error {
|
|||||||
func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error {
|
func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error {
|
||||||
switch status {
|
switch status {
|
||||||
case repo_model.RepositoryBeingMigrated:
|
case repo_model.RepositoryBeingMigrated:
|
||||||
return fmt.Errorf("repo is not ready, currently migrating")
|
return errors.New("repo is not ready, currently migrating")
|
||||||
case repo_model.RepositoryPendingTransfer:
|
case repo_model.RepositoryPendingTransfer:
|
||||||
return ErrRepoTransferInProgress{}
|
return ErrRepoTransferInProgress{}
|
||||||
}
|
}
|
||||||
|
@ -988,9 +988,8 @@ func GetUserIDsByNames(ctx context.Context, names []string, ignoreNonExistent bo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if ignoreNonExistent {
|
if ignoreNonExistent {
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
ids = append(ids, u.ID)
|
ids = append(ids, u.ID)
|
||||||
}
|
}
|
||||||
|
@ -63,16 +63,16 @@ func NewComplexity() {
|
|||||||
func setupComplexity(values []string) {
|
func setupComplexity(values []string) {
|
||||||
if len(values) != 1 || values[0] != "off" {
|
if len(values) != 1 || values[0] != "off" {
|
||||||
for _, val := range values {
|
for _, val := range values {
|
||||||
if complex, ok := charComplexities[val]; ok {
|
if complexity, ok := charComplexities[val]; ok {
|
||||||
validChars += complex.ValidChars
|
validChars += complexity.ValidChars
|
||||||
requiredList = append(requiredList, complex)
|
requiredList = append(requiredList, complexity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(requiredList) == 0 {
|
if len(requiredList) == 0 {
|
||||||
// No valid character classes found; use all classes as default
|
// No valid character classes found; use all classes as default
|
||||||
for _, complex := range charComplexities {
|
for _, complexity := range charComplexities {
|
||||||
validChars += complex.ValidChars
|
validChars += complexity.ValidChars
|
||||||
requiredList = append(requiredList, complex)
|
requiredList = append(requiredList, complexity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,10 +307,10 @@ func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBu
|
|||||||
|
|
||||||
// Deal with the binary hash
|
// Deal with the binary hash
|
||||||
idx = 0
|
idx = 0
|
||||||
len := objectFormat.FullLength() / 2
|
length := objectFormat.FullLength() / 2
|
||||||
for idx < len {
|
for idx < length {
|
||||||
var read int
|
var read int
|
||||||
read, err = rd.Read(shaBuf[idx:len])
|
read, err = rd.Read(shaBuf[idx:length])
|
||||||
n += read
|
n += read
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mode, fname, sha, n, err
|
return mode, fname, sha, n, err
|
||||||
|
@ -468,7 +468,7 @@ func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) {
|
|||||||
_, _ = rd.Discard(1)
|
_, _ = rd.Discard(1)
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
modifier, err := rd.ReadSlice('\x00')
|
modifier, err := rd.ReadString('\x00')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
|
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
|
||||||
|
@ -49,9 +49,8 @@ readLoop:
|
|||||||
if len(line) > 0 && line[0] == ' ' {
|
if len(line) > 0 && line[0] == ' ' {
|
||||||
_, _ = signatureSB.Write(line[1:])
|
_, _ = signatureSB.Write(line[1:])
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
pgpsig = false
|
|
||||||
}
|
}
|
||||||
|
pgpsig = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !message {
|
if !message {
|
||||||
|
@ -232,7 +232,6 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
|
|||||||
errChan <- err
|
errChan <- err
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -251,18 +251,18 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
len := objectFormat.FullLength()
|
length := objectFormat.FullLength()
|
||||||
commits := []*Commit{}
|
commits := []*Commit{}
|
||||||
shaline := make([]byte, len+1)
|
shaline := make([]byte, length+1)
|
||||||
for {
|
for {
|
||||||
n, err := io.ReadFull(stdoutReader, shaline)
|
n, err := io.ReadFull(stdoutReader, shaline)
|
||||||
if err != nil || n < len {
|
if err != nil || n < length {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
return commits, err
|
return commits, err
|
||||||
}
|
}
|
||||||
objectID, err := NewIDFromString(string(shaline[0:len]))
|
objectID, err := NewIDFromString(string(shaline[0:length]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,6 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
|
|||||||
// ex: git@try.gitea.io:go-gitea/gitea
|
// ex: git@try.gitea.io:go-gitea/gitea
|
||||||
match := scpSyntax.FindAllStringSubmatch(refURI, -1)
|
match := scpSyntax.FindAllStringSubmatch(refURI, -1)
|
||||||
if len(match) > 0 {
|
if len(match) > 0 {
|
||||||
|
|
||||||
m := match[0]
|
m := match[0]
|
||||||
refHostname := m[2]
|
refHostname := m[2]
|
||||||
pth := m[3]
|
pth := m[3]
|
||||||
|
@ -191,7 +191,6 @@ func (b *Indexer) addDelete(filename string, repo *repo_model.Repository, batch
|
|||||||
func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error {
|
func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error {
|
||||||
batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize)
|
batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize)
|
||||||
if len(changes.Updates) > 0 {
|
if len(changes.Updates) > 0 {
|
||||||
|
|
||||||
// Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
|
// Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
|
||||||
if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil {
|
if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil {
|
||||||
log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err)
|
log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err)
|
||||||
@ -335,7 +334,6 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
|
|||||||
if result, err = b.inner.Indexer.Search(facetRequest); err != nil {
|
if result, err = b.inner.Indexer.Search(facetRequest); err != nil {
|
||||||
return 0, nil, nil, err
|
return 0, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
languagesFacet := result.Facets["languages"]
|
languagesFacet := result.Facets["languages"]
|
||||||
for _, term := range languagesFacet.Terms.Terms() {
|
for _, term := range languagesFacet.Terms.Terms() {
|
||||||
|
@ -68,7 +68,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
|
|||||||
searchOpt.Paginator = opts.Paginator
|
searchOpt.Paginator = opts.Paginator
|
||||||
|
|
||||||
switch opts.SortType {
|
switch opts.SortType {
|
||||||
case "":
|
case "", "latest":
|
||||||
searchOpt.SortBy = SortByCreatedDesc
|
searchOpt.SortBy = SortByCreatedDesc
|
||||||
case "oldest":
|
case "oldest":
|
||||||
searchOpt.SortBy = SortByCreatedAsc
|
searchOpt.SortBy = SortByCreatedAsc
|
||||||
@ -86,7 +86,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
|
|||||||
searchOpt.SortBy = SortByDeadlineDesc
|
searchOpt.SortBy = SortByDeadlineDesc
|
||||||
case "priority", "priorityrepo", "project-column-sorting":
|
case "priority", "priorityrepo", "project-column-sorting":
|
||||||
// Unsupported sort type for search
|
// Unsupported sort type for search
|
||||||
searchOpt.SortBy = SortByUpdatedDesc
|
fallthrough
|
||||||
default:
|
default:
|
||||||
searchOpt.SortBy = SortByUpdatedDesc
|
searchOpt.SortBy = SortByUpdatedDesc
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,6 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||||||
query := elastic.NewBoolQuery()
|
query := elastic.NewBoolQuery()
|
||||||
|
|
||||||
if options.Keyword != "" {
|
if options.Keyword != "" {
|
||||||
|
|
||||||
searchType := esMultiMatchTypePhrasePrefix
|
searchType := esMultiMatchTypePhrasePrefix
|
||||||
if options.IsFuzzyKeyword {
|
if options.IsFuzzyKeyword {
|
||||||
searchType = esMultiMatchTypeBestFields
|
searchType = esMultiMatchTypeBestFields
|
||||||
|
@ -125,7 +125,6 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
|
|||||||
if mode.Colorize {
|
if mode.Colorize {
|
||||||
buf = append(buf, resetBytes...)
|
buf = append(buf, resetBytes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if flags&(Lshortfile|Llongfile) != 0 {
|
if flags&(Lshortfile|Llongfile) != 0 {
|
||||||
if mode.Colorize {
|
if mode.Colorize {
|
||||||
|
@ -466,7 +466,6 @@ func TestColorPreview(t *testing.T) {
|
|||||||
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
|
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
|
||||||
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
|
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
|
||||||
assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
|
assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
negativeTests := []string{
|
negativeTests := []string{
|
||||||
@ -549,7 +548,6 @@ func TestMathBlock(t *testing.T) {
|
|||||||
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
|
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
|
||||||
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
|
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
|
||||||
assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
|
assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,35 +147,35 @@ func (e *MarshalEncoder) marshalIntInternal(i int64) error {
|
|||||||
return e.w.WriteByte(byte(i - 5))
|
return e.w.WriteByte(byte(i - 5))
|
||||||
}
|
}
|
||||||
|
|
||||||
var len int
|
var length int
|
||||||
if 122 < i && i <= 0xff {
|
if 122 < i && i <= 0xff {
|
||||||
len = 1
|
length = 1
|
||||||
} else if 0xff < i && i <= 0xffff {
|
} else if 0xff < i && i <= 0xffff {
|
||||||
len = 2
|
length = 2
|
||||||
} else if 0xffff < i && i <= 0xffffff {
|
} else if 0xffff < i && i <= 0xffffff {
|
||||||
len = 3
|
length = 3
|
||||||
} else if 0xffffff < i && i <= 0x3fffffff {
|
} else if 0xffffff < i && i <= 0x3fffffff {
|
||||||
len = 4
|
length = 4
|
||||||
} else if -0x100 <= i && i < -123 {
|
} else if -0x100 <= i && i < -123 {
|
||||||
len = -1
|
length = -1
|
||||||
} else if -0x10000 <= i && i < -0x100 {
|
} else if -0x10000 <= i && i < -0x100 {
|
||||||
len = -2
|
length = -2
|
||||||
} else if -0x1000000 <= i && i < -0x100000 {
|
} else if -0x1000000 <= i && i < -0x100000 {
|
||||||
len = -3
|
length = -3
|
||||||
} else if -0x40000000 <= i && i < -0x1000000 {
|
} else if -0x40000000 <= i && i < -0x1000000 {
|
||||||
len = -4
|
length = -4
|
||||||
} else {
|
} else {
|
||||||
return ErrInvalidIntRange
|
return ErrInvalidIntRange
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := e.w.WriteByte(byte(len)); err != nil {
|
if err := e.w.WriteByte(byte(length)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len < 0 {
|
if length < 0 {
|
||||||
len = -len
|
length = -length
|
||||||
}
|
}
|
||||||
|
|
||||||
for c := 0; c < len; c++ {
|
for c := 0; c < length; c++ {
|
||||||
if err := e.w.WriteByte(byte(i >> uint(8*c) & 0xff)); err != nil {
|
if err := e.w.WriteByte(byte(i >> uint(8*c) & 0xff)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -244,13 +244,13 @@ func (e *MarshalEncoder) marshalArray(arr reflect.Value) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
len := arr.Len()
|
length := arr.Len()
|
||||||
|
|
||||||
if err := e.marshalIntInternal(int64(len)); err != nil {
|
if err := e.marshalIntInternal(int64(length)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len; i++ {
|
for i := 0; i < length; i++ {
|
||||||
if err := e.marshal(arr.Index(i).Interface()); err != nil {
|
if err := e.marshal(arr.Index(i).Interface()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,6 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int
|
|||||||
}
|
}
|
||||||
sort.Slice(processes, after(processes))
|
sort.Slice(processes, after(processes))
|
||||||
if !flat {
|
if !flat {
|
||||||
|
|
||||||
var sortChildren func(process *Process)
|
var sortChildren func(process *Process)
|
||||||
|
|
||||||
sortChildren = func(process *Process) {
|
sortChildren = func(process *Process) {
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var (
|
||||||
backoffBegin = 50 * time.Millisecond
|
backoffBegin = 50 * time.Millisecond
|
||||||
backoffUpper = 2 * time.Second
|
backoffUpper = 2 * time.Second
|
||||||
)
|
)
|
||||||
@ -18,6 +18,14 @@ type (
|
|||||||
backoffFuncErr func() (retry bool, err error)
|
backoffFuncErr func() (retry bool, err error)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func mockBackoffDuration(d time.Duration) func() {
|
||||||
|
oldBegin, oldUpper := backoffBegin, backoffUpper
|
||||||
|
backoffBegin, backoffUpper = d, d
|
||||||
|
return func() {
|
||||||
|
backoffBegin, backoffUpper = oldBegin, oldUpper
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func backoffRetErr[T any](ctx context.Context, begin, upper time.Duration, end <-chan time.Time, fn backoffFuncRetErr[T]) (ret T, err error) {
|
func backoffRetErr[T any](ctx context.Context, begin, upper time.Duration, end <-chan time.Time, fn backoffFuncRetErr[T]) (ret T, err error) {
|
||||||
d := begin
|
d := begin
|
||||||
for {
|
for {
|
||||||
|
@ -63,6 +63,8 @@ func (q *WorkerPoolQueue[T]) doDispatchBatchToWorker(wg *workerGroup[T], flushCh
|
|||||||
// TODO: the logic could be improved in the future, to avoid a data-race between "doStartNewWorker" and "workerNum"
|
// TODO: the logic could be improved in the future, to avoid a data-race between "doStartNewWorker" and "workerNum"
|
||||||
// The root problem is that if we skip "doStartNewWorker" here, the "workerNum" might be decreased by other workers later
|
// The root problem is that if we skip "doStartNewWorker" here, the "workerNum" might be decreased by other workers later
|
||||||
// So ideally, it should check whether there are enough workers by some approaches, and start new workers if necessary.
|
// So ideally, it should check whether there are enough workers by some approaches, and start new workers if necessary.
|
||||||
|
// This data-race is not serious, as long as a new worker will be started soon to make sure there are enough workers,
|
||||||
|
// so no need to hugely refactor at the moment.
|
||||||
q.workerNumMu.Lock()
|
q.workerNumMu.Lock()
|
||||||
noWorker := q.workerNum == 0
|
noWorker := q.workerNum == 0
|
||||||
if full || noWorker {
|
if full || noWorker {
|
||||||
@ -136,6 +138,14 @@ func (q *WorkerPoolQueue[T]) basePushForShutdown(items ...T) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resetIdleTicker(t *time.Ticker, dur time.Duration) {
|
||||||
|
t.Reset(dur)
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// doStartNewWorker starts a new worker for the queue, the worker reads from worker's channel and handles the items.
|
// doStartNewWorker starts a new worker for the queue, the worker reads from worker's channel and handles the items.
|
||||||
func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
|
func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
|
||||||
wp.wg.Add(1)
|
wp.wg.Add(1)
|
||||||
@ -146,8 +156,6 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
|
|||||||
log.Debug("Queue %q starts new worker", q.GetName())
|
log.Debug("Queue %q starts new worker", q.GetName())
|
||||||
defer log.Debug("Queue %q stops idle worker", q.GetName())
|
defer log.Debug("Queue %q stops idle worker", q.GetName())
|
||||||
|
|
||||||
atomic.AddInt32(&q.workerStartedCounter, 1) // Only increase counter, used for debugging
|
|
||||||
|
|
||||||
t := time.NewTicker(workerIdleDuration)
|
t := time.NewTicker(workerIdleDuration)
|
||||||
defer t.Stop()
|
defer t.Stop()
|
||||||
|
|
||||||
@ -169,11 +177,7 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
|
|||||||
}
|
}
|
||||||
q.doWorkerHandle(batch)
|
q.doWorkerHandle(batch)
|
||||||
// reset the idle ticker, and drain the tick after reset in case a tick is already triggered
|
// reset the idle ticker, and drain the tick after reset in case a tick is already triggered
|
||||||
t.Reset(workerIdleDuration)
|
resetIdleTicker(t, workerIdleDuration) // key code for TestWorkerPoolQueueWorkerIdleReset
|
||||||
select {
|
|
||||||
case <-t.C:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
q.workerNumMu.Lock()
|
q.workerNumMu.Lock()
|
||||||
keepWorking = q.workerNum <= 1 // keep the last worker running
|
keepWorking = q.workerNum <= 1 // keep the last worker running
|
||||||
|
@ -40,8 +40,6 @@ type WorkerPoolQueue[T any] struct {
|
|||||||
workerMaxNum int
|
workerMaxNum int
|
||||||
workerActiveNum int
|
workerActiveNum int
|
||||||
workerNumMu sync.Mutex
|
workerNumMu sync.Mutex
|
||||||
|
|
||||||
workerStartedCounter int32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type flushType chan struct{}
|
type flushType chan struct{}
|
||||||
|
@ -5,8 +5,10 @@ package queue
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -250,22 +252,34 @@ func TestWorkerPoolQueueShutdown(t *testing.T) {
|
|||||||
|
|
||||||
func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) {
|
func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) {
|
||||||
defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)()
|
defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)()
|
||||||
|
defer mockBackoffDuration(5 * time.Millisecond)()
|
||||||
|
|
||||||
|
var q *WorkerPoolQueue[int]
|
||||||
|
var handledCount atomic.Int32
|
||||||
|
var hasOnlyOneWorkerRunning atomic.Bool
|
||||||
handler := func(items ...int) (unhandled []int) {
|
handler := func(items ...int) (unhandled []int) {
|
||||||
time.Sleep(50 * time.Millisecond)
|
handledCount.Add(int32(len(items)))
|
||||||
|
// make each work have different duration, and check the active worker number periodically
|
||||||
|
var activeNums []int
|
||||||
|
for i := 0; i < 5-items[0]%2; i++ {
|
||||||
|
time.Sleep(workerIdleDuration * 2)
|
||||||
|
activeNums = append(activeNums, q.GetWorkerActiveNumber())
|
||||||
|
}
|
||||||
|
// When the queue never becomes empty, the existing workers should keep working
|
||||||
|
// It is not 100% true at the moment because the data-race in workergroup.go is not resolved, see that TODO */
|
||||||
|
// If the "active worker numbers" is like [2 2 ... 1 1], it means that an existing worker exited and the no new worker is started.
|
||||||
|
if slices.Equal([]int{1, 1}, activeNums[len(activeNums)-2:]) {
|
||||||
|
hasOnlyOneWorkerRunning.Store(true)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
|
||||||
q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
|
|
||||||
stop := runWorkerPoolQueue(q)
|
stop := runWorkerPoolQueue(q)
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
assert.NoError(t, q.Push(i))
|
assert.NoError(t, q.Push(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
time.Sleep(500 * time.Millisecond)
|
||||||
assert.EqualValues(t, 2, q.GetWorkerNumber())
|
assert.Greater(t, int(handledCount.Load()), 4) // make sure there are enough items handled during the test
|
||||||
assert.EqualValues(t, 2, q.GetWorkerActiveNumber())
|
assert.False(t, hasOnlyOneWorkerRunning.Load(), "a slow handler should not block other workers from starting")
|
||||||
// when the queue never becomes empty, the existing workers should keep working
|
|
||||||
assert.EqualValues(t, 2, q.workerStartedCounter)
|
|
||||||
stop()
|
stop()
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ func CreateTemporaryPath(prefix string) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err)
|
log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err)
|
||||||
return "", fmt.Errorf("Failed to create dir %s-*.git: %w", prefix, err)
|
return "", fmt.Errorf("Failed to create dir %s-*.git: %w", prefix, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
return basePath, nil
|
return basePath, nil
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,6 @@ package session
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
"code.gitea.io/gitea/modules/web/middleware"
|
|
||||||
|
|
||||||
"gitea.com/go-chi/session"
|
"gitea.com/go-chi/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,10 +18,12 @@ type Store interface {
|
|||||||
|
|
||||||
// RegenerateSession regenerates the underlying session and returns the new store
|
// RegenerateSession regenerates the underlying session and returns the new store
|
||||||
func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) {
|
func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) {
|
||||||
// Ensure that a cookie with a trailing slash does not take precedence over
|
for _, f := range BeforeRegenerateSession {
|
||||||
// the cookie written by the middleware.
|
f(resp, req)
|
||||||
middleware.DeleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
|
}
|
||||||
|
|
||||||
s, err := session.RegenerateSession(resp, req)
|
s, err := session.RegenerateSession(resp, req)
|
||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeforeRegenerateSession is a list of functions that are called before a session is regenerated.
|
||||||
|
var BeforeRegenerateSession []func(http.ResponseWriter, *http.Request)
|
||||||
|
@ -318,7 +318,7 @@ func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) {
|
|||||||
// StartupProblems contains the messages for various startup problems, including: setting option, file/folder, etc
|
// StartupProblems contains the messages for various startup problems, including: setting option, file/folder, etc
|
||||||
var StartupProblems []string
|
var StartupProblems []string
|
||||||
|
|
||||||
func logStartupProblem(skip int, level log.Level, format string, args ...any) {
|
func LogStartupProblem(skip int, level log.Level, format string, args ...any) {
|
||||||
msg := fmt.Sprintf(format, args...)
|
msg := fmt.Sprintf(format, args...)
|
||||||
log.Log(skip+1, level, "%s", msg)
|
log.Log(skip+1, level, "%s", msg)
|
||||||
StartupProblems = append(StartupProblems, msg)
|
StartupProblems = append(StartupProblems, msg)
|
||||||
@ -326,14 +326,14 @@ func logStartupProblem(skip int, level log.Level, format string, args ...any) {
|
|||||||
|
|
||||||
func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
|
func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
|
||||||
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||||
logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
|
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
|
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
|
||||||
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
|
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
|
||||||
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||||
logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
|
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ func GetGeneralTokenSigningSecret() []byte {
|
|||||||
}
|
}
|
||||||
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
|
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
|
||||||
// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
|
// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
|
||||||
logStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
|
LogStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
|
||||||
return jwtSecret
|
return jwtSecret
|
||||||
}
|
}
|
||||||
return *generalSigningSecret.Load()
|
return *generalSigningSecret.Load()
|
||||||
|
@ -235,7 +235,7 @@ var configuredPaths = make(map[string]string)
|
|||||||
func checkOverlappedPath(name, path string) {
|
func checkOverlappedPath(name, path string) {
|
||||||
// TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path)
|
// TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path)
|
||||||
if targetName, ok := configuredPaths[path]; ok && targetName != name {
|
if targetName, ok := configuredPaths[path]; ok && targetName != name {
|
||||||
logStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
|
LogStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
|
||||||
}
|
}
|
||||||
configuredPaths[path] = name
|
configuredPaths[path] = name
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,8 @@ func loadTimeFrom(rootCfg ConfigProvider) {
|
|||||||
DefaultUILocation, err = time.LoadLocation(zone)
|
DefaultUILocation, err = time.LoadLocation(zone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Load time zone failed: %v", err)
|
log.Fatal("Load time zone failed: %v", err)
|
||||||
} else {
|
|
||||||
log.Info("Default UI Location is %v", zone)
|
|
||||||
}
|
}
|
||||||
|
log.Info("Default UI Location is %v", zone)
|
||||||
}
|
}
|
||||||
if DefaultUILocation == nil {
|
if DefaultUILocation == nil {
|
||||||
DefaultUILocation = time.Local
|
DefaultUILocation = time.Local
|
||||||
|
@ -82,7 +82,6 @@ var UI = struct {
|
|||||||
ReactionMaxUserNum: 10,
|
ReactionMaxUserNum: 10,
|
||||||
MaxDisplayFileSize: 8388608,
|
MaxDisplayFileSize: 8388608,
|
||||||
DefaultTheme: `gitea-auto`,
|
DefaultTheme: `gitea-auto`,
|
||||||
Themes: []string{`gitea-auto`, `gitea-light`, `gitea-dark`},
|
|
||||||
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
|
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
|
||||||
CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
|
CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
|
||||||
CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
|
CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/services/gitdiff"
|
"code.gitea.io/gitea/services/gitdiff"
|
||||||
|
"code.gitea.io/gitea/services/webtheme"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewFuncMap returns functions for injecting to templates
|
// NewFuncMap returns functions for injecting to templates
|
||||||
@ -137,12 +138,7 @@ func NewFuncMap() template.FuncMap {
|
|||||||
"DisableImportLocal": func() bool {
|
"DisableImportLocal": func() bool {
|
||||||
return !setting.ImportLocalPaths
|
return !setting.ImportLocalPaths
|
||||||
},
|
},
|
||||||
"ThemeName": func(user *user_model.User) string {
|
"UserThemeName": UserThemeName,
|
||||||
if user == nil || user.Theme == "" {
|
|
||||||
return setting.UI.DefaultTheme
|
|
||||||
}
|
|
||||||
return user.Theme
|
|
||||||
},
|
|
||||||
"NotificationSettings": func() map[string]any {
|
"NotificationSettings": func() map[string]any {
|
||||||
return map[string]any{
|
return map[string]any{
|
||||||
"MinTimeout": int(setting.UI.Notification.MinTimeout / time.Millisecond),
|
"MinTimeout": int(setting.UI.Notification.MinTimeout / time.Millisecond),
|
||||||
@ -261,3 +257,13 @@ func Eval(tokens ...any) (any, error) {
|
|||||||
n, err := eval.Expr(tokens...)
|
n, err := eval.Expr(tokens...)
|
||||||
return n.Value, err
|
return n.Value, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UserThemeName(user *user_model.User) string {
|
||||||
|
if user == nil || user.Theme == "" {
|
||||||
|
return setting.UI.DefaultTheme
|
||||||
|
}
|
||||||
|
if webtheme.IsThemeAvailable(user.Theme) {
|
||||||
|
return user.Theme
|
||||||
|
}
|
||||||
|
return setting.UI.DefaultTheme
|
||||||
|
}
|
||||||
|
@ -138,10 +138,9 @@ func wrapTmplErrMsg(msg string) {
|
|||||||
if setting.IsProd {
|
if setting.IsProd {
|
||||||
// in prod mode, Gitea must have correct templates to run
|
// in prod mode, Gitea must have correct templates to run
|
||||||
log.Fatal("Gitea can't run with template errors: %s", msg)
|
log.Fatal("Gitea can't run with template errors: %s", msg)
|
||||||
} else {
|
|
||||||
// in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded
|
|
||||||
log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg)
|
|
||||||
}
|
}
|
||||||
|
// in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded
|
||||||
|
log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
type templateErrorPrettier struct {
|
type templateErrorPrettier struct {
|
||||||
|
@ -84,9 +84,8 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) {
|
|||||||
if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil {
|
if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil {
|
||||||
if firstRun {
|
if firstRun {
|
||||||
log.Fatal("Failed to parse mail template, err: %v", err)
|
log.Fatal("Failed to parse mail template, err: %v", err)
|
||||||
} else {
|
|
||||||
log.Error("Failed to parse mail template, err: %v", err)
|
|
||||||
}
|
}
|
||||||
|
log.Error("Failed to parse mail template, err: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,9 +121,9 @@ func Test_NormalizeEOL(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_RandomInt(t *testing.T) {
|
func Test_RandomInt(t *testing.T) {
|
||||||
int, err := CryptoRandomInt(255)
|
randInt, err := CryptoRandomInt(255)
|
||||||
assert.True(t, int >= 0)
|
assert.True(t, randInt >= 0)
|
||||||
assert.True(t, int <= 255)
|
assert.True(t, randInt <= 255)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +128,16 @@ func hasResponseBeenWritten(argsIn []reflect.Value) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrapHandlerProvider[T http.Handler](hp func(next http.Handler) T, funcInfo *routing.FuncInfo) func(next http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
|
||||||
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
routing.UpdateFuncInfo(req.Context(), funcInfo)
|
||||||
|
h.ServeHTTP(resp, req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// toHandlerProvider converts a handler to a handler provider
|
// toHandlerProvider converts a handler to a handler provider
|
||||||
// A handler provider is a function that takes a "next" http.Handler, it can be used as a middleware
|
// A handler provider is a function that takes a "next" http.Handler, it can be used as a middleware
|
||||||
func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
|
func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
|
||||||
@ -138,13 +148,9 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hp, ok := handler.(func(next http.Handler) http.Handler); ok {
|
if hp, ok := handler.(func(next http.Handler) http.Handler); ok {
|
||||||
return func(next http.Handler) http.Handler {
|
return wrapHandlerProvider(hp, funcInfo)
|
||||||
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
|
} else if hp, ok := handler.(func(http.Handler) http.HandlerFunc); ok {
|
||||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
return wrapHandlerProvider(hp, funcInfo)
|
||||||
routing.UpdateFuncInfo(req.Context(), funcInfo)
|
|
||||||
h.ServeHTTP(resp, req)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := func(next http.Handler) http.Handler {
|
provider := func(next http.Handler) http.Handler {
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/session"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,12 +49,12 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
|
|||||||
// Previous versions would use a cookie path with a trailing /.
|
// Previous versions would use a cookie path with a trailing /.
|
||||||
// These are more specific than cookies without a trailing /, so
|
// These are more specific than cookies without a trailing /, so
|
||||||
// we need to delete these if they exist.
|
// we need to delete these if they exist.
|
||||||
DeleteLegacySiteCookie(resp, name)
|
deleteLegacySiteCookie(resp, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteLegacySiteCookie deletes the cookie with the given name at the cookie
|
// deleteLegacySiteCookie deletes the cookie with the given name at the cookie
|
||||||
// path with a trailing /, which would unintentionally override the cookie.
|
// path with a trailing /, which would unintentionally override the cookie.
|
||||||
func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
|
func deleteLegacySiteCookie(resp http.ResponseWriter, name string) {
|
||||||
if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") {
|
if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") {
|
||||||
// If the cookie path ends with /, no legacy cookies will take
|
// If the cookie path ends with /, no legacy cookies will take
|
||||||
// precedence, so do nothing. The exception is that cookies with no
|
// precedence, so do nothing. The exception is that cookies with no
|
||||||
@ -74,3 +75,11 @@ func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
|
|||||||
}
|
}
|
||||||
resp.Header().Add("Set-Cookie", cookie.String())
|
resp.Header().Add("Set-Cookie", cookie.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
session.BeforeRegenerateSession = append(session.BeforeRegenerateSession, func(resp http.ResponseWriter, _ *http.Request) {
|
||||||
|
// Ensure that a cookie with a trailing slash does not take precedence over
|
||||||
|
// the cookie written by the middleware.
|
||||||
|
deleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
10
options/license/HPND-UC-export-US
Normal file
10
options/license/HPND-UC-export-US
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Copyright (C) 1985, 1990 Regents of the University of California.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this
|
||||||
|
software and its documentation for any purpose and without
|
||||||
|
fee is hereby granted, provided that the above copyright
|
||||||
|
notice appear in all copies. The University of California
|
||||||
|
makes no representations about the suitability of this
|
||||||
|
software for any purpose. It is provided "as is" without
|
||||||
|
express or implied warranty. Export of this software outside
|
||||||
|
of the United States of America may require an export license.
|
32
options/license/NCL
Normal file
32
options/license/NCL
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
Copyright (c) 2004 the University Corporation for Atmospheric
|
||||||
|
Research ("UCAR"). All rights reserved. Developed by NCAR's
|
||||||
|
Computational and Information Systems Laboratory, UCAR,
|
||||||
|
www.cisl.ucar.edu.
|
||||||
|
|
||||||
|
Redistribution and use of the Software in source and binary forms,
|
||||||
|
with or without modification, is permitted provided that the
|
||||||
|
following conditions are met:
|
||||||
|
|
||||||
|
- Neither the names of NCAR's Computational and Information Systems
|
||||||
|
Laboratory, the University Corporation for Atmospheric Research,
|
||||||
|
nor the names of its sponsors or contributors may be used to
|
||||||
|
endorse or promote products derived from this Software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notices, this list of conditions, and the disclaimer below.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions, and the disclaimer below in the
|
||||||
|
documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
THIS 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 CONTRIBUTORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL 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 WITH THE
|
||||||
|
SOFTWARE.
|
@ -1186,7 +1186,6 @@ action.blocked_user=Nelze provést akci, protože jste zablokování vlastníkem
|
|||||||
download_archive=Stáhnout repozitář
|
download_archive=Stáhnout repozitář
|
||||||
more_operations=Další operace
|
more_operations=Další operace
|
||||||
|
|
||||||
no_desc=Bez popisu
|
|
||||||
quick_guide=Krátká příručka
|
quick_guide=Krátká příručka
|
||||||
clone_this_repo=Naklonovat tento repozitář
|
clone_this_repo=Naklonovat tento repozitář
|
||||||
cite_this_repo=Citovat tento repozitář
|
cite_this_repo=Citovat tento repozitář
|
||||||
|
@ -1187,7 +1187,6 @@ action.blocked_user=Die Aktion kann nicht ausgeführt werden, da du vom Reposito
|
|||||||
download_archive=Repository herunterladen
|
download_archive=Repository herunterladen
|
||||||
more_operations=Weitere Operationen
|
more_operations=Weitere Operationen
|
||||||
|
|
||||||
no_desc=Keine Beschreibung
|
|
||||||
quick_guide=Kurzanleitung
|
quick_guide=Kurzanleitung
|
||||||
clone_this_repo=Dieses Repository klonen
|
clone_this_repo=Dieses Repository klonen
|
||||||
cite_this_repo=Dieses Repository zitieren
|
cite_this_repo=Dieses Repository zitieren
|
||||||
|
@ -1118,7 +1118,6 @@ fork=Fork
|
|||||||
download_archive=Λήψη Αποθετηρίου
|
download_archive=Λήψη Αποθετηρίου
|
||||||
more_operations=Περισσότερες Λειτουργίες
|
more_operations=Περισσότερες Λειτουργίες
|
||||||
|
|
||||||
no_desc=Χωρίς Περιγραφή
|
|
||||||
quick_guide=Γρήγορος Οδηγός
|
quick_guide=Γρήγορος Οδηγός
|
||||||
clone_this_repo=Κλωνοποίηση αυτού του αποθετηρίου
|
clone_this_repo=Κλωνοποίηση αυτού του αποθετηρίου
|
||||||
cite_this_repo=Αναφορά σε αυτό το αποθετήριο
|
cite_this_repo=Αναφορά σε αυτό το αποθετήριο
|
||||||
|
@ -763,6 +763,8 @@ manage_themes = Select default theme
|
|||||||
manage_openid = Manage OpenID Addresses
|
manage_openid = Manage OpenID Addresses
|
||||||
email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations.
|
email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations.
|
||||||
theme_desc = This will be your default theme across the site.
|
theme_desc = This will be your default theme across the site.
|
||||||
|
theme_colorblindness_help = Colorblindness Theme Support
|
||||||
|
theme_colorblindness_prompt = Gitea just gets some themes with basic colorblindness support, which only have a few colors defined. The work is still in progress. More improvements could be done by defining more colors in the theme CSS files.
|
||||||
primary = Primary
|
primary = Primary
|
||||||
activated = Activated
|
activated = Activated
|
||||||
requires_activation = Requires activation
|
requires_activation = Requires activation
|
||||||
@ -1193,7 +1195,6 @@ action.blocked_user = Cannot perform action because you are blocked by the repos
|
|||||||
download_archive = Download Repository
|
download_archive = Download Repository
|
||||||
more_operations = More Operations
|
more_operations = More Operations
|
||||||
|
|
||||||
no_desc = No Description
|
|
||||||
quick_guide = Quick Guide
|
quick_guide = Quick Guide
|
||||||
clone_this_repo = Clone this repository
|
clone_this_repo = Clone this repository
|
||||||
cite_this_repo = Cite this repository
|
cite_this_repo = Cite this repository
|
||||||
|
@ -1111,7 +1111,6 @@ fork=Fork
|
|||||||
download_archive=Descargar repositorio
|
download_archive=Descargar repositorio
|
||||||
more_operations=Más operaciones
|
more_operations=Más operaciones
|
||||||
|
|
||||||
no_desc=Sin descripción
|
|
||||||
quick_guide=Guía rápida
|
quick_guide=Guía rápida
|
||||||
clone_this_repo=Clonar este repositorio
|
clone_this_repo=Clonar este repositorio
|
||||||
cite_this_repo=Citar este repositorio
|
cite_this_repo=Citar este repositorio
|
||||||
|
@ -874,7 +874,6 @@ star=ستاره دار کن
|
|||||||
fork=انشعاب
|
fork=انشعاب
|
||||||
download_archive=دانلود مخزن
|
download_archive=دانلود مخزن
|
||||||
|
|
||||||
no_desc=بدون توضیح
|
|
||||||
quick_guide=راهنمای سریع
|
quick_guide=راهنمای سریع
|
||||||
clone_this_repo=همسانسازی این مخزن
|
clone_this_repo=همسانسازی این مخزن
|
||||||
create_new_repo_command=ایجاد یک مخزن جدید در خط فرمان
|
create_new_repo_command=ایجاد یک مخزن جدید در خط فرمان
|
||||||
|
@ -718,7 +718,6 @@ unstar=Poista tähti
|
|||||||
star=Tähti
|
star=Tähti
|
||||||
download_archive=Lataa repo
|
download_archive=Lataa repo
|
||||||
|
|
||||||
no_desc=Ei kuvausta
|
|
||||||
quick_guide=Pikaopas
|
quick_guide=Pikaopas
|
||||||
clone_this_repo=Kloonaa tämä repo
|
clone_this_repo=Kloonaa tämä repo
|
||||||
|
|
||||||
|
@ -150,6 +150,10 @@ filter.private=Privé
|
|||||||
|
|
||||||
|
|
||||||
[search]
|
[search]
|
||||||
|
exact=Exact
|
||||||
|
exact_tooltip=Inclure uniquement les résultats qui correspondent exactement au terme de recherche
|
||||||
|
issue_kind=Recherche de tickets…
|
||||||
|
pull_kind=Recherche de demandes d’ajouts…
|
||||||
|
|
||||||
[aria]
|
[aria]
|
||||||
navbar=Barre de navigation
|
navbar=Barre de navigation
|
||||||
@ -824,6 +828,7 @@ repo_and_org_access=Accès aux Organisations et Dépôts
|
|||||||
permissions_public_only=Publique uniquement
|
permissions_public_only=Publique uniquement
|
||||||
permissions_access_all=Tout (public, privé et limité)
|
permissions_access_all=Tout (public, privé et limité)
|
||||||
select_permissions=Sélectionner les autorisations
|
select_permissions=Sélectionner les autorisations
|
||||||
|
permission_not_set=Non défini
|
||||||
permission_no_access=Aucun accès
|
permission_no_access=Aucun accès
|
||||||
permission_read=Lecture
|
permission_read=Lecture
|
||||||
permission_write=Lecture et écriture
|
permission_write=Lecture et écriture
|
||||||
@ -1125,7 +1130,6 @@ fork=Bifurcation
|
|||||||
download_archive=Télécharger ce dépôt
|
download_archive=Télécharger ce dépôt
|
||||||
more_operations=Plus d'opérations
|
more_operations=Plus d'opérations
|
||||||
|
|
||||||
no_desc=Aucune description
|
|
||||||
quick_guide=Introduction rapide
|
quick_guide=Introduction rapide
|
||||||
clone_this_repo=Cloner ce dépôt
|
clone_this_repo=Cloner ce dépôt
|
||||||
cite_this_repo=Citer ce dépôt
|
cite_this_repo=Citer ce dépôt
|
||||||
@ -2016,6 +2020,7 @@ 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
|
||||||
settings.use_internal_wiki=Utiliser le wiki interne
|
settings.use_internal_wiki=Utiliser le wiki interne
|
||||||
|
settings.default_wiki_everyone_access=Autorisation d’accès par défaut pour les utilisateurs connectés :
|
||||||
settings.use_external_wiki=Utiliser un wiki externe
|
settings.use_external_wiki=Utiliser un wiki externe
|
||||||
settings.external_wiki_url=URL Wiki externe
|
settings.external_wiki_url=URL Wiki externe
|
||||||
settings.external_wiki_url_error=L’URL du wiki externe n’est pas une URL valide.
|
settings.external_wiki_url_error=L’URL du wiki externe n’est pas une URL valide.
|
||||||
|
@ -656,7 +656,6 @@ star=Csillagozás
|
|||||||
fork=Tükrözés
|
fork=Tükrözés
|
||||||
download_archive=Tároló letöltése
|
download_archive=Tároló letöltése
|
||||||
|
|
||||||
no_desc=Nincs leírás
|
|
||||||
quick_guide=Gyors útmutató
|
quick_guide=Gyors útmutató
|
||||||
clone_this_repo=Tároló klónozása
|
clone_this_repo=Tároló klónozása
|
||||||
create_new_repo_command=Egy új tároló létrehozása a parancssorból
|
create_new_repo_command=Egy új tároló létrehozása a parancssorból
|
||||||
|
@ -570,7 +570,6 @@ star=Bintang
|
|||||||
fork=Garpu
|
fork=Garpu
|
||||||
download_archive=Unduh Repositori
|
download_archive=Unduh Repositori
|
||||||
|
|
||||||
no_desc=Tidak ada Deskripsi
|
|
||||||
quick_guide=Panduan Cepat
|
quick_guide=Panduan Cepat
|
||||||
clone_this_repo=Klon repositori ini
|
clone_this_repo=Klon repositori ini
|
||||||
create_new_repo_command=Membuat repositori baru pada baris perintah
|
create_new_repo_command=Membuat repositori baru pada baris perintah
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user