diff --git a/.deadcode-out b/.deadcode-out index e366abee94..5b5698c5de 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -192,6 +192,9 @@ forgejo.org/modules/translation MockLocale.HasKey MockLocale.PrettyNumber +forgejo.org/modules/translation/localeiter + IterateMessagesContent + forgejo.org/modules/util OptionalArg @@ -210,9 +213,6 @@ forgejo.org/modules/zstd Writer.Write Writer.Close -forgejo.org/routers/web - NotFound - forgejo.org/routers/web/org MustEnableProjects diff --git a/.editorconfig b/.editorconfig index a547e8a585..5476eb02fb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,6 +12,9 @@ insert_final_newline = true [{*.{go,tmpl,html},Makefile,go.mod}] indent_style = tab +[go.*] +indent_style = tab + [templates/custom/*.tmpl] insert_final_newline = false diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index dbba9a82bb..2d6da0c63e 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:39.212.0 + image: data.forgejo.org/renovate/renovate:39.222.1 steps: - name: Load renovate repo cache diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index 62136b1b28..4d88d3efb0 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -116,7 +116,7 @@ jobs: - uses: ./.forgejo/workflows-composite/build-backend - name: Get changed files id: changed-files - uses: https://data.forgejo.org/tj-actions/changed-files@v45 + uses: https://data.forgejo.org/tj-actions/changed-files@v46 with: separator: '\n' - run: | diff --git a/.golangci.yml b/.golangci.yml index 136c0e624a..532132838d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,7 +1,9 @@ +version: "2" +output: + sort-order: + - file linters: - enable-all: false - disable-all: true - fast: false + default: none enable: - bidichk - depguard @@ -9,142 +11,155 @@ linters: - errcheck - forbidigo - gocritic - - gofmt - - gofumpt - - gosimple - govet - ineffassign - nakedret - nolintlint - revive - staticcheck - - stylecheck - testifylint - - typecheck - unconvert - - unused - unparam + - unused - usetesting - wastedassign - -run: - timeout: 10m - -output: - sort-results: true - sort-order: [file] - show-stats: true - -linters-settings: - stylecheck: - checks: ["all", "-ST1005", "-ST1003"] - nakedret: - max-func-lines: 0 - gocritic: - disabled-checks: - - ifElseChain - revive: - severity: error + settings: + depguard: + rules: + main: + deny: + - pkg: encoding/json + desc: use gitea's modules/json instead of encoding/json + - pkg: github.com/unknwon/com + desc: use gitea's util and replacements + - pkg: io/ioutil + desc: use os or io instead + - pkg: golang.org/x/exp + desc: it's experimental and unreliable + - pkg: forgejo.org/modules/git/internal + desc: do not use the internal package, use AddXxx function instead + - pkg: gopkg.in/ini.v1 + desc: do not use the ini package, use gitea's config system instead + - pkg: github.com/minio/sha256-simd + desc: use crypto/sha256 instead, see https://codeberg.org/forgejo/forgejo/pulls/1528 + gocritic: + disabled-checks: + - ifElseChain + revive: + severity: error + rules: + - name: atomic + - name: bare-return + - name: blank-imports + - name: constant-logical-expr + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: duplicated-imports + - name: empty-lines + - name: error-naming + - name: error-return + - name: error-strings + - name: errorf + - name: exported + - name: identical-branches + - name: if-return + - name: increment-decrement + - name: indent-error-flow + - name: modifies-value-receiver + - name: package-comments + - name: range + - name: receiver-naming + - name: redefines-builtin-id + - name: string-of-int + - name: superfluous-else + - name: time-naming + - name: unconditional-recursion + - name: unexported-return + - name: unreachable-code + - name: var-declaration + - name: var-naming + - name: redefines-builtin-id + disabled: true + staticcheck: + checks: + - all + testifylint: + disable: + - go-require + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling rules: - - name: atomic - - name: bare-return - - name: blank-imports - - name: constant-logical-expr - - name: context-as-argument - - name: context-keys-type - - name: dot-imports - - name: duplicated-imports - - name: empty-lines - - name: error-naming - - name: error-return - - name: error-strings - - name: errorf - - name: exported - - name: identical-branches - - name: if-return - - name: increment-decrement - - name: indent-error-flow - - name: modifies-value-receiver - - name: package-comments - - name: range - - name: receiver-naming - - name: redefines-builtin-id - - name: string-of-int - - name: superfluous-else - - name: time-naming - - name: unconditional-recursion - - name: unexported-return - - name: unreachable-code - - name: var-declaration - - name: var-naming - - name: redefines-builtin-id - disabled: true - gofumpt: - extra-rules: true - depguard: - rules: - main: - deny: - - pkg: encoding/json - desc: use gitea's modules/json instead of encoding/json - - pkg: github.com/unknwon/com - desc: use gitea's util and replacements - - pkg: io/ioutil - desc: use os or io instead - - pkg: golang.org/x/exp - desc: it's experimental and unreliable - - pkg: forgejo.org/modules/git/internal - desc: do not use the internal package, use AddXxx function instead - - pkg: gopkg.in/ini.v1 - desc: do not use the ini package, use gitea's config system instead - - pkg: github.com/minio/sha256-simd - desc: use crypto/sha256 instead, see https://codeberg.org/forgejo/forgejo/pulls/1528 - testifylint: - disable: - - go-require - + - linters: + - nolintlint + path: models/db/sql_postgres_with_schema.go + - linters: + - dupl + - errcheck + - gocyclo + - gosec + - staticcheck + - unparam + path: _test\.go + - linters: + - dupl + - errcheck + - gocyclo + - gosec + path: models/migrations/v + - linters: + - forbidigo + path: cmd + - linters: + - dupl + text: (?i)webhook + - linters: + - gocritic + text: (?i)`ID' should not be capitalized + - linters: + - deadcode + - unused + text: (?i)swagger + - linters: + - staticcheck + text: (?i)argument x is overwritten before first use + - linters: + - gocritic + text: '(?i)commentFormatting: put a space between `//` and comment text' + - linters: + - gocritic + text: '(?i)exitAfterDefer:' + - linters: + - staticcheck + text: "(ST1005|ST1003|QF1001):" + paths: + - node_modules + - public + - web_src + - third_party$ + - builtin$ + - examples$ issues: max-issues-per-linter: 0 max-same-issues: 0 - exclude-dirs: [node_modules, public, web_src] - exclude-case-sensitive: true - exclude-rules: - - path: models/db/sql_postgres_with_schema.go - linters: - - nolintlint - - path: _test\.go - linters: - - gocyclo - - errcheck - - dupl - - gosec - - unparam - - staticcheck - - path: models/migrations/v - linters: - - gocyclo - - errcheck - - dupl - - gosec - - path: cmd - linters: - - forbidigo - - text: "webhook" - linters: - - dupl - - text: "`ID' should not be capitalized" - linters: - - gocritic - - text: "swagger" - linters: - - unused - - deadcode - - text: "argument x is overwritten before first use" - linters: - - staticcheck - - text: "commentFormatting: put a space between `//` and comment text" - linters: - - gocritic - - text: "exitAfterDefer:" - linters: - - gocritic +formatters: + enable: + - gofmt + - gofumpt + settings: + gofumpt: + extra-rules: true + exclusions: + generated: lax + paths: + - node_modules + - public + - web_src + - third_party$ + - builtin$ + - examples$ diff --git a/Makefile b/Makefile index 4fb06db918..4f7aa3b707 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.1 # renovate: datasource=go GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0 # renovate: datasource=go -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.7 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.0.2 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0 # renovate: datasource=go SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go @@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.31.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@39.212.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@39.222.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... @@ -462,7 +462,7 @@ lint-locale: .PHONY: lint-locale-usage lint-locale-usage: - $(GO) run build/lint-locale-usage/lint-locale-usage.go --allow-missing-msgids + $(GO) run build/lint-locale-usage/lint-locale-usage.go .PHONY: lint-md lint-md: node_modules @@ -523,7 +523,7 @@ lint-yaml: .venv .PHONY: security-check security-check: - go run $(GOVULNCHECK_PACKAGE) ./... + go run $(GOVULNCHECK_PACKAGE) -show color ./... ### # Development and testing targets diff --git a/build/lint-locale-usage/lint-locale-usage.go b/build/lint-locale-usage/lint-locale-usage.go index 8aeae26c4c..88375c1c36 100644 --- a/build/lint-locale-usage/lint-locale-usage.go +++ b/build/lint-locale-usage/lint-locale-usage.go @@ -18,8 +18,8 @@ import ( tmplParser "text/template/parse" "forgejo.org/modules/container" - "forgejo.org/modules/locale" fjTemplates "forgejo.org/modules/templates" + "forgejo.org/modules/translation/localeiter" "forgejo.org/modules/util" ) @@ -264,7 +264,7 @@ func (handler Handler) HandleTemplateFile(fname string, src any) error { Err: err, } } - handler.handleTemplateFileNodes(fset, tmplParsed.Tree.Root.Nodes) + handler.handleTemplateFileNodes(fset, tmplParsed.Root.Nodes) return nil } @@ -300,10 +300,6 @@ func main() { } msgids := make(container.Set[string]) - onMsgid := func(trKey, trValue string) error { - msgids[trKey] = struct{}{} - return nil - } localeFile := filepath.Join(filepath.Join("options", "locale"), "locale_en-US.ini") localeContent, err := os.ReadFile(localeFile) @@ -312,7 +308,10 @@ func main() { os.Exit(2) } - if err = locale.IterateMessagesContent(localeContent, onMsgid); err != nil { + if err = localeiter.IterateMessagesContent(localeContent, func(trKey, trValue string) error { + msgids[trKey] = struct{}{} + return nil + }); err != nil { fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) os.Exit(2) } @@ -324,7 +323,11 @@ func main() { os.Exit(2) } - if err := locale.IterateMessagesNextContent(localeContent, onMsgid); err != nil { + if err := localeiter.IterateMessagesNextContent(localeContent, func(trKey, pluralForm, trValue string) error { + // ignore plural form + msgids[trKey] = struct{}{} + return nil + }); err != nil { fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) os.Exit(2) } diff --git a/build/lint-locale-usage/lint-locale-usage_test.go b/build/lint-locale-usage/lint-locale-usage_test.go index e573fb196e..81ca12c6db 100644 --- a/build/lint-locale-usage/lint-locale-usage_test.go +++ b/build/lint-locale-usage/lint-locale-usage_test.go @@ -37,13 +37,13 @@ func HandleTemplateFileWrapped(t *testing.T, fname, src string) []string { func TestUsagesParser(t *testing.T) { t.Run("go, simple", func(t *testing.T) { - assert.EqualValues(t, + assert.Equal(t, []string{"what.an.example"}, HandleGoFileWrapped(t, "<g1>", "package main\nfunc Render(ctx *context.Context) string { return ctx.Tr(\"what.an.example\"); }\n")) }) t.Run("template, simple", func(t *testing.T) { - assert.EqualValues(t, + assert.Equal(t, []string{"what.an.example"}, HandleTemplateFileWrapped(t, "<t1>", "{{ ctx.Locale.Tr \"what.an.example\" }}\n")) }) diff --git a/build/lint-locale/lint-locale.go b/build/lint-locale/lint-locale.go index 94ce941e62..0d80ffa4b0 100644 --- a/build/lint-locale/lint-locale.go +++ b/build/lint-locale/lint-locale.go @@ -14,7 +14,7 @@ import ( "slices" "strings" - "forgejo.org/modules/locale" + "forgejo.org/modules/translation/localeiter" "github.com/microcosm-cc/bluemonday" "github.com/sergi/go-diff/diffmatchpatch" @@ -100,7 +100,7 @@ func checkValue(trKey, value string) []string { func checkLocaleContent(localeContent []byte) []string { errors := []string{} - if err := locale.IterateMessagesContent(localeContent, func(trKey, trValue string) error { + if err := localeiter.IterateMessagesContent(localeContent, func(trKey, trValue string) error { errors = append(errors, checkValue(trKey, trValue)...) return nil }); err != nil { @@ -113,8 +113,12 @@ func checkLocaleContent(localeContent []byte) []string { func checkLocaleNextContent(localeContent []byte) []string { errors := []string{} - if err := locale.IterateMessagesNextContent(localeContent, func(trKey, trValue string) error { - errors = append(errors, checkValue(trKey, trValue)...) + if err := localeiter.IterateMessagesNextContent(localeContent, func(trKey, pluralForm, trValue string) error { + fullKey := trKey + if pluralForm != "" { + fullKey = trKey + "." + pluralForm + } + errors = append(errors, checkValue(fullKey, trValue)...) return nil }); err != nil { panic(err) diff --git a/build/lint-locale/lint-locale_test.go b/build/lint-locale/lint-locale_test.go index 791f5278bc..9e9a931feb 100644 --- a/build/lint-locale/lint-locale_test.go +++ b/build/lint-locale/lint-locale_test.go @@ -15,9 +15,9 @@ func TestLocalizationPolicy(t *testing.T) { t.Run("Remove tags", func(t *testing.T) { assert.Empty(t, checkLocaleContent([]byte(`hidden_comment_types_description = Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all "<user> added/removed <label>" comments.`))) - assert.EqualValues(t, []string{"key: \x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG"}, checkLocaleContent([]byte(`key = "<not-an-allowed-key> <label>"`))) - assert.EqualValues(t, []string{"key: \x1b[31m<user@example.com>\x1b[0m REPLACED-TAG"}, checkLocaleContent([]byte(`key = "<user@example.com> <email@example.com>"`))) - assert.EqualValues(t, []string{"key: \x1b[31m<tag>\x1b[0m REPLACED-TAG \x1b[31m</tag>\x1b[0m"}, checkLocaleContent([]byte(`key = "<tag> <email@example.com> </tag>"`))) + assert.Equal(t, []string{"key: \x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG"}, checkLocaleContent([]byte(`key = "<not-an-allowed-key> <label>"`))) + assert.Equal(t, []string{"key: \x1b[31m<user@example.com>\x1b[0m REPLACED-TAG"}, checkLocaleContent([]byte(`key = "<user@example.com> <email@example.com>"`))) + assert.Equal(t, []string{"key: \x1b[31m<tag>\x1b[0m REPLACED-TAG \x1b[31m</tag>\x1b[0m"}, checkLocaleContent([]byte(`key = "<tag> <email@example.com> </tag>"`))) }) t.Run("Specific exception", func(t *testing.T) { @@ -25,11 +25,11 @@ func TestLocalizationPolicy(t *testing.T) { assert.Empty(t, checkLocaleContent([]byte(`pulls.title_desc_one = wants to merge %[1]d commit from <code>%[2]s</code> into <code id="%[4]s">%[3]s</code>`))) assert.Empty(t, checkLocaleContent([]byte(`editor.commit_directly_to_this_branch = Commit directly to the <strong class="%[2]s">%[1]s</strong> branch.`))) - assert.EqualValues(t, []string{"workflow.dispatch.trigger_found: This workflow has a \x1b[31m<d>\x1b[0mworkflow_dispatch\x1b[31m</d>\x1b[0m event trigger."}, checkLocaleContent([]byte(`workflow.dispatch.trigger_found = This workflow has a <d>workflow_dispatch</d> event trigger.`))) - assert.EqualValues(t, []string{"key: <code\x1b[31m id=\"branch_targe\"\x1b[0m>%[3]s</code>"}, checkLocaleContent([]byte(`key = <code id="branch_targe">%[3]s</code>`))) - assert.EqualValues(t, []string{"key: <a\x1b[31m class=\"ui sh\"\x1b[0m href=\"https://TO-BE-REPLACED.COM\">"}, checkLocaleContent([]byte(`key = <a class="ui sh" href="%[3]s">`))) - assert.EqualValues(t, []string{"key: <a\x1b[31m class=\"js-click-me\"\x1b[0m href=\"https://TO-BE-REPLACED.COM\">"}, checkLocaleContent([]byte(`key = <a class="js-click-me" href="%[3]s">`))) - assert.EqualValues(t, []string{"key: <strong\x1b[31m class=\"branch-target\"\x1b[0m>%[1]s</strong>"}, checkLocaleContent([]byte(`key = <strong class="branch-target">%[1]s</strong>`))) + assert.Equal(t, []string{"workflow.dispatch.trigger_found: This workflow has a \x1b[31m<d>\x1b[0mworkflow_dispatch\x1b[31m</d>\x1b[0m event trigger."}, checkLocaleContent([]byte(`workflow.dispatch.trigger_found = This workflow has a <d>workflow_dispatch</d> event trigger.`))) + assert.Equal(t, []string{"key: <code\x1b[31m id=\"branch_targe\"\x1b[0m>%[3]s</code>"}, checkLocaleContent([]byte(`key = <code id="branch_targe">%[3]s</code>`))) + assert.Equal(t, []string{"key: <a\x1b[31m class=\"ui sh\"\x1b[0m href=\"https://TO-BE-REPLACED.COM\">"}, checkLocaleContent([]byte(`key = <a class="ui sh" href="%[3]s">`))) + assert.Equal(t, []string{"key: <a\x1b[31m class=\"js-click-me\"\x1b[0m href=\"https://TO-BE-REPLACED.COM\">"}, checkLocaleContent([]byte(`key = <a class="js-click-me" href="%[3]s">`))) + assert.Equal(t, []string{"key: <strong\x1b[31m class=\"branch-target\"\x1b[0m>%[1]s</strong>"}, checkLocaleContent([]byte(`key = <strong class="branch-target">%[1]s</strong>`))) }) t.Run("General safe tags", func(t *testing.T) { @@ -38,7 +38,7 @@ func TestLocalizationPolicy(t *testing.T) { assert.Empty(t, checkLocaleContent([]byte("sqlite_helper = File path for the SQLite3 database.<br>Enter an absolute path if you run Forgejo as a service."))) assert.Empty(t, checkLocaleContent([]byte("hi_user_x = Hi <b>%s</b>,"))) - assert.EqualValues(t, []string{"error404: The page you are trying to reach either <strong\x1b[31m title='aaa'\x1b[0m>does not exist</strong> or <strong>you are not authorized</strong> to view it."}, checkLocaleContent([]byte("error404 = The page you are trying to reach either <strong title='aaa'>does not exist</strong> or <strong>you are not authorized</strong> to view it."))) + assert.Equal(t, []string{"error404: The page you are trying to reach either <strong\x1b[31m title='aaa'\x1b[0m>does not exist</strong> or <strong>you are not authorized</strong> to view it."}, checkLocaleContent([]byte("error404 = The page you are trying to reach either <strong title='aaa'>does not exist</strong> or <strong>you are not authorized</strong> to view it."))) }) t.Run("<a>", func(t *testing.T) { @@ -47,20 +47,20 @@ func TestLocalizationPolicy(t *testing.T) { assert.Empty(t, checkLocaleContent([]byte(`webauthn_desc = Security keys are hardware devices containing cryptographic keys. They can be used for two-factor authentication. Security keys must support the <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a> standard.`))) assert.Empty(t, checkLocaleContent([]byte("issues.closed_at = `closed this issue <a id=\"%[1]s\" href=\"#%[1]s\">%[2]s</a>`"))) - assert.EqualValues(t, []string{"key: \x1b[31m<a href=\"https://example.com\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="https://example.com">`))) - assert.EqualValues(t, []string{"key: \x1b[31m<a href=\"javascript:alert('1')\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="javascript:alert('1')">`))) - assert.EqualValues(t, []string{"key: <a href=\"https://TO-BE-REPLACED.COM\"\x1b[31m download\x1b[0m>"}, checkLocaleContent([]byte(`key = <a href="%s" download>`))) - assert.EqualValues(t, []string{"key: <a href=\"https://TO-BE-REPLACED.COM\"\x1b[31m target=\"_self\"\x1b[0m>"}, checkLocaleContent([]byte(`key = <a href="%s" target="_self">`))) - assert.EqualValues(t, []string{"key: \x1b[31m<a href=\"https://example.com/%s\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="https://example.com/%s">`))) - assert.EqualValues(t, []string{"key: \x1b[31m<a href=\"https://example.com/?q=%s\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="https://example.com/?q=%s">`))) - assert.EqualValues(t, []string{"key: \x1b[31m<a href=\"%s/open-redirect\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="%s/open-redirect">`))) - assert.EqualValues(t, []string{"key: \x1b[31m<a href=\"%s?q=open-redirect\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="%s?q=open-redirect">`))) + assert.Equal(t, []string{"key: \x1b[31m<a href=\"https://example.com\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="https://example.com">`))) + assert.Equal(t, []string{"key: \x1b[31m<a href=\"javascript:alert('1')\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="javascript:alert('1')">`))) + assert.Equal(t, []string{"key: <a href=\"https://TO-BE-REPLACED.COM\"\x1b[31m download\x1b[0m>"}, checkLocaleContent([]byte(`key = <a href="%s" download>`))) + assert.Equal(t, []string{"key: <a href=\"https://TO-BE-REPLACED.COM\"\x1b[31m target=\"_self\"\x1b[0m>"}, checkLocaleContent([]byte(`key = <a href="%s" target="_self">`))) + assert.Equal(t, []string{"key: \x1b[31m<a href=\"https://example.com/%s\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="https://example.com/%s">`))) + assert.Equal(t, []string{"key: \x1b[31m<a href=\"https://example.com/?q=%s\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="https://example.com/?q=%s">`))) + assert.Equal(t, []string{"key: \x1b[31m<a href=\"%s/open-redirect\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="%s/open-redirect">`))) + assert.Equal(t, []string{"key: \x1b[31m<a href=\"%s?q=open-redirect\">\x1b[0m"}, checkLocaleContent([]byte(`key = <a href="%s?q=open-redirect">`))) }) t.Run("Escaped HTML characters", func(t *testing.T) { assert.Empty(t, checkLocaleContent([]byte("activity.git_stats_push_to_branch = `إلى %s و\"`"))) - assert.EqualValues(t, []string{"key: و\x1b[31m \x1b[0m\x1b[32m\u00a0\x1b[0m"}, checkLocaleContent([]byte(`key = و `))) + assert.Equal(t, []string{"key: و\x1b[31m \x1b[0m\x1b[32m\u00a0\x1b[0m"}, checkLocaleContent([]byte(`key = و `))) }) } @@ -75,7 +75,7 @@ func TestNextLocalizationPolicy(t *testing.T) { } }`))) - assert.EqualValues(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent([]byte(`{ + assert.Equal(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent([]byte(`{ "settings": { "hidden_comment_types_description": "\"<not-an-allowed-key> <label>\"" } @@ -87,8 +87,20 @@ func TestNextLocalizationPolicy(t *testing.T) { "settings.hidden_comment_types_description": "Comment types checked here will not be shown inside issue pages. Checking \"Label\" for example removes all \"<user> added/removed <label>\" comments." }`))) - assert.EqualValues(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent([]byte(`{ + assert.Equal(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent([]byte(`{ "settings.hidden_comment_types_description": "\"<not-an-allowed-key> <label>\"" }`))) }) + + t.Run("Plural form", func(t *testing.T) { + assert.Equal(t, []string{"repo.pulls.title_desc: key = \x1b[31m<a href=\"https://example.com\">\x1b[0m"}, checkLocaleNextContent([]byte(`{"repo.pulls.title_desc": { + "few": "key = <a href=\"%s\">", + "other": "key = <a href=\"https://example.com\">" + }}`))) + + assert.Equal(t, []string{"repo.pulls.title_desc.few: key = \x1b[31m<a href=\"https://example.com\">\x1b[0m"}, checkLocaleNextContent([]byte(`{"repo.pulls.title_desc": { + "few": "key = <a href=\"https://example.com\">", + "other": "key = <a href=\"%s\">" + }}`))) + }) } diff --git a/cmd/admin_auth_ldap_test.go b/cmd/admin_auth_ldap_test.go index 269af55d96..7ca0264454 100644 --- a/cmd/admin_auth_ldap_test.go +++ b/cmd/admin_auth_ldap_test.go @@ -216,11 +216,11 @@ func TestAddLdapBindDn(t *testing.T) { return nil }, updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { - assert.FailNow(t, "case %d: should not call updateAuthSource", n) + assert.FailNow(t, "should not call updateAuthSource", "case: %d", n) return nil }, getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { - assert.FailNow(t, "case %d: should not call getAuthSourceByID", n) + assert.FailNow(t, "should not call getAuthSourceByID", "case: %d", n) return nil, nil }, } @@ -447,11 +447,11 @@ func TestAddLdapSimpleAuth(t *testing.T) { return nil }, updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { - assert.FailNow(t, "case %d: should not call updateAuthSource", n) + assert.FailNow(t, "should not call updateAuthSource", "case: %d", n) return nil }, getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { - assert.FailNow(t, "case %d: should not call getAuthSourceByID", n) + assert.FailNow(t, "should not call getAuthSourceByID", "case: %d", n) return nil, nil }, } @@ -898,7 +898,7 @@ func TestUpdateLdapBindDn(t *testing.T) { return nil }, createAuthSource: func(ctx context.Context, authSource *auth.Source) error { - assert.FailNow(t, "case %d: should not call createAuthSource", n) + assert.FailNow(t, "should not call createAuthSource", "case: %d", n) return nil }, updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { @@ -1288,7 +1288,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { return nil }, createAuthSource: func(ctx context.Context, authSource *auth.Source) error { - assert.FailNow(t, "case %d: should not call createAuthSource", n) + assert.FailNow(t, "should not call createAuthSource", "case: %d", n) return nil }, updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { diff --git a/cmd/doctor.go b/cmd/doctor.go index b3420982f5..3807623ccd 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -20,7 +20,6 @@ import ( "forgejo.org/services/doctor" "github.com/urfave/cli/v2" - "xorm.io/xorm" ) // CmdDoctor represents the available doctor sub-command. @@ -120,7 +119,7 @@ func runRecreateTable(ctx *cli.Context) error { args := ctx.Args() names := make([]string, 0, ctx.NArg()) - for i := 0; i < ctx.NArg(); i++ { + for i := range ctx.NArg() { names = append(names, args.Get(i)) } @@ -130,11 +129,17 @@ func runRecreateTable(ctx *cli.Context) error { } recreateTables := migrate_base.RecreateTables(beans...) - return db.InitEngineWithMigration(stdCtx, func(x *xorm.Engine) error { - if err := migrations.EnsureUpToDate(x); err != nil { + return db.InitEngineWithMigration(stdCtx, func(x db.Engine) error { + engine, err := db.GetMasterEngine(x) + if err != nil { return err } - return recreateTables(x) + + if err := migrations.EnsureUpToDate(engine); err != nil { + return err + } + + return recreateTables(engine) }) } @@ -143,11 +148,12 @@ func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) { setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr) logFile := ctx.String("log-file") - if logFile == "" { + switch logFile { + case "": return // if no doctor log-file is set, do not show any log from default logger - } else if logFile == "-" { + case "-": setupConsoleLogger(log.TRACE, colorize, os.Stdout) - } else { + default: logFile, _ = filepath.Abs(logFile) writeMode := log.WriterMode{Level: log.TRACE, WriterOption: log.WriterFileOption{FileName: logFile}} writer, err := log.NewEventWriter("console-to-file", "file", writeMode) diff --git a/cmd/dump.go b/cmd/dump.go index ac72a2bb18..bc0b269924 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -122,7 +122,6 @@ It can be used for backup and capture Forgejo server image to send to maintainer &cli.StringFlag{ Name: "tempdir", Aliases: []string{"t"}, - Value: os.TempDir(), Usage: "Temporary dir path", }, &cli.StringFlag{ @@ -288,18 +287,31 @@ func runDump(ctx *cli.Context) error { } tmpDir := ctx.String("tempdir") + if tmpDir == "" { + tmpDir, err = os.MkdirTemp("", "forgejo-dump-*") + if err != nil { + fatal("Failed to create temporary directory: %v", err) + } + + defer func() { + if err := util.Remove(tmpDir); err != nil { + log.Warn("Failed to remove temporary directory: %s: Error: %v", tmpDir, err) + } + }() + } + if _, err := os.Stat(tmpDir); os.IsNotExist(err) { fatal("Path does not exist: %s", tmpDir) } dbDump, err := os.CreateTemp(tmpDir, "forgejo-db.sql") if err != nil { - fatal("Failed to create tmp file: %v", err) + fatal("Failed to create temporary file: %v", err) } defer func() { _ = dbDump.Close() if err := util.Remove(dbDump.Name()); err != nil { - log.Warn("Failed to remove temporary file: %s: Error: %v", dbDump.Name(), err) + log.Warn("Failed to remove temporary database file: %s: Error: %v", dbDump.Name(), err) } }() diff --git a/cmd/generate.go b/cmd/generate.go index 8d3c221ec5..dcbdcd0353 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -70,10 +70,7 @@ func runGenerateInternalToken(c *cli.Context) error { } func runGenerateLfsJwtSecret(c *cli.Context) error { - _, jwtSecretBase64, err := generate.NewJwtSecret() - if err != nil { - return err - } + _, jwtSecretBase64 := generate.NewJwtSecret() fmt.Printf("%s", jwtSecretBase64) diff --git a/cmd/hook.go b/cmd/hook.go index 1630c41edd..935c1b08ea 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -168,7 +168,7 @@ func runHookPreReceive(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), true) if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { if setting.OnlyAllowPushIfGiteaEnvironmentSet { @@ -220,10 +220,7 @@ Forgejo or set your environment appropriately.`, "") } } - supportProcReceive := false - if git.CheckGitVersionAtLeast("2.29") == nil { - supportProcReceive = true - } + supportProcReceive := git.CheckGitVersionAtLeast("2.29") == nil for scanner.Scan() { // TODO: support news feeds for wiki @@ -330,7 +327,7 @@ func runHookPostReceive(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), true) // First of all run update-server-info no matter what if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil { @@ -494,7 +491,7 @@ func runHookProcReceive(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), true) if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { if setting.OnlyAllowPushIfGiteaEnvironmentSet { diff --git a/cmd/keys.go b/cmd/keys.go index b12daee1bc..2d241984c4 100644 --- a/cmd/keys.go +++ b/cmd/keys.go @@ -71,7 +71,7 @@ func runKeys(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), true) authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content) // do not use handleCliResponseExtra or cli.NewExitError, if it exists immediately, it breaks some tests like Test_CmdKeys diff --git a/cmd/main.go b/cmd/main.go index 5f48814cd0..9d559e078f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -122,9 +122,9 @@ func NewMainApp(version, versionExtra string) *cli.App { } executable := filepath.Base(path) - var subCmdsStandalone []*cli.Command = make([]*cli.Command, 0, 10) - var subCmdWithConfig []*cli.Command = make([]*cli.Command, 0, 10) - var globalFlags []cli.Flag = make([]cli.Flag, 0, 10) + subCmdsStandalone := make([]*cli.Command, 0, 10) + subCmdWithConfig := make([]*cli.Command, 0, 10) + globalFlags := make([]cli.Flag, 0, 10) // // If the executable is forgejo-cli, provide a Forgejo specific CLI diff --git a/cmd/main_test.go b/cmd/main_test.go index 5e0b36d028..1ff71005e3 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -132,14 +132,14 @@ func TestCliCmdError(t *testing.T) { r, err := runTestApp(app, "./gitea", "test-cmd") require.Error(t, err) assert.Equal(t, 1, r.ExitCode) - assert.Equal(t, "", r.Stdout) + assert.Empty(t, r.Stdout) assert.Equal(t, "Command error: normal error\n", r.Stderr) app = newTestApp(func(ctx *cli.Context) error { return cli.Exit("exit error", 2) }) r, err = runTestApp(app, "./gitea", "test-cmd") require.Error(t, err) assert.Equal(t, 2, r.ExitCode) - assert.Equal(t, "", r.Stdout) + assert.Empty(t, r.Stdout) assert.Equal(t, "exit error\n", r.Stderr) app = newTestApp(func(ctx *cli.Context) error { return nil }) @@ -147,12 +147,12 @@ func TestCliCmdError(t *testing.T) { require.Error(t, err) assert.Equal(t, 1, r.ExitCode) assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stdout) - assert.Equal(t, "", r.Stderr) // the cli package's strange behavior, the error message is not in stderr .... + assert.Empty(t, r.Stderr) // the cli package's strange behavior, the error message is not in stderr .... app = newTestApp(func(ctx *cli.Context) error { return nil }) r, err = runTestApp(app, "./gitea", "test-cmd") require.NoError(t, err) assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called - assert.Equal(t, "", r.Stdout) - assert.Equal(t, "", r.Stderr) + assert.Empty(t, r.Stdout) + assert.Empty(t, r.Stderr) } diff --git a/cmd/manager.go b/cmd/manager.go index 0cb1e60f73..56089947fd 100644 --- a/cmd/manager.go +++ b/cmd/manager.go @@ -112,7 +112,7 @@ func runShutdown(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.Shutdown(ctx) return handleCliResponseExtra(extra) } @@ -121,7 +121,7 @@ func runRestart(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.Restart(ctx) return handleCliResponseExtra(extra) } @@ -130,7 +130,7 @@ func runReloadTemplates(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.ReloadTemplates(ctx) return handleCliResponseExtra(extra) } @@ -139,7 +139,7 @@ func runFlushQueues(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking")) return handleCliResponseExtra(extra) } @@ -148,7 +148,7 @@ func runProcesses(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.Processes(ctx, os.Stdout, c.Bool("flat"), c.Bool("no-system"), c.Bool("stacktraces"), c.Bool("json"), c.String("cancel")) return handleCliResponseExtra(extra) } diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go index ac2c1eb418..c6db2bb05a 100644 --- a/cmd/manager_logging.go +++ b/cmd/manager_logging.go @@ -199,7 +199,7 @@ func runRemoveLogger(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) logger := c.String("logger") if len(logger) == 0 { logger = log.DEFAULT @@ -214,7 +214,7 @@ func runAddConnLogger(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) vals := map[string]any{} mode := "conn" vals["net"] = "tcp" @@ -244,7 +244,7 @@ func runAddFileLogger(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) vals := map[string]any{} mode := "file" if c.IsSet("filename") { @@ -311,7 +311,7 @@ func runPauseLogging(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) userMsg := private.PauseLogging(ctx) _, _ = fmt.Fprintln(os.Stdout, userMsg) return nil @@ -321,7 +321,7 @@ func runResumeLogging(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) userMsg := private.ResumeLogging(ctx) _, _ = fmt.Fprintln(os.Stdout, userMsg) return nil @@ -331,7 +331,7 @@ func runReleaseReopenLogging(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) userMsg := private.ReleaseReopenLogging(ctx) _, _ = fmt.Fprintln(os.Stdout, userMsg) return nil @@ -340,7 +340,7 @@ func runReleaseReopenLogging(c *cli.Context) error { func runSetLogSQL(c *cli.Context) error { ctx, cancel := installSignals() defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.SetLogSQL(ctx, !c.Bool("off")) return handleCliResponseExtra(extra) diff --git a/cmd/migrate.go b/cmd/migrate.go index ab291cfb66..c192ca1966 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -36,7 +36,13 @@ func runMigrate(ctx *cli.Context) error { log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) - if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { + if err := db.InitEngineWithMigration(context.Background(), func(dbEngine db.Engine) error { + masterEngine, err := db.GetMasterEngine(dbEngine) + if err != nil { + return err + } + return migrations.Migrate(masterEngine) + }); err != nil { log.Fatal("Failed to initialize ORM engine: %v", err) return err } diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go index 1b839e7169..6a04dd48ae 100644 --- a/cmd/migrate_storage.go +++ b/cmd/migrate_storage.go @@ -23,6 +23,7 @@ import ( "forgejo.org/modules/storage" "github.com/urfave/cli/v2" + "xorm.io/xorm" ) // CmdMigrateStorage represents the available migrate storage sub-command. @@ -195,7 +196,9 @@ func runMigrateStorage(ctx *cli.Context) error { log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) - if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { + if err := db.InitEngineWithMigration(context.Background(), func(e db.Engine) error { + return migrations.Migrate(e.(*xorm.Engine)) + }); err != nil { log.Fatal("Failed to initialize ORM engine: %v", err) return err } diff --git a/cmd/migrate_storage_test.go b/cmd/migrate_storage_test.go index 28af63f4c8..b75eb3cfa0 100644 --- a/cmd/migrate_storage_test.go +++ b/cmd/migrate_storage_test.go @@ -81,8 +81,8 @@ func TestMigratePackages(t *testing.T) { entries, err := os.ReadDir(p) require.NoError(t, err) assert.Len(t, entries, 2) - assert.EqualValues(t, "01", entries[0].Name()) - assert.EqualValues(t, "tmp", entries[1].Name()) + assert.Equal(t, "01", entries[0].Name()) + assert.Equal(t, "tmp", entries[1].Name()) } func TestMigrateActionsArtifacts(t *testing.T) { diff --git a/cmd/serv.go b/cmd/serv.go index 0884d6c36b..4b288632d2 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -57,19 +57,22 @@ var CmdServ = &cli.Command{ }, } -func setup(ctx context.Context, debug bool) { +func setup(ctx context.Context, debug, gitNeeded bool) { if debug { setupConsoleLogger(log.TRACE, false, os.Stderr) } else { setupConsoleLogger(log.FATAL, false, os.Stderr) } setting.MustInstalled() + // Sanity check to ensure path is not relative, see: https://github.com/go-gitea/gitea/pull/19317 if _, err := os.Stat(setting.RepoRootPath); err != nil { _ = fail(ctx, "Unable to access repository path", "Unable to access repository path %q, err: %v", setting.RepoRootPath, err) return } - if err := git.InitSimple(context.Background()); err != nil { - _ = fail(ctx, "Failed to init git", "Failed to init git, err: %v", err) + if gitNeeded { + if err := git.InitSimple(context.Background()); err != nil { + _ = fail(ctx, "Failed to init git", "Failed to init git, err: %v", err) + } } } @@ -133,7 +136,7 @@ func runServ(c *cli.Context) error { defer cancel() // FIXME: This needs to internationalised - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), true) if setting.SSH.Disabled { fmt.Println("Forgejo: SSH has been disabled") @@ -253,11 +256,12 @@ func runServ(c *cli.Context) error { } if verb == lfsAuthenticateVerb { - if lfsVerb == "upload" { + switch lfsVerb { + case "upload": requestedMode = perm.AccessModeWrite - } else if lfsVerb == "download" { + case "download": requestedMode = perm.AccessModeRead - } else { + default: return fail(ctx, "Unknown LFS verb", "Unknown lfs verb %s", lfsVerb) } } diff --git a/cmd/web.go b/cmd/web.go index 3e7fdee4bf..1e67b2e922 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -198,9 +198,6 @@ func serveInstalled(ctx *cli.Context) error { for fn := range publicFilesSet.Seq() { log.Error("Found legacy public asset %q in CustomPath. Please move it to %s/public/assets/%s", fn, setting.CustomPath, fn) } - if _, err := os.Stat(filepath.Join(setting.CustomPath, "robots.txt")); err == nil { - log.Error(`Found legacy public asset "robots.txt" in CustomPath. Please move it to %s/public/robots.txt`, setting.CustomPath) - } routers.InitWebInstalled(graceful.GetManager().HammerContext()) diff --git a/eslint.config.mjs b/eslint.config.mjs index 17f461a8f4..f635a8b504 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -318,8 +318,9 @@ export default tseslint.config( 'no-jquery/no-data': [0], 'no-jquery/no-deferred': [2], 'no-jquery/no-delegate': [2], + 'no-jquery/no-done-fail': [2], 'no-jquery/no-each-collection': [0], - 'no-jquery/no-each-util': [0], + 'no-jquery/no-each-util': [2], 'no-jquery/no-each': [0], 'no-jquery/no-error-shorthand': [2], 'no-jquery/no-error': [2], @@ -331,6 +332,7 @@ export default tseslint.config( 'no-jquery/no-find-collection': [0], 'no-jquery/no-find-util': [2], 'no-jquery/no-find': [0], + 'no-jquery/no-fx': [2], 'no-jquery/no-fx-interval': [2], 'no-jquery/no-global-eval': [2], 'no-jquery/no-global-selector': [0], @@ -350,7 +352,7 @@ export default tseslint.config( 'no-jquery/no-live': [2], 'no-jquery/no-load-shorthand': [2], 'no-jquery/no-load': [2], - 'no-jquery/no-map-collection': [0], + 'no-jquery/no-map-collection': [2], 'no-jquery/no-map-util': [2], 'no-jquery/no-map': [2], 'no-jquery/no-merge': [2], @@ -374,12 +376,12 @@ export default tseslint.config( 'no-jquery/no-selector-prop': [2], 'no-jquery/no-serialize': [2], 'no-jquery/no-size': [2], - 'no-jquery/no-sizzle': [0], + 'no-jquery/no-sizzle': [2], 'no-jquery/no-slide': [2], 'no-jquery/no-sub': [2], 'no-jquery/no-support': [2], 'no-jquery/no-text': [0], - 'no-jquery/no-trigger': [0], + 'no-jquery/no-trigger': [2], 'no-jquery/no-trim': [2], 'no-jquery/no-type': [2], 'no-jquery/no-unique': [2], diff --git a/go.mod b/go.mod index 4284ff362b..12caf738bb 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module forgejo.org go 1.24 -toolchain go1.24.1 +toolchain go1.24.2 require ( code.forgejo.org/f3/gof3/v3 v3.10.6 @@ -14,7 +14,7 @@ require ( code.forgejo.org/go-chi/captcha v1.0.1 code.forgejo.org/go-chi/session v1.0.1 code.gitea.io/actions-proto-go v0.4.0 - code.gitea.io/sdk/gitea v0.20.0 + code.gitea.io/sdk/gitea v0.21.0 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 connectrpc.com/connect v1.17.0 github.com/42wim/httpsig v1.2.2 @@ -23,7 +23,7 @@ require ( github.com/ProtonMail/go-crypto v1.1.6 github.com/PuerkitoBio/goquery v1.10.2 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 - github.com/alecthomas/chroma/v2 v2.15.0 + github.com/alecthomas/chroma/v2 v2.16.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.4.4 github.com/buildkite/terminal-to-html/v3 v3.16.8 @@ -49,14 +49,14 @@ require ( github.com/go-openapi/spec v0.20.14 github.com/go-sql-driver/mysql v1.9.1 github.com/go-testfixtures/testfixtures/v3 v3.14.0 - github.com/go-webauthn/webauthn v0.12.2 + github.com/go-webauthn/webauthn v0.12.3 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 github.com/golang-jwt/jwt/v5 v5.2.2 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/google/go-github/v64 v64.0.0 - github.com/google/pprof v0.0.0-20241017200806-017d972448fc + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.2.0 github.com/gorilla/sessions v1.4.0 @@ -72,7 +72,7 @@ require ( github.com/lib/pq v1.10.9 github.com/markbates/goth v1.80.0 github.com/mattn/go-isatty v0.0.20 - github.com/mattn/go-sqlite3 v1.14.24 + github.com/mattn/go-sqlite3 v1.14.27 github.com/meilisearch/meilisearch-go v0.31.0 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.27 @@ -103,7 +103,7 @@ require ( go.uber.org/mock v0.5.0 golang.org/x/crypto v0.36.0 golang.org/x/image v0.25.0 - golang.org/x/net v0.37.0 + golang.org/x/net v0.38.0 golang.org/x/oauth2 v0.28.0 golang.org/x/sync v0.12.0 golang.org/x/sys v0.31.0 @@ -170,14 +170,14 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dlclark/regexp2 v1.11.4 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-enry/go-oniguruma v1.2.1 // indirect @@ -190,7 +190,7 @@ require ( github.com/go-openapi/jsonpointer v0.20.2 // indirect github.com/go-openapi/jsonreference v0.20.4 // indirect github.com/go-openapi/swag v0.22.7 // indirect - github.com/go-webauthn/x v0.1.19 // indirect + github.com/go-webauthn/x v0.1.20 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect diff --git a/go.sum b/go.sum index 67a1642186..61de25c8ea 100644 --- a/go.sum +++ b/go.sum @@ -636,8 +636,8 @@ code.forgejo.org/go-chi/session v1.0.1 h1:RNkcJQZJBqlvJoIFXSth87b3kMFZLDBA18Vcit code.forgejo.org/go-chi/session v1.0.1/go.mod h1:y69sjS984wc7k4xyu77yNE5HKeSlBoQW8VSGdsK7RAs= code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU= code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= -code.gitea.io/sdk/gitea v0.20.0 h1:Zm/QDwwZK1awoM4AxdjeAQbxolzx2rIP8dDfmKu+KoU= -code.gitea.io/sdk/gitea v0.20.0/go.mod h1:faouBHC/zyx5wLgjmRKR62ydyvMzwWf3QnU0bH7Cw6U= +code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4= +code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk= @@ -691,8 +691,8 @@ github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGW github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= -github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc= -github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio= +github.com/alecthomas/chroma/v2 v2.16.0 h1:QC5ZMizk67+HzxFDjQ4ASjni5kWBTGiigRG1u23IGvA= +github.com/alecthomas/chroma/v2 v2.16.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= @@ -835,8 +835,8 @@ github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmW github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= -github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4= @@ -895,8 +895,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= +github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 h1:j2TrkUG/NATGi/EQS+MvEoF79CxiRUmT16ErFroNcKI= github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9/go.mod h1:cJ9Ye0ZNSMN7RzZDBRY3E+8M3Bpf/R1JX22Ir9yX6WI= @@ -967,10 +967,10 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-testfixtures/testfixtures/v3 v3.14.0 h1:aRt5qyH2XjzFgCC5NizNs6QrzjO7rC4pQZ1oJpPIdo8= github.com/go-testfixtures/testfixtures/v3 v3.14.0/go.mod h1:HHb6Yd8spzm6aFZU6jwBj9qFvVUNNkx5nGbjG4UHeOE= -github.com/go-webauthn/webauthn v0.12.2 h1:yLaNPgBUEXDQtWnOjhsGhMMCEWbXwjg/aNkC8riJQI8= -github.com/go-webauthn/webauthn v0.12.2/go.mod h1:Q8SZPPj4sZ469fNTcQXxRpzJOdb30jQrn/36FX8jilA= -github.com/go-webauthn/x v0.1.19 h1:IUfdHiBRoTdujpBA/14qbrMXQ3LGzYe/PRGWdZcmudg= -github.com/go-webauthn/x v0.1.19/go.mod h1:C5arLuTQ3pVHKPw89v7CDGnqAZSZJj+4Jnr40dsn7tk= +github.com/go-webauthn/webauthn v0.12.3 h1:hHQl1xkUuabUU9uS+ISNCMLs9z50p9mDUZI/FmkayNE= +github.com/go-webauthn/webauthn v0.12.3/go.mod h1:4JRe8Z3W7HIw8NGEWn2fnUwecoDzkkeach/NnvhkqGY= +github.com/go-webauthn/x v0.1.20 h1:brEBDqfiPtNNCdS/peu8gARtq8fIPsHz0VzpPjGvgiw= +github.com/go-webauthn/x v0.1.20/go.mod h1:n/gAc8ssZJGATM0qThE+W+vfgXiMedsWi3wf/C4lld0= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= @@ -1091,8 +1091,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs= -github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= @@ -1252,8 +1252,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= -github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU= +github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY= github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g= github.com/mholt/acmez/v3 v3.1.1 h1:Jh+9uKHkPxUJdxM16q5mOr+G2V0aqkuFtNA28ihCxhQ= @@ -1647,8 +1647,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/models/actions/forgejo_test.go b/models/actions/forgejo_test.go index fc4ccfa628..5702068c1b 100644 --- a/models/actions/forgejo_test.go +++ b/models/actions/forgejo_test.go @@ -24,9 +24,9 @@ func TestActions_RegisterRunner_Token(t *testing.T) { version := "v1.2.3" runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version) require.NoError(t, err) - assert.EqualValues(t, name, runner.Name) + assert.Equal(t, name, runner.Name) - assert.EqualValues(t, 1, subtle.ConstantTimeCompare([]byte(runner.TokenHash), []byte(auth_model.HashToken(token, runner.TokenSalt))), "the token cannot be verified with the same method as routers/api/actions/runner/interceptor.go as of 8228751c55d6a4263f0fec2932ca16181c09c97d") + assert.Equal(t, 1, subtle.ConstantTimeCompare([]byte(runner.TokenHash), []byte(auth_model.HashToken(token, runner.TokenSalt))), "the token cannot be verified with the same method as routers/api/actions/runner/interceptor.go as of 8228751c55d6a4263f0fec2932ca16181c09c97d") } // TestActions_RegisterRunner_TokenUpdate tests that a token's secret is updated @@ -73,19 +73,19 @@ func TestActions_RegisterRunner_CreateWithLabels(t *testing.T) { require.NoError(t, err) // Check that the returned record has been updated, except for the labels - assert.EqualValues(t, ownerID, runner.OwnerID) - assert.EqualValues(t, repoID, runner.RepoID) - assert.EqualValues(t, name, runner.Name) - assert.EqualValues(t, version, runner.Version) - assert.EqualValues(t, labelsCopy, runner.AgentLabels) + assert.Equal(t, ownerID, runner.OwnerID) + assert.Equal(t, repoID, runner.RepoID) + assert.Equal(t, name, runner.Name) + assert.Equal(t, version, runner.Version) + assert.Equal(t, labelsCopy, runner.AgentLabels) // Check that whatever is in the DB has been updated, except for the labels after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: runner.ID}) - assert.EqualValues(t, ownerID, after.OwnerID) - assert.EqualValues(t, repoID, after.RepoID) - assert.EqualValues(t, name, after.Name) - assert.EqualValues(t, version, after.Version) - assert.EqualValues(t, labelsCopy, after.AgentLabels) + assert.Equal(t, ownerID, after.OwnerID) + assert.Equal(t, repoID, after.RepoID) + assert.Equal(t, name, after.Name) + assert.Equal(t, version, after.Version) + assert.Equal(t, labelsCopy, after.AgentLabels) } func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) { @@ -100,19 +100,19 @@ func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) { require.NoError(t, err) // Check that the returned record has been updated, except for the labels - assert.EqualValues(t, ownerID, runner.OwnerID) - assert.EqualValues(t, repoID, runner.RepoID) - assert.EqualValues(t, name, runner.Name) - assert.EqualValues(t, version, runner.Version) - assert.EqualValues(t, []string{}, runner.AgentLabels) + assert.Equal(t, ownerID, runner.OwnerID) + assert.Equal(t, repoID, runner.RepoID) + assert.Equal(t, name, runner.Name) + assert.Equal(t, version, runner.Version) + assert.Equal(t, []string{}, runner.AgentLabels) // Check that whatever is in the DB has been updated, except for the labels after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: runner.ID}) - assert.EqualValues(t, ownerID, after.OwnerID) - assert.EqualValues(t, repoID, after.RepoID) - assert.EqualValues(t, name, after.Name) - assert.EqualValues(t, version, after.Version) - assert.EqualValues(t, []string{}, after.AgentLabels) + assert.Equal(t, ownerID, after.OwnerID) + assert.Equal(t, repoID, after.RepoID) + assert.Equal(t, name, after.Name) + assert.Equal(t, version, after.Version) + assert.Equal(t, []string{}, after.AgentLabels) } func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) { @@ -132,19 +132,19 @@ func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) { require.NoError(t, err) // Check that the returned record has been updated - assert.EqualValues(t, newOwnerID, runner.OwnerID) - assert.EqualValues(t, newRepoID, runner.RepoID) - assert.EqualValues(t, newName, runner.Name) - assert.EqualValues(t, newVersion, runner.Version) - assert.EqualValues(t, labelsCopy, runner.AgentLabels) + assert.Equal(t, newOwnerID, runner.OwnerID) + assert.Equal(t, newRepoID, runner.RepoID) + assert.Equal(t, newName, runner.Name) + assert.Equal(t, newVersion, runner.Version) + assert.Equal(t, labelsCopy, runner.AgentLabels) // Check that whatever is in the DB has been updated after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) - assert.EqualValues(t, newOwnerID, after.OwnerID) - assert.EqualValues(t, newRepoID, after.RepoID) - assert.EqualValues(t, newName, after.Name) - assert.EqualValues(t, newVersion, after.Version) - assert.EqualValues(t, labelsCopy, after.AgentLabels) + assert.Equal(t, newOwnerID, after.OwnerID) + assert.Equal(t, newRepoID, after.RepoID) + assert.Equal(t, newName, after.Name) + assert.Equal(t, newVersion, after.Version) + assert.Equal(t, labelsCopy, after.AgentLabels) } func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) { @@ -162,17 +162,17 @@ func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) { require.NoError(t, err) // Check that the returned record has been updated, except for the labels - assert.EqualValues(t, newOwnerID, runner.OwnerID) - assert.EqualValues(t, newRepoID, runner.RepoID) - assert.EqualValues(t, newName, runner.Name) - assert.EqualValues(t, newVersion, runner.Version) - assert.EqualValues(t, before.AgentLabels, runner.AgentLabels) + assert.Equal(t, newOwnerID, runner.OwnerID) + assert.Equal(t, newRepoID, runner.RepoID) + assert.Equal(t, newName, runner.Name) + assert.Equal(t, newVersion, runner.Version) + assert.Equal(t, before.AgentLabels, runner.AgentLabels) // Check that whatever is in the DB has been updated, except for the labels after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) - assert.EqualValues(t, newOwnerID, after.OwnerID) - assert.EqualValues(t, newRepoID, after.RepoID) - assert.EqualValues(t, newName, after.Name) - assert.EqualValues(t, newVersion, after.Version) - assert.EqualValues(t, before.AgentLabels, after.AgentLabels) + assert.Equal(t, newOwnerID, after.OwnerID) + assert.Equal(t, newRepoID, after.RepoID) + assert.Equal(t, newName, after.Name) + assert.Equal(t, newVersion, after.Version) + assert.Equal(t, before.AgentLabels, after.AgentLabels) } diff --git a/models/actions/runner.go b/models/actions/runner.go index 99173000fb..4d5056b425 100644 --- a/models/actions/runner.go +++ b/models/actions/runner.go @@ -87,9 +87,10 @@ func (r *ActionRunner) BelongsToOwnerType() types.OwnerType { return types.OwnerTypeRepository } if r.OwnerID != 0 { - if r.Owner.Type == user_model.UserTypeOrganization { + switch r.Owner.Type { + case user_model.UserTypeOrganization: return types.OwnerTypeOrganization - } else if r.Owner.Type == user_model.UserTypeIndividual { + case user_model.UserTypeIndividual: return types.OwnerTypeIndividual } } @@ -167,12 +168,7 @@ func (r *ActionRunner) GenerateToken() (err error) { // UpdateSecret updates the hash based on the specified token. It does not // ensure that the runner's UUID matches the first 16 bytes of the token. func (r *ActionRunner) UpdateSecret(token string) error { - saltBytes, err := util.CryptoRandomBytes(16) - if err != nil { - return fmt.Errorf("CryptoRandomBytes %v", err) - } - - salt := hex.EncodeToString(saltBytes) + salt := hex.EncodeToString(util.CryptoRandomBytes(16)) r.Token = token r.TokenSalt = salt diff --git a/models/actions/runner_token_test.go b/models/actions/runner_token_test.go index 65d83a8fd0..0de9ca5648 100644 --- a/models/actions/runner_token_test.go +++ b/models/actions/runner_token_test.go @@ -18,7 +18,7 @@ func TestGetLatestRunnerToken(t *testing.T) { token := unittest.AssertExistsAndLoadBean(t, &ActionRunnerToken{ID: 3}) expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0) require.NoError(t, err) - assert.EqualValues(t, expectedToken, token) + assert.Equal(t, expectedToken, token) } func TestNewRunnerToken(t *testing.T) { @@ -27,7 +27,7 @@ func TestNewRunnerToken(t *testing.T) { require.NoError(t, err) expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0) require.NoError(t, err) - assert.EqualValues(t, expectedToken, token) + assert.Equal(t, expectedToken, token) } func TestUpdateRunnerToken(t *testing.T) { @@ -37,5 +37,5 @@ func TestUpdateRunnerToken(t *testing.T) { require.NoError(t, UpdateRunnerToken(db.DefaultContext, token)) expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0) require.NoError(t, err) - assert.EqualValues(t, expectedToken, token) + assert.Equal(t, expectedToken, token) } diff --git a/models/actions/utils.go b/models/actions/utils.go index 7dd3f7ec12..d8e053b0ba 100644 --- a/models/actions/utils.go +++ b/models/actions/utils.go @@ -22,11 +22,7 @@ func generateSaltedToken() (string, string, string, string, error) { if err != nil { return "", "", "", "", err } - buf, err := util.CryptoRandomBytes(20) - if err != nil { - return "", "", "", "", err - } - token := hex.EncodeToString(buf) + token := hex.EncodeToString(util.CryptoRandomBytes(20)) hash := auth_model.HashToken(token, salt) return token, salt, hash, token[len(token)-8:], nil } diff --git a/models/activities/action_test.go b/models/activities/action_test.go index ebc40cffa5..8b9b2f6929 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -56,7 +56,7 @@ func TestGetFeeds(t *testing.T) { require.NoError(t, err) if assert.Len(t, actions, 1) { assert.EqualValues(t, 1, actions[0].ID) - assert.EqualValues(t, user.ID, actions[0].UserID) + assert.Equal(t, user.ID, actions[0].UserID) } assert.Equal(t, int64(1), count) @@ -133,7 +133,7 @@ func TestGetFeeds2(t *testing.T) { assert.Len(t, actions, 1) if assert.Len(t, actions, 1) { assert.EqualValues(t, 2, actions[0].ID) - assert.EqualValues(t, org.ID, actions[0].UserID) + assert.Equal(t, org.ID, actions[0].UserID) } assert.Equal(t, int64(1), count) @@ -291,7 +291,7 @@ func TestDeleteIssueActions(t *testing.T) { // load an issue issue := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 4}) - assert.NotEqualValues(t, issue.ID, issue.Index) // it needs to use different ID/Index to test the DeleteIssueActions to delete some actions by IssueIndex + assert.NotEqual(t, issue.ID, issue.Index) // it needs to use different ID/Index to test the DeleteIssueActions to delete some actions by IssueIndex // insert a comment err := db.Insert(db.DefaultContext, &issue_model.Comment{Type: issue_model.CommentTypeComment, IssueID: issue.ID}) diff --git a/models/activities/notification_list.go b/models/activities/notification_list.go index 9b09dde7ab..9b266f5d96 100644 --- a/models/activities/notification_list.go +++ b/models/activities/notification_list.go @@ -107,7 +107,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n return err } toNotify.AddMultiple(issueWatches...) - if !(issue.IsPull && issues_model.HasWorkInProgressPrefix(issue.Title)) { + if !issue.IsPull || !issues_model.HasWorkInProgressPrefix(issue.Title) { repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID) if err != nil { return err diff --git a/models/activities/notification_test.go b/models/activities/notification_test.go index 305a2ae430..dfce92d491 100644 --- a/models/activities/notification_test.go +++ b/models/activities/notification_test.go @@ -45,11 +45,11 @@ func TestNotificationsForUser(t *testing.T) { require.NoError(t, err) if assert.Len(t, notfs, 3) { assert.EqualValues(t, 5, notfs[0].ID) - assert.EqualValues(t, user.ID, notfs[0].UserID) + assert.Equal(t, user.ID, notfs[0].UserID) assert.EqualValues(t, 4, notfs[1].ID) - assert.EqualValues(t, user.ID, notfs[1].UserID) + assert.Equal(t, user.ID, notfs[1].UserID) assert.EqualValues(t, 2, notfs[2].ID) - assert.EqualValues(t, user.ID, notfs[2].UserID) + assert.Equal(t, user.ID, notfs[2].UserID) } } @@ -59,7 +59,7 @@ func TestNotification_GetRepo(t *testing.T) { repo, err := notf.GetRepo(db.DefaultContext) require.NoError(t, err) assert.Equal(t, repo, notf.Repository) - assert.EqualValues(t, notf.RepoID, repo.ID) + assert.Equal(t, notf.RepoID, repo.ID) } func TestNotification_GetIssue(t *testing.T) { @@ -68,7 +68,7 @@ func TestNotification_GetIssue(t *testing.T) { issue, err := notf.GetIssue(db.DefaultContext) require.NoError(t, err) assert.Equal(t, issue, notf.Issue) - assert.EqualValues(t, notf.IssueID, issue.ID) + assert.Equal(t, notf.IssueID, issue.ID) } func TestGetNotificationCount(t *testing.T) { @@ -137,5 +137,5 @@ func TestSetIssueReadBy(t *testing.T) { nt, err := activities_model.GetIssueNotification(db.DefaultContext, user.ID, issue.ID) require.NoError(t, err) - assert.EqualValues(t, activities_model.NotificationStatusRead, nt.Status) + assert.Equal(t, activities_model.NotificationStatusRead, nt.Status) } diff --git a/models/activities/repo_activity_test.go b/models/activities/repo_activity_test.go index c111c50208..ce930ada2d 100644 --- a/models/activities/repo_activity_test.go +++ b/models/activities/repo_activity_test.go @@ -23,8 +23,8 @@ func TestGetActivityStats(t *testing.T) { stats, err := GetActivityStats(db.DefaultContext, repo, time.Unix(0, 0), true, true, true, true) require.NoError(t, err) - assert.EqualValues(t, 2, stats.ActiveIssueCount()) - assert.EqualValues(t, 2, stats.OpenedIssueCount()) - assert.EqualValues(t, 0, stats.ClosedIssueCount()) - assert.EqualValues(t, 3, stats.ActivePRCount()) + assert.Equal(t, 2, stats.ActiveIssueCount()) + assert.Equal(t, 2, stats.OpenedIssueCount()) + assert.Equal(t, 0, stats.ClosedIssueCount()) + assert.Equal(t, 3, stats.ActivePRCount()) } diff --git a/models/asymkey/gpg_key_test.go b/models/asymkey/gpg_key_test.go index 4db07b84c2..d265f438eb 100644 --- a/models/asymkey/gpg_key_test.go +++ b/models/asymkey/gpg_key_test.go @@ -293,7 +293,7 @@ heiQvzkApQup5c+BhH5zFDFdKJ2CBByxw9+7QjMFI/wgLixKuE0Ob2kAokXf7RlB require.NoError(t, err) assert.Len(t, keys, 1) assert.Len(t, keys[0].Emails, 1) - assert.EqualValues(t, "no-reply@golang.com", keys[0].Emails[0].Email) + assert.Equal(t, "no-reply@golang.com", keys[0].Emails[0].Email) primaryKeyID := "D68172F48E9C5283" // Assert primary key diff --git a/models/asymkey/ssh_key_test.go b/models/asymkey/ssh_key_test.go index f3c3e41955..bc8cd7d36b 100644 --- a/models/asymkey/ssh_key_test.go +++ b/models/asymkey/ssh_key_test.go @@ -44,7 +44,7 @@ func Test_SSHParsePublicKey(t *testing.T) { keyTypeN, lengthN, err := SSHNativeParsePublicKey(tc.content) require.NoError(t, err) assert.Equal(t, tc.keyType, keyTypeN) - assert.EqualValues(t, tc.length, lengthN) + assert.Equal(t, tc.length, lengthN) }) if tc.skipSSHKeygen { return @@ -54,19 +54,19 @@ func Test_SSHParsePublicKey(t *testing.T) { if err != nil { // Some servers do not support ecdsa format. if !strings.Contains(err.Error(), "line 1 too long:") { - assert.FailNow(t, "%v", err) + require.NoError(t, err) } } assert.Equal(t, tc.keyType, keyTypeK) - assert.EqualValues(t, tc.length, lengthK) + assert.Equal(t, tc.length, lengthK) }) t.Run("SSHParseKeyNative", func(t *testing.T) { keyTypeK, lengthK, err := SSHNativeParsePublicKey(tc.content) if err != nil { - assert.FailNow(t, "%v", err) + require.NoError(t, err) } assert.Equal(t, tc.keyType, keyTypeK) - assert.EqualValues(t, tc.length, lengthK) + assert.Equal(t, tc.length, lengthK) }) }) } diff --git a/models/auth/access_token.go b/models/auth/access_token.go index 31d88c6b20..695702b7a0 100644 --- a/models/auth/access_token.go +++ b/models/auth/access_token.go @@ -111,12 +111,8 @@ func generateAccessToken(t *AccessToken) error { if err != nil { return err } - token, err := util.CryptoRandomBytes(20) - if err != nil { - return err - } t.TokenSalt = salt - t.Token = hex.EncodeToString(token) + t.Token = hex.EncodeToString(util.CryptoRandomBytes(20)) t.TokenHash = HashToken(t.Token, t.TokenSalt) t.TokenLastEight = t.Token[len(t.Token)-8:] return nil diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go index a3ac9c4c1a..d09aebcf85 100644 --- a/models/auth/auth_token.go +++ b/models/auth/auth_token.go @@ -63,10 +63,7 @@ func (authToken *AuthorizationToken) IsExpired() bool { func GenerateAuthToken(ctx context.Context, userID int64, expiry timeutil.TimeStamp, purpose AuthorizationPurpose) (lookupKey, validator string, err error) { // Request 64 random bytes. The first 32 bytes will be used for the lookupKey // and the other 32 bytes will be used for the validator. - rBytes, err := util.CryptoRandomBytes(64) - if err != nil { - return "", "", err - } + rBytes := util.CryptoRandomBytes(64) hexEncoded := hex.EncodeToString(rBytes) validator, lookupKey = hexEncoded[64:], hexEncoded[:64] diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index fb0a451566..fa68197cf0 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -184,13 +184,9 @@ var base32Lower = base32.NewEncoding(lowerBase32Chars).WithPadding(base32.NoPadd // GenerateClientSecret will generate the client secret and returns the plaintext and saves the hash at the database func (app *OAuth2Application) GenerateClientSecret(ctx context.Context) (string, error) { - rBytes, err := util.CryptoRandomBytes(32) - if err != nil { - return "", err - } // Add a prefix to the base32, this is in order to make it easier // for code scanners to grab sensitive tokens. - clientSecret := "gto_" + base32Lower.EncodeToString(rBytes) + clientSecret := "gto_" + base32Lower.EncodeToString(util.CryptoRandomBytes(32)) hashedSecret, err := bcrypt.GenerateFromPassword([]byte(clientSecret), bcrypt.DefaultCost) if err != nil { @@ -475,13 +471,9 @@ func (grant *OAuth2Grant) TableName() string { // GenerateNewAuthorizationCode generates a new authorization code for a grant and saves it to the database func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redirectURI, codeChallenge, codeChallengeMethod string) (code *OAuth2AuthorizationCode, err error) { - rBytes, err := util.CryptoRandomBytes(32) - if err != nil { - return &OAuth2AuthorizationCode{}, err - } // Add a prefix to the base32, this is in order to make it easier // for code scanners to grab sensitive tokens. - codeSecret := "gta_" + base32Lower.EncodeToString(rBytes) + codeSecret := "gta_" + base32Lower.EncodeToString(util.CryptoRandomBytes(32)) code = &OAuth2AuthorizationCode{ Grant: grant, diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go index 877541e0ff..7ad64b3f9f 100644 --- a/models/auth/oauth2_test.go +++ b/models/auth/oauth2_test.go @@ -130,7 +130,7 @@ func TestOAuth2Application_CreateGrant(t *testing.T) { assert.NotNil(t, grant) assert.Equal(t, int64(2), grant.UserID) assert.Equal(t, int64(1), grant.ApplicationID) - assert.Equal(t, "", grant.Scope) + assert.Empty(t, grant.Scope) } //////////////////// Grant @@ -271,7 +271,7 @@ func TestOAuth2AuthorizationCode_TableName(t *testing.T) { func TestBuiltinApplicationsClientIDs(t *testing.T) { clientIDs := auth_model.BuiltinApplicationsClientIDs() slices.Sort(clientIDs) - assert.EqualValues(t, []string{"a4792ccc-144e-407e-86c9-5e7d8d9c3269", "d57cb8c4-630c-4168-8324-ec79935e18d4", "e90ee53c-94e2-48ac-9358-a874fb9e0662"}, clientIDs) + assert.Equal(t, []string{"a4792ccc-144e-407e-86c9-5e7d8d9c3269", "d57cb8c4-630c-4168-8324-ec79935e18d4", "e90ee53c-94e2-48ac-9358-a874fb9e0662"}, clientIDs) } func TestOrphanedOAuth2Applications(t *testing.T) { diff --git a/models/auth/session_test.go b/models/auth/session_test.go index ab6415f289..0f07038187 100644 --- a/models/auth/session_test.go +++ b/models/auth/session_test.go @@ -37,7 +37,7 @@ func TestAuthSession(t *testing.T) { // New session is created. sess, err := auth.ReadSession(db.DefaultContext, key) require.NoError(t, err) - assert.EqualValues(t, key, sess.Key) + assert.Equal(t, key, sess.Key) assert.Empty(t, sess.Data) assert.EqualValues(t, now.Unix(), sess.Expiry) @@ -67,8 +67,8 @@ func TestAuthSession(t *testing.T) { // Ensure data is updated and expiry is set from the update session call. sess, err := auth.ReadSession(db.DefaultContext, key) require.NoError(t, err) - assert.EqualValues(t, key, sess.Key) - assert.EqualValues(t, data, sess.Data) + assert.Equal(t, key, sess.Key) + assert.Equal(t, data, sess.Data) assert.EqualValues(t, now.Unix(), sess.Expiry) timeutil.MockSet(now) diff --git a/models/auth/twofactor.go b/models/auth/twofactor.go index f4c1e36b3f..fa3bc68781 100644 --- a/models/auth/twofactor.go +++ b/models/auth/twofactor.go @@ -61,17 +61,13 @@ func init() { } // GenerateScratchToken recreates the scratch token the user is using. -func (t *TwoFactor) GenerateScratchToken() (string, error) { - tokenBytes, err := util.CryptoRandomBytes(6) - if err != nil { - return "", err - } +func (t *TwoFactor) GenerateScratchToken() string { // these chars are specially chosen, avoid ambiguous chars like `0`, `O`, `1`, `I`. const base32Chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" - token := base32.NewEncoding(base32Chars).WithPadding(base32.NoPadding).EncodeToString(tokenBytes) + token := base32.NewEncoding(base32Chars).WithPadding(base32.NoPadding).EncodeToString(util.CryptoRandomBytes(6)) t.ScratchSalt, _ = util.CryptoRandomString(10) t.ScratchHash = HashToken(token, t.ScratchSalt) - return token, nil + return token } // HashToken return the hashable salt diff --git a/models/db/engine.go b/models/db/engine.go index ca6576da8a..7283b1d516 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -95,34 +95,70 @@ func init() { } } -// newXORMEngine returns a new XORM engine from the configuration -func newXORMEngine() (*xorm.Engine, error) { - connStr, err := setting.DBConnStr() +// newXORMEngineGroup creates an xorm.EngineGroup (with one master and one or more slaves). +// It assumes you have separate master and slave DSNs defined via the settings package. +func newXORMEngineGroup() (Engine, error) { + // Retrieve master DSN from settings. + masterConnStr, err := setting.DBMasterConnStr() if err != nil { - return nil, err + return nil, fmt.Errorf("failed to determine master DSN: %w", err) } - var engine *xorm.Engine - + var masterEngine *xorm.Engine + // For PostgreSQL: if a schema is provided, we use the special "postgresschema" driver. if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 { - // OK whilst we sort out our schema issues - create a schema aware postgres registerPostgresSchemaDriver() - engine, err = xorm.NewEngine("postgresschema", connStr) + masterEngine, err = xorm.NewEngine("postgresschema", masterConnStr) } else { - engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr) + masterEngine, err = xorm.NewEngine(setting.Database.Type.String(), masterConnStr) } - if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create master engine: %w", err) } if setting.Database.Type.IsMySQL() { - engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"}) + masterEngine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"}) } - engine.SetSchema(setting.Database.Schema) - return engine, nil + masterEngine.SetSchema(setting.Database.Schema) + + slaveConnStrs, err := setting.DBSlaveConnStrs() + if err != nil { + return nil, fmt.Errorf("failed to load slave DSNs: %w", err) + } + + var slaveEngines []*xorm.Engine + // Iterate over all slave DSNs and create engines + for _, dsn := range slaveConnStrs { + slaveEngine, err := xorm.NewEngine(setting.Database.Type.String(), dsn) + if err != nil { + return nil, fmt.Errorf("failed to create slave engine for dsn %q: %w", dsn, err) + } + if setting.Database.Type.IsMySQL() { + slaveEngine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"}) + } + slaveEngine.SetSchema(setting.Database.Schema) + slaveEngines = append(slaveEngines, slaveEngine) + } + + policy := setting.BuildLoadBalancePolicy(&setting.Database, slaveEngines) + + // Create the EngineGroup using the selected policy + group, err := xorm.NewEngineGroup(masterEngine, slaveEngines, policy) + if err != nil { + return nil, fmt.Errorf("failed to create engine group: %w", err) + } + return engineGroupWrapper{group}, nil } -// SyncAllTables sync the schemas of all tables, is required by unit test code +type engineGroupWrapper struct { + *xorm.EngineGroup +} + +func (w engineGroupWrapper) AddHook(hook contexts.Hook) bool { + w.EngineGroup.AddHook(hook) + return true +} + +// SyncAllTables sync the schemas of all tables func SyncAllTables() error { _, err := x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{ WarnIfDatabaseColumnMissed: true, @@ -130,52 +166,61 @@ func SyncAllTables() error { return err } -// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext +// InitEngine initializes the xorm EngineGroup and sets it as db.DefaultContext func InitEngine(ctx context.Context) error { - xormEngine, err := newXORMEngine() + xormEngine, err := newXORMEngineGroup() if err != nil { return fmt.Errorf("failed to connect to database: %w", err) } + // Try to cast to the concrete type to access diagnostic methods + if eng, ok := xormEngine.(engineGroupWrapper); ok { + eng.SetMapper(names.GonicMapper{}) + // WARNING: for serv command, MUST remove the output to os.Stdout, + // so use a log file instead of printing to stdout. + eng.SetLogger(NewXORMLogger(setting.Database.LogSQL)) + eng.ShowSQL(setting.Database.LogSQL) + eng.SetMaxOpenConns(setting.Database.MaxOpenConns) + eng.SetMaxIdleConns(setting.Database.MaxIdleConns) + eng.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) + eng.SetConnMaxIdleTime(setting.Database.ConnMaxIdleTime) + eng.SetDefaultContext(ctx) - xormEngine.SetMapper(names.GonicMapper{}) - // WARNING: for serv command, MUST remove the output to os.stdout, - // so use log file to instead print to stdout. - xormEngine.SetLogger(NewXORMLogger(setting.Database.LogSQL)) - xormEngine.ShowSQL(setting.Database.LogSQL) - xormEngine.SetMaxOpenConns(setting.Database.MaxOpenConns) - xormEngine.SetMaxIdleConns(setting.Database.MaxIdleConns) - xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) - xormEngine.SetConnMaxIdleTime(setting.Database.ConnMaxIdleTime) - xormEngine.SetDefaultContext(ctx) + if setting.Database.SlowQueryThreshold > 0 { + eng.AddHook(&SlowQueryHook{ + Treshold: setting.Database.SlowQueryThreshold, + Logger: log.GetLogger("xorm"), + }) + } - if setting.Database.SlowQueryThreshold > 0 { - xormEngine.AddHook(&SlowQueryHook{ - Treshold: setting.Database.SlowQueryThreshold, - Logger: log.GetLogger("xorm"), + errorLogger := log.GetLogger("xorm") + if setting.IsInTesting { + errorLogger = log.GetLogger(log.DEFAULT) + } + + eng.AddHook(&ErrorQueryHook{ + Logger: errorLogger, }) + + eng.AddHook(&TracingHook{}) + + SetDefaultEngine(ctx, eng) + } else { + // Fallback: if type assertion fails, set default engine without extended diagnostics + SetDefaultEngine(ctx, xormEngine) } - - errorLogger := log.GetLogger("xorm") - if setting.IsInTesting { - errorLogger = log.GetLogger(log.DEFAULT) - } - - xormEngine.AddHook(&ErrorQueryHook{ - Logger: errorLogger, - }) - - xormEngine.AddHook(&TracingHook{}) - - SetDefaultEngine(ctx, xormEngine) return nil } -// SetDefaultEngine sets the default engine for db -func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) { - x = eng +// SetDefaultEngine sets the default engine for db. +func SetDefaultEngine(ctx context.Context, eng Engine) { + masterEngine, err := GetMasterEngine(eng) + if err == nil { + x = masterEngine + } + DefaultContext = &Context{ Context: ctx, - e: x, + e: eng, } } @@ -191,12 +236,12 @@ func UnsetDefaultEngine() { DefaultContext = nil } -// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext +// InitEngineWithMigration initializes a new xorm EngineGroup, runs migrations, and sets it as db.DefaultContext // This function must never call .Sync() if the provided migration function fails. // When called from the "doctor" command, the migration function is a version check // that prevents the doctor from fixing anything in the database if the migration level // is different from the expected value. -func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) { +func InitEngineWithMigration(ctx context.Context, migrateFunc func(Engine) error) (err error) { if err = InitEngine(ctx); err != nil { return err } @@ -230,14 +275,14 @@ func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) return nil } -// NamesToBean return a list of beans or an error +// NamesToBean returns a list of beans given names func NamesToBean(names ...string) ([]any, error) { beans := []any{} if len(names) == 0 { beans = append(beans, tables...) return beans, nil } - // Need to map provided names to beans... + // Map provided names to beans beanMap := make(map[string]any) for _, bean := range tables { beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean @@ -259,7 +304,7 @@ func NamesToBean(names ...string) ([]any, error) { return beans, nil } -// DumpDatabase dumps all data from database according the special database SQL syntax to file system. +// DumpDatabase dumps all data from database using special SQL syntax to the file system. func DumpDatabase(filePath, dbType string) error { var tbs []*schemas.Table for _, t := range tables { @@ -295,29 +340,33 @@ func MaxBatchInsertSize(bean any) int { return 999 / len(t.ColumnsSeq()) } -// IsTableNotEmpty returns true if table has at least one record +// IsTableNotEmpty returns true if the table has at least one record func IsTableNotEmpty(beanOrTableName any) (bool, error) { return x.Table(beanOrTableName).Exist() } -// DeleteAllRecords will delete all the records of this table +// DeleteAllRecords deletes all records in the given table. func DeleteAllRecords(tableName string) error { _, err := x.Exec(fmt.Sprintf("DELETE FROM %s", tableName)) return err } -// GetMaxID will return max id of the table +// GetMaxID returns the maximum id in the table func GetMaxID(beanOrTableName any) (maxID int64, err error) { _, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID) return maxID, err } func SetLogSQL(ctx context.Context, on bool) { - e := GetEngine(ctx) - if x, ok := e.(*xorm.Engine); ok { - x.ShowSQL(on) - } else if sess, ok := e.(*xorm.Session); ok { + ctxEngine := GetEngine(ctx) + + if sess, ok := ctxEngine.(*xorm.Session); ok { sess.Engine().ShowSQL(on) + } else if wrapper, ok := ctxEngine.(engineGroupWrapper); ok { + // Handle engineGroupWrapper directly + wrapper.ShowSQL(on) + } else if masterEngine, err := GetMasterEngine(ctxEngine); err == nil { + masterEngine.ShowSQL(on) } } @@ -374,3 +423,18 @@ func (h *ErrorQueryHook) AfterProcess(c *contexts.ContextHook) error { } return nil } + +// GetMasterEngine extracts the master xorm.Engine from the provided xorm.Engine. +// This handles both direct xorm.Engine cases and engines that implement a Master() method. +func GetMasterEngine(x Engine) (*xorm.Engine, error) { + if getter, ok := x.(interface{ Master() *xorm.Engine }); ok { + return getter.Master(), nil + } + + engine, ok := x.(*xorm.Engine) + if !ok { + return nil, fmt.Errorf("unsupported engine type: %T", x) + } + + return engine, nil +} diff --git a/models/db/engine_test.go b/models/db/engine_test.go index 5d20e3d602..036ab88945 100644 --- a/models/db/engine_test.go +++ b/models/db/engine_test.go @@ -56,7 +56,7 @@ func TestDeleteOrphanedObjects(t *testing.T) { countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{}) require.NoError(t, err) - assert.EqualValues(t, countBefore, countAfter) + assert.Equal(t, countBefore, countAfter) } func TestPrimaryKeys(t *testing.T) { diff --git a/models/db/index_test.go b/models/db/index_test.go index 929e514329..b64a816bd2 100644 --- a/models/db/index_test.go +++ b/models/db/index_test.go @@ -33,10 +33,11 @@ func getCurrentResourceIndex(ctx context.Context, tableName string, groupID int6 func TestSyncMaxResourceIndex(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - xe := unittest.GetXORMEngine() + xe, err := unittest.GetXORMEngine() + require.NoError(t, err) require.NoError(t, xe.Sync(&TestIndex{})) - err := db.SyncMaxResourceIndex(db.DefaultContext, "test_index", 10, 51) + err = db.SyncMaxResourceIndex(db.DefaultContext, "test_index", 10, 51) require.NoError(t, err) // sync new max index @@ -88,7 +89,8 @@ func TestSyncMaxResourceIndex(t *testing.T) { func TestGetNextResourceIndex(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - xe := unittest.GetXORMEngine() + xe, err := unittest.GetXORMEngine() + require.NoError(t, err) require.NoError(t, xe.Sync(&TestIndex{})) // create a new record diff --git a/models/db/install/db.go b/models/db/install/db.go index 104a7a8e39..e64e85cf01 100644 --- a/models/db/install/db.go +++ b/models/db/install/db.go @@ -6,12 +6,10 @@ package install import ( "forgejo.org/models/db" "forgejo.org/modules/setting" - - "xorm.io/xorm" ) -func getXORMEngine() *xorm.Engine { - return db.DefaultContext.(*db.Context).Engine().(*xorm.Engine) +func getXORMEngine() db.Engine { + return db.DefaultContext.(*db.Context).Engine() } // CheckDatabaseConnection checks the database connection diff --git a/models/db/iterate_test.go b/models/db/iterate_test.go index 47b6a956f4..bdeaa876d5 100644 --- a/models/db/iterate_test.go +++ b/models/db/iterate_test.go @@ -17,7 +17,8 @@ import ( func TestIterate(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - xe := unittest.GetXORMEngine() + xe, err := unittest.GetXORMEngine() + require.NoError(t, err) require.NoError(t, xe.Sync(&repo_model.RepoUnit{})) cnt, err := db.GetEngine(db.DefaultContext).Count(&repo_model.RepoUnit{}) diff --git a/models/db/list_test.go b/models/db/list_test.go index f13958496a..502372782d 100644 --- a/models/db/list_test.go +++ b/models/db/list_test.go @@ -29,11 +29,12 @@ func (opts mockListOptions) ToConds() builder.Cond { func TestFind(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - xe := unittest.GetXORMEngine() + xe, err := unittest.GetXORMEngine() + require.NoError(t, err) require.NoError(t, xe.Sync(&repo_model.RepoUnit{})) var repoUnitCount int - _, err := db.GetEngine(db.DefaultContext).SQL("SELECT COUNT(*) FROM repo_unit").Get(&repoUnitCount) + _, err = db.GetEngine(db.DefaultContext).SQL("SELECT COUNT(*) FROM repo_unit").Get(&repoUnitCount) require.NoError(t, err) assert.NotEmpty(t, repoUnitCount) @@ -48,6 +49,6 @@ func TestFind(t *testing.T) { repoUnits, newCnt, err := db.FindAndCount[repo_model.RepoUnit](db.DefaultContext, opts) require.NoError(t, err) - assert.EqualValues(t, cnt, newCnt) + assert.Equal(t, cnt, newCnt) assert.Len(t, repoUnits, repoUnitCount) } diff --git a/models/dbfs/dbfs_test.go b/models/dbfs/dbfs_test.go index 8e42c54f31..10da231e44 100644 --- a/models/dbfs/dbfs_test.go +++ b/models/dbfs/dbfs_test.go @@ -32,15 +32,15 @@ func TestDbfsBasic(t *testing.T) { n, err := f.Write([]byte("0123456789")) // blocks: 0123 4567 89 require.NoError(t, err) - assert.EqualValues(t, 10, n) + assert.Equal(t, 10, n) _, err = f.Seek(0, io.SeekStart) require.NoError(t, err) buf, err := io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, 10, n) - assert.EqualValues(t, "0123456789", string(buf)) + assert.Equal(t, 10, n) + assert.Equal(t, "0123456789", string(buf)) // write some new data _, err = f.Seek(1, io.SeekStart) @@ -51,14 +51,14 @@ func TestDbfsBasic(t *testing.T) { // read from offset buf, err = io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "9", string(buf)) + assert.Equal(t, "9", string(buf)) // read all _, err = f.Seek(0, io.SeekStart) require.NoError(t, err) buf, err = io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "0bcdefghi9", string(buf)) + assert.Equal(t, "0bcdefghi9", string(buf)) // write to new size _, err = f.Seek(-1, io.SeekEnd) @@ -69,7 +69,7 @@ func TestDbfsBasic(t *testing.T) { require.NoError(t, err) buf, err = io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "0bcdefghiJKLMNOP", string(buf)) + assert.Equal(t, "0bcdefghiJKLMNOP", string(buf)) // write beyond EOF and fill with zero _, err = f.Seek(5, io.SeekCurrent) @@ -80,7 +80,7 @@ func TestDbfsBasic(t *testing.T) { require.NoError(t, err) buf, err = io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "0bcdefghiJKLMNOP\x00\x00\x00\x00\x00xyzu", string(buf)) + assert.Equal(t, "0bcdefghiJKLMNOP\x00\x00\x00\x00\x00xyzu", string(buf)) // write to the block with zeros _, err = f.Seek(-6, io.SeekCurrent) @@ -91,7 +91,7 @@ func TestDbfsBasic(t *testing.T) { require.NoError(t, err) buf, err = io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "0bcdefghiJKLMNOP\x00\x00\x00ABCDzu", string(buf)) + assert.Equal(t, "0bcdefghiJKLMNOP\x00\x00\x00ABCDzu", string(buf)) require.NoError(t, f.Close()) @@ -118,7 +118,7 @@ func TestDbfsBasic(t *testing.T) { require.NoError(t, err) stat, err := f.Stat() require.NoError(t, err) - assert.EqualValues(t, "test.txt", stat.Name()) + assert.Equal(t, "test.txt", stat.Name()) assert.EqualValues(t, 0, stat.Size()) _, err = f.Write([]byte("0123456789")) require.NoError(t, err) @@ -145,7 +145,7 @@ func TestDbfsReadWrite(t *testing.T) { line, err := f2r.ReadString('\n') require.NoError(t, err) - assert.EqualValues(t, "line 1\n", line) + assert.Equal(t, "line 1\n", line) _, err = f2r.ReadString('\n') require.ErrorIs(t, err, io.EOF) @@ -154,7 +154,7 @@ func TestDbfsReadWrite(t *testing.T) { line, err = f2r.ReadString('\n') require.NoError(t, err) - assert.EqualValues(t, "line 2\n", line) + assert.Equal(t, "line 2\n", line) _, err = f2r.ReadString('\n') require.ErrorIs(t, err, io.EOF) } @@ -187,5 +187,5 @@ func TestDbfsSeekWrite(t *testing.T) { buf, err := io.ReadAll(fr) require.NoError(t, err) - assert.EqualValues(t, "111333", string(buf)) + assert.Equal(t, "111333", string(buf)) } diff --git a/models/fixtures/action_variable.yml b/models/fixtures/action_variable.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/action_variable.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/forgefed/federationhost.go b/models/forgefed/federationhost.go index 00f13ea399..7db49e58e8 100644 --- a/models/forgefed/federationhost.go +++ b/models/forgefed/federationhost.go @@ -4,6 +4,7 @@ package forgefed import ( + "database/sql" "fmt" "strings" "time" @@ -15,12 +16,14 @@ import ( // FederationHost data type // swagger:model type FederationHost struct { - ID int64 `xorm:"pk autoincr"` - HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"` - NodeInfo NodeInfo `xorm:"extends NOT NULL"` - LatestActivity time.Time `xorm:"NOT NULL"` - Created timeutil.TimeStamp `xorm:"created"` - Updated timeutil.TimeStamp `xorm:"updated"` + ID int64 `xorm:"pk autoincr"` + HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"` + NodeInfo NodeInfo `xorm:"extends NOT NULL"` + LatestActivity time.Time `xorm:"NOT NULL"` + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` + KeyID sql.NullString `xorm:"key_id UNIQUE"` + PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"` } // Factory function for FederationHost. Created struct is asserted to be valid. diff --git a/models/forgefed/federationhost_repository.go b/models/forgefed/federationhost_repository.go index b04a5cd882..fa1f906824 100644 --- a/models/forgefed/federationhost_repository.go +++ b/models/forgefed/federationhost_repository.go @@ -30,9 +30,9 @@ func GetFederationHost(ctx context.Context, ID int64) (*FederationHost, error) { return host, nil } -func FindFederationHostByFqdn(ctx context.Context, fqdn string) (*FederationHost, error) { +func findFederationHostFromDB(ctx context.Context, searchKey, searchValue string) (*FederationHost, error) { host := new(FederationHost) - has, err := db.GetEngine(ctx).Where("host_fqdn=?", strings.ToLower(fqdn)).Get(host) + has, err := db.GetEngine(ctx).Where(searchKey, searchValue).Get(host) if err != nil { return nil, err } else if !has { @@ -44,6 +44,14 @@ func FindFederationHostByFqdn(ctx context.Context, fqdn string) (*FederationHost return host, nil } +func FindFederationHostByFqdn(ctx context.Context, fqdn string) (*FederationHost, error) { + return findFederationHostFromDB(ctx, "host_fqdn=?", strings.ToLower(fqdn)) +} + +func FindFederationHostByKeyID(ctx context.Context, keyID string) (*FederationHost, error) { + return findFederationHostFromDB(ctx, "key_id=?", keyID) +} + func CreateFederationHost(ctx context.Context, host *FederationHost) error { if res, err := validation.IsValid(host); !res { return err diff --git a/models/forgejo/semver/semver_test.go b/models/forgejo/semver/semver_test.go index 2d055e86bb..10989ecad3 100644 --- a/models/forgejo/semver/semver_test.go +++ b/models/forgejo/semver/semver_test.go @@ -22,7 +22,7 @@ func TestForgejoSemVerSetGet(t *testing.T) { require.NoError(t, SetVersionString(ctx, newVersion.String())) databaseVersion, err := GetVersion(ctx) require.NoError(t, err) - assert.EqualValues(t, newVersion.String(), databaseVersion.String()) + assert.Equal(t, newVersion.String(), databaseVersion.String()) assert.True(t, newVersion.Equal(databaseVersion)) } @@ -36,12 +36,12 @@ func TestForgejoSemVerMissing(t *testing.T) { v, err := GetVersion(ctx) require.NoError(t, err) - assert.EqualValues(t, "1.0.0", v.String()) + assert.Equal(t, "1.0.0", v.String()) _, err = e.Exec("drop table forgejo_sem_ver") require.NoError(t, err) v, err = GetVersion(ctx) require.NoError(t, err) - assert.EqualValues(t, "1.0.0", v.String()) + assert.Equal(t, "1.0.0", v.String()) } diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index a4cbca70c1..ef446add4e 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -94,6 +94,8 @@ var migrations = []*Migration{ NewMigration("Add `created_unix` column to `user_redirect` table", AddCreatedUnixToRedirect), // v27 -> v28 NewMigration("Add pronoun privacy settings to user", AddHidePronounsOptionToUser), + // v28 -> v29 + NewMigration("Add public key information to `FederatedUser` and `FederationHost`", AddPublicKeyInformationForFederation), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v1_22/v8_test.go b/models/forgejo_migrations/v1_22/v8_test.go index 2af9e431b1..baaba7290f 100644 --- a/models/forgejo_migrations/v1_22/v8_test.go +++ b/models/forgejo_migrations/v1_22/v8_test.go @@ -29,7 +29,7 @@ func Test_RemoveSSHSignaturesFromReleaseNotes(t *testing.T) { require.NoError(t, err) assert.Len(t, releases, 3) - assert.Equal(t, "", releases[0].Note) + assert.Empty(t, releases[0].Note) assert.Equal(t, "A message.\n", releases[1].Note) assert.Equal(t, "no signature present here", releases[2].Note) } diff --git a/models/forgejo_migrations/v29.go b/models/forgejo_migrations/v29.go new file mode 100644 index 0000000000..d0c2f723ae --- /dev/null +++ b/models/forgejo_migrations/v29.go @@ -0,0 +1,29 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package forgejo_migrations //nolint:revive + +import ( + "database/sql" + + "xorm.io/xorm" +) + +func AddPublicKeyInformationForFederation(x *xorm.Engine) error { + type FederationHost struct { + KeyID sql.NullString `xorm:"key_id UNIQUE"` + PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"` + } + + err := x.Sync(&FederationHost{}) + if err != nil { + return err + } + + type FederatedUser struct { + KeyID sql.NullString `xorm:"key_id UNIQUE"` + PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"` + } + + return x.Sync(&FederatedUser{}) +} diff --git a/models/git/branch_test.go b/models/git/branch_test.go index 5c1762750e..4340e8f729 100644 --- a/models/git/branch_test.go +++ b/models/git/branch_test.go @@ -22,7 +22,7 @@ import ( func TestAddDeletedBranch(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - assert.EqualValues(t, git.Sha1ObjectFormat.Name(), repo.ObjectFormatName) + assert.Equal(t, git.Sha1ObjectFormat.Name(), repo.ObjectFormatName) firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1}) assert.True(t, firstBranch.IsDeleted) diff --git a/models/git/lfs_test.go b/models/git/lfs_test.go index 8a2d3c8caf..be8c29bc3d 100644 --- a/models/git/lfs_test.go +++ b/models/git/lfs_test.go @@ -42,7 +42,7 @@ func TestIterateRepositoryIDsWithLFSMetaObjects(t *testing.T) { return nil }) require.NoError(t, err) - assert.EqualValues(t, expected, cases) + assert.Equal(t, expected, cases) }) t.Run("Low batch size", func(t *testing.T) { @@ -54,7 +54,7 @@ func TestIterateRepositoryIDsWithLFSMetaObjects(t *testing.T) { return nil }) require.NoError(t, err) - assert.EqualValues(t, expected, cases) + assert.Equal(t, expected, cases) }) } @@ -72,7 +72,7 @@ func TestIterateLFSMetaObjectsForRepo(t *testing.T) { return nil }, &IterateLFSMetaObjectsForRepoOptions{}) require.NoError(t, err) - assert.EqualValues(t, expectedIDs, actualIDs) + assert.Equal(t, expectedIDs, actualIDs) }) t.Run("Low batch size", func(t *testing.T) { @@ -84,7 +84,7 @@ func TestIterateLFSMetaObjectsForRepo(t *testing.T) { return nil }, &IterateLFSMetaObjectsForRepoOptions{}) require.NoError(t, err) - assert.EqualValues(t, expectedIDs, actualIDs) + assert.Equal(t, expectedIDs, actualIDs) t.Run("Batch handles updates", func(t *testing.T) { actualIDs := []int64{} @@ -96,7 +96,7 @@ func TestIterateLFSMetaObjectsForRepo(t *testing.T) { return nil }, &IterateLFSMetaObjectsForRepoOptions{}) require.NoError(t, err) - assert.EqualValues(t, expectedIDs, actualIDs) + assert.Equal(t, expectedIDs, actualIDs) }) }) } diff --git a/models/git/protected_branch_list_test.go b/models/git/protected_branch_list_test.go index db7e54f685..0685e67999 100644 --- a/models/git/protected_branch_list_test.go +++ b/models/git/protected_branch_list_test.go @@ -71,7 +71,7 @@ func TestBranchRuleMatchPriority(t *testing.T) { require.Error(t, fmt.Errorf("no matched rules but expected %s[%d]", kase.Rules[kase.ExpectedMatchIdx], kase.ExpectedMatchIdx)) } } else { - assert.EqualValues(t, kase.Rules[kase.ExpectedMatchIdx], matchedPB.RuleName) + assert.Equal(t, kase.Rules[kase.ExpectedMatchIdx], matchedPB.RuleName) } } } diff --git a/models/git/protected_branch_test.go b/models/git/protected_branch_test.go index 278fa9fee4..f3aeb19b78 100644 --- a/models/git/protected_branch_test.go +++ b/models/git/protected_branch_test.go @@ -64,6 +64,6 @@ func TestBranchRuleMatch(t *testing.T) { for _, kase := range kases { pb := ProtectedBranch{RuleName: kase.Rule} - assert.EqualValues(t, kase.ExpectedMatch, pb.Match(kase.BranchName), "%s - %s", kase.BranchName, kase.Rule) + assert.Equal(t, kase.ExpectedMatch, pb.Match(kase.BranchName), "%s - %s", kase.BranchName, kase.Rule) } } diff --git a/models/issues/action_aggregator.go b/models/issues/action_aggregator.go index cf5be753f1..d3643adeef 100644 --- a/models/issues/action_aggregator.go +++ b/models/issues/action_aggregator.go @@ -50,11 +50,12 @@ func (agg *ActionAggregator) aggregateAction(c *Comment, index int) { } agg.EndInd = index - if c.Type == CommentTypeClose { + switch c.Type { + case CommentTypeClose: agg.IsClosed = true - } else if c.Type == CommentTypeReopen { + case CommentTypeReopen: agg.IsClosed = false - } else if c.Type == CommentTypeReviewRequest { + case CommentTypeReviewRequest: if c.AssigneeID > 0 { req := RequestReviewTarget{User: c.Assignee} if c.RemovedAssignee { @@ -78,13 +79,13 @@ func (agg *ActionAggregator) aggregateAction(c *Comment, index int) { for _, r := range c.AddedRequestReview { agg.addReviewRequest(r) } - } else if c.Type == CommentTypeLabel { + case CommentTypeLabel: if c.Content == "1" { agg.addLabel(c.Label) } else { agg.delLabel(c.Label) } - } else if c.Type == CommentTypeAggregator { + case CommentTypeAggregator: agg.Merge(c.Aggregator) } } diff --git a/models/issues/comment_test.go b/models/issues/comment_test.go index 0e257f533c..c7adf6f62e 100644 --- a/models/issues/comment_test.go +++ b/models/issues/comment_test.go @@ -36,10 +36,10 @@ func TestCreateComment(t *testing.T) { require.NoError(t, err) then := time.Now().Unix() - assert.EqualValues(t, issues_model.CommentTypeComment, comment.Type) - assert.EqualValues(t, "Hello", comment.Content) - assert.EqualValues(t, issue.ID, comment.IssueID) - assert.EqualValues(t, doer.ID, comment.PosterID) + assert.Equal(t, issues_model.CommentTypeComment, comment.Type) + assert.Equal(t, "Hello", comment.Content) + assert.Equal(t, issue.ID, comment.IssueID) + assert.Equal(t, doer.ID, comment.PosterID) unittest.AssertInt64InRange(t, now, then, int64(comment.CreatedUnix)) unittest.AssertExistsAndLoadBean(t, comment) // assert actually added to DB @@ -95,7 +95,7 @@ func TestMigrate_InsertIssueComments(t *testing.T) { require.NoError(t, err) issueModified := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}) - assert.EqualValues(t, issue.NumComments+1, issueModified.NumComments) + assert.Equal(t, issue.NumComments+1, issueModified.NumComments) unittest.CheckConsistencyFor(t, &issues_model.Issue{}) } @@ -132,5 +132,5 @@ func Test_UpdateIssueNumComments(t *testing.T) { require.NoError(t, issues_model.UpdateIssueNumComments(db.DefaultContext, issue2.ID)) issue2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) - assert.EqualValues(t, 1, issue2.NumComments) + assert.Equal(t, 1, issue2.NumComments) } diff --git a/models/issues/issue.go b/models/issues/issue.go index 142f2de182..d6a0029638 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -646,7 +646,7 @@ func (issue *Issue) BlockedByDependencies(ctx context.Context, opts db.ListOptio err = sess.Find(&issueDeps) for _, depInfo := range issueDeps { - depInfo.Issue.Repo = &depInfo.Repository + depInfo.Repo = &depInfo.Repository } return issueDeps, err @@ -664,7 +664,7 @@ func (issue *Issue) BlockingDependencies(ctx context.Context) (issueDeps []*Depe Find(&issueDeps) for _, depInfo := range issueDeps { - depInfo.Issue.Repo = &depInfo.Repository + depInfo.Repo = &depInfo.Repository } return issueDeps, err diff --git a/models/issues/issue_list_test.go b/models/issues/issue_list_test.go index 7aa5222958..8d43d34186 100644 --- a/models/issues/issue_list_test.go +++ b/models/issues/issue_list_test.go @@ -29,7 +29,7 @@ func TestIssueList_LoadRepositories(t *testing.T) { require.NoError(t, err) assert.Len(t, repos, 2) for _, issue := range issueList { - assert.EqualValues(t, issue.RepoID, issue.Repo.ID) + assert.Equal(t, issue.RepoID, issue.Repo.ID) } } @@ -43,28 +43,28 @@ func TestIssueList_LoadAttributes(t *testing.T) { require.NoError(t, issueList.LoadAttributes(db.DefaultContext)) for _, issue := range issueList { - assert.EqualValues(t, issue.RepoID, issue.Repo.ID) + assert.Equal(t, issue.RepoID, issue.Repo.ID) for _, label := range issue.Labels { - assert.EqualValues(t, issue.RepoID, label.RepoID) + assert.Equal(t, issue.RepoID, label.RepoID) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID}) } if issue.PosterID > 0 { - assert.EqualValues(t, issue.PosterID, issue.Poster.ID) + assert.Equal(t, issue.PosterID, issue.Poster.ID) } if issue.AssigneeID > 0 { - assert.EqualValues(t, issue.AssigneeID, issue.Assignee.ID) + assert.Equal(t, issue.AssigneeID, issue.Assignee.ID) } if issue.MilestoneID > 0 { - assert.EqualValues(t, issue.MilestoneID, issue.Milestone.ID) + assert.Equal(t, issue.MilestoneID, issue.Milestone.ID) } if issue.IsPull { - assert.EqualValues(t, issue.ID, issue.PullRequest.IssueID) + assert.Equal(t, issue.ID, issue.PullRequest.IssueID) } for _, attachment := range issue.Attachments { - assert.EqualValues(t, issue.ID, attachment.IssueID) + assert.Equal(t, issue.ID, attachment.IssueID) } for _, comment := range issue.Comments { - assert.EqualValues(t, issue.ID, comment.IssueID) + assert.Equal(t, issue.ID, comment.IssueID) } if issue.ID == int64(1) { assert.Equal(t, int64(400), issue.TotalTrackedTime) diff --git a/models/issues/issue_project_test.go b/models/issues/issue_project_test.go index f518679f34..2ccf018795 100644 --- a/models/issues/issue_project_test.go +++ b/models/issues/issue_project_test.go @@ -39,15 +39,15 @@ func TestPrivateIssueProjects(t *testing.T) { issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.None[bool]()) require.NoError(t, err) - assert.EqualValues(t, 2, issuesNum) + assert.Equal(t, 2, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(true)) require.NoError(t, err) - assert.EqualValues(t, 0, issuesNum) + assert.Equal(t, 0, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(false)) require.NoError(t, err) - assert.EqualValues(t, 2, issuesNum) + assert.Equal(t, 2, issuesNum) }) t.Run("Anonymous user", func(t *testing.T) { @@ -59,15 +59,15 @@ func TestPrivateIssueProjects(t *testing.T) { issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, nil, org, optional.None[bool]()) require.NoError(t, err) - assert.EqualValues(t, 1, issuesNum) + assert.Equal(t, 1, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, nil, org, optional.Some(true)) require.NoError(t, err) - assert.EqualValues(t, 0, issuesNum) + assert.Equal(t, 0, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, nil, org, optional.Some(false)) require.NoError(t, err) - assert.EqualValues(t, 1, issuesNum) + assert.Equal(t, 1, issuesNum) }) }) @@ -85,15 +85,15 @@ func TestPrivateIssueProjects(t *testing.T) { issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.None[bool]()) require.NoError(t, err) - assert.EqualValues(t, 2, issuesNum) + assert.Equal(t, 2, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.Some(true)) require.NoError(t, err) - assert.EqualValues(t, 0, issuesNum) + assert.Equal(t, 0, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.Some(false)) require.NoError(t, err) - assert.EqualValues(t, 2, issuesNum) + assert.Equal(t, 2, issuesNum) }) t.Run("Anonymous user", func(t *testing.T) { @@ -105,15 +105,15 @@ func TestPrivateIssueProjects(t *testing.T) { issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, userProject, nil, nil, optional.None[bool]()) require.NoError(t, err) - assert.EqualValues(t, 1, issuesNum) + assert.Equal(t, 1, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, nil, nil, optional.Some(true)) require.NoError(t, err) - assert.EqualValues(t, 0, issuesNum) + assert.Equal(t, 0, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, nil, nil, optional.Some(false)) require.NoError(t, err) - assert.EqualValues(t, 1, issuesNum) + assert.Equal(t, 1, issuesNum) }) }) } @@ -137,15 +137,15 @@ func TestPrivateRepoProjects(t *testing.T) { issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, user29, org, optional.None[bool]()) require.NoError(t, err) - assert.EqualValues(t, 1, issuesNum) + assert.Equal(t, 1, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user29, org, optional.Some(true)) require.NoError(t, err) - assert.EqualValues(t, 0, issuesNum) + assert.Equal(t, 0, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user29, org, optional.Some(false)) require.NoError(t, err) - assert.EqualValues(t, 1, issuesNum) + assert.Equal(t, 1, issuesNum) }) t.Run("Full access", func(t *testing.T) { @@ -160,14 +160,14 @@ func TestPrivateRepoProjects(t *testing.T) { issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.None[bool]()) require.NoError(t, err) - assert.EqualValues(t, 2, issuesNum) + assert.Equal(t, 2, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(true)) require.NoError(t, err) - assert.EqualValues(t, 0, issuesNum) + assert.Equal(t, 0, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(false)) require.NoError(t, err) - assert.EqualValues(t, 2, issuesNum) + assert.Equal(t, 2, issuesNum) }) } diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go index afca27dfcf..8f40d3bdd9 100644 --- a/models/issues/issue_test.go +++ b/models/issues/issue_test.go @@ -143,8 +143,8 @@ func TestUpdateIssueCols(t *testing.T) { then := time.Now().Unix() updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID}) - assert.EqualValues(t, newTitle, updatedIssue.Title) - assert.EqualValues(t, prevContent, updatedIssue.Content) + assert.Equal(t, newTitle, updatedIssue.Title) + assert.Equal(t, prevContent, updatedIssue.Content) unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix)) } @@ -216,7 +216,7 @@ func TestIssues(t *testing.T) { require.NoError(t, err) if assert.Len(t, issues, len(test.ExpectedIssueIDs)) { for i, issue := range issues { - assert.EqualValues(t, test.ExpectedIssueIDs[i], issue.ID) + assert.Equal(t, test.ExpectedIssueIDs[i], issue.ID) } } } @@ -249,10 +249,10 @@ func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *is has, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Get(&newIssue) require.NoError(t, err) assert.True(t, has) - assert.EqualValues(t, issue.Title, newIssue.Title) - assert.EqualValues(t, issue.Content, newIssue.Content) + assert.Equal(t, issue.Title, newIssue.Title) + assert.Equal(t, issue.Content, newIssue.Content) if expectIndex > 0 { - assert.EqualValues(t, expectIndex, newIssue.Index) + assert.Equal(t, expectIndex, newIssue.Index) } }) return &newIssue @@ -286,7 +286,7 @@ func TestIssue_ResolveMentions(t *testing.T) { ids[i] = user.ID } sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) - assert.EqualValues(t, expected, ids) + assert.Equal(t, expected, ids) } // Public repo, existing user @@ -421,28 +421,28 @@ func TestIssueLoadAttributes(t *testing.T) { for _, issue := range issueList { require.NoError(t, issue.LoadAttributes(db.DefaultContext)) - assert.EqualValues(t, issue.RepoID, issue.Repo.ID) + assert.Equal(t, issue.RepoID, issue.Repo.ID) for _, label := range issue.Labels { - assert.EqualValues(t, issue.RepoID, label.RepoID) + assert.Equal(t, issue.RepoID, label.RepoID) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID}) } if issue.PosterID > 0 { - assert.EqualValues(t, issue.PosterID, issue.Poster.ID) + assert.Equal(t, issue.PosterID, issue.Poster.ID) } if issue.AssigneeID > 0 { - assert.EqualValues(t, issue.AssigneeID, issue.Assignee.ID) + assert.Equal(t, issue.AssigneeID, issue.Assignee.ID) } if issue.MilestoneID > 0 { - assert.EqualValues(t, issue.MilestoneID, issue.Milestone.ID) + assert.Equal(t, issue.MilestoneID, issue.Milestone.ID) } if issue.IsPull { - assert.EqualValues(t, issue.ID, issue.PullRequest.IssueID) + assert.Equal(t, issue.ID, issue.PullRequest.IssueID) } for _, attachment := range issue.Attachments { - assert.EqualValues(t, issue.ID, attachment.IssueID) + assert.Equal(t, issue.ID, attachment.IssueID) } for _, comment := range issue.Comments { - assert.EqualValues(t, issue.ID, comment.IssueID) + assert.Equal(t, issue.ID, comment.IssueID) } if issue.ID == int64(1) { assert.Equal(t, int64(400), issue.TotalTrackedTime) diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 9d0bc84454..739aa05b33 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -275,6 +275,11 @@ func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User } } + // If the issue was reported as abusive, a shadow copy should be created before first update. + if err := IfNeededCreateShadowCopyForIssue(ctx, issue); err != nil { + return err + } + issue.Content = content issue.ContentVersion = contentVersion + 1 diff --git a/models/issues/label_test.go b/models/issues/label_test.go index f2ba28a6d2..f289ac0f86 100644 --- a/models/issues/label_test.go +++ b/models/issues/label_test.go @@ -21,7 +21,7 @@ func TestLabel_CalOpenIssues(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) label.CalOpenIssues() - assert.EqualValues(t, 2, label.NumOpenIssues) + assert.Equal(t, 2, label.NumOpenIssues) } func TestLabel_LoadSelectedLabelsAfterClick(t *testing.T) { @@ -156,7 +156,7 @@ func TestGetLabelsByRepoID(t *testing.T) { require.NoError(t, err) assert.Len(t, labels, len(expectedIssueIDs)) for i, label := range labels { - assert.EqualValues(t, expectedIssueIDs[i], label.ID) + assert.Equal(t, expectedIssueIDs[i], label.ID) } } testSuccess(1, "leastissues", []int64{2, 1}) @@ -223,7 +223,7 @@ func TestGetLabelsByOrgID(t *testing.T) { require.NoError(t, err) assert.Len(t, labels, len(expectedIssueIDs)) for i, label := range labels { - assert.EqualValues(t, expectedIssueIDs[i], label.ID) + assert.Equal(t, expectedIssueIDs[i], label.ID) } } testSuccess(3, "leastissues", []int64{3, 4}) @@ -269,10 +269,10 @@ func TestUpdateLabel(t *testing.T) { label.Name = update.Name require.NoError(t, issues_model.UpdateLabel(db.DefaultContext, update)) newLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) - assert.EqualValues(t, label.ID, newLabel.ID) - assert.EqualValues(t, label.Color, newLabel.Color) - assert.EqualValues(t, label.Name, newLabel.Name) - assert.EqualValues(t, label.Description, newLabel.Description) + assert.Equal(t, label.ID, newLabel.ID) + assert.Equal(t, label.Color, newLabel.Color) + assert.Equal(t, label.Name, newLabel.Name) + assert.Equal(t, label.Description, newLabel.Description) assert.EqualValues(t, 0, newLabel.ArchivedUnix) unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{}) } @@ -315,7 +315,7 @@ func TestNewIssueLabel(t *testing.T) { Content: "1", }) label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}) - assert.EqualValues(t, prevNumIssues+1, label.NumIssues) + assert.Equal(t, prevNumIssues+1, label.NumIssues) // re-add existing IssueLabel require.NoError(t, issues_model.NewIssueLabel(db.DefaultContext, issue, label, doer)) @@ -368,11 +368,11 @@ func TestNewIssueLabels(t *testing.T) { }) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label1.ID}) label1 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) - assert.EqualValues(t, 3, label1.NumIssues) - assert.EqualValues(t, 1, label1.NumClosedIssues) + assert.Equal(t, 3, label1.NumIssues) + assert.Equal(t, 1, label1.NumClosedIssues) label2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}) - assert.EqualValues(t, 1, label2.NumIssues) - assert.EqualValues(t, 1, label2.NumClosedIssues) + assert.Equal(t, 1, label2.NumIssues) + assert.Equal(t, 1, label2.NumClosedIssues) // corner case: test empty slice require.NoError(t, issues_model.NewIssueLabels(db.DefaultContext, issue, []*issues_model.Label{}, doer)) @@ -410,8 +410,8 @@ func TestDeleteIssueLabel(t *testing.T) { LabelID: labelID, }, `content=""`) label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID}) - assert.EqualValues(t, expectedNumIssues, label.NumIssues) - assert.EqualValues(t, expectedNumClosedIssues, label.NumClosedIssues) + assert.Equal(t, expectedNumIssues, label.NumIssues) + assert.Equal(t, expectedNumClosedIssues, label.NumClosedIssues) } testSuccess(1, 1, 2) testSuccess(2, 5, 2) diff --git a/models/issues/milestone_test.go b/models/issues/milestone_test.go index bfb4f38ad0..7391cc0894 100644 --- a/models/issues/milestone_test.go +++ b/models/issues/milestone_test.go @@ -70,7 +70,7 @@ func TestGetMilestonesByRepoID(t *testing.T) { assert.Len(t, milestones, n) for _, milestone := range milestones { - assert.EqualValues(t, repoID, milestone.RepoID) + assert.Equal(t, repoID, milestone.RepoID) } } test(1, api.StateOpen) @@ -327,7 +327,7 @@ func TestUpdateMilestone(t *testing.T) { milestone.Content = "newMilestoneContent" require.NoError(t, issues_model.UpdateMilestone(db.DefaultContext, milestone, milestone.IsClosed)) milestone = unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}) - assert.EqualValues(t, "newMilestoneName", milestone.Name) + assert.Equal(t, "newMilestoneName", milestone.Name) unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) } @@ -364,7 +364,7 @@ func TestMigrate_InsertMilestones(t *testing.T) { require.NoError(t, err) unittest.AssertExistsAndLoadBean(t, ms) repoModified := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo.ID}) - assert.EqualValues(t, repo.NumMilestones+1, repoModified.NumMilestones) + assert.Equal(t, repo.NumMilestones+1, repoModified.NumMilestones) unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) } diff --git a/models/issues/moderation.go b/models/issues/moderation.go index 0d855f33a9..635d295db0 100644 --- a/models/issues/moderation.go +++ b/models/issues/moderation.go @@ -11,9 +11,39 @@ import ( "forgejo.org/modules/timeutil" ) +// IssueData represents a trimmed down issue that is used for preserving +// only the fields needed for abusive content reports (mainly string fields). +type IssueData struct { + RepoID int64 + Index int64 + PosterID int64 + Title string + Content string + ContentVersion int + CreatedUnix timeutil.TimeStamp + UpdatedUnix timeutil.TimeStamp +} + +// newIssueData creates a trimmed down issue to be used just to create a JSON structure +// (keeping only the fields relevant for moderation purposes) +func newIssueData(issue *Issue) IssueData { + return IssueData{ + RepoID: issue.RepoID, + Index: issue.Index, + PosterID: issue.PosterID, + Content: issue.Content, + Title: issue.Title, + ContentVersion: issue.ContentVersion, + CreatedUnix: issue.CreatedUnix, + UpdatedUnix: issue.UpdatedUnix, + } +} + // CommentData represents a trimmed down comment that is used for preserving // only the fields needed for abusive content reports (mainly string fields). type CommentData struct { + PosterID int64 + IssueID int64 Content string ContentVersion int CreatedUnix timeutil.TimeStamp @@ -24,6 +54,8 @@ type CommentData struct { // (keeping only the fields relevant for moderation purposes) func newCommentData(comment *Comment) CommentData { return CommentData{ + PosterID: comment.PosterID, + IssueID: comment.IssueID, Content: comment.Content, ContentVersion: comment.ContentVersion, CreatedUnix: comment.CreatedUnix, @@ -31,12 +63,37 @@ func newCommentData(comment *Comment) CommentData { } } +// IfNeededCreateShadowCopyForIssue checks if for the given issue there are any reports of abusive content submitted +// and if found a shadow copy of relevant issue fields will be stored into DB and linked to the above report(s). +// This function should be called before a issue is deleted or updated. +func IfNeededCreateShadowCopyForIssue(ctx context.Context, issue *Issue) error { + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeIssue, issue.ID) + if err != nil { + return err + } + + if shadowCopyNeeded { + issueData := newIssueData(issue) + content, err := json.Marshal(issueData) + if err != nil { + return err + } + return moderation.CreateShadowCopyForIssue(ctx, issue.ID, string(content)) + } + + return nil +} + // IfNeededCreateShadowCopyForComment checks if for the given comment there are any reports of abusive content submitted // and if found a shadow copy of relevant comment fields will be stored into DB and linked to the above report(s). -// This function should be called when a comment is deleted or updated. +// This function should be called before a comment is deleted or updated. func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment) error { - // TODO check comment.Type? - if moderation.IsReported(ctx, moderation.ReportedContentTypeComment, comment.ID) { + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeComment, comment.ID) + if err != nil { + return err + } + + if shadowCopyNeeded { commentData := newCommentData(comment) content, err := json.Marshal(commentData) if err != nil { diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go index a2dde055a6..be52ce27c0 100644 --- a/models/issues/pull_test.go +++ b/models/issues/pull_test.go @@ -399,7 +399,7 @@ func TestDeleteOrphanedObjects(t *testing.T) { countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{}) require.NoError(t, err) - assert.EqualValues(t, countBefore, countAfter) + assert.Equal(t, countBefore, countAfter) } func TestParseCodeOwnersLine(t *testing.T) { @@ -432,7 +432,7 @@ func TestGetApprovers(t *testing.T) { setting.Repository.PullRequest.DefaultMergeMessageOfficialApproversOnly = false approvers := pr.GetApprovers(db.DefaultContext) expected := "Reviewed-by: User Five <user5@example.com>\nReviewed-by: Org Six <org6@example.com>\n" - assert.EqualValues(t, expected, approvers) + assert.Equal(t, expected, approvers) } func TestGetPullRequestByMergedCommit(t *testing.T) { diff --git a/models/issues/stopwatch_test.go b/models/issues/stopwatch_test.go index 8920023a3b..4863bef6db 100644 --- a/models/issues/stopwatch_test.go +++ b/models/issues/stopwatch_test.go @@ -92,7 +92,7 @@ func TestGetUIDsAndStopwatch(t *testing.T) { uidStopwatches, err := issues_model.GetUIDsAndStopwatch(db.DefaultContext) require.NoError(t, err) - assert.EqualValues(t, map[int64][]*issues_model.Stopwatch{ + assert.Equal(t, map[int64][]*issues_model.Stopwatch{ 1: { { ID: 1, diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 11933014d7..aea9b593bd 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -8,6 +8,7 @@ import ( "context" "fmt" + "forgejo.org/models/db" "forgejo.org/models/forgejo_migrations" "forgejo.org/models/migrations/v1_10" "forgejo.org/models/migrations/v1_11" @@ -510,3 +511,12 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t // Execute Forgejo specific migrations. return forgejo_migrations.Migrate(x) } + +// WrapperMigrate is a wrapper for Migrate to be called in diagnostics +func WrapperMigrate(e db.Engine) error { + engine, err := db.GetMasterEngine(e) + if err != nil { + return err + } + return Migrate(engine) +} diff --git a/models/migrations/migrations_test.go b/models/migrations/migrations_test.go index ea941b9a09..468c918c93 100644 --- a/models/migrations/migrations_test.go +++ b/models/migrations/migrations_test.go @@ -21,7 +21,7 @@ func TestMigrations(t *testing.T) { assert.EqualValues(t, 71, migrationIDNumberToDBVersion(70)) - assert.EqualValues(t, []*migration{{idNumber: 70}, {idNumber: 71}}, getPendingMigrations(70, preparedMigrations)) - assert.EqualValues(t, []*migration{{idNumber: 71}}, getPendingMigrations(71, preparedMigrations)) - assert.EqualValues(t, []*migration{}, getPendingMigrations(72, preparedMigrations)) + assert.Equal(t, []*migration{{idNumber: 70}, {idNumber: 71}}, getPendingMigrations(70, preparedMigrations)) + assert.Equal(t, []*migration{{idNumber: 71}}, getPendingMigrations(71, preparedMigrations)) + assert.Equal(t, []*migration{}, getPendingMigrations(72, preparedMigrations)) } diff --git a/models/migrations/test/tests.go b/models/migrations/test/tests.go index 07487cf58a..c1f0caf19b 100644 --- a/models/migrations/test/tests.go +++ b/models/migrations/test/tests.go @@ -175,7 +175,10 @@ func newXORMEngine() (*xorm.Engine, error) { if err := db.InitEngine(context.Background()); err != nil { return nil, err } - x := unittest.GetXORMEngine() + x, err := unittest.GetXORMEngine() + if err != nil { + return nil, err + } return x, nil } diff --git a/models/migrations/v1_15/v181_test.go b/models/migrations/v1_15/v181_test.go index 4544ca6520..4154e0b1e9 100644 --- a/models/migrations/v1_15/v181_test.go +++ b/models/migrations/v1_15/v181_test.go @@ -50,7 +50,7 @@ func Test_AddPrimaryEmail2EmailAddress(t *testing.T) { require.NoError(t, err) assert.True(t, has) assert.True(t, emailAddress.IsPrimary) - assert.EqualValues(t, user.IsActive, emailAddress.IsActivated) - assert.EqualValues(t, user.ID, emailAddress.UID) + assert.Equal(t, user.IsActive, emailAddress.IsActivated) + assert.Equal(t, user.ID, emailAddress.UID) } } diff --git a/models/migrations/v1_16/v189_test.go b/models/migrations/v1_16/v189_test.go index e72c385168..90b721d5f1 100644 --- a/models/migrations/v1_16/v189_test.go +++ b/models/migrations/v1_16/v189_test.go @@ -76,8 +76,8 @@ func Test_UnwrapLDAPSourceCfg(t *testing.T) { return } - assert.EqualValues(t, expected, converted, "UnwrapLDAPSourceCfg failed for %d", source.ID) - assert.EqualValues(t, source.ID%2 == 0, source.IsActive, "UnwrapLDAPSourceCfg failed for %d", source.ID) + assert.Equal(t, expected, converted, "UnwrapLDAPSourceCfg failed for %d", source.ID) + assert.Equal(t, source.ID%2 == 0, source.IsActive, "UnwrapLDAPSourceCfg failed for %d", source.ID) } } } diff --git a/models/migrations/v1_16/v193_test.go b/models/migrations/v1_16/v193_test.go index ab39bcd98c..8260acf32d 100644 --- a/models/migrations/v1_16/v193_test.go +++ b/models/migrations/v1_16/v193_test.go @@ -63,7 +63,7 @@ func Test_AddRepoIDForAttachment(t *testing.T) { has, err := x.ID(attach.IssueID).Get(&issue) require.NoError(t, err) assert.True(t, has) - assert.EqualValues(t, attach.RepoID, issue.RepoID) + assert.Equal(t, attach.RepoID, issue.RepoID) } var releaseAttachments []*NewAttachment @@ -76,6 +76,6 @@ func Test_AddRepoIDForAttachment(t *testing.T) { has, err := x.ID(attach.ReleaseID).Get(&release) require.NoError(t, err) assert.True(t, has) - assert.EqualValues(t, attach.RepoID, release.RepoID) + assert.Equal(t, attach.RepoID, release.RepoID) } } diff --git a/models/migrations/v1_16/v210_test.go b/models/migrations/v1_16/v210_test.go index 010cd8a770..f6423a5821 100644 --- a/models/migrations/v1_16/v210_test.go +++ b/models/migrations/v1_16/v210_test.go @@ -84,5 +84,5 @@ func Test_RemigrateU2FCredentials(t *testing.T) { err = x.Table("webauthn_credential").Select("id, credential_id").Asc("id").Find(&got) require.NoError(t, err) - assert.EqualValues(t, expected, got) + assert.Equal(t, expected, got) } diff --git a/models/migrations/v1_19/v233_test.go b/models/migrations/v1_19/v233_test.go index de025ca2b7..4dc35d1e27 100644 --- a/models/migrations/v1_19/v233_test.go +++ b/models/migrations/v1_19/v233_test.go @@ -63,7 +63,7 @@ func Test_AddHeaderAuthorizationEncryptedColWebhook(t *testing.T) { assert.Equal(t, e.Meta, got[i].Meta) if e.HeaderAuthorization == "" { - assert.Equal(t, "", got[i].HeaderAuthorizationEncrypted) + assert.Empty(t, got[i].HeaderAuthorizationEncrypted) } else { cipherhex := got[i].HeaderAuthorizationEncrypted cleartext, err := secret.DecryptSecret(setting.SecretKey, cipherhex) diff --git a/models/migrations/v1_20/v259_test.go b/models/migrations/v1_20/v259_test.go index d67cc9dd81..32e4aa3050 100644 --- a/models/migrations/v1_20/v259_test.go +++ b/models/migrations/v1_20/v259_test.go @@ -97,7 +97,7 @@ func Test_ConvertScopedAccessTokens(t *testing.T) { tokens := make([]AccessToken, 0) err = x.Find(&tokens) require.NoError(t, err) - assert.Equal(t, len(tests), len(tokens)) + assert.Len(t, tokens, len(tests)) // sort the tokens (insertion order by auto-incrementing primary key) sort.Slice(tokens, func(i, j int) bool { diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go index e6f8d4096e..c63deef495 100644 --- a/models/migrations/v1_22/v286_test.go +++ b/models/migrations/v1_22/v286_test.go @@ -109,11 +109,11 @@ func Test_RepositoryFormat(t *testing.T) { ok, err := x.ID(2).Get(repo) require.NoError(t, err) assert.True(t, ok) - assert.EqualValues(t, "sha1", repo.ObjectFormatName) + assert.Equal(t, "sha1", repo.ObjectFormatName) repo = new(Repository) ok, err = x.ID(id).Get(repo) require.NoError(t, err) assert.True(t, ok) - assert.EqualValues(t, "sha256", repo.ObjectFormatName) + assert.Equal(t, "sha256", repo.ObjectFormatName) } diff --git a/models/moderation/abuse_report.go b/models/moderation/abuse_report.go index c8b0356a8f..c7846e2840 100644 --- a/models/moderation/abuse_report.go +++ b/models/moderation/abuse_report.go @@ -17,7 +17,7 @@ import ( ) // ReportStatusType defines the statuses a report (of abusive content) can have. -type ReportStatusType int //revive:disable-line:exported +type ReportStatusType int const ( // ReportStatusTypeOpen represents the status of open reports that were not yet handled in any way. @@ -30,7 +30,7 @@ const ( type ( // AbuseCategoryType defines the categories in which a user can include the reported content. - AbuseCategoryType int //revive:disable-line:exported + AbuseCategoryType int // AbuseCategoryItem defines a pair of value and it's corresponding translation key // (used to add options within the dropdown shown when new reports are submitted). @@ -60,7 +60,7 @@ func GetAbuseCategoriesList() []AbuseCategoryItem { // ReportedContentType defines the types of content that can be reported // (i.e. user/organization profile, repository, issue/pull, comment). -type ReportedContentType int //revive:disable-line:exported +type ReportedContentType int const ( // ReportedContentTypeUser should be used when reporting abusive users or organizations. @@ -90,7 +90,7 @@ func (t ReportedContentType) IsValid() bool { // AbuseReport represents a report of abusive content. type AbuseReport struct { ID int64 `xorm:"pk autoincr"` - Status ReportStatusType `xorm:"NOT NULL DEFAULT 1"` + Status ReportStatusType `xorm:"INDEX NOT NULL DEFAULT 1"` // The ID of the user who submitted the report. ReporterID int64 `xorm:"NOT NULL"` // Reported content type: user/organization profile, repository, issue/pull or comment. @@ -115,12 +115,12 @@ func init() { db.RegisterModel(new(AbuseReport)) } -// IsReported reports whether one or more reports were already submitted for contentType and contentID -// (regardless the status of the reports). -func IsReported(ctx context.Context, contentType ReportedContentType, contentID int64) bool { - // TODO: only consider the reports with 'New' status (and adjust the function name)?! - reported, _ := db.GetEngine(ctx).Exist(&AbuseReport{ContentType: contentType, ContentID: contentID}) - return reported +// IsShadowCopyNeeded reports whether one or more reports were already submitted +// for contentType and contentID and not yet linked to a shadow copy (regardless their status). +func IsShadowCopyNeeded(ctx context.Context, contentType ReportedContentType, contentID int64) (bool, error) { + return db.GetEngine(ctx).Cols("id").Where(builder.IsNull{"shadow_copy_id"}).Exist( + &AbuseReport{ContentType: contentType, ContentID: contentID}, + ) } // AlreadyReportedByAndOpen returns if doerID has already submitted a report for contentType and contentID that is still Open. diff --git a/models/moderation/shadow_copy.go b/models/moderation/shadow_copy.go index ee12f45b09..cdd8f69c52 100644 --- a/models/moderation/shadow_copy.go +++ b/models/moderation/shadow_copy.go @@ -53,16 +53,6 @@ func createShadowCopy(ctx context.Context, contentType ReportedContentType, cont err := db.WithTx(ctx, func(ctx context.Context) error { sess := db.GetEngine(ctx) - notLinkedFound, err := sess.Cols("id").Where(builder.IsNull{"shadow_copy_id"}).Exist( - &AbuseReport{ContentType: contentType, ContentID: contentID}, - ) - if err != nil { - return err - } else if !notLinkedFound { - log.Warn("Requested to create a shadow copy for reported content with type %d and ID %d but there is no such report without a shadow copy ID.", contentType, contentID) - return nil - } - shadowCopy := &AbuseReportShadowCopy{RawValue: content} affected, err := sess.Insert(shadowCopy) if err != nil { diff --git a/models/org_team_test.go b/models/org_team_test.go index dc1fdb4b3b..730bc65f7d 100644 --- a/models/org_team_test.go +++ b/models/org_team_test.go @@ -82,7 +82,7 @@ func TestUpdateTeam(t *testing.T) { assert.True(t, strings.HasPrefix(team.Description, "A long description!")) access := unittest.AssertExistsAndLoadBean(t, &access_model.Access{UserID: 4, RepoID: 3}) - assert.EqualValues(t, perm.AccessModeAdmin, access.Mode) + assert.Equal(t, perm.AccessModeAdmin, access.Mode) unittest.CheckConsistencyFor(t, &organization.Team{ID: team.ID}) } diff --git a/models/org_test.go b/models/org_test.go index 45e21da0e0..4c87f1ee4a 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -49,7 +49,7 @@ func TestRemoveOrgUser(t *testing.T) { require.NoError(t, RemoveOrgUser(db.DefaultContext, orgID, userID)) unittest.AssertNotExistsBean(t, &organization.OrgUser{OrgID: orgID, UID: userID}) org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}) - assert.EqualValues(t, expectedNumMembers, org.NumMembers) + assert.Equal(t, expectedNumMembers, org.NumMembers) } testSuccess(3, 4) testSuccess(3, 4) diff --git a/models/organization/org.go b/models/organization/org.go index 1339f7415d..ff95261051 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -195,7 +195,7 @@ type FindOrgMembersOpts struct { } func (opts FindOrgMembersOpts) PublicOnly() bool { - return opts.Doer == nil || !(opts.IsDoerMember || opts.Doer.IsAdmin) + return opts.Doer == nil || (!opts.IsDoerMember && !opts.Doer.IsAdmin) } // CountOrgMembers counts the organization's members @@ -289,12 +289,8 @@ func CreateOrganization(ctx context.Context, org *Organization, owner *user_mode } org.LowerName = strings.ToLower(org.Name) - if org.Rands, err = user_model.GetUserSalt(); err != nil { - return err - } - if org.Salt, err = user_model.GetUserSalt(); err != nil { - return err - } + org.Rands = user_model.GetUserSalt() + org.Salt = user_model.GetUserSalt() org.UseCustomAvatar = true org.MaxRepoCreation = -1 org.NumTeams = 1 @@ -478,7 +474,7 @@ func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUs sess.And("is_public = ?", true) } - if opts.ListOptions.PageSize > 0 { + if opts.PageSize > 0 { sess = db.SetSessionPagination(sess, opts) ous := make([]*OrgUser, 0, opts.PageSize) diff --git a/models/organization/org_list_test.go b/models/organization/org_list_test.go index 0e27feee59..780616b71f 100644 --- a/models/organization/org_list_test.go +++ b/models/organization/org_list_test.go @@ -73,7 +73,7 @@ func TestGetUserOrgsList(t *testing.T) { if assert.Len(t, orgs, 1) { assert.EqualValues(t, 3, orgs[0].ID) // repo_id: 3 is in the team, 32 is public, 5 is private with no team - assert.EqualValues(t, 2, orgs[0].NumRepos) + assert.Equal(t, 2, orgs[0].NumRepos) } } diff --git a/models/organization/org_test.go b/models/organization/org_test.go index 212b893a42..0d564f7258 100644 --- a/models/organization/org_test.go +++ b/models/organization/org_test.go @@ -136,7 +136,7 @@ func TestIsOrganizationOwner(t *testing.T) { test := func(orgID, userID int64, expected bool) { isOwner, err := organization.IsOrganizationOwner(db.DefaultContext, orgID, userID) require.NoError(t, err) - assert.EqualValues(t, expected, isOwner) + assert.Equal(t, expected, isOwner) } test(3, 2, true) test(3, 3, false) @@ -150,7 +150,7 @@ func TestIsOrganizationMember(t *testing.T) { test := func(orgID, userID int64, expected bool) { isMember, err := organization.IsOrganizationMember(db.DefaultContext, orgID, userID) require.NoError(t, err) - assert.EqualValues(t, expected, isMember) + assert.Equal(t, expected, isMember) } test(3, 2, true) test(3, 3, false) @@ -165,7 +165,7 @@ func TestIsPublicMembership(t *testing.T) { test := func(orgID, userID int64, expected bool) { isMember, err := organization.IsPublicMembership(db.DefaultContext, orgID, userID) require.NoError(t, err) - assert.EqualValues(t, expected, isMember) + assert.Equal(t, expected, isMember) } test(3, 2, true) test(3, 3, false) @@ -188,7 +188,7 @@ func TestGetOrgUsersByOrgID(t *testing.T) { sort.Slice(orgUsers, func(i, j int) bool { return orgUsers[i].ID < orgUsers[j].ID }) - assert.EqualValues(t, []*organization.OrgUser{{ + assert.Equal(t, []*organization.OrgUser{{ ID: 1, OrgID: 3, UID: 2, @@ -255,7 +255,7 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) { require.NoError(t, err) count, err := env.CountRepos() require.NoError(t, err) - assert.EqualValues(t, expectedCount, count) + assert.Equal(t, expectedCount, count) } testSuccess(2, 3) testSuccess(4, 2) @@ -494,25 +494,25 @@ func TestUnitPermission(t *testing.T) { user := &user_model.User{ID: 1001} t.Run("Anonymous", func(t *testing.T) { t.Run("Public", func(t *testing.T) { - assert.EqualValues(t, perm.AccessModeRead, publicOrg.UnitPermission(db.DefaultContext, nil, unit.TypeCode)) + assert.Equal(t, perm.AccessModeRead, publicOrg.UnitPermission(db.DefaultContext, nil, unit.TypeCode)) }) t.Run("Limited", func(t *testing.T) { - assert.EqualValues(t, perm.AccessModeNone, limitedOrg.UnitPermission(db.DefaultContext, nil, unit.TypeCode)) + assert.Equal(t, perm.AccessModeNone, limitedOrg.UnitPermission(db.DefaultContext, nil, unit.TypeCode)) }) t.Run("Private", func(t *testing.T) { - assert.EqualValues(t, perm.AccessModeNone, privateOrg.UnitPermission(db.DefaultContext, nil, unit.TypeCode)) + assert.Equal(t, perm.AccessModeNone, privateOrg.UnitPermission(db.DefaultContext, nil, unit.TypeCode)) }) }) t.Run("Logged in", func(t *testing.T) { t.Run("Public", func(t *testing.T) { - assert.EqualValues(t, perm.AccessModeRead, publicOrg.UnitPermission(db.DefaultContext, user, unit.TypeCode)) + assert.Equal(t, perm.AccessModeRead, publicOrg.UnitPermission(db.DefaultContext, user, unit.TypeCode)) }) t.Run("Limited", func(t *testing.T) { - assert.EqualValues(t, perm.AccessModeRead, limitedOrg.UnitPermission(db.DefaultContext, user, unit.TypeCode)) + assert.Equal(t, perm.AccessModeRead, limitedOrg.UnitPermission(db.DefaultContext, user, unit.TypeCode)) }) t.Run("Private", func(t *testing.T) { - assert.EqualValues(t, perm.AccessModeNone, privateOrg.UnitPermission(db.DefaultContext, user, unit.TypeCode)) + assert.Equal(t, perm.AccessModeNone, privateOrg.UnitPermission(db.DefaultContext, user, unit.TypeCode)) }) }) } diff --git a/models/organization/org_user_test.go b/models/organization/org_user_test.go index 3f6799e8a1..a1472fde4b 100644 --- a/models/organization/org_user_test.go +++ b/models/organization/org_user_test.go @@ -140,7 +140,7 @@ func TestAddOrgUser(t *testing.T) { unittest.AssertExistsAndLoadBean(t, ou) assert.Equal(t, isPublic, ou.IsPublic) org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}) - assert.EqualValues(t, expectedNumMembers, org.NumMembers) + assert.Equal(t, expectedNumMembers, org.NumMembers) } setting.Service.DefaultOrgMemberVisible = false diff --git a/models/organization/team_test.go b/models/organization/team_test.go index 0237f76de8..b3ea23a296 100644 --- a/models/organization/team_test.go +++ b/models/organization/team_test.go @@ -77,7 +77,7 @@ func TestGetTeam(t *testing.T) { testSuccess := func(orgID int64, name string) { team, err := organization.GetTeam(db.DefaultContext, orgID, name) require.NoError(t, err) - assert.EqualValues(t, orgID, team.OrgID) + assert.Equal(t, orgID, team.OrgID) assert.Equal(t, name, team.Name) } testSuccess(3, "Owners") @@ -95,7 +95,7 @@ func TestGetTeamByID(t *testing.T) { testSuccess := func(teamID int64) { team, err := organization.GetTeamByID(db.DefaultContext, teamID) require.NoError(t, err) - assert.EqualValues(t, teamID, team.ID) + assert.Equal(t, teamID, team.ID) } testSuccess(1) testSuccess(2) @@ -163,7 +163,7 @@ func TestGetUserOrgTeams(t *testing.T) { teams, err := organization.GetUserOrgTeams(db.DefaultContext, orgID, userID) require.NoError(t, err) for _, team := range teams { - assert.EqualValues(t, orgID, team.OrgID) + assert.Equal(t, orgID, team.OrgID) unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{TeamID: team.ID, UID: userID}) } } diff --git a/models/perm/access/access.go b/models/perm/access/access.go index 76b547f772..87ee600a15 100644 --- a/models/perm/access/access.go +++ b/models/perm/access/access.go @@ -133,7 +133,7 @@ func refreshCollaboratorAccesses(ctx context.Context, repoID int64, accessMap ma return fmt.Errorf("getCollaborations: %w", err) } for _, c := range collaborators { - if c.User.IsGhost() { + if c.IsGhost() { continue } updateUserAccess(accessMap, c.User, c.Collaboration.Mode) diff --git a/models/project/column_test.go b/models/project/column_test.go index 2ef27de3b5..4fc452df33 100644 --- a/models/project/column_test.go +++ b/models/project/column_test.go @@ -98,9 +98,9 @@ func Test_MoveColumnsOnProject(t *testing.T) { columnsAfter, err := project1.GetColumns(db.DefaultContext) require.NoError(t, err) assert.Len(t, columnsAfter, 3) - assert.EqualValues(t, columns[1].ID, columnsAfter[0].ID) - assert.EqualValues(t, columns[2].ID, columnsAfter[1].ID) - assert.EqualValues(t, columns[0].ID, columnsAfter[2].ID) + assert.Equal(t, columns[1].ID, columnsAfter[0].ID) + assert.Equal(t, columns[2].ID, columnsAfter[1].ID) + assert.Equal(t, columns[0].ID, columnsAfter[2].ID) } func Test_NewColumn(t *testing.T) { diff --git a/models/project/project_test.go b/models/project/project_test.go index ee9fdaa2e2..ab23bab0bf 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -114,10 +114,10 @@ func TestProjectsSort(t *testing.T) { OrderBy: GetSearchOrderByBySortType(tt.sortType), }) require.NoError(t, err) - assert.EqualValues(t, int64(6), count) + assert.Equal(t, int64(6), count) if assert.Len(t, projects, 6) { for i := range projects { - assert.EqualValues(t, tt.wants[i], projects[i].ID) + assert.Equal(t, tt.wants[i], projects[i].ID) } } } diff --git a/models/repo/collaboration_test.go b/models/repo/collaboration_test.go index 783091ba9e..36aefe4cd9 100644 --- a/models/repo/collaboration_test.go +++ b/models/repo/collaboration_test.go @@ -27,8 +27,8 @@ func TestRepository_GetCollaborators(t *testing.T) { require.NoError(t, err) assert.Len(t, collaborators, int(expectedLen)) for _, collaborator := range collaborators { - assert.EqualValues(t, collaborator.User.ID, collaborator.Collaboration.UserID) - assert.EqualValues(t, repoID, collaborator.Collaboration.RepoID) + assert.Equal(t, collaborator.User.ID, collaborator.Collaboration.UserID) + assert.Equal(t, repoID, collaborator.Collaboration.RepoID) } } test(1) @@ -47,7 +47,7 @@ func TestRepository_GetCollaborators(t *testing.T) { require.NoError(t, err) assert.Len(t, collaborators2, 1) - assert.NotEqualValues(t, collaborators1[0].ID, collaborators2[0].ID) + assert.NotEqual(t, collaborators1[0].ID, collaborators2[0].ID) } func TestRepository_IsCollaborator(t *testing.T) { @@ -72,10 +72,10 @@ func TestRepository_ChangeCollaborationAccessMode(t *testing.T) { require.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessModeAdmin)) collaboration := unittest.AssertExistsAndLoadBean(t, &repo_model.Collaboration{RepoID: repo.ID, UserID: 4}) - assert.EqualValues(t, perm.AccessModeAdmin, collaboration.Mode) + assert.Equal(t, perm.AccessModeAdmin, collaboration.Mode) access := unittest.AssertExistsAndLoadBean(t, &access_model.Access{UserID: 4, RepoID: repo.ID}) - assert.EqualValues(t, perm.AccessModeAdmin, access.Mode) + assert.Equal(t, perm.AccessModeAdmin, access.Mode) require.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessModeAdmin)) diff --git a/models/repo/moderation.go b/models/repo/moderation.go new file mode 100644 index 0000000000..d7b87dffa0 --- /dev/null +++ b/models/repo/moderation.go @@ -0,0 +1,70 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package repo + +import ( + "context" + + "forgejo.org/models/moderation" + "forgejo.org/modules/json" + "forgejo.org/modules/timeutil" +) + +// RepositoryData represents a trimmed down repository that is used for preserving +// only the fields needed for abusive content reports (mainly string fields). +type RepositoryData struct { + OwnerID int64 + OwnerName string + Name string + Description string + Website string + Topics []string + Avatar string + CreatedUnix timeutil.TimeStamp + UpdatedUnix timeutil.TimeStamp +} + +// newRepositoryData creates a trimmed down repository to be used just to create a JSON structure +// (keeping only the fields relevant for moderation purposes) +func newRepositoryData(repo *Repository) RepositoryData { + return RepositoryData{ + OwnerID: repo.OwnerID, + OwnerName: repo.OwnerName, + Name: repo.Name, + Description: repo.Description, + Website: repo.Website, + Topics: repo.Topics, + Avatar: repo.Avatar, + CreatedUnix: repo.CreatedUnix, + UpdatedUnix: repo.UpdatedUnix, + } +} + +// IfNeededCreateShadowCopyForRepository checks if for the given repository there are any reports of abusive content submitted +// and if found a shadow copy of relevant repository fields will be stored into DB and linked to the above report(s). +// This function should be called when a repository is deleted or updated. +func IfNeededCreateShadowCopyForRepository(ctx context.Context, repo *Repository, forUpdates bool) error { + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeRepository, repo.ID) + if err != nil { + return err + } + + if shadowCopyNeeded { + if forUpdates { + // get the unmodified repository fields + repo, err = GetRepositoryByID(ctx, repo.ID) + if err != nil { + return err + } + } + repoData := newRepositoryData(repo) + content, err := json.Marshal(repoData) + if err != nil { + return err + } + return moderation.CreateShadowCopyForRepository(ctx, repo.ID, string(content)) + } + + return nil +} diff --git a/models/repo/pushmirror_test.go b/models/repo/pushmirror_test.go index de6c9b0a41..c11cbd26ef 100644 --- a/models/repo/pushmirror_test.go +++ b/models/repo/pushmirror_test.go @@ -67,7 +67,7 @@ func TestPushMirrorPrivatekey(t *testing.T) { t.Run("Normal retrieval", func(t *testing.T) { actualPrivateKey, err := m.Privatekey() require.NoError(t, err) - assert.EqualValues(t, privateKey, actualPrivateKey) + assert.Equal(t, privateKey, actualPrivateKey) }) t.Run("Incorrect retrieval", func(t *testing.T) { diff --git a/models/repo/repo.go b/models/repo/repo.go index 8d204d5594..9db1fd9039 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -402,27 +402,28 @@ func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit return ru } - if tp == unit.TypeExternalWiki { + switch tp { + case unit.TypeExternalWiki: return &RepoUnit{ Type: tp, Config: new(ExternalWikiConfig), } - } else if tp == unit.TypeExternalTracker { + case unit.TypeExternalTracker: return &RepoUnit{ Type: tp, Config: new(ExternalTrackerConfig), } - } else if tp == unit.TypePullRequests { + case unit.TypePullRequests: return &RepoUnit{ Type: tp, Config: new(PullRequestsConfig), } - } else if tp == unit.TypeIssues { + case unit.TypeIssues: return &RepoUnit{ Type: tp, Config: new(IssuesConfig), } - } else if tp == unit.TypeActions { + case unit.TypeActions: return &RepoUnit{ Type: tp, Config: new(ActionsConfig), diff --git a/models/repo/repo_list_test.go b/models/repo/repo_list_test.go index b13db64137..18e39969bc 100644 --- a/models/repo/repo_list_test.go +++ b/models/repo/repo_list_test.go @@ -445,6 +445,6 @@ func TestSearchRepositoryIDsByCondition(t *testing.T) { require.NoError(t, err) slices.Sort(repoIDs) - assert.EqualValues(t, testCase.repoIDs, repoIDs) + assert.Equal(t, testCase.repoIDs, repoIDs) } } diff --git a/models/repo/repo_unit_test.go b/models/repo/repo_unit_test.go index 210b830d02..a1964519bd 100644 --- a/models/repo/repo_unit_test.go +++ b/models/repo/repo_unit_test.go @@ -16,21 +16,21 @@ import ( func TestActionsConfig(t *testing.T) { cfg := &ActionsConfig{} cfg.DisableWorkflow("test1.yaml") - assert.EqualValues(t, []string{"test1.yaml"}, cfg.DisabledWorkflows) + assert.Equal(t, []string{"test1.yaml"}, cfg.DisabledWorkflows) cfg.DisableWorkflow("test1.yaml") - assert.EqualValues(t, []string{"test1.yaml"}, cfg.DisabledWorkflows) + assert.Equal(t, []string{"test1.yaml"}, cfg.DisabledWorkflows) cfg.EnableWorkflow("test1.yaml") - assert.EqualValues(t, []string{}, cfg.DisabledWorkflows) + assert.Equal(t, []string{}, cfg.DisabledWorkflows) cfg.EnableWorkflow("test1.yaml") - assert.EqualValues(t, []string{}, cfg.DisabledWorkflows) + assert.Equal(t, []string{}, cfg.DisabledWorkflows) cfg.DisableWorkflow("test1.yaml") cfg.DisableWorkflow("test2.yaml") cfg.DisableWorkflow("test3.yaml") - assert.EqualValues(t, "test1.yaml,test2.yaml,test3.yaml", cfg.ToString()) + assert.Equal(t, "test1.yaml,test2.yaml,test3.yaml", cfg.ToString()) } func TestRepoUnitAccessMode(t *testing.T) { diff --git a/models/repo/topic_test.go b/models/repo/topic_test.go index 26ad27896e..541dcafbfe 100644 --- a/models/repo/topic_test.go +++ b/models/repo/topic_test.go @@ -53,7 +53,7 @@ func TestAddTopic(t *testing.T) { repo2NrOfTopics = 2 totalNrOfTopics++ topic := unittest.AssertExistsAndLoadBean(t, &repo_model.Topic{Name: "gitea"}) - assert.EqualValues(t, 1, topic.RepoCount) + assert.Equal(t, 1, topic.RepoCount) topics, _, err = repo_model.FindTopics(db.DefaultContext, &repo_model.FindTopicOptions{}) require.NoError(t, err) diff --git a/models/repo/watch_test.go b/models/repo/watch_test.go index 059489afbf..698f6a5f49 100644 --- a/models/repo/watch_test.go +++ b/models/repo/watch_test.go @@ -36,7 +36,7 @@ func TestGetWatchers(t *testing.T) { // One watchers are inactive, thus minus 1 assert.Len(t, watches, repo.NumWatches-1) for _, watch := range watches { - assert.EqualValues(t, repo.ID, watch.RepoID) + assert.Equal(t, repo.ID, watch.RepoID) } watches, err = repo_model.GetWatchers(db.DefaultContext, unittest.NonexistentID) diff --git a/models/repo_test.go b/models/repo_test.go index 6fbef8edf6..6d732632d2 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -30,10 +30,10 @@ func Test_repoStatsCorrectIssueNumComments(t *testing.T) { issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) assert.NotNil(t, issue2) - assert.EqualValues(t, 0, issue2.NumComments) // the fixture data is wrong, but we don't fix it here + assert.Equal(t, 0, issue2.NumComments) // the fixture data is wrong, but we don't fix it here require.NoError(t, repoStatsCorrectIssueNumComments(db.DefaultContext, 2)) // reload the issue issue2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) - assert.EqualValues(t, 1, issue2.NumComments) + assert.Equal(t, 1, issue2.NumComments) } diff --git a/models/repo_transfer_test.go b/models/repo_transfer_test.go index 6449e40fce..7f01ac2b97 100644 --- a/models/repo_transfer_test.go +++ b/models/repo_transfer_test.go @@ -23,6 +23,6 @@ func TestGetPendingTransferIDs(t *testing.T) { pendingTransferIDs, err := GetPendingTransferIDs(db.DefaultContext, reciepient.ID, doer.ID) require.NoError(t, err) if assert.Len(t, pendingTransferIDs, 1) { - assert.EqualValues(t, pendingTransfer.ID, pendingTransferIDs[0]) + assert.Equal(t, pendingTransfer.ID, pendingTransferIDs[0]) } } diff --git a/models/system/setting_test.go b/models/system/setting_test.go index 1abaf2f16b..c0376afa00 100644 --- a/models/system/setting_test.go +++ b/models/system/setting_test.go @@ -22,24 +22,24 @@ func TestSettings(t *testing.T) { rev, settings, err := system.GetAllSettings(db.DefaultContext) require.NoError(t, err) - assert.EqualValues(t, 1, rev) + assert.Equal(t, 1, rev) assert.Len(t, settings, 1) // there is only one "revision" key err = system.SetSettings(db.DefaultContext, map[string]string{keyName: "true"}) require.NoError(t, err) rev, settings, err = system.GetAllSettings(db.DefaultContext) require.NoError(t, err) - assert.EqualValues(t, 2, rev) + assert.Equal(t, 2, rev) assert.Len(t, settings, 2) - assert.EqualValues(t, "true", settings[keyName]) + assert.Equal(t, "true", settings[keyName]) err = system.SetSettings(db.DefaultContext, map[string]string{keyName: "false"}) require.NoError(t, err) rev, settings, err = system.GetAllSettings(db.DefaultContext) require.NoError(t, err) - assert.EqualValues(t, 3, rev) + assert.Equal(t, 3, rev) assert.Len(t, settings, 2) - assert.EqualValues(t, "false", settings[keyName]) + assert.Equal(t, "false", settings[keyName]) // setting the same value should not trigger DuplicateKey error, and the "version" should be increased err = system.SetSettings(db.DefaultContext, map[string]string{keyName: "false"}) @@ -48,5 +48,5 @@ func TestSettings(t *testing.T) { rev, settings, err = system.GetAllSettings(db.DefaultContext) require.NoError(t, err) assert.Len(t, settings, 2) - assert.EqualValues(t, 4, rev) + assert.Equal(t, 4, rev) } diff --git a/models/unittest/consistency.go b/models/unittest/consistency.go index fd2d4b7d75..de93b8f855 100644 --- a/models/unittest/consistency.go +++ b/models/unittest/consistency.go @@ -49,7 +49,7 @@ func checkForConsistency(t *testing.T, bean any) { require.NoError(t, err) f := consistencyCheckMap[tb.Name] if f == nil { - assert.FailNow(t, "unknown bean type: %#v", bean) + assert.FailNow(t, "unknown bean type", "%#v", bean) } f(t, bean) } @@ -73,8 +73,8 @@ func init() { AssertCountByCond(t, "follow", builder.Eq{"user_id": user.int("ID")}, user.int("NumFollowing")) AssertCountByCond(t, "follow", builder.Eq{"follow_id": user.int("ID")}, user.int("NumFollowers")) if user.int("Type") != modelsUserTypeOrganization { - assert.EqualValues(t, 0, user.int("NumMembers"), "Unexpected number of members for user id: %d", user.int("ID")) - assert.EqualValues(t, 0, user.int("NumTeams"), "Unexpected number of teams for user id: %d", user.int("ID")) + assert.Equal(t, 0, user.int("NumMembers"), "Unexpected number of members for user id: %d", user.int("ID")) + assert.Equal(t, 0, user.int("NumTeams"), "Unexpected number of teams for user id: %d", user.int("ID")) } } @@ -121,7 +121,7 @@ func init() { assert.EqualValues(t, issue.int("NumComments"), actual, "Unexpected number of comments for issue id: %d", issue.int("ID")) if issue.bool("IsPull") { prRow := AssertExistsAndLoadMap(t, "pull_request", builder.Eq{"issue_id": issue.int("ID")}) - assert.EqualValues(t, parseInt(prRow["index"]), issue.int("Index"), "Unexpected index for issue id: %d", issue.int("ID")) + assert.Equal(t, parseInt(prRow["index"]), issue.int("Index"), "Unexpected index for issue id: %d", issue.int("ID")) } } @@ -129,7 +129,7 @@ func init() { pr := reflectionWrap(bean) issueRow := AssertExistsAndLoadMap(t, "issue", builder.Eq{"id": pr.int("IssueID")}) assert.True(t, parseBool(issueRow["is_pull"])) - assert.EqualValues(t, parseInt(issueRow["index"]), pr.int("Index"), "Unexpected index for pull request id: %d", pr.int("ID")) + assert.Equal(t, parseInt(issueRow["index"]), pr.int("Index"), "Unexpected index for pull request id: %d", pr.int("ID")) } checkForMilestoneConsistency := func(t *testing.T, bean any) { diff --git a/models/unittest/fixtures.go b/models/unittest/fixtures.go index 6402fd9466..940830d20f 100644 --- a/models/unittest/fixtures.go +++ b/models/unittest/fixtures.go @@ -22,11 +22,11 @@ import ( var fixturesLoader *testfixtures.Loader // GetXORMEngine gets the XORM engine -func GetXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) { +func GetXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine, err error) { if len(engine) == 1 { - return engine[0] + return engine[0], nil } - return db.DefaultContext.(*db.Context).Engine().(*xorm.Engine) + return db.GetMasterEngine(db.DefaultContext.(*db.Context).Engine()) } func OverrideFixtures(opts FixturesOptions, engine ...*xorm.Engine) func() { @@ -41,7 +41,10 @@ func OverrideFixtures(opts FixturesOptions, engine ...*xorm.Engine) func() { // InitFixtures initialize test fixtures for a test database func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { - e := GetXORMEngine(engine...) + e, err := GetXORMEngine(engine...) + if err != nil { + return err + } var fixtureOptionFiles func(*testfixtures.Loader) error if opts.Dir != "" { fixtureOptionFiles = testfixtures.Directory(opts.Dir) @@ -93,10 +96,12 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { // LoadFixtures load fixtures for a test database func LoadFixtures(engine ...*xorm.Engine) error { - e := GetXORMEngine(engine...) - var err error + e, err := GetXORMEngine(engine...) + if err != nil { + return err + } // (doubt) database transaction conflicts could occur and result in ROLLBACK? just try for a few times. - for i := 0; i < 5; i++ { + for range 5 { if err = fixturesLoader.Load(); err == nil { break } diff --git a/models/unittest/mock_http.go b/models/unittest/mock_http.go index e749275282..6064e07e9b 100644 --- a/models/unittest/mock_http.go +++ b/models/unittest/mock_http.go @@ -67,7 +67,7 @@ func NewMockWebServer(t *testing.T, liveServerBaseURL, testDataDir string, liveM for headerName, headerValues := range response.Header { for _, headerValue := range headerValues { if !slices.Contains(ignoredHeaders, strings.ToLower(headerName)) { - _, err := fixtureWriter.WriteString(fmt.Sprintf("%s: %s\n", headerName, headerValue)) + _, err := fmt.Fprintf(fixtureWriter, "%s: %s\n", headerName, headerValue) require.NoError(t, err, "writing the header of the HTTP response to the fixture file failed") } } diff --git a/models/user/activitypub.go b/models/user/activitypub.go new file mode 100644 index 0000000000..490615239c --- /dev/null +++ b/models/user/activitypub.go @@ -0,0 +1,44 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package user + +import ( + "context" + "fmt" + "net/url" + + "forgejo.org/models/db" + "forgejo.org/modules/setting" + "forgejo.org/modules/validation" +) + +// APActorID returns the IRI to the api endpoint of the user +func (u *User) APActorID() string { + if u.IsAPServerActor() { + return fmt.Sprintf("%sapi/v1/activitypub/actor", setting.AppURL) + } + + return fmt.Sprintf("%sapi/v1/activitypub/user-id/%s", setting.AppURL, url.PathEscape(fmt.Sprintf("%d", u.ID))) +} + +// APActorKeyID returns the ID of the user's public key +func (u *User) APActorKeyID() string { + return u.APActorID() + "#main-key" +} + +func GetUserByFederatedURI(ctx context.Context, federatedURI string) (*User, error) { + user := new(User) + has, err := db.GetEngine(ctx).Where("normalized_federated_uri=?", federatedURI).Get(user) + if err != nil { + return nil, err + } else if !has { + return nil, nil + } + + if res, err := validation.IsValid(*user); !res { + return nil, err + } + + return user, nil +} diff --git a/models/user/email_address.go b/models/user/email_address.go index f9eaec56c9..a3c33ffc00 100644 --- a/models/user/email_address.go +++ b/models/user/email_address.go @@ -266,9 +266,7 @@ func updateActivation(ctx context.Context, email *EmailAddress, activate bool) e if err != nil { return err } - if user.Rands, err = GetUserSalt(); err != nil { - return err - } + user.Rands = GetUserSalt() email.IsActivated = activate if _, err := db.GetEngine(ctx).ID(email.ID).Cols("is_activated").Update(email); err != nil { return err @@ -403,9 +401,7 @@ func ActivateUserEmail(ctx context.Context, userID int64, email string, activate // The user's activation state should be synchronized with the primary email if user.IsActive != activate { user.IsActive = activate - if user.Rands, err = GetUserSalt(); err != nil { - return fmt.Errorf("unable to generate salt: %w", err) - } + user.Rands = GetUserSalt() if err = UpdateUserCols(ctx, user, "is_active", "rands"); err != nil { return fmt.Errorf("unable to updateUserCols() for user ID: %d: %w", userID, err) } diff --git a/models/user/federated_user.go b/models/user/federated_user.go index fc07836408..d32f42867d 100644 --- a/models/user/federated_user.go +++ b/models/user/federated_user.go @@ -4,14 +4,20 @@ package user import ( + "context" + "database/sql" + + "forgejo.org/models/db" "forgejo.org/modules/validation" ) type FederatedUser struct { - ID int64 `xorm:"pk autoincr"` - UserID int64 `xorm:"NOT NULL"` - ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` - FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"NOT NULL"` + ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + KeyID sql.NullString `xorm:"key_id UNIQUE"` + PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"` } func NewFederatedUser(userID int64, externalID string, federationHostID int64) (FederatedUser, error) { @@ -26,6 +32,30 @@ func NewFederatedUser(userID int64, externalID string, federationHostID int64) ( return result, nil } +func getFederatedUserFromDB(ctx context.Context, searchKey, searchValue any) (*FederatedUser, error) { + federatedUser := new(FederatedUser) + has, err := db.GetEngine(ctx).Where(searchKey, searchValue).Get(federatedUser) + if err != nil { + return nil, err + } else if !has { + return nil, nil + } + + if res, err := validation.IsValid(*federatedUser); !res { + return nil, err + } + + return federatedUser, nil +} + +func GetFederatedUserByKeyID(ctx context.Context, keyID string) (*FederatedUser, error) { + return getFederatedUserFromDB(ctx, "key_id=?", keyID) +} + +func GetFederatedUserByUserID(ctx context.Context, userID int64) (*FederatedUser, error) { + return getFederatedUserFromDB(ctx, "user_id=?", userID) +} + func (user FederatedUser) Validate() []string { var result []string result = append(result, validation.ValidateNotEmpty(user.UserID, "UserID")...) diff --git a/models/user/moderation.go b/models/user/moderation.go index 81a0c86610..afda497f02 100644 --- a/models/user/moderation.go +++ b/models/user/moderation.go @@ -7,6 +7,7 @@ import ( "context" "reflect" "slices" + "sync" "forgejo.org/models/moderation" "forgejo.org/modules/json" @@ -17,7 +18,7 @@ import ( // UserData represents a trimmed down user that is used for preserving // only the fields needed for abusive content reports (mainly string fields). -type UserData struct { +type UserData struct { //revive:disable-line:exported Name string FullName string Email string @@ -56,9 +57,21 @@ func newUserData(user *User) UserData { } } +// userDataColumnNames builds (only once) and returns a slice with the column names +// (e.g. FieldName -> field_name) corresponding to UserData struct fields. +var userDataColumnNames = sync.OnceValue(func() []string { + mapper := new(names.GonicMapper) + udType := reflect.TypeOf(UserData{}) + columnNames := make([]string, 0, udType.NumField()) + for i := 0; i < udType.NumField(); i++ { + columnNames = append(columnNames, mapper.Obj2Table(udType.Field(i).Name)) + } + return columnNames +}) + // IfNeededCreateShadowCopyForUser checks if for the given user there are any reports of abusive content submitted // and if found a shadow copy of relevant user fields will be stored into DB and linked to the above report(s). -// This function should be called when a user is deleted or updated. +// This function should be called before a user is deleted or updated. // // For deletions alteredCols argument must be omitted. // @@ -67,23 +80,26 @@ func newUserData(user *User) UserData { func IfNeededCreateShadowCopyForUser(ctx context.Context, user *User, alteredCols ...string) error { // TODO: this can be triggered quite often (e.g. by routers/web/repo/middlewares.go SetDiffViewStyle()) - shouldCheckIfReported := len(alteredCols) == 0 // no columns being updated, therefore a deletion - if !shouldCheckIfReported { + shouldCheckIfNeeded := len(alteredCols) == 0 // no columns being updated, therefore a deletion + if !shouldCheckIfNeeded { // for updates we need to go further only if certain column are being changed - mapper := new(names.GonicMapper) - ucType := reflect.TypeOf(UserData{}) - - for i := 0; i < ucType.NumField(); i++ { - field := ucType.Field(i) - // get the corresponding column name for a field name (e.g. FieldName -> field_name). - colName := mapper.Obj2Table(field.Name) - if shouldCheckIfReported = slices.Contains(alteredCols, colName); shouldCheckIfReported { + for _, colName := range userDataColumnNames() { + if shouldCheckIfNeeded = slices.Contains(alteredCols, colName); shouldCheckIfNeeded { break } } } - if shouldCheckIfReported && moderation.IsReported(ctx, moderation.ReportedContentTypeUser, user.ID) { + if !shouldCheckIfNeeded { + return nil + } + + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, user.ID) + if err != nil { + return err + } + + if shadowCopyNeeded { userData := newUserData(user) content, err := json.Marshal(userData) if err != nil { diff --git a/models/user/search.go b/models/user/search.go index d2b9901823..b30422e269 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -52,13 +52,14 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess cond = builder.Eq{"type": opts.Type} } if opts.IncludeReserved { - if opts.Type == UserTypeIndividual { + switch opts.Type { + case UserTypeIndividual: cond = cond.Or(builder.Eq{"type": UserTypeUserReserved}).Or( builder.Eq{"type": UserTypeBot}, ).Or( builder.Eq{"type": UserTypeRemoteUser}, ) - } else if opts.Type == UserTypeOrganization { + case UserTypeOrganization: cond = cond.Or(builder.Eq{"type": UserTypeOrganizationReserved}) } } diff --git a/models/user/setting_test.go b/models/user/setting_test.go index 7b6658041f..9c71821875 100644 --- a/models/user/setting_test.go +++ b/models/user/setting_test.go @@ -31,15 +31,15 @@ func TestSettings(t *testing.T) { settings, err := user_model.GetSettings(db.DefaultContext, 99, []string{keyName}) require.NoError(t, err) assert.Len(t, settings, 1) - assert.EqualValues(t, newSetting.SettingValue, settings[keyName].SettingValue) + assert.Equal(t, newSetting.SettingValue, settings[keyName].SettingValue) settingValue, err := user_model.GetUserSetting(db.DefaultContext, 99, keyName) require.NoError(t, err) - assert.EqualValues(t, newSetting.SettingValue, settingValue) + assert.Equal(t, newSetting.SettingValue, settingValue) settingValue, err = user_model.GetUserSetting(db.DefaultContext, 99, "no_such") require.NoError(t, err) - assert.EqualValues(t, "", settingValue) + assert.Empty(t, settingValue) // updated setting updatedSetting := &user_model.Setting{UserID: 99, SettingKey: keyName, SettingValue: "Updated"} @@ -50,7 +50,7 @@ func TestSettings(t *testing.T) { settings, err = user_model.GetUserAllSettings(db.DefaultContext, 99) require.NoError(t, err) assert.Len(t, settings, 1) - assert.EqualValues(t, updatedSetting.SettingValue, settings[updatedSetting.SettingKey].SettingValue) + assert.Equal(t, updatedSetting.SettingValue, settings[updatedSetting.SettingKey].SettingValue) // delete setting err = user_model.DeleteUserSetting(db.DefaultContext, 99, keyName) diff --git a/models/user/user.go b/models/user/user.go index 37d48b1bfa..9ad403ae4f 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -314,11 +314,6 @@ func (u *User) HTMLURL() string { return setting.AppURL + url.PathEscape(u.Name) } -// APActorID returns the IRI to the api endpoint of the user -func (u *User) APActorID() string { - return fmt.Sprintf("%vapi/v1/activitypub/user-id/%v", setting.AppURL, url.PathEscape(fmt.Sprintf("%v", u.ID))) -} - // OrganisationLink returns the organization sub page link. func (u *User) OrganisationLink() string { return setting.AppSubURL + "/org/" + url.PathEscape(u.Name) @@ -395,9 +390,7 @@ func (u *User) SetPassword(passwd string) (err error) { return err } - if u.Salt, err = GetUserSalt(); err != nil { - return err - } + u.Salt = GetUserSalt() if u.Passwd, err = hash.Parse(setting.PasswordHashAlgo).Hash(passwd, u.Salt); err != nil { return err } @@ -567,13 +560,9 @@ func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) { const SaltByteLength = 16 // GetUserSalt returns a random user salt token. -func GetUserSalt() (string, error) { - rBytes, err := util.CryptoRandomBytes(SaltByteLength) - if err != nil { - return "", err - } +func GetUserSalt() string { // Returns a 32 bytes long string. - return hex.EncodeToString(rBytes), nil + return hex.EncodeToString(util.CryptoRandomBytes(SaltByteLength)) } // Note: The set of characters here can safely expand without a breaking change, @@ -704,7 +693,7 @@ func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefa u.MaxRepoCreation = -1 u.Theme = setting.UI.DefaultTheme u.IsRestricted = setting.Service.DefaultUserIsRestricted - u.IsActive = !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm) + u.IsActive = !setting.Service.RegisterEmailConfirm && !setting.Service.RegisterManualConfirm // Ensure consistency of the dates. if u.UpdatedUnix < u.CreatedUnix { @@ -781,9 +770,7 @@ func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefa u.LowerName = strings.ToLower(u.Name) u.AvatarEmail = u.Email - if u.Rands, err = GetUserSalt(); err != nil { - return err - } + u.Rands = GetUserSalt() if u.Passwd != "" { if err = u.SetPassword(u.Passwd); err != nil { return err diff --git a/models/user/user_system.go b/models/user/user_system.go index f1585b512a..82805cc8ee 100644 --- a/models/user/user_system.go +++ b/models/user/user_system.go @@ -73,30 +73,30 @@ func (u *User) IsActions() bool { } const ( - APActorUserID = -3 - APActorUserName = "actor" - APActorEmail = "noreply@forgejo.org" + APServerActorUserID = -3 + APServerActorUserName = "actor" + APServerActorEmail = "noreply@forgejo.org" ) -func NewAPActorUser() *User { +func NewAPServerActor() *User { return &User{ - ID: APActorUserID, - Name: APActorUserName, - LowerName: APActorUserName, + ID: APServerActorUserID, + Name: APServerActorUserName, + LowerName: APServerActorUserName, IsActive: true, - Email: APActorEmail, + Email: APServerActorEmail, KeepEmailPrivate: true, - LoginName: APActorUserName, + LoginName: APServerActorUserName, Type: UserTypeIndividual, Visibility: structs.VisibleTypePublic, } } -func APActorUserAPActorID() string { +func APServerActorID() string { path, _ := url.JoinPath(setting.AppURL, "/api/v1/activitypub/actor") return path } -func (u *User) IsAPActor() bool { - return u != nil && u.ID == APActorUserID +func (u *User) IsAPServerActor() bool { + return u != nil && u.ID == APServerActorUserID } diff --git a/models/user/user_test.go b/models/user/user_test.go index dde0146470..52ebe2e204 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -139,9 +139,21 @@ func TestAPActorID(t *testing.T) { user := user_model.User{ID: 1} url := user.APActorID() expected := "https://try.gitea.io/api/v1/activitypub/user-id/1" - if url != expected { - t.Errorf("unexpected APActorID, expected: %q, actual: %q", expected, url) - } + assert.Equal(t, expected, url) +} + +func TestAPActorID_APActorID(t *testing.T) { + user := user_model.User{ID: user_model.APServerActorUserID} + url := user.APActorID() + expected := "https://try.gitea.io/api/v1/activitypub/actor" + assert.Equal(t, expected, url) +} + +func TestAPActorKeyID(t *testing.T) { + user := user_model.User{ID: 1} + url := user.APActorKeyID() + expected := "https://try.gitea.io/api/v1/activitypub/user-id/1#main-key" + assert.Equal(t, expected, url) } func TestSearchUsers(t *testing.T) { @@ -153,7 +165,7 @@ func TestSearchUsers(t *testing.T) { cassText := fmt.Sprintf("ids: %v, opts: %v", expectedUserOrOrgIDs, opts) if assert.Len(t, users, len(expectedUserOrOrgIDs), "case: %s", cassText) { for i, expectedID := range expectedUserOrOrgIDs { - assert.EqualValues(t, expectedID, users[i].ID, "case: %s", cassText) + assert.Equal(t, expectedID, users[i].ID, "case: %s", cassText) } } } @@ -616,7 +628,7 @@ func Test_ValidateUser(t *testing.T) { {ID: 2, Visibility: structs.VisibleTypePrivate}: true, } for kase, expected := range kases { - assert.EqualValues(t, expected, nil == user_model.ValidateUser(kase)) + assert.Equal(t, expected, nil == user_model.ValidateUser(kase)) } } @@ -644,7 +656,7 @@ func Test_NormalizeUserFromEmail(t *testing.T) { for _, testCase := range testCases { normalizedName, err := user_model.NormalizeUserName(testCase.Input) require.NoError(t, err) - assert.EqualValues(t, testCase.Expected, normalizedName) + assert.Equal(t, testCase.Expected, normalizedName) if testCase.IsNormalizedValid { require.NoError(t, user_model.IsUsableUsername(normalizedName)) } else { @@ -674,13 +686,13 @@ func TestEmailTo(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.result, func(t *testing.T) { testUser := &user_model.User{FullName: testCase.fullName, Email: testCase.mail} - assert.EqualValues(t, testCase.result, testUser.EmailTo()) + assert.Equal(t, testCase.result, testUser.EmailTo()) }) } t.Run("Override user's email", func(t *testing.T) { testUser := &user_model.User{FullName: "Christine Jorgensen", Email: "christine@test.com"} - assert.EqualValues(t, `"Christine Jorgensen" <christine@example.org>`, testUser.EmailTo("christine@example.org")) + assert.Equal(t, `"Christine Jorgensen" <christine@example.org>`, testUser.EmailTo("christine@example.org")) }) } @@ -736,7 +748,7 @@ func TestGenerateEmailAuthorizationCode(t *testing.T) { authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation) require.NoError(t, err) assert.False(t, authToken.IsExpired()) - assert.EqualValues(t, authToken.HashedValidator, auth.HashValidator(rawValidator)) + assert.Equal(t, authToken.HashedValidator, auth.HashValidator(rawValidator)) authToken.Expiry = authToken.Expiry.Add(-int64(setting.Service.ActiveCodeLives) * 60) assert.True(t, authToken.IsExpired()) @@ -763,7 +775,7 @@ func TestVerifyUserAuthorizationToken(t *testing.T) { t.Run("No delete", func(t *testing.T) { u, _, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation) require.NoError(t, err) - assert.EqualValues(t, user.ID, u.ID) + assert.Equal(t, user.ID, u.ID) authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation) require.NoError(t, err) @@ -773,7 +785,7 @@ func TestVerifyUserAuthorizationToken(t *testing.T) { t.Run("Delete", func(t *testing.T) { u, deleteToken, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation) require.NoError(t, err) - assert.EqualValues(t, user.ID, u.ID) + assert.Equal(t, user.ID, u.ID) require.NoError(t, deleteToken()) authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation) @@ -803,14 +815,14 @@ func TestPronounsPrivacy(t *testing.T) { user.Pronouns = "" user.KeepPronounsPrivate = false - assert.Equal(t, "", user.GetPronouns(false)) + assert.Empty(t, user.GetPronouns(false)) }) t.Run("EmptyPronounsIfSetButPrivateAndNotLoggedIn", func(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) user.Pronouns = "any" user.KeepPronounsPrivate = true - assert.Equal(t, "", user.GetPronouns(false)) + assert.Empty(t, user.GetPronouns(false)) }) t.Run("ReturnPronounsIfSetAndNotPrivateAndNotLoggedIn", func(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go index 58600cb8bf..f5f73842ae 100644 --- a/models/webhook/hooktask.go +++ b/models/webhook/hooktask.go @@ -198,7 +198,8 @@ func MarkTaskDelivered(ctx context.Context, task *HookTask) (bool, error) { func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType, olderThan time.Duration, numberToKeep int) error { log.Trace("Doing: CleanupHookTaskTable") - if cleanupType == OlderThan { + switch cleanupType { + case OlderThan: deleteOlderThan := time.Now().Add(-olderThan).UnixNano() deletes, err := db.GetEngine(ctx). Where("is_delivered = ? and delivered < ?", true, deleteOlderThan). @@ -207,7 +208,7 @@ func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType, return err } log.Trace("Deleted %d rows from hook_task", deletes) - } else if cleanupType == PerWebhook { + case PerWebhook: hookIDs := make([]int64, 0, 10) err := db.GetEngine(ctx). Table("webhook"). diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index 0691f231b2..356f5fdcc9 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -170,139 +170,139 @@ func (w *Webhook) UpdateEvent() error { // HasCreateEvent returns true if hook enabled create event. func (w *Webhook) HasCreateEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.Create) + (w.ChooseEvents && w.Create) } // HasDeleteEvent returns true if hook enabled delete event. func (w *Webhook) HasDeleteEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.Delete) + (w.ChooseEvents && w.Delete) } // HasForkEvent returns true if hook enabled fork event. func (w *Webhook) HasForkEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.Fork) + (w.ChooseEvents && w.Fork) } // HasIssuesEvent returns true if hook enabled issues event. func (w *Webhook) HasIssuesEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.Issues) + (w.ChooseEvents && w.Issues) } // HasIssuesAssignEvent returns true if hook enabled issues assign event. func (w *Webhook) HasIssuesAssignEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.IssueAssign) + (w.ChooseEvents && w.IssueAssign) } // HasIssuesLabelEvent returns true if hook enabled issues label event. func (w *Webhook) HasIssuesLabelEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.IssueLabel) + (w.ChooseEvents && w.IssueLabel) } // HasIssuesMilestoneEvent returns true if hook enabled issues milestone event. func (w *Webhook) HasIssuesMilestoneEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.IssueMilestone) + (w.ChooseEvents && w.IssueMilestone) } // HasIssueCommentEvent returns true if hook enabled issue_comment event. func (w *Webhook) HasIssueCommentEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.IssueComment) + (w.ChooseEvents && w.IssueComment) } // HasPushEvent returns true if hook enabled push event. func (w *Webhook) HasPushEvent() bool { return w.PushOnly || w.SendEverything || - (w.ChooseEvents && w.HookEvents.Push) + (w.ChooseEvents && w.Push) } // HasPullRequestEvent returns true if hook enabled pull request event. func (w *Webhook) HasPullRequestEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.PullRequest) + (w.ChooseEvents && w.PullRequest) } // HasPullRequestAssignEvent returns true if hook enabled pull request assign event. func (w *Webhook) HasPullRequestAssignEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.PullRequestAssign) + (w.ChooseEvents && w.PullRequestAssign) } // HasPullRequestLabelEvent returns true if hook enabled pull request label event. func (w *Webhook) HasPullRequestLabelEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.PullRequestLabel) + (w.ChooseEvents && w.PullRequestLabel) } // HasPullRequestMilestoneEvent returns true if hook enabled pull request milestone event. func (w *Webhook) HasPullRequestMilestoneEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.PullRequestMilestone) + (w.ChooseEvents && w.PullRequestMilestone) } // HasPullRequestCommentEvent returns true if hook enabled pull_request_comment event. func (w *Webhook) HasPullRequestCommentEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.PullRequestComment) + (w.ChooseEvents && w.PullRequestComment) } // HasPullRequestApprovedEvent returns true if hook enabled pull request review event. func (w *Webhook) HasPullRequestApprovedEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.PullRequestReview) + (w.ChooseEvents && w.PullRequestReview) } // HasPullRequestRejectedEvent returns true if hook enabled pull request review event. func (w *Webhook) HasPullRequestRejectedEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.PullRequestReview) + (w.ChooseEvents && w.PullRequestReview) } // HasPullRequestReviewCommentEvent returns true if hook enabled pull request review event. func (w *Webhook) HasPullRequestReviewCommentEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.PullRequestReview) + (w.ChooseEvents && w.PullRequestReview) } // HasPullRequestSyncEvent returns true if hook enabled pull request sync event. func (w *Webhook) HasPullRequestSyncEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.PullRequestSync) + (w.ChooseEvents && w.PullRequestSync) } // HasWikiEvent returns true if hook enabled wiki event. func (w *Webhook) HasWikiEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvent.Wiki) + (w.ChooseEvents && w.Wiki) } // HasReleaseEvent returns if hook enabled release event. func (w *Webhook) HasReleaseEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.Release) + (w.ChooseEvents && w.Release) } // HasRepositoryEvent returns if hook enabled repository event. func (w *Webhook) HasRepositoryEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.Repository) + (w.ChooseEvents && w.Repository) } // HasPackageEvent returns if hook enabled package event. func (w *Webhook) HasPackageEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.Package) + (w.ChooseEvents && w.Package) } // HasPullRequestReviewRequestEvent returns true if hook enabled pull request review request event. func (w *Webhook) HasPullRequestReviewRequestEvent() bool { return w.SendEverything || - (w.ChooseEvents && w.HookEvents.PullRequestReviewRequest) + (w.ChooseEvents && w.PullRequestReviewRequest) } // EventCheckers returns event checkers diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go index d43e9c2bb0..91c767c589 100644 --- a/modules/activitypub/client.go +++ b/modules/activitypub/client.go @@ -191,10 +191,17 @@ func (c *Client) GetBody(uri string) ([]byte, error) { return nil, err } defer response.Body.Close() - body, err := io.ReadAll(response.Body) + if response.ContentLength > setting.Federation.MaxSize { + return nil, fmt.Errorf("Request returned %d bytes (max allowed incomming size: %d bytes)", response.ContentLength, setting.Federation.MaxSize) + } else if response.ContentLength == -1 { + log.Warn("Request to %v returned an unknown content length, response may be truncated to %d bytes", uri, setting.Federation.MaxSize) + } + + body, err := io.ReadAll(io.LimitReader(response.Body, setting.Federation.MaxSize)) if err != nil { return nil, err } + log.Debug("Client: got body: %v", charLimiter(string(body), 120)) return body, nil } diff --git a/modules/assetfs/layered_test.go b/modules/assetfs/layered_test.go index 58876d9be2..87d1f92b00 100644 --- a/modules/assetfs/layered_test.go +++ b/modules/assetfs/layered_test.go @@ -53,7 +53,7 @@ func TestLayered(t *testing.T) { require.NoError(t, err) bs, err := io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "f1", string(bs)) + assert.Equal(t, "f1", string(bs)) _ = f.Close() assertRead := func(expected string, expectedErr error, elems ...string) { @@ -77,27 +77,27 @@ func TestLayered(t *testing.T) { files, err := assets.ListFiles(".", true) require.NoError(t, err) - assert.EqualValues(t, []string{"f1", "f2", "fa"}, files) + assert.Equal(t, []string{"f1", "f2", "fa"}, files) files, err = assets.ListFiles(".", false) require.NoError(t, err) - assert.EqualValues(t, []string{"d1", "d2", "da"}, files) + assert.Equal(t, []string{"d1", "d2", "da"}, files) files, err = assets.ListFiles(".") require.NoError(t, err) - assert.EqualValues(t, []string{"d1", "d2", "da", "f1", "f2", "fa"}, files) + assert.Equal(t, []string{"d1", "d2", "da", "f1", "f2", "fa"}, files) files, err = assets.ListAllFiles(".", true) require.NoError(t, err) - assert.EqualValues(t, []string{"d1/f", "d2/f", "da/f", "f1", "f2", "fa"}, files) + assert.Equal(t, []string{"d1/f", "d2/f", "da/f", "f1", "f2", "fa"}, files) files, err = assets.ListAllFiles(".", false) require.NoError(t, err) - assert.EqualValues(t, []string{"d1", "d2", "da", "da/sub1", "da/sub2"}, files) + assert.Equal(t, []string{"d1", "d2", "da", "da/sub1", "da/sub2"}, files) files, err = assets.ListAllFiles(".") require.NoError(t, err) - assert.EqualValues(t, []string{ + assert.Equal(t, []string{ "d1", "d1/f", "d2", "d2/f", "da", "da/f", "da/sub1", "da/sub2", @@ -105,6 +105,6 @@ func TestLayered(t *testing.T) { }, files) assert.Empty(t, assets.GetFileLayerName("no-such")) - assert.EqualValues(t, "l1", assets.GetFileLayerName("f1")) - assert.EqualValues(t, "l2", assets.GetFileLayerName("f2")) + assert.Equal(t, "l1", assets.GetFileLayerName("f1")) + assert.Equal(t, "l2", assets.GetFileLayerName("f2")) } diff --git a/modules/avatar/avatar_test.go b/modules/avatar/avatar_test.go index 2166ca51b0..5c21ad5824 100644 --- a/modules/avatar/avatar_test.go +++ b/modules/avatar/avatar_test.go @@ -95,8 +95,8 @@ func Test_ProcessAvatarImage(t *testing.T) { assert.NotEqual(t, origin, result) decoded, err := png.Decode(bytes.NewReader(result)) require.NoError(t, err) - assert.EqualValues(t, scaledSize, decoded.Bounds().Max.X) - assert.EqualValues(t, scaledSize, decoded.Bounds().Max.Y) + assert.Equal(t, scaledSize, decoded.Bounds().Max.X) + assert.Equal(t, scaledSize, decoded.Bounds().Max.Y) // if origin image is smaller than the default size, use the origin image origin = newImgData(1) diff --git a/modules/avatar/hash_test.go b/modules/avatar/hash_test.go index 0a2db53ad4..8ee27131a8 100644 --- a/modules/avatar/hash_test.go +++ b/modules/avatar/hash_test.go @@ -19,8 +19,8 @@ func Test_HashAvatar(t *testing.T) { var buff bytes.Buffer png.Encode(&buff, myImage) - assert.EqualValues(t, "9ddb5bac41d57e72aa876321d0c09d71090c05f94bc625303801be2f3240d2cb", avatar.HashAvatar(1, buff.Bytes())) - assert.EqualValues(t, "9a5d44e5d637b9582a976676e8f3de1dccd877c2fe3e66ca3fab1629f2f47609", avatar.HashAvatar(8, buff.Bytes())) - assert.EqualValues(t, "ed7399158672088770de6f5211ce15528ebd675e92fc4fc060c025f4b2794ccb", avatar.HashAvatar(1024, buff.Bytes())) - assert.EqualValues(t, "161178642c7d59eb25a61dddced5e6b66eae1c70880d5f148b1b497b767e72d9", avatar.HashAvatar(1024, []byte{})) + assert.Equal(t, "9ddb5bac41d57e72aa876321d0c09d71090c05f94bc625303801be2f3240d2cb", avatar.HashAvatar(1, buff.Bytes())) + assert.Equal(t, "9a5d44e5d637b9582a976676e8f3de1dccd877c2fe3e66ca3fab1629f2f47609", avatar.HashAvatar(8, buff.Bytes())) + assert.Equal(t, "ed7399158672088770de6f5211ce15528ebd675e92fc4fc060c025f4b2794ccb", avatar.HashAvatar(1024, buff.Bytes())) + assert.Equal(t, "161178642c7d59eb25a61dddced5e6b66eae1c70880d5f148b1b497b767e72d9", avatar.HashAvatar(1024, []byte{})) } diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go index ed1b469161..aa57f0d77b 100644 --- a/modules/base/tool_test.go +++ b/modules/base/tool_test.go @@ -73,7 +73,7 @@ func TestEllipsisString(t *testing.T) { } func TestTruncateString(t *testing.T) { - assert.Equal(t, "", TruncateString("foobar", 0)) + assert.Empty(t, TruncateString("foobar", 0)) assert.Equal(t, "f", TruncateString("foobar", 1)) assert.Equal(t, "fo", TruncateString("foobar", 2)) assert.Equal(t, "foo", TruncateString("foobar", 3)) @@ -125,5 +125,5 @@ func TestFormatNumberSI(t *testing.T) { assert.Equal(t, "1.3k", FormatNumberSI(int64(1317))) assert.Equal(t, "21.3M", FormatNumberSI(21317675)) assert.Equal(t, "45.7G", FormatNumberSI(45721317675)) - assert.Equal(t, "", FormatNumberSI("test")) + assert.Empty(t, FormatNumberSI("test")) } diff --git a/modules/cache/cache_test.go b/modules/cache/cache_test.go index 8e931d5b2c..f7bc400598 100644 --- a/modules/cache/cache_test.go +++ b/modules/cache/cache_test.go @@ -48,19 +48,19 @@ func TestGetString(t *testing.T) { return "", fmt.Errorf("some error") }) require.Error(t, err) - assert.Equal(t, "", data) + assert.Empty(t, data) data, err = GetString("key", func() (string, error) { return "", nil }) require.NoError(t, err) - assert.Equal(t, "", data) + assert.Empty(t, data) data, err = GetString("key", func() (string, error) { return "some data", nil }) require.NoError(t, err) - assert.Equal(t, "", data) + assert.Empty(t, data) Remove("key") data, err = GetString("key", func() (string, error) { diff --git a/modules/cache/context_test.go b/modules/cache/context_test.go index 4f0f06f535..dd89986d0c 100644 --- a/modules/cache/context_test.go +++ b/modules/cache/context_test.go @@ -23,7 +23,7 @@ func TestWithCacheContext(t *testing.T) { SetContextData(ctx, field, "my_config1", 1) v = GetContextData(ctx, field, "my_config1") assert.NotNil(t, v) - assert.EqualValues(t, 1, v.(int)) + assert.Equal(t, 1, v.(int)) RemoveContextData(ctx, field, "my_config1") RemoveContextData(ctx, field, "my_config2") // remove a non-exist key @@ -35,7 +35,7 @@ func TestWithCacheContext(t *testing.T) { return 1, nil }) require.NoError(t, err) - assert.EqualValues(t, 1, vInt) + assert.Equal(t, 1, vInt) v = GetContextData(ctx, field, "my_config1") assert.EqualValues(t, 1, v) diff --git a/modules/charset/ambiguous_gen_test.go b/modules/charset/ambiguous_gen_test.go index 221c27d0e1..d3be0b1a13 100644 --- a/modules/charset/ambiguous_gen_test.go +++ b/modules/charset/ambiguous_gen_test.go @@ -14,7 +14,7 @@ import ( func TestAmbiguousCharacters(t *testing.T) { for locale, ambiguous := range AmbiguousCharacters { assert.Equal(t, locale, ambiguous.Locale) - assert.Equal(t, len(ambiguous.Confusable), len(ambiguous.With)) + assert.Len(t, ambiguous.With, len(ambiguous.Confusable)) assert.True(t, sort.SliceIsSorted(ambiguous.Confusable, func(i, j int) bool { return ambiguous.Confusable[i] < ambiguous.Confusable[j] })) diff --git a/modules/charset/charset_test.go b/modules/charset/charset_test.go index ef0d1565d6..358220494b 100644 --- a/modules/charset/charset_test.go +++ b/modules/charset/charset_test.go @@ -253,7 +253,7 @@ func TestToUTF8WithFallbackReader(t *testing.T) { input += "// Выключаем" rd := ToUTF8WithFallbackReader(bytes.NewReader([]byte(input)), ConvertOpts{}) r, _ := io.ReadAll(rd) - assert.EqualValuesf(t, input, string(r), "testing string len=%d", testLen) + assert.Equalf(t, input, string(r), "testing string len=%d", testLen) } truncatedOneByteExtension := failFastBytes diff --git a/modules/charset/escape_stream.go b/modules/charset/escape_stream.go index 01ebf52a15..2bc5df9539 100644 --- a/modules/charset/escape_stream.go +++ b/modules/charset/escape_stream.go @@ -151,7 +151,7 @@ func (e *escapeStreamer) brokenRune(bs []byte) error { e.escaped.Escaped = true e.escaped.HasBadRunes = true - if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{ + if err := e.StartTag("span", html.Attribute{ Key: "class", Val: "broken-code-point", }); err != nil { @@ -161,14 +161,14 @@ func (e *escapeStreamer) brokenRune(bs []byte) error { return err } - return e.PassthroughHTMLStreamer.EndTag("span") + return e.EndTag("span") } func (e *escapeStreamer) ambiguousRune(r, c rune) error { e.escaped.Escaped = true e.escaped.HasAmbiguous = true - if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{ + if err := e.StartTag("span", html.Attribute{ Key: "class", Val: "ambiguous-code-point", }, html.Attribute{ @@ -177,7 +177,7 @@ func (e *escapeStreamer) ambiguousRune(r, c rune) error { }); err != nil { return err } - if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{ + if err := e.StartTag("span", html.Attribute{ Key: "class", Val: "char", }); err != nil { @@ -186,18 +186,18 @@ func (e *escapeStreamer) ambiguousRune(r, c rune) error { if err := e.PassthroughHTMLStreamer.Text(string(r)); err != nil { return err } - if err := e.PassthroughHTMLStreamer.EndTag("span"); err != nil { + if err := e.EndTag("span"); err != nil { return err } - return e.PassthroughHTMLStreamer.EndTag("span") + return e.EndTag("span") } func (e *escapeStreamer) invisibleRune(r rune) error { e.escaped.Escaped = true e.escaped.HasInvisible = true - if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{ + if err := e.StartTag("span", html.Attribute{ Key: "class", Val: "escaped-code-point", }, html.Attribute{ @@ -206,7 +206,7 @@ func (e *escapeStreamer) invisibleRune(r rune) error { }); err != nil { return err } - if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{ + if err := e.StartTag("span", html.Attribute{ Key: "class", Val: "char", }); err != nil { @@ -215,11 +215,11 @@ func (e *escapeStreamer) invisibleRune(r rune) error { if err := e.PassthroughHTMLStreamer.Text(string(r)); err != nil { return err } - if err := e.PassthroughHTMLStreamer.EndTag("span"); err != nil { + if err := e.EndTag("span"); err != nil { return err } - return e.PassthroughHTMLStreamer.EndTag("span") + return e.EndTag("span") } type runeCountType struct { diff --git a/modules/charset/escape_test.go b/modules/charset/escape_test.go index eec6f102cb..0518045b29 100644 --- a/modules/charset/escape_test.go +++ b/modules/charset/escape_test.go @@ -190,5 +190,5 @@ func TestAmbiguousUnicodeDetectionContext(t *testing.T) { assert.EqualValues(t, `a<span class="escaped-code-point" data-escaped="[U+00A0]"><span class="char"> </span></span>test`, out) _, out = EscapeControlHTML(input, &translation.MockLocale{}, testContext) - assert.EqualValues(t, input, out) + assert.Equal(t, input, out) } diff --git a/modules/container/set_test.go b/modules/container/set_test.go index e54e31a052..af5e9126ab 100644 --- a/modules/container/set_test.go +++ b/modules/container/set_test.go @@ -36,7 +36,7 @@ func TestSet(t *testing.T) { called++ assert.True(t, slices.Contains(values, value)) } - assert.EqualValues(t, len(values), called) + assert.Equal(t, len(values), called) s = SetOf("key6", "key7") assert.False(t, s.Contains("key1")) diff --git a/modules/csv/csv_test.go b/modules/csv/csv_test.go index 6eb3b3056f..27cea46ca2 100644 --- a/modules/csv/csv_test.go +++ b/modules/csv/csv_test.go @@ -101,10 +101,10 @@ j, ,\x20 for n, c := range cases { rd, err := CreateReaderAndDetermineDelimiter(nil, strings.NewReader(decodeSlashes(t, c.csv))) require.NoError(t, err, "case %d: should not throw error: %v\n", n, err) - assert.EqualValues(t, c.expectedDelimiter, rd.Comma, "case %d: delimiter should be '%c', got '%c'", n, c.expectedDelimiter, rd.Comma) + assert.Equal(t, c.expectedDelimiter, rd.Comma, "case %d: delimiter should be '%c', got '%c'", n, c.expectedDelimiter, rd.Comma) rows, err := rd.ReadAll() require.NoError(t, err, "case %d: should not throw error: %v\n", n, err) - assert.EqualValues(t, c.expectedRows, rows, "case %d: rows should be equal", n) + assert.Equal(t, c.expectedRows, rows, "case %d: rows should be equal", n) } } @@ -236,7 +236,7 @@ John Doe john@doe.com This,note,had,a,lot,of,commas,to,test,delimiters`, Ctx: git.DefaultContext, RelativePath: c.filename, }, []byte(decodeSlashes(t, c.csv))) - assert.EqualValues(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter) + assert.Equal(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter) } } @@ -301,7 +301,7 @@ abc | |123 for n, c := range cases { modifiedText := removeQuotedString(decodeSlashes(t, c.text)) - assert.EqualValues(t, c.expectedText, modifiedText, "case %d: modified text should be equal", n) + assert.Equal(t, c.expectedText, modifiedText, "case %d: modified text should be equal", n) } } @@ -456,7 +456,7 @@ jkl`, for n, c := range cases { delimiter := guessDelimiter([]byte(decodeSlashes(t, c.csv))) - assert.EqualValues(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter) + assert.Equal(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter) } } @@ -548,7 +548,7 @@ a|"he said, ""here I am"""`, for n, c := range cases { delimiter := guessFromBeforeAfterQuotes([]byte(decodeSlashes(t, c.csv))) - assert.EqualValues(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter) + assert.Equal(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter) } } @@ -584,7 +584,7 @@ func TestFormatError(t *testing.T) { require.Error(t, err, "case %d: expected an error to be returned", n) } else { require.NoError(t, err, "case %d: no error was expected, got error: %v", n, err) - assert.EqualValues(t, c.expectedMessage, message, "case %d: messages should be equal, expected '%s' got '%s'", n, c.expectedMessage, message) + assert.Equal(t, c.expectedMessage, message, "case %d: messages should be equal, expected '%s' got '%s'", n, c.expectedMessage, message) } } } diff --git a/modules/generate/generate.go b/modules/generate/generate.go index 9738195da9..16a797ef5a 100644 --- a/modules/generate/generate.go +++ b/modules/generate/generate.go @@ -5,10 +5,8 @@ package generate import ( - "crypto/rand" "encoding/base64" "fmt" - "io" "time" "forgejo.org/modules/util" @@ -18,18 +16,11 @@ import ( // NewInternalToken generate a new value intended to be used by INTERNAL_TOKEN. func NewInternalToken() (string, error) { - secretBytes := make([]byte, 32) - _, err := io.ReadFull(rand.Reader, secretBytes) - if err != nil { - return "", err - } - - secretKey := base64.RawURLEncoding.EncodeToString(secretBytes) + secretKey := base64.RawURLEncoding.EncodeToString(util.CryptoRandomBytes(32)) now := time.Now() - var internalToken string - internalToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + internalToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "nbf": now.Unix(), }).SignedString([]byte(secretKey)) if err != nil { @@ -54,14 +45,9 @@ func DecodeJwtSecret(src string) ([]byte, error) { } // NewJwtSecret generates a new base64 encoded value intended to be used for JWT secrets. -func NewJwtSecret() ([]byte, string, error) { - bytes := make([]byte, 32) - _, err := rand.Read(bytes) - if err != nil { - return nil, "", err - } - - return bytes, base64.RawURLEncoding.EncodeToString(bytes), nil +func NewJwtSecret() ([]byte, string) { + bytes := util.CryptoRandomBytes(32) + return bytes, base64.RawURLEncoding.EncodeToString(bytes) } // NewSecretKey generate a new value intended to be used by SECRET_KEY. diff --git a/modules/generate/generate_test.go b/modules/generate/generate_test.go index eb7178af33..b51d17deb2 100644 --- a/modules/generate/generate_test.go +++ b/modules/generate/generate_test.go @@ -26,8 +26,7 @@ func TestDecodeJwtSecret(t *testing.T) { } func TestNewJwtSecret(t *testing.T) { - secret, encoded, err := NewJwtSecret() - require.NoError(t, err) + secret, encoded := NewJwtSecret() assert.Len(t, secret, 32) decoded, err := DecodeJwtSecret(encoded) require.NoError(t, err) diff --git a/modules/git/command_test.go b/modules/git/command_test.go index ace43598fc..aaede9a071 100644 --- a/modules/git/command_test.go +++ b/modules/git/command_test.go @@ -55,10 +55,10 @@ func TestGitArgument(t *testing.T) { func TestCommandString(t *testing.T) { cmd := NewCommandContextNoGlobals(t.Context(), "a", "-m msg", "it's a test", `say "hello"`) - assert.EqualValues(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.String()) + assert.Equal(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.String()) cmd = NewCommandContextNoGlobals(t.Context(), "url: https://a:b@c/") - assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/"`, cmd.toString(true)) + assert.Equal(t, cmd.prog+` "url: https://sanitized-credential@c/"`, cmd.toString(true)) } func TestGrepOnlyFunction(t *testing.T) { diff --git a/modules/git/commit.go b/modules/git/commit.go index baefe3820d..816bd051f6 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -220,7 +220,7 @@ func (c *Commit) HasPreviousCommit(objectID ObjectID) (bool, error) { } var exitError *exec.ExitError if errors.As(err, &exitError) { - if exitError.ProcessState.ExitCode() == 1 && len(exitError.Stderr) == 0 { + if exitError.ExitCode() == 1 && len(exitError.Stderr) == 0 { return false, nil } } diff --git a/modules/git/commit_sha256_test.go b/modules/git/commit_sha256_test.go index 9e56829f45..b2606105fa 100644 --- a/modules/git/commit_sha256_test.go +++ b/modules/git/commit_sha256_test.go @@ -107,7 +107,7 @@ signed commit` return } assert.EqualValues(t, sha, commitFromReader.ID) - assert.EqualValues(t, `-----BEGIN PGP SIGNATURE----- + assert.Equal(t, `-----BEGIN PGP SIGNATURE----- iQIrBAABCgAtFiEES+fB08xlgTrzSdQvhkUIsBsmec8FAmU/wKoPHGFtYWplckBz dXNlLmRlAAoJEIZFCLAbJnnP4s4PQIJATa++WPzR6/H4etT7bsOGoMyguEJYyWOd @@ -124,19 +124,19 @@ HKRr3NlRM/DygzTyj0gN74uoa0goCIbyAQhiT42nm0cuhM7uN/W0ayrlZjGF1cbR =xybZ -----END PGP SIGNATURE----- `, commitFromReader.Signature.Signature) - assert.EqualValues(t, `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e + assert.Equal(t, `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8 author Adam Majer <amajer@suse.de> 1698676906 +0100 committer Adam Majer <amajer@suse.de> 1698676906 +0100 signed commit`, commitFromReader.Signature.Payload) - assert.EqualValues(t, "Adam Majer <amajer@suse.de>", commitFromReader.Author.String()) + assert.Equal(t, "Adam Majer <amajer@suse.de>", commitFromReader.Author.String()) commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n")) require.NoError(t, err) commitFromReader.CommitMessage += "\n\n" commitFromReader.Signature.Payload += "\n\n" - assert.EqualValues(t, commitFromReader, commitFromReader2) + assert.Equal(t, commitFromReader, commitFromReader2) } func TestHasPreviousCommitSha256(t *testing.T) { diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go index 6bb7d776f5..0acd5ac446 100644 --- a/modules/git/commit_test.go +++ b/modules/git/commit_test.go @@ -92,7 +92,7 @@ empty commit` require.NoError(t, err) require.NotNil(t, commitFromReader) assert.EqualValues(t, sha, commitFromReader.ID) - assert.EqualValues(t, `-----BEGIN PGP SIGNATURE----- + assert.Equal(t, `-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEWPb2jX6FS2mqyJRQLmK0HJOGlEMFAl00zmEACgkQLmK0HJOG lEMDFBAAhQKKqLD1VICygJMEB8t1gBmNLgvziOLfpX4KPWdPtBk3v/QJ7OrfMrVK @@ -109,19 +109,19 @@ mfeFhT57UbE4qukTDIQ0Y0WM40UYRTakRaDY7ubhXgLgx09Cnp9XTVMsHgT6j9/i =FRsO -----END PGP SIGNATURE----- `, commitFromReader.Signature.Signature) - assert.EqualValues(t, `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930 + assert.Equal(t, `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930 parent 37991dec2c8e592043f47155ce4808d4580f9123 author silverwind <me@silverwind.io> 1563741793 +0200 committer silverwind <me@silverwind.io> 1563741793 +0200 empty commit`, commitFromReader.Signature.Payload) - assert.EqualValues(t, "silverwind <me@silverwind.io>", commitFromReader.Author.String()) + assert.Equal(t, "silverwind <me@silverwind.io>", commitFromReader.Author.String()) commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n")) require.NoError(t, err) commitFromReader.CommitMessage += "\n\n" commitFromReader.Signature.Payload += "\n\n" - assert.EqualValues(t, commitFromReader, commitFromReader2) + assert.Equal(t, commitFromReader, commitFromReader2) } func TestCommitWithEncodingFromReader(t *testing.T) { @@ -158,7 +158,7 @@ ISO-8859-1` require.NoError(t, err) require.NotNil(t, commitFromReader) assert.EqualValues(t, sha, commitFromReader.ID) - assert.EqualValues(t, `-----BEGIN PGP SIGNATURE----- + assert.Equal(t, `-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE9HRrbqvYxPT8PXbefPSEkrowAa8FAmYGg7IACgkQfPSEkrow Aa9olwv+P0HhtCM6CRvlUmPaqswRsDPNR4i66xyXGiSxdI9V5oJL7HLiQIM7KrFR @@ -173,20 +173,20 @@ jw4YcO5u =r3UU -----END PGP SIGNATURE----- `, commitFromReader.Signature.Signature) - assert.EqualValues(t, `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5 + assert.Equal(t, `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5 parent 47b24e7ab977ed31c5a39989d570847d6d0052af author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100 committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100 encoding ISO-8859-1 ISO-8859-1`, commitFromReader.Signature.Payload) - assert.EqualValues(t, "KN4CK3R <admin@oldschoolhack.me>", commitFromReader.Author.String()) + assert.Equal(t, "KN4CK3R <admin@oldschoolhack.me>", commitFromReader.Author.String()) commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n")) require.NoError(t, err) commitFromReader.CommitMessage += "\n\n" commitFromReader.Signature.Payload += "\n\n" - assert.EqualValues(t, commitFromReader, commitFromReader2) + assert.Equal(t, commitFromReader, commitFromReader2) } func TestHasPreviousCommit(t *testing.T) { diff --git a/modules/git/diff_test.go b/modules/git/diff_test.go index 0855a7de1c..9130767c66 100644 --- a/modules/git/diff_test.go +++ b/modules/git/diff_test.go @@ -162,8 +162,8 @@ func BenchmarkCutDiffAroundLine(b *testing.B) { func TestParseDiffHunkString(t *testing.T) { leftLine, leftHunk, rightLine, rightHunk := ParseDiffHunkString("@@ -19,3 +19,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER") - assert.EqualValues(t, 19, leftLine) - assert.EqualValues(t, 3, leftHunk) - assert.EqualValues(t, 19, rightLine) - assert.EqualValues(t, 5, rightHunk) + assert.Equal(t, 19, leftLine) + assert.Equal(t, 3, leftHunk) + assert.Equal(t, 19, rightLine) + assert.Equal(t, 5, rightHunk) } diff --git a/modules/git/error.go b/modules/git/error.go index 427eb4a5b9..06c6994706 100644 --- a/modules/git/error.go +++ b/modules/git/error.go @@ -120,10 +120,7 @@ func (err *ErrPushRejected) GenerateMessage() { err.Message = "" return } - for { - if len(err.StdErr) <= i+8 { - break - } + for len(err.StdErr) > i+8 { if err.StdErr[i:i+8] != "remote: " { break } diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go index e98e8c19a3..50786e7a42 100644 --- a/modules/git/log_name_status.go +++ b/modules/git/log_name_status.go @@ -118,11 +118,12 @@ func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int g.buffull = false g.next, err = g.rd.ReadSlice('\x00') if err != nil { - if err == bufio.ErrBufferFull { + switch err { + case bufio.ErrBufferFull: g.buffull = true - } else if err == io.EOF { + case io.EOF: return nil, nil - } else { + default: return nil, err } } @@ -132,11 +133,12 @@ func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int if bytes.Equal(g.next, []byte("commit\000")) { g.next, err = g.rd.ReadSlice('\x00') if err != nil { - if err == bufio.ErrBufferFull { + switch err { + case bufio.ErrBufferFull: g.buffull = true - } else if err == io.EOF { + case io.EOF: return nil, nil - } else { + default: return nil, err } } @@ -169,7 +171,7 @@ func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int } } - if err == io.EOF || !(g.next[0] == '\n' || g.next[0] == '\000') { + if err == io.EOF || (g.next[0] != '\n' && g.next[0] != '\000') { return &ret, nil } @@ -214,11 +216,12 @@ diffloop: } g.next, err = g.rd.ReadSlice('\x00') if err != nil { - if err == bufio.ErrBufferFull { + switch err { + case bufio.ErrBufferFull: g.buffull = true - } else if err == io.EOF { + case io.EOF: return &ret, nil - } else { + default: return nil, err } } diff --git a/modules/git/parse_test.go b/modules/git/parse_test.go index 89c6e0399b..03f359f6c1 100644 --- a/modules/git/parse_test.go +++ b/modules/git/parse_test.go @@ -57,7 +57,7 @@ func TestParseTreeEntriesLong(t *testing.T) { require.NoError(t, err) assert.Len(t, entries, len(testCase.Expected)) for i, entry := range entries { - assert.EqualValues(t, testCase.Expected[i], entry) + assert.Equal(t, testCase.Expected[i], entry) } } } @@ -90,7 +90,7 @@ func TestParseTreeEntriesShort(t *testing.T) { require.NoError(t, err) assert.Len(t, entries, len(testCase.Expected)) for i, entry := range entries { - assert.EqualValues(t, testCase.Expected[i], entry) + assert.Equal(t, testCase.Expected[i], entry) } } } diff --git a/modules/git/pushoptions/pushoptions_test.go b/modules/git/pushoptions/pushoptions_test.go index 49bf2d275b..1cb36d9d1e 100644 --- a/modules/git/pushoptions/pushoptions_test.go +++ b/modules/git/pushoptions/pushoptions_test.go @@ -22,8 +22,8 @@ func TestToAndFromMap(t *testing.T) { options.Parse(fmt.Sprintf("%v", RepoPrivate)) actual := options.Map() expected := map[string]string{string(RepoPrivate): "true"} - assert.EqualValues(t, expected, actual) - assert.EqualValues(t, expected, NewFromMap(&actual).Map()) + assert.Equal(t, expected, actual) + assert.Equal(t, expected, NewFromMap(&actual).Map()) } func TestChangeRepositorySettings(t *testing.T) { @@ -46,7 +46,7 @@ func TestParse(t *testing.T) { val, ok := options.GetString(RepoPrivate) assert.False(t, ok) - assert.Equal(t, "", val) + assert.Empty(t, val) assert.True(t, options.GetBool(RepoPrivate, true)) assert.False(t, options.GetBool(RepoPrivate, false)) diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go index ee89373b90..0006559709 100644 --- a/modules/git/repo_attribute_test.go +++ b/modules/git/repo_attribute_test.go @@ -101,7 +101,7 @@ func TestGitAttributeBareNonBare(t *testing.T) { cloneStats, err := gitRepo.GitAttributes(commitID, "i-am-a-python.p", LinguistAttributes...) require.NoError(t, err) - assert.EqualValues(t, cloneStats, bareStats) + assert.Equal(t, cloneStats, bareStats) refStats := cloneStats t.Run("GitAttributeChecker/"+commitID+"/SupportBare", func(t *testing.T) { @@ -111,7 +111,7 @@ func TestGitAttributeBareNonBare(t *testing.T) { bareStats, err := bareChecker.CheckPath("i-am-a-python.p") require.NoError(t, err) - assert.EqualValues(t, refStats, bareStats) + assert.Equal(t, refStats, bareStats) }) t.Run("GitAttributeChecker/"+commitID+"/NoBareSupport", func(t *testing.T) { defer test.MockVariableValue(&SupportCheckAttrOnBare, false)() @@ -122,7 +122,7 @@ func TestGitAttributeBareNonBare(t *testing.T) { cloneStats, err := cloneChecker.CheckPath("i-am-a-python.p") require.NoError(t, err) - assert.EqualValues(t, refStats, cloneStats) + assert.Equal(t, refStats, cloneStats) }) } } @@ -135,7 +135,7 @@ func TestGitAttributes(t *testing.T) { attr, err := gitRepo.GitAttributes("8fee858da5796dfb37704761701bb8e800ad9ef3", "i-am-a-python.p", LinguistAttributes...) require.NoError(t, err) - assert.EqualValues(t, map[string]GitAttribute{ + assert.Equal(t, map[string]GitAttribute{ "gitlab-language": "unspecified", "linguist-detectable": "unspecified", "linguist-documentation": "unspecified", @@ -146,7 +146,7 @@ func TestGitAttributes(t *testing.T) { attr, err = gitRepo.GitAttributes("341fca5b5ea3de596dc483e54c2db28633cd2f97", "i-am-a-python.p", LinguistAttributes...) require.NoError(t, err) - assert.EqualValues(t, map[string]GitAttribute{ + assert.Equal(t, map[string]GitAttribute{ "gitlab-language": "unspecified", "linguist-detectable": "unspecified", "linguist-documentation": "unspecified", @@ -177,13 +177,13 @@ func TestGitAttributeFirst(t *testing.T) { t.Run("none is specified", func(t *testing.T) { language, err := gitRepo.GitAttributeFirst("8fee858da5796dfb37704761701bb8e800ad9ef3", "i-am-a-python.p", "linguist-detectable", "gitlab-language", "non-existing") require.NoError(t, err) - assert.Equal(t, "", language.String()) + assert.Empty(t, language.String()) }) } func TestGitAttributeStruct(t *testing.T) { - assert.Equal(t, "", GitAttribute("").String()) - assert.Equal(t, "", GitAttribute("unspecified").String()) + assert.Empty(t, GitAttribute("").String()) + assert.Empty(t, GitAttribute("unspecified").String()) assert.Equal(t, "python", GitAttribute("python").String()) diff --git a/modules/git/repo_base_test.go b/modules/git/repo_base_test.go index c9ac6a8559..5b0cfd1b37 100644 --- a/modules/git/repo_base_test.go +++ b/modules/git/repo_base_test.go @@ -45,7 +45,7 @@ func TestCatFileBatch(t *testing.T) { sha, typ, size, err := ReadBatchLine(r) require.NoError(t, err) assert.Equal(t, "commit", typ) - assert.EqualValues(t, []byte("95bb4d39648ee7e325106df01a621c530863a653"), sha) + assert.Equal(t, []byte("95bb4d39648ee7e325106df01a621c530863a653"), sha) assert.EqualValues(t, 144, size) }) @@ -60,7 +60,7 @@ func TestCatFileBatch(t *testing.T) { sha, typ, size, err := ReadBatchLine(r) require.NoError(t, err) assert.Equal(t, "commit", typ) - assert.EqualValues(t, []byte("95bb4d39648ee7e325106df01a621c530863a653"), sha) + assert.Equal(t, []byte("95bb4d39648ee7e325106df01a621c530863a653"), sha) assert.EqualValues(t, 144, size) }) @@ -120,7 +120,7 @@ func TestCatFileBatchCheck(t *testing.T) { sha, typ, size, err := ReadBatchLine(r) require.NoError(t, err) assert.Equal(t, "tag", typ) - assert.EqualValues(t, []byte("3ad28a9149a2864384548f3d17ed7f38014c9e8a"), sha) + assert.Equal(t, []byte("3ad28a9149a2864384548f3d17ed7f38014c9e8a"), sha) assert.EqualValues(t, 807, size) }) @@ -135,7 +135,7 @@ func TestCatFileBatchCheck(t *testing.T) { sha, typ, size, err := ReadBatchLine(r) require.NoError(t, err) assert.Equal(t, "tag", typ) - assert.EqualValues(t, []byte("3ad28a9149a2864384548f3d17ed7f38014c9e8a"), sha) + assert.Equal(t, []byte("3ad28a9149a2864384548f3d17ed7f38014c9e8a"), sha) assert.EqualValues(t, 807, size) }) diff --git a/modules/git/repo_branch_test.go b/modules/git/repo_branch_test.go index 610c8457d9..1e0fea7cd4 100644 --- a/modules/git/repo_branch_test.go +++ b/modules/git/repo_branch_test.go @@ -21,21 +21,21 @@ func TestRepository_GetBranches(t *testing.T) { require.NoError(t, err) assert.Len(t, branches, 2) - assert.EqualValues(t, 3, countAll) + assert.Equal(t, 3, countAll) assert.ElementsMatch(t, []string{"master", "branch2"}, branches) branches, countAll, err = bareRepo1.GetBranchNames(0, 0) require.NoError(t, err) assert.Len(t, branches, 3) - assert.EqualValues(t, 3, countAll) + assert.Equal(t, 3, countAll) assert.ElementsMatch(t, []string{"master", "branch2", "branch1"}, branches) branches, countAll, err = bareRepo1.GetBranchNames(5, 1) require.NoError(t, err) assert.Empty(t, branches) - assert.EqualValues(t, 3, countAll) + assert.Equal(t, 3, countAll) assert.ElementsMatch(t, []string{}, branches) } @@ -71,15 +71,15 @@ func TestGetRefsBySha(t *testing.T) { // refs/pull/1/head branches, err = bareRepo5.GetRefsBySha("c83380d7056593c51a699d12b9c00627bd5743e9", PullPrefix) require.NoError(t, err) - assert.EqualValues(t, []string{"refs/pull/1/head"}, branches) + assert.Equal(t, []string{"refs/pull/1/head"}, branches) branches, err = bareRepo5.GetRefsBySha("d8e0bbb45f200e67d9a784ce55bd90821af45ebd", BranchPrefix) require.NoError(t, err) - assert.EqualValues(t, []string{"refs/heads/master", "refs/heads/master-clone"}, branches) + assert.Equal(t, []string{"refs/heads/master", "refs/heads/master-clone"}, branches) branches, err = bareRepo5.GetRefsBySha("58a4bcc53ac13e7ff76127e0fb518b5262bf09af", BranchPrefix) require.NoError(t, err) - assert.EqualValues(t, []string{"refs/heads/test-patch-1"}, branches) + assert.Equal(t, []string{"refs/heads/test-patch-1"}, branches) } func BenchmarkGetRefsBySha(b *testing.B) { diff --git a/modules/git/repo_commit_test.go b/modules/git/repo_commit_test.go index 9cbc40eee7..53760b208e 100644 --- a/modules/git/repo_commit_test.go +++ b/modules/git/repo_commit_test.go @@ -127,19 +127,19 @@ func TestGetTagCommit(t *testing.T) { aTagID, err := bareRepo1.GetTagCommitID(aTagName) require.NoError(t, err) - assert.NotEqualValues(t, aTagCommitID, aTagID) + assert.NotEqual(t, aTagCommitID, aTagID) lTagID, err := bareRepo1.GetTagCommitID(lTagName) require.NoError(t, err) - assert.EqualValues(t, lTagCommitID, lTagID) + assert.Equal(t, lTagCommitID, lTagID) aTag, err := bareRepo1.GetTagCommit(aTagName) require.NoError(t, err) - assert.EqualValues(t, aTagCommitID, aTag.ID.String()) + assert.Equal(t, aTagCommitID, aTag.ID.String()) lTag, err := bareRepo1.GetTagCommit(lTagName) require.NoError(t, err) - assert.EqualValues(t, lTagCommitID, lTag.ID.String()) + assert.Equal(t, lTagCommitID, lTag.ID.String()) } func TestCommitsByRange(t *testing.T) { diff --git a/modules/git/repo_language_stats.go b/modules/git/repo_language_stats.go index 24879a26fe..46dfc0dbd9 100644 --- a/modules/git/repo_language_stats.go +++ b/modules/git/repo_language_stats.go @@ -234,7 +234,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err sizes[language] += f.Size() } else if len(sizes) == 0 && (firstExcludedLanguage == "" || firstExcludedLanguage == language) { // Only consider Programming or Markup languages as fallback - if !(langType == enry.Programming || langType == enry.Markup) { + if langType != enry.Programming && langType != enry.Markup { continue } firstExcludedLanguage = language diff --git a/modules/git/repo_language_stats_test.go b/modules/git/repo_language_stats_test.go index fd80e44a86..f18bfb3ba3 100644 --- a/modules/git/repo_language_stats_test.go +++ b/modules/git/repo_language_stats_test.go @@ -21,14 +21,14 @@ func TestRepository_GetLanguageStats(t *testing.T) { stats, err := gitRepo.GetLanguageStats("8fee858da5796dfb37704761701bb8e800ad9ef3") require.NoError(t, err) - assert.EqualValues(t, map[string]int64{ + assert.Equal(t, map[string]int64{ "Python": 134, "Java": 112, }, stats) } func TestMergeLanguageStats(t *testing.T) { - assert.EqualValues(t, map[string]int64{ + assert.Equal(t, map[string]int64{ "PHP": 1, "python": 10, "JAVA": 700, diff --git a/modules/git/repo_ref.go b/modules/git/repo_ref.go index 3c8b863f75..1c7ff7b709 100644 --- a/modules/git/repo_ref.go +++ b/modules/git/repo_ref.go @@ -22,11 +22,12 @@ func (repo *Repository) GetRefs() ([]*Reference, error) { // refType should only be a literal "branch" or "tag" and nothing else func (repo *Repository) ListOccurrences(ctx context.Context, refType, commitSHA string) ([]string, error) { cmd := NewCommand(ctx) - if refType == "branch" { + switch refType { + case "branch": cmd.AddArguments("branch") - } else if refType == "tag" { + case "tag": cmd.AddArguments("tag") - } else { + default: return nil, util.NewInvalidArgumentErrorf(`can only use "branch" or "tag" for refType, but got %q`, refType) } stdout, _, err := cmd.AddArguments("--no-color", "--sort=-creatordate", "--contains").AddDynamicArguments(commitSHA).RunStdString(&RunOpts{Dir: repo.Path}) diff --git a/modules/git/repo_stats_test.go b/modules/git/repo_stats_test.go index 2a15b6f1b7..9284ab5d5d 100644 --- a/modules/git/repo_stats_test.go +++ b/modules/git/repo_stats_test.go @@ -31,7 +31,7 @@ func TestRepository_GetCodeActivityStats(t *testing.T) { assert.EqualValues(t, 10, code.Additions) assert.EqualValues(t, 1, code.Deletions) assert.Len(t, code.Authors, 3) - assert.EqualValues(t, "tris.git@shoddynet.org", code.Authors[1].Email) + assert.Equal(t, "tris.git@shoddynet.org", code.Authors[1].Email) assert.EqualValues(t, 3, code.Authors[1].Commits) assert.EqualValues(t, 5, code.Authors[0].Commits) } diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go index a4b13bf03d..d88851551f 100644 --- a/modules/git/repo_tag_test.go +++ b/modules/git/repo_tag_test.go @@ -28,30 +28,24 @@ func TestRepository_GetTags(t *testing.T) { } assert.Len(t, tags, 2) assert.Len(t, tags, total) - assert.EqualValues(t, "signed-tag", tags[0].Name) - assert.EqualValues(t, "36f97d9a96457e2bab511db30fe2db03893ebc64", tags[0].ID.String()) - assert.EqualValues(t, "tag", tags[0].Type) - assert.EqualValues(t, time.Date(2022, time.November, 13, 16, 40, 20, 0, time.FixedZone("", 3600)), tags[0].Tagger.When) - assert.EqualValues(t, "test", tags[1].Name) - assert.EqualValues(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", tags[1].ID.String()) - assert.EqualValues(t, "tag", tags[1].Type) - assert.EqualValues(t, time.Date(2018, time.June, 16, 20, 13, 18, 0, time.FixedZone("", -25200)), tags[1].Tagger.When) + assert.Equal(t, "signed-tag", tags[0].Name) + assert.Equal(t, "36f97d9a96457e2bab511db30fe2db03893ebc64", tags[0].ID.String()) + assert.Equal(t, "tag", tags[0].Type) + assert.Equal(t, time.Date(2022, time.November, 13, 16, 40, 20, 0, time.FixedZone("", 3600)), tags[0].Tagger.When) + assert.Equal(t, "test", tags[1].Name) + assert.Equal(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", tags[1].ID.String()) + assert.Equal(t, "tag", tags[1].Type) + assert.Equal(t, time.Date(2018, time.June, 16, 20, 13, 18, 0, time.FixedZone("", -25200)), tags[1].Tagger.When) } func TestRepository_GetTag(t *testing.T) { bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") clonedPath, err := cloneRepo(t, bareRepo1Path) - if err != nil { - require.NoError(t, err) - return - } + require.NoError(t, err) bareRepo1, err := openRepositoryWithDefaultContext(clonedPath) - if err != nil { - require.NoError(t, err) - return - } + require.NoError(t, err) defer bareRepo1.Close() // LIGHTWEIGHT TAGS @@ -60,25 +54,16 @@ func TestRepository_GetTag(t *testing.T) { // Create the lightweight tag err = bareRepo1.CreateTag(lTagName, lTagCommitID) - if err != nil { - require.NoError(t, err, "Unable to create the lightweight tag: %s for ID: %s. Error: %v", lTagName, lTagCommitID, err) - return - } + require.NoError(t, err, "Unable to create the lightweight tag: %s for ID: %s. Error: %v", lTagName, lTagCommitID, err) // and try to get the Tag for lightweight tag lTag, err := bareRepo1.GetTag(lTagName) - if err != nil { - require.NoError(t, err) - return - } - if lTag == nil { - assert.NotNil(t, lTag) - assert.FailNow(t, "nil lTag: %s", lTagName) - } - assert.EqualValues(t, lTagName, lTag.Name) - assert.EqualValues(t, lTagCommitID, lTag.ID.String()) - assert.EqualValues(t, lTagCommitID, lTag.Object.String()) - assert.EqualValues(t, "commit", lTag.Type) + require.NoError(t, err) + require.NotNil(t, lTag) + assert.Equal(t, lTagName, lTag.Name) + assert.Equal(t, lTagCommitID, lTag.ID.String()) + assert.Equal(t, lTagCommitID, lTag.Object.String()) + assert.Equal(t, "commit", lTag.Type) // ANNOTATED TAGS aTagCommitID := "8006ff9adbf0cb94da7dad9e537e53817f9fa5c0" @@ -87,32 +72,20 @@ func TestRepository_GetTag(t *testing.T) { // Create the annotated tag err = bareRepo1.CreateAnnotatedTag(aTagName, aTagMessage, aTagCommitID) - if err != nil { - require.NoError(t, err, "Unable to create the annotated tag: %s for ID: %s. Error: %v", aTagName, aTagCommitID, err) - return - } + require.NoError(t, err, "Unable to create the annotated tag: %s for ID: %s. Error: %v", aTagName, aTagCommitID, err) // Now try to get the tag for the annotated Tag aTagID, err := bareRepo1.GetTagID(aTagName) - if err != nil { - require.NoError(t, err) - return - } + require.NoError(t, err) aTag, err := bareRepo1.GetTag(aTagName) - if err != nil { - require.NoError(t, err) - return - } - if aTag == nil { - assert.NotNil(t, aTag) - assert.FailNow(t, "nil aTag: %s", aTagName) - } - assert.EqualValues(t, aTagName, aTag.Name) - assert.EqualValues(t, aTagID, aTag.ID.String()) + require.NoError(t, err) + require.NotNil(t, aTag) + assert.Equal(t, aTagName, aTag.Name) + assert.Equal(t, aTagID, aTag.ID.String()) assert.NotEqual(t, aTagID, aTag.Object.String()) - assert.EqualValues(t, aTagCommitID, aTag.Object.String()) - assert.EqualValues(t, "tag", aTag.Type) + assert.Equal(t, aTagCommitID, aTag.Object.String()) + assert.Equal(t, "tag", aTag.Type) // RELEASE TAGS @@ -120,24 +93,15 @@ func TestRepository_GetTag(t *testing.T) { rTagName := "release/" + lTagName err = bareRepo1.CreateTag(rTagName, rTagCommitID) - if err != nil { - require.NoError(t, err, "Unable to create the tag: %s for ID: %s. Error: %v", rTagName, rTagCommitID, err) - return - } + require.NoError(t, err, "Unable to create the tag: %s for ID: %s. Error: %v", rTagName, rTagCommitID, err) rTagID, err := bareRepo1.GetTagID(rTagName) - if err != nil { - require.NoError(t, err) - return - } - assert.EqualValues(t, rTagCommitID, rTagID) + require.NoError(t, err) + assert.Equal(t, rTagCommitID, rTagID) oTagID, err := bareRepo1.GetTagID(lTagName) - if err != nil { - require.NoError(t, err) - return - } - assert.EqualValues(t, lTagCommitID, oTagID) + require.NoError(t, err) + assert.Equal(t, lTagCommitID, oTagID) } func TestRepository_GetAnnotatedTag(t *testing.T) { @@ -173,9 +137,9 @@ func TestRepository_GetAnnotatedTag(t *testing.T) { return } assert.NotNil(t, tag) - assert.EqualValues(t, aTagName, tag.Name) - assert.EqualValues(t, aTagID, tag.ID.String()) - assert.EqualValues(t, "tag", tag.Type) + assert.Equal(t, aTagName, tag.Name) + assert.Equal(t, aTagID, tag.ID.String()) + assert.Equal(t, "tag", tag.Type) // Annotated tag's Commit ID should fail tag2, err := bareRepo1.GetAnnotatedTag(aTagCommitID) diff --git a/modules/git/repo_tree.go b/modules/git/repo_tree.go index 53d94d9d7d..e552496607 100644 --- a/modules/git/repo_tree.go +++ b/modules/git/repo_tree.go @@ -97,7 +97,7 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) { if err != nil { return nil, err } - commit.Tree.ResolvedID = resolvedID + commit.ResolvedID = resolvedID return &commit.Tree, nil case "commit": commit, err := CommitFromReader(repo, id, io.LimitReader(rd, size)) @@ -107,7 +107,7 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) { if _, err := rd.Discard(1); err != nil { return nil, err } - commit.Tree.ResolvedID = commit.ID + commit.ResolvedID = commit.ID return &commit.Tree, nil case "tree": tree := NewTree(repo, id) diff --git a/modules/git/signature_test.go b/modules/git/signature_test.go index 92681feea9..b9b5aff61b 100644 --- a/modules/git/signature_test.go +++ b/modules/git/signature_test.go @@ -42,6 +42,6 @@ func TestParseSignatureFromCommitLine(t *testing.T) { } for _, test := range tests { got := parseSignatureFromCommitLine(test.line) - assert.EqualValues(t, test.want, got) + assert.Equal(t, test.want, got) } } diff --git a/modules/git/submodule_test.go b/modules/git/submodule_test.go index e05f2510c4..a396e4ea0d 100644 --- a/modules/git/submodule_test.go +++ b/modules/git/submodule_test.go @@ -37,6 +37,6 @@ func TestGetRefURL(t *testing.T) { } for _, kase := range kases { - assert.EqualValues(t, kase.expect, getRefURL(kase.refURL, kase.prefixURL, kase.parentPath, kase.SSHDomain)) + assert.Equal(t, kase.expect, getRefURL(kase.refURL, kase.prefixURL, kase.parentPath, kase.SSHDomain)) } } diff --git a/modules/git/tag_test.go b/modules/git/tag_test.go index 8279066b2f..72409f959b 100644 --- a/modules/git/tag_test.go +++ b/modules/git/tag_test.go @@ -87,21 +87,21 @@ v0 for _, test := range testData { tag, err := parseTagData(Sha1ObjectFormat, test.data) require.NoError(t, err) - assert.EqualValues(t, test.tag.ID, tag.ID) - assert.EqualValues(t, test.tag.Object, tag.Object) - assert.EqualValues(t, test.tag.Name, tag.Name) - assert.EqualValues(t, test.tag.Message, tag.Message) - assert.EqualValues(t, test.tag.Type, tag.Type) + assert.Equal(t, test.tag.ID, tag.ID) + assert.Equal(t, test.tag.Object, tag.Object) + assert.Equal(t, test.tag.Name, tag.Name) + assert.Equal(t, test.tag.Message, tag.Message) + assert.Equal(t, test.tag.Type, tag.Type) if test.tag.Signature != nil && assert.NotNil(t, tag.Signature) { - assert.EqualValues(t, test.tag.Signature.Signature, tag.Signature.Signature) - assert.EqualValues(t, test.tag.Signature.Payload, tag.Signature.Payload) + assert.Equal(t, test.tag.Signature.Signature, tag.Signature.Signature) + assert.Equal(t, test.tag.Signature.Payload, tag.Signature.Payload) } else { assert.Nil(t, tag.Signature) } if test.tag.Tagger != nil && assert.NotNil(t, tag.Tagger) { - assert.EqualValues(t, test.tag.Tagger.Name, tag.Tagger.Name) - assert.EqualValues(t, test.tag.Tagger.Email, tag.Tagger.Email) - assert.EqualValues(t, test.tag.Tagger.When.Unix(), tag.Tagger.When.Unix()) + assert.Equal(t, test.tag.Tagger.Name, tag.Tagger.Name) + assert.Equal(t, test.tag.Tagger.Email, tag.Tagger.Email) + assert.Equal(t, test.tag.Tagger.When.Unix(), tag.Tagger.When.Unix()) } else { assert.Nil(t, tag.Tagger) } diff --git a/modules/git/tree_test.go b/modules/git/tree_test.go index 7e439628f2..aa092cc56b 100644 --- a/modules/git/tree_test.go +++ b/modules/git/tree_test.go @@ -34,10 +34,10 @@ func Test_GetTreePathLatestCommit(t *testing.T) { commitID, err := repo.GetBranchCommitID("master") require.NoError(t, err) - assert.EqualValues(t, "544d8f7a3b15927cddf2299b4b562d6ebd71b6a7", commitID) + assert.Equal(t, "544d8f7a3b15927cddf2299b4b562d6ebd71b6a7", commitID) commit, err := repo.GetTreePathLatestCommit("master", "blame.txt") require.NoError(t, err) assert.NotNil(t, commit) - assert.EqualValues(t, "45fb6cbc12f970b04eacd5cd4165edd11c8d7376", commit.ID.String()) + assert.Equal(t, "45fb6cbc12f970b04eacd5cd4165edd11c8d7376", commit.ID.String()) } diff --git a/modules/git/url/url_test.go b/modules/git/url/url_test.go index e1e52c0ed5..54655633bf 100644 --- a/modules/git/url/url_test.go +++ b/modules/git/url/url_test.go @@ -160,8 +160,8 @@ func TestParseGitURLs(t *testing.T) { t.Run(kase.kase, func(t *testing.T) { u, err := Parse(kase.kase) require.NoError(t, err) - assert.EqualValues(t, kase.expected.extraMark, u.extraMark) - assert.EqualValues(t, *kase.expected, *u) + assert.Equal(t, kase.expected.extraMark, u.extraMark) + assert.Equal(t, *kase.expected, *u) }) } } diff --git a/modules/graceful/releasereopen/releasereopen_test.go b/modules/graceful/releasereopen/releasereopen_test.go index 6ab9f955f6..869bd2fdcf 100644 --- a/modules/graceful/releasereopen/releasereopen_test.go +++ b/modules/graceful/releasereopen/releasereopen_test.go @@ -31,14 +31,14 @@ func TestManager(t *testing.T) { _ = m.Register(t3) require.NoError(t, m.ReleaseReopen()) - assert.EqualValues(t, 1, t1.count) - assert.EqualValues(t, 1, t2.count) - assert.EqualValues(t, 1, t3.count) + assert.Equal(t, 1, t1.count) + assert.Equal(t, 1, t2.count) + assert.Equal(t, 1, t3.count) c2() require.NoError(t, m.ReleaseReopen()) - assert.EqualValues(t, 2, t1.count) - assert.EqualValues(t, 1, t2.count) - assert.EqualValues(t, 2, t3.count) + assert.Equal(t, 2, t1.count) + assert.Equal(t, 1, t2.count) + assert.Equal(t, 2, t3.count) } diff --git a/modules/graceful/server.go b/modules/graceful/server.go index 121bbed364..e812117dbd 100644 --- a/modules/graceful/server.go +++ b/modules/graceful/server.go @@ -260,7 +260,7 @@ func (w *wrappedConn) Write(p []byte) (n int, err error) { if minDeadline.After(w.deadline) { w.deadline = minDeadline } - _ = w.Conn.SetWriteDeadline(w.deadline) + _ = w.SetWriteDeadline(w.deadline) } return w.Conn.Write(p) } diff --git a/modules/graceful/server_hooks.go b/modules/graceful/server_hooks.go index 06be783361..334f749003 100644 --- a/modules/graceful/server_hooks.go +++ b/modules/graceful/server_hooks.go @@ -61,10 +61,7 @@ func (srv *Server) doHammer() { return } log.Warn("Forcefully shutting down parent") - for { - if srv.getState() == stateTerminate { - break - } + for srv.getState() != stateTerminate { srv.wg.Done() // Give other goroutines a chance to finish before we forcibly stop them. diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go index 6464999033..ed5d190764 100644 --- a/modules/highlight/highlight_test.go +++ b/modules/highlight/highlight_test.go @@ -133,7 +133,7 @@ c=2 t.Run(tt.name, func(t *testing.T) { out, lexerName, err := File(tt.name, "", []byte(tt.code)) require.NoError(t, err) - assert.EqualValues(t, tt.want, out) + assert.Equal(t, tt.want, out) assert.Equal(t, tt.lexerName, lexerName) }) } @@ -196,7 +196,7 @@ c=2`), for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { out := PlainText([]byte(tt.code)) - assert.EqualValues(t, tt.want, out) + assert.Equal(t, tt.want, out) }) } } diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index f793b5db3f..6adee68a0d 100644 --- a/modules/indexer/code/bleve/bleve.go +++ b/modules/indexer/code/bleve/bleve.go @@ -179,7 +179,8 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro return err } else if !typesniffer.DetectContentType(fileContents).IsText() { // FIXME: UTF-16 files will probably fail here - return nil + // Even if the file is not recognized as a "text file", we could still put its name into the indexers to make the filename become searchable, while leave the content to empty. + fileContents = nil } if _, err = batchReader.Discard(1); err != nil { diff --git a/modules/indexer/code/elasticsearch/elasticsearch_test.go b/modules/indexer/code/elasticsearch/elasticsearch_test.go index c6ba93e76d..58683bf4b2 100644 --- a/modules/indexer/code/elasticsearch/elasticsearch_test.go +++ b/modules/indexer/code/elasticsearch/elasticsearch_test.go @@ -11,6 +11,6 @@ import ( func TestIndexPos(t *testing.T) { startIdx, endIdx := indexPos("test index start and end", "start", "end") - assert.EqualValues(t, 11, startIdx) - assert.EqualValues(t, 24, endIdx) + assert.Equal(t, 11, startIdx) + assert.Equal(t, 24, endIdx) } diff --git a/modules/indexer/code/indexer_test.go b/modules/indexer/code/indexer_test.go index 29b2936fa1..740d2e1b5c 100644 --- a/modules/indexer/code/indexer_test.go +++ b/modules/indexer/code/indexer_test.go @@ -110,9 +110,9 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) { ids := make([]int64, 0, len(res)) for _, hit := range res { ids = append(ids, hit.RepoID) - assert.EqualValues(t, "# repo1\n\nDescription for repo1", hit.Content) + assert.Equal(t, "# repo1\n\nDescription for repo1", hit.Content) } - assert.EqualValues(t, kw.IDs, ids) + assert.Equal(t, kw.IDs, ids) }) } @@ -131,7 +131,7 @@ func TestBleveIndexAndSearch(t *testing.T) { if idx != nil { idx.Close() } - assert.FailNow(t, "Unable to create bleve indexer Error: %v", err) + require.NoError(t, err) } defer idx.Close() @@ -152,7 +152,7 @@ func TestESIndexAndSearch(t *testing.T) { if indexer != nil { indexer.Close() } - assert.FailNow(t, "Unable to init ES indexer Error: %v", err) + assert.FailNow(t, "Unable to init ES indexer", "error: %v", err) } defer indexer.Close() diff --git a/modules/indexer/internal/bleve/metadata_test.go b/modules/indexer/internal/bleve/metadata_test.go index 31603a92a3..3b0d3aca3d 100644 --- a/modules/indexer/internal/bleve/metadata_test.go +++ b/modules/indexer/internal/bleve/metadata_test.go @@ -24,5 +24,5 @@ func TestMetadata(t *testing.T) { meta, err = readIndexMetadata(dir) require.NoError(t, err) - assert.EqualValues(t, 24, meta.Version) + assert.Equal(t, 24, meta.Version) } diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index 8308cb7f60..f0cbbfef46 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -93,7 +93,7 @@ var cases = []*testIndexerCase{ Name: "default", SearchOptions: &internal.SearchOptions{}, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Equal(t, len(data), len(result.Hits)) + assert.Len(t, result.Hits, len(data)) assert.Equal(t, len(data), int(result.Total)) }, }, @@ -557,7 +557,7 @@ var cases = []*testIndexerCase{ SortBy: internal.SortByCreatedDesc, }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Equal(t, len(data), len(result.Hits)) + assert.Len(t, result.Hits, len(data)) assert.Equal(t, len(data), int(result.Total)) for i, v := range result.Hits { if i < len(result.Hits)-1 { @@ -573,7 +573,7 @@ var cases = []*testIndexerCase{ SortBy: internal.SortByUpdatedDesc, }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Equal(t, len(data), len(result.Hits)) + assert.Len(t, result.Hits, len(data)) assert.Equal(t, len(data), int(result.Total)) for i, v := range result.Hits { if i < len(result.Hits)-1 { @@ -589,7 +589,7 @@ var cases = []*testIndexerCase{ SortBy: internal.SortByCommentsDesc, }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Equal(t, len(data), len(result.Hits)) + assert.Len(t, result.Hits, len(data)) assert.Equal(t, len(data), int(result.Total)) for i, v := range result.Hits { if i < len(result.Hits)-1 { @@ -605,7 +605,7 @@ var cases = []*testIndexerCase{ SortBy: internal.SortByDeadlineDesc, }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Equal(t, len(data), len(result.Hits)) + assert.Len(t, result.Hits, len(data)) assert.Equal(t, len(data), int(result.Total)) for i, v := range result.Hits { if i < len(result.Hits)-1 { @@ -621,7 +621,7 @@ var cases = []*testIndexerCase{ SortBy: internal.SortByScore, }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Equal(t, len(data), len(result.Hits)) + assert.Len(t, result.Hits, len(data)) assert.Equal(t, len(data), int(result.Total)) for i, v := range result.Hits { if i < len(result.Hits)-1 { @@ -637,7 +637,7 @@ var cases = []*testIndexerCase{ SortBy: internal.SortByCreatedAsc, }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Equal(t, len(data), len(result.Hits)) + assert.Len(t, result.Hits, len(data)) assert.Equal(t, len(data), int(result.Total)) for i, v := range result.Hits { if i < len(result.Hits)-1 { @@ -653,7 +653,7 @@ var cases = []*testIndexerCase{ SortBy: internal.SortByUpdatedAsc, }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Equal(t, len(data), len(result.Hits)) + assert.Len(t, result.Hits, len(data)) assert.Equal(t, len(data), int(result.Total)) for i, v := range result.Hits { if i < len(result.Hits)-1 { @@ -669,7 +669,7 @@ var cases = []*testIndexerCase{ SortBy: internal.SortByCommentsAsc, }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Equal(t, len(data), len(result.Hits)) + assert.Len(t, result.Hits, len(data)) assert.Equal(t, len(data), int(result.Total)) for i, v := range result.Hits { if i < len(result.Hits)-1 { @@ -685,7 +685,7 @@ var cases = []*testIndexerCase{ SortBy: internal.SortByDeadlineAsc, }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Equal(t, len(data), len(result.Hits)) + assert.Len(t, result.Hits, len(data)) assert.Equal(t, len(data), int(result.Total)) for i, v := range result.Hits { if i < len(result.Hits)-1 { diff --git a/modules/indexer/issues/meilisearch/meilisearch_test.go b/modules/indexer/issues/meilisearch/meilisearch_test.go index 01160a5240..55677e031b 100644 --- a/modules/indexer/issues/meilisearch/meilisearch_test.go +++ b/modules/indexer/issues/meilisearch/meilisearch_test.go @@ -85,13 +85,13 @@ func TestConvertHits(t *testing.T) { } hits, err := convertHits(validResponse) require.NoError(t, err) - assert.EqualValues(t, []internal.Match{{ID: 11}, {ID: 22}, {ID: 33}}, hits) + assert.Equal(t, []internal.Match{{ID: 11}, {ID: 22}, {ID: 33}}, hits) } func TestDoubleQuoteKeyword(t *testing.T) { - assert.EqualValues(t, "", doubleQuoteKeyword("")) - assert.EqualValues(t, `"a" "b" "c"`, doubleQuoteKeyword("a b c")) - assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g")) - assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g")) - assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword(`a "" "d" """g`)) + assert.Empty(t, doubleQuoteKeyword("")) + assert.Equal(t, `"a" "b" "c"`, doubleQuoteKeyword("a b c")) + assert.Equal(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g")) + assert.Equal(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g")) + assert.Equal(t, `"a" "d" "g"`, doubleQuoteKeyword(`a "" "d" """g`)) } diff --git a/modules/issue/template/template.go b/modules/issue/template/template.go index 8e07cbecd8..859d27cda6 100644 --- a/modules/issue/template/template.go +++ b/modules/issue/template/template.go @@ -191,7 +191,7 @@ func validateOptions(field *api.IssueFormField, idx int) error { } for _, visibleType := range visibilityList { visibleType, ok := visibleType.(string) - if !ok || !(visibleType == "form" || visibleType == "content") { + if !ok || (visibleType != "form" && visibleType != "content") { return position.Errorf("'visible' list can only contain strings of 'form' and 'content'") } } diff --git a/modules/issue/template/template_test.go b/modules/issue/template/template_test.go index 89e8924e95..10ede4be81 100644 --- a/modules/issue/template/template_test.go +++ b/modules/issue/template/template_test.go @@ -902,7 +902,7 @@ Option 1 of dropdown, Option 2 of dropdown t.Fatal(err) } if got := RenderToMarkdown(template, tt.args.values); got != tt.want { - assert.EqualValues(t, tt.want, got) + assert.Equal(t, tt.want, got) } }) } diff --git a/modules/keying/keying.go b/modules/keying/keying.go index 30c664180c..c859e30e9f 100644 --- a/modules/keying/keying.go +++ b/modules/keying/keying.go @@ -17,10 +17,11 @@ package keying import ( "crypto/hkdf" - "crypto/rand" "crypto/sha256" "encoding/binary" + "forgejo.org/modules/util" + "golang.org/x/crypto/chacha20poly1305" ) @@ -95,10 +96,7 @@ func (k *Key) Encrypt(plaintext, additionalData []byte) []byte { } // Generate a random nonce. - nonce := make([]byte, aeadNonceSize) - if n, err := rand.Read(nonce); err != nil || n != aeadNonceSize { - panic(err) - } + nonce := util.CryptoRandomBytes(aeadNonceSize) // Returns the ciphertext of this plaintext. return e.Seal(nonce, nonce, plaintext, additionalData) diff --git a/modules/keying/keying_test.go b/modules/keying/keying_test.go index 87dce0a566..f73440b357 100644 --- a/modules/keying/keying_test.go +++ b/modules/keying/keying_test.go @@ -50,7 +50,7 @@ func TestKeying(t *testing.T) { cipherText2 := key.Encrypt(plainText, []byte{0x05, 0x06}) // Ensure ciphertexts don't have an deterministic output. - assert.NotEqualValues(t, cipherText, cipherText2) + assert.NotEqual(t, cipherText, cipherText2) }) t.Run("Decrypt", func(t *testing.T) { @@ -59,7 +59,7 @@ func TestKeying(t *testing.T) { t.Run("Successful", func(t *testing.T) { convertedPlainText, err := key.Decrypt(cipherText, []byte{0x05, 0x06}) require.NoError(t, err) - assert.EqualValues(t, plainText, convertedPlainText) + assert.Equal(t, plainText, convertedPlainText) }) t.Run("Not enough additional data", func(t *testing.T) { @@ -97,15 +97,15 @@ func TestKeying(t *testing.T) { } func TestKeyingColumnAndID(t *testing.T) { - assert.EqualValues(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, keying.ColumnAndID("table", math.MinInt64)) - assert.EqualValues(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, keying.ColumnAndID("table", -1)) - assert.EqualValues(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, keying.ColumnAndID("table", 0)) - assert.EqualValues(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, keying.ColumnAndID("table", 1)) - assert.EqualValues(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, keying.ColumnAndID("table", math.MaxInt64)) + assert.Equal(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, keying.ColumnAndID("table", math.MinInt64)) + assert.Equal(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, keying.ColumnAndID("table", -1)) + assert.Equal(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, keying.ColumnAndID("table", 0)) + assert.Equal(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, keying.ColumnAndID("table", 1)) + assert.Equal(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, keying.ColumnAndID("table", math.MaxInt64)) - assert.EqualValues(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x3a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, keying.ColumnAndID("table2", math.MinInt64)) - assert.EqualValues(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x3a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, keying.ColumnAndID("table2", -1)) - assert.EqualValues(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, keying.ColumnAndID("table2", 0)) - assert.EqualValues(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, keying.ColumnAndID("table2", 1)) - assert.EqualValues(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x3a, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, keying.ColumnAndID("table2", math.MaxInt64)) + assert.Equal(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x3a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, keying.ColumnAndID("table2", math.MinInt64)) + assert.Equal(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x3a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, keying.ColumnAndID("table2", -1)) + assert.Equal(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, keying.ColumnAndID("table2", 0)) + assert.Equal(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, keying.ColumnAndID("table2", 1)) + assert.Equal(t, []byte{0x74, 0x61, 0x62, 0x6c, 0x65, 0x32, 0x3a, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, keying.ColumnAndID("table2", math.MaxInt64)) } diff --git a/modules/lfs/content_store.go b/modules/lfs/content_store.go index ba23cec2be..c76b2c142c 100644 --- a/modules/lfs/content_store.go +++ b/modules/lfs/content_store.go @@ -79,7 +79,7 @@ func (s *ContentStore) Put(pointer Pointer, r io.Reader) error { // Exists returns true if the object exists in the content store. func (s *ContentStore) Exists(pointer Pointer) (bool, error) { - _, err := s.ObjectStorage.Stat(pointer.RelativePath()) + _, err := s.Stat(pointer.RelativePath()) if err != nil { if os.IsNotExist(err) { return false, nil @@ -92,7 +92,7 @@ func (s *ContentStore) Exists(pointer Pointer) (bool, error) { // Verify returns true if the object exists in the content store and size is correct. func (s *ContentStore) Verify(pointer Pointer) (bool, error) { p := pointer.RelativePath() - fi, err := s.ObjectStorage.Stat(p) + fi, err := s.Stat(p) if os.IsNotExist(err) || (err == nil && fi.Size() != pointer.Size) { return false, nil } else if err != nil { diff --git a/modules/locale/utils.go b/modules/locale/utils.go deleted file mode 100644 index 726dc92adc..0000000000 --- a/modules/locale/utils.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -// extracted from `/build/lint-locale.go`, `/build/lint-locale-usage.go` - -package locale - -import ( - "encoding/json" //nolint:depguard - "fmt" - - "gopkg.in/ini.v1" //nolint:depguard -) - -func IterateMessagesContent(localeContent []byte, onMsgid func(string, string) error) error { - // Same configuration as Forgejo uses. - cfg := ini.Empty(ini.LoadOptions{ - IgnoreContinuation: true, - }) - cfg.NameMapper = ini.SnackCase - - if err := cfg.Append(localeContent); err != nil { - return err - } - - for _, section := range cfg.Sections() { - for _, key := range section.Keys() { - var trKey string - if section.Name() == "" || section.Name() == "DEFAULT" || section.Name() == "common" { - trKey = key.Name() - } else { - trKey = section.Name() + "." + key.Name() - } - if err := onMsgid(trKey, key.Value()); err != nil { - return err - } - } - } - - return nil -} - -func iterateMessagesNextInner(onMsgid func(string, string) error, data map[string]any, trKey ...string) error { - for key, value := range data { - currentKey := key - if len(trKey) == 1 { - currentKey = trKey[0] + "." + key - } - - switch value := value.(type) { - case string: - if err := onMsgid(currentKey, value); err != nil { - return err - } - case map[string]any: - if err := iterateMessagesNextInner(onMsgid, value, currentKey); err != nil { - return err - } - default: - return fmt.Errorf("unexpected type: %s - %T", currentKey, value) - } - } - - return nil -} - -func IterateMessagesNextContent(localeContent []byte, onMsgid func(string, string) error) error { - var localeData map[string]any - if err := json.Unmarshal(localeContent, &localeData); err != nil { - return err - } - return iterateMessagesNextInner(onMsgid, localeData) -} diff --git a/modules/log/event_writer_base.go b/modules/log/event_writer_base.go index c327c48ca2..9189ca4e90 100644 --- a/modules/log/event_writer_base.go +++ b/modules/log/event_writer_base.go @@ -105,7 +105,7 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) { case io.WriterTo: _, err = msg.WriteTo(b.OutputWriteCloser) default: - _, err = b.OutputWriteCloser.Write([]byte(fmt.Sprint(msg))) + _, err = fmt.Fprint(b.OutputWriteCloser, msg) } if err != nil { FallbackErrorf("unable to write log message of %q (%v): %v", b.Name, err, event.Msg) diff --git a/modules/log/flags_test.go b/modules/log/flags_test.go index 1ee322c630..1af9a58ed6 100644 --- a/modules/log/flags_test.go +++ b/modules/log/flags_test.go @@ -13,19 +13,19 @@ import ( ) func TestFlags(t *testing.T) { - assert.EqualValues(t, Ldefault, Flags{}.Bits()) + assert.Equal(t, Ldefault, Flags{}.Bits()) assert.EqualValues(t, 0, FlagsFromString("").Bits()) - assert.EqualValues(t, Lgopid, FlagsFromString("", Lgopid).Bits()) + assert.Equal(t, Lgopid, FlagsFromString("", Lgopid).Bits()) assert.EqualValues(t, 0, FlagsFromString("none", Lgopid).Bits()) - assert.EqualValues(t, Ldate|Ltime, FlagsFromString("date,time", Lgopid).Bits()) + assert.Equal(t, Ldate|Ltime, FlagsFromString("date,time", Lgopid).Bits()) - assert.EqualValues(t, "stdflags", FlagsFromString("stdflags").String()) - assert.EqualValues(t, "medfile", FlagsFromString("medfile").String()) + assert.Equal(t, "stdflags", FlagsFromString("stdflags").String()) + assert.Equal(t, "medfile", FlagsFromString("medfile").String()) bs, err := json.Marshal(FlagsFromString("utc,level")) require.NoError(t, err) - assert.EqualValues(t, `"level,utc"`, string(bs)) + assert.Equal(t, `"level,utc"`, string(bs)) var flags Flags require.NoError(t, json.Unmarshal(bs, &flags)) - assert.EqualValues(t, LUTC|Llevel, flags.Bits()) + assert.Equal(t, LUTC|Llevel, flags.Bits()) } diff --git a/modules/log/groutinelabel_test.go b/modules/log/groutinelabel_test.go index df8c1e9259..98d7b4e129 100644 --- a/modules/log/groutinelabel_test.go +++ b/modules/log/groutinelabel_test.go @@ -15,18 +15,18 @@ func Test_getGoroutineLabels(t *testing.T) { pprof.Do(t.Context(), pprof.Labels(), func(ctx context.Context) { currentLabels := getGoroutineLabels() pprof.ForLabels(ctx, func(key, value string) bool { - assert.EqualValues(t, value, currentLabels[key]) + assert.Equal(t, value, currentLabels[key]) return true }) pprof.Do(ctx, pprof.Labels("Test_getGoroutineLabels", "Test_getGoroutineLabels_child1"), func(ctx context.Context) { currentLabels := getGoroutineLabels() pprof.ForLabels(ctx, func(key, value string) bool { - assert.EqualValues(t, value, currentLabels[key]) + assert.Equal(t, value, currentLabels[key]) return true }) if assert.NotNil(t, currentLabels) { - assert.EqualValues(t, "Test_getGoroutineLabels_child1", currentLabels["Test_getGoroutineLabels"]) + assert.Equal(t, "Test_getGoroutineLabels_child1", currentLabels["Test_getGoroutineLabels"]) } }) }) diff --git a/modules/log/logger_test.go b/modules/log/logger_test.go index e04c9da8b0..6d6ceb69d7 100644 --- a/modules/log/logger_test.go +++ b/modules/log/logger_test.go @@ -56,16 +56,16 @@ func TestLogger(t *testing.T) { dump := logger.DumpWriters() assert.Empty(t, dump) - assert.EqualValues(t, NONE, logger.GetLevel()) + assert.Equal(t, NONE, logger.GetLevel()) assert.False(t, logger.IsEnabled()) w1 := newDummyWriter("dummy-1", DEBUG, 0) logger.AddWriters(w1) - assert.EqualValues(t, DEBUG, logger.GetLevel()) + assert.Equal(t, DEBUG, logger.GetLevel()) w2 := newDummyWriter("dummy-2", WARN, 200*time.Millisecond) logger.AddWriters(w2) - assert.EqualValues(t, DEBUG, logger.GetLevel()) + assert.Equal(t, DEBUG, logger.GetLevel()) dump = logger.DumpWriters() assert.Len(t, dump, 2) diff --git a/modules/markup/common/footnote.go b/modules/markup/common/footnote.go index 0e75e2adfd..05125dbcc1 100644 --- a/modules/markup/common/footnote.go +++ b/modules/markup/common/footnote.go @@ -459,7 +459,7 @@ func (r *FootnoteHTMLRenderer) renderFootnoteList(w util.BufWriter, source []byt html.RenderAttributes(w, node, html.GlobalAttributeFilter) } _ = w.WriteByte('>') - if r.Config.XHTML { + if r.XHTML { _, _ = w.WriteString("\n<hr />\n") } else { _, _ = w.WriteString("\n<hr>\n") diff --git a/modules/markup/common/linkify.go b/modules/markup/common/linkify.go index f84680205e..6e8ef0c973 100644 --- a/modules/markup/common/linkify.go +++ b/modules/markup/common/linkify.go @@ -73,9 +73,10 @@ func (s *linkifyParser) Parse(parent ast.Node, block text.Reader, pc parser.Cont } else if lastChar == ')' { closing := 0 for i := m[1] - 1; i >= m[0]; i-- { - if line[i] == ')' { + switch line[i] { + case ')': closing++ - } else if line[i] == '(' { + case '(': closing-- } } diff --git a/modules/markup/console/console_test.go b/modules/markup/console/console_test.go index 11e0a54e5d..0e8a0738c2 100644 --- a/modules/markup/console/console_test.go +++ b/modules/markup/console/console_test.go @@ -28,6 +28,6 @@ func TestRenderConsole(t *testing.T) { err := render.Render(&markup.RenderContext{Ctx: git.DefaultContext}, strings.NewReader(k), &buf) require.NoError(t, err) - assert.EqualValues(t, v, buf.String()) + assert.Equal(t, v, buf.String()) } } diff --git a/modules/markup/csv/csv_test.go b/modules/markup/csv/csv_test.go index 008a899c05..b536530196 100644 --- a/modules/markup/csv/csv_test.go +++ b/modules/markup/csv/csv_test.go @@ -28,6 +28,6 @@ func TestRenderCSV(t *testing.T) { err := render.Render(&markup.RenderContext{Ctx: git.DefaultContext}, strings.NewReader(k), &buf) require.NoError(t, err) - assert.EqualValues(t, v, buf.String()) + assert.Equal(t, v, buf.String()) } } diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 5499eff18c..8b1442ed08 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -341,7 +341,7 @@ func (p *FilePreview) CreateHTML(locale translation.Locale) *html.Node { psubtitle := &html.Node{ Type: html.ElementNode, Data: atom.Span.String(), - Attr: []html.Attribute{{Key: "class", Val: "text small grey"}}, + Attr: []html.Attribute{{Key: "class", Val: "text grey"}}, } psubtitle.AppendChild(&html.Node{ Type: html.RawNode, diff --git a/modules/markup/html.go b/modules/markup/html.go index 323adf931d..8ea32b6b86 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -363,7 +363,7 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) { // Add user-content- to IDs and "#" links if they don't already have them for idx, attr := range node.Attr { val := strings.TrimPrefix(attr.Val, "#") - notHasPrefix := !(strings.HasPrefix(val, "user-content-") || blackfridayExtRegex.MatchString(val)) + notHasPrefix := !strings.HasPrefix(val, "user-content-") && !blackfridayExtRegex.MatchString(val) if attr.Key == "id" && notHasPrefix { node.Attr[idx].Val = "user-content-" + attr.Val diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index d503796eb6..9d0c40c9e8 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -721,7 +721,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+ `</span>`+ `</div>`+ @@ -755,7 +755,7 @@ func TestRender_FilePreview(t *testing.T) { `<a href="http://localhost:3000/gogits/gogs/" rel="nofollow">gogits/gogs</a> – `+ `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">gogits/gogs@190d949</a>`+ `</span>`+ `</div>`+ @@ -791,7 +791,7 @@ func TestRender_FilePreview(t *testing.T) { `<a href="http://localhost:3000/gogits/gogs/" rel="nofollow">gogits/gogs</a> – `+ `<a href="http://localhost:3000/gogits/gogs/src/commit/4c1aaf56bcb9f39dcf65f3f250726850aed13cd6/single-line.txt#L1" class="muted" rel="nofollow">single-line.txt</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Line 1 in <a href="http://localhost:3000/gogits/gogs/src/commit/4c1aaf56bcb9f39dcf65f3f250726850aed13cd6" class="text black" rel="nofollow">gogits/gogs@4c1aaf5</a>`+ `</span>`+ `</div>`+ @@ -834,7 +834,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 2 to 3 in <a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+ `</span>`+ `</div>`+ @@ -865,7 +865,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 2 to 3 in <a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+ `</span>`+ `</div>`+ @@ -898,7 +898,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+ `</span>`+ `</div>`+ @@ -923,7 +923,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+ `</span>`+ `</div>`+ @@ -954,7 +954,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+ `</span>`+ `</div>`+ @@ -979,7 +979,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+ `</span>`+ `</div>`+ @@ -1004,7 +1004,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+ `</span>`+ `</div>`+ @@ -1039,7 +1039,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be/path/to/file.md?display=source#L1-L2" class="muted" rel="nofollow">path/to/file.md</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 1 to 2 in <a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be" class="text black" rel="nofollow">c991312</a>`+ `</span>`+ `</div>`+ @@ -1072,7 +1072,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be/path/to/file.md?display=source#L1-L2" class="muted" rel="nofollow">path/to/file.md</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 1 to 2 in <a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be" class="text black" rel="nofollow">c991312</a>`+ `</span>`+ `</div>`+ @@ -1107,7 +1107,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go?display=source#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+ `</span>`+ `</div>`+ @@ -1142,7 +1142,7 @@ func TestRender_FilePreview(t *testing.T) { `<div>`+ `<a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d/path/to/file%20%23.txt#L1" class="muted" rel="nofollow">path/to/file #.txt</a>`+ `</div>`+ - `<span class="text small grey">`+ + `<span class="text grey">`+ `Line 1 in <a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d" class="text black" rel="nofollow">eeb243c</a>`+ `</span>`+ `</div>`+ diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index 9a901a2287..d229afa8e3 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -131,7 +131,7 @@ func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast. if entering { _, err = w.WriteString("<div") if err == nil { - _, err = w.WriteString(fmt.Sprintf(` lang=%q`, val)) + _, err = fmt.Fprintf(w, ` lang=%q`, val) } if err == nil { _, err = w.WriteRune('>') @@ -203,7 +203,7 @@ func (r *HTMLRenderer) renderIcon(w util.BufWriter, source []byte, node ast.Node return ast.WalkContinue, nil } - _, err := w.WriteString(fmt.Sprintf(`<i class="icon %s"></i>`, name)) + _, err := fmt.Fprintf(w, `<i class="icon %s"></i>`, name) if err != nil { return ast.WalkStop, err } diff --git a/modules/markup/markdown/math/inline_parser.go b/modules/markup/markdown/math/inline_parser.go index b11195d551..4ffbf1257f 100644 --- a/modules/markup/markdown/math/inline_parser.go +++ b/modules/markup/markdown/math/inline_parser.go @@ -99,8 +99,8 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser. suceedingCharacter := line[pos] // check valid ending character if !isPunctuation(suceedingCharacter) && - !(suceedingCharacter == ' ') && - !(suceedingCharacter == '\n') && + (suceedingCharacter != ' ') && + (suceedingCharacter != '\n') && !isBracket(suceedingCharacter) { return nil } @@ -139,12 +139,12 @@ func trimBlock(node *Inline, block text.Reader) { // trim first space and last space first := node.FirstChild().(*ast.Text) - if !(!first.Segment.IsEmpty() && block.Source()[first.Segment.Start] == ' ') { + if first.Segment.IsEmpty() || block.Source()[first.Segment.Start] != ' ' { return } last := node.LastChild().(*ast.Text) - if !(!last.Segment.IsEmpty() && block.Source()[last.Segment.Stop-1] == ' ') { + if last.Segment.IsEmpty() || block.Source()[last.Segment.Stop-1] != ' ' { return } diff --git a/modules/markup/markdown/meta_test.go b/modules/markup/markdown/meta_test.go index d341ae43e4..aaf116ff20 100644 --- a/modules/markup/markdown/meta_test.go +++ b/modules/markup/markdown/meta_test.go @@ -54,7 +54,7 @@ func TestExtractMetadata(t *testing.T) { var meta IssueTemplate body, err := ExtractMetadata(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest), &meta) require.NoError(t, err) - assert.Equal(t, "", body) + assert.Empty(t, body) assert.Equal(t, metaTest, meta) assert.True(t, meta.Valid()) }) @@ -86,7 +86,7 @@ func TestExtractMetadataBytes(t *testing.T) { var meta IssueTemplate body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest)), &meta) require.NoError(t, err) - assert.Equal(t, "", string(body)) + assert.Empty(t, string(body)) assert.Equal(t, metaTest, meta) assert.True(t, meta.Valid()) }) diff --git a/modules/markup/markdown/transform_codespan.go b/modules/markup/markdown/transform_codespan.go index 15c3a44f0a..fc88db026b 100644 --- a/modules/markup/markdown/transform_codespan.go +++ b/modules/markup/markdown/transform_codespan.go @@ -40,7 +40,7 @@ func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Nod r.Writer.RawWrite(w, value) } case *ColorPreview: - _, _ = w.WriteString(fmt.Sprintf(`<span class="color-preview" style="background-color: %v"></span>`, string(v.Color))) + _, _ = fmt.Fprintf(w, `<span class="color-preview" style="background-color: %v"></span>`, string(v.Color)) } } return ast.WalkSkipChildren, nil diff --git a/modules/markup/mdstripper/mdstripper.go b/modules/markup/mdstripper/mdstripper.go index 6a34ac81c4..7e5abc2ebe 100644 --- a/modules/markup/mdstripper/mdstripper.go +++ b/modules/markup/mdstripper/mdstripper.go @@ -107,11 +107,12 @@ func (r *stripRenderer) processAutoLink(w io.Writer, link []byte) { } var sep string - if parts[3] == "issues" { + switch parts[3] { + case "issues": sep = "#" - } else if parts[3] == "pulls" { + case "pulls": sep = "!" - } else { + default: // Process out of band r.links = append(r.links, linkStr) return diff --git a/modules/markup/mdstripper/mdstripper_test.go b/modules/markup/mdstripper/mdstripper_test.go index ea34df0a3b..7fb49c1e01 100644 --- a/modules/markup/mdstripper/mdstripper_test.go +++ b/modules/markup/mdstripper/mdstripper_test.go @@ -79,7 +79,7 @@ A HIDDEN ` + "`" + `GHOST` + "`" + ` IN THIS LINE. lines = append(lines, line) } } - assert.EqualValues(t, test.expectedText, lines) - assert.EqualValues(t, test.expectedLinks, links) + assert.Equal(t, test.expectedText, lines) + assert.Equal(t, test.expectedLinks, links) } } diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index 7ff11f0844..384dd1fe94 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -122,7 +122,7 @@ func createDefaultPolicy() *bluemonday.Policy { policy.AllowAttrs("class").Matching(regexp.MustCompile("^ui table$")).OnElements("div") policy.AllowAttrs("class").Matching(regexp.MustCompile("^header$")).OnElements("div") policy.AllowAttrs("data-line-number").Matching(regexp.MustCompile("^[0-9]+$")).OnElements("span") - policy.AllowAttrs("class").Matching(regexp.MustCompile("^text small grey$")).OnElements("span") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^text grey$")).OnElements("span") policy.AllowAttrs("class").Matching(regexp.MustCompile("^file-preview$")).OnElements("table") policy.AllowAttrs("class").Matching(regexp.MustCompile("^lines-escape$")).OnElements("td") policy.AllowAttrs("class").Matching(regexp.MustCompile("^toggle-escape-button btn interact-bg$")).OnElements("button") diff --git a/modules/migration/pullrequest.go b/modules/migration/pullrequest.go index 933348d671..0861ab24f1 100644 --- a/modules/migration/pullrequest.go +++ b/modules/migration/pullrequest.go @@ -34,6 +34,7 @@ type PullRequest struct { Assignees []string IsLocked bool `yaml:"is_locked"` Reactions []*Reaction + Flow int64 ForeignIndex int64 Context DownloaderContext `yaml:"-"` EnsuredSafe bool `yaml:"ensured_safe"` diff --git a/modules/optional/option.go b/modules/optional/option.go index af9e5ac852..ccbad259c2 100644 --- a/modules/optional/option.go +++ b/modules/optional/option.go @@ -3,6 +3,8 @@ package optional +import "strconv" + type Option[T any] []T func None[T any]() Option[T] { @@ -43,3 +45,12 @@ func (o Option[T]) ValueOrDefault(v T) T { } return v } + +// ParseBool get the corresponding optional.Option[bool] of a string using strconv.ParseBool +func ParseBool(s string) Option[bool] { + v, e := strconv.ParseBool(s) + if e != nil { + return None[bool]() + } + return Some(v) +} diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go index f6d22d2431..a674caf633 100644 --- a/modules/optional/option_test.go +++ b/modules/optional/option_test.go @@ -57,3 +57,16 @@ func TestOption(t *testing.T) { assert.True(t, opt3.Has()) assert.Equal(t, int(1), opt3.Value()) } + +func Test_ParseBool(t *testing.T) { + assert.Equal(t, optional.None[bool](), optional.ParseBool("")) + assert.Equal(t, optional.None[bool](), optional.ParseBool("x")) + + assert.Equal(t, optional.Some(false), optional.ParseBool("0")) + assert.Equal(t, optional.Some(false), optional.ParseBool("f")) + assert.Equal(t, optional.Some(false), optional.ParseBool("False")) + + assert.Equal(t, optional.Some(true), optional.ParseBool("1")) + assert.Equal(t, optional.Some(true), optional.ParseBool("t")) + assert.Equal(t, optional.Some(true), optional.ParseBool("True")) +} diff --git a/modules/optional/serialization_test.go b/modules/optional/serialization_test.go index 80fe1c9805..14bf3b7814 100644 --- a/modules/optional/serialization_test.go +++ b/modules/optional/serialization_test.go @@ -52,11 +52,11 @@ func TestOptionalToJson(t *testing.T) { t.Run(tc.name, func(t *testing.T) { b, err := json.Marshal(tc.obj) require.NoError(t, err) - assert.EqualValues(t, tc.want, string(b), "gitea json module returned unexpected") + assert.Equal(t, tc.want, string(b), "gitea json module returned unexpected") b, err = std_json.Marshal(tc.obj) require.NoError(t, err) - assert.EqualValues(t, tc.want, string(b), "std json module returned unexpected") + assert.Equal(t, tc.want, string(b), "std json module returned unexpected") }) } } @@ -90,12 +90,12 @@ func TestOptionalFromJson(t *testing.T) { var obj1 testSerializationStruct err := json.Unmarshal([]byte(tc.data), &obj1) require.NoError(t, err) - assert.EqualValues(t, tc.want, obj1, "gitea json module returned unexpected") + assert.Equal(t, tc.want, obj1, "gitea json module returned unexpected") var obj2 testSerializationStruct err = std_json.Unmarshal([]byte(tc.data), &obj2) require.NoError(t, err) - assert.EqualValues(t, tc.want, obj2, "std json module returned unexpected") + assert.Equal(t, tc.want, obj2, "std json module returned unexpected") }) } } @@ -136,7 +136,7 @@ optional_two_string: null t.Run(tc.name, func(t *testing.T) { b, err := yaml.Marshal(tc.obj) require.NoError(t, err) - assert.EqualValues(t, tc.want, string(b), "yaml module returned unexpected") + assert.Equal(t, tc.want, string(b), "yaml module returned unexpected") }) } } @@ -185,7 +185,7 @@ optional_twostring: null var obj testSerializationStruct err := yaml.Unmarshal([]byte(tc.data), &obj) require.NoError(t, err) - assert.EqualValues(t, tc.want, obj, "yaml module returned unexpected") + assert.Equal(t, tc.want, obj, "yaml module returned unexpected") }) } } diff --git a/modules/packages/cargo/parser_test.go b/modules/packages/cargo/parser_test.go index 8792a7a977..d2470fab42 100644 --- a/modules/packages/cargo/parser_test.go +++ b/modules/packages/cargo/parser_test.go @@ -98,8 +98,8 @@ func TestParsePackage(t *testing.T) { assert.Equal(t, []string{author}, cp.Metadata.Authors) assert.Len(t, cp.Metadata.Dependencies, 2) assert.Equal(t, "dep", cp.Metadata.Dependencies[0].Name) - assert.EqualValues(t, "v4l2-sys-mit", cp.Metadata.Dependencies[1].Name) - assert.EqualValues(t, "v4l2-sys", *cp.Metadata.Dependencies[1].Package) + assert.Equal(t, "v4l2-sys-mit", cp.Metadata.Dependencies[1].Name) + assert.Equal(t, "v4l2-sys", *cp.Metadata.Dependencies[1].Package) assert.Equal(t, homepage, cp.Metadata.ProjectURL) assert.Equal(t, license, cp.Metadata.License) content, _ := io.ReadAll(cp.Content) diff --git a/modules/process/manager_stacktraces_test.go b/modules/process/manager_stacktraces_test.go index 509b424d8b..936fab40d8 100644 --- a/modules/process/manager_stacktraces_test.go +++ b/modules/process/manager_stacktraces_test.go @@ -23,19 +23,19 @@ func TestProcessStacktraces(t *testing.T) { t.Run("No flat with no system process", func(t *testing.T) { processes, processCount, _, err := GetManager().ProcessStacktraces(false, true) require.NoError(t, err) - assert.EqualValues(t, 4, processCount) + assert.Equal(t, 4, processCount) assert.Len(t, processes, 2) - assert.EqualValues(t, "Children normal process", processes[0].Description) - assert.EqualValues(t, NormalProcessType, processes[0].Type) + assert.Equal(t, "Children normal process", processes[0].Description) + assert.Equal(t, NormalProcessType, processes[0].Type) assert.Empty(t, processes[0].ParentPID) assert.Len(t, processes[0].Children, 1) - assert.EqualValues(t, "Children process", processes[0].Children[0].Description) - assert.EqualValues(t, processes[0].PID, processes[0].Children[0].ParentPID) + assert.Equal(t, "Children process", processes[0].Children[0].Description) + assert.Equal(t, processes[0].PID, processes[0].Children[0].ParentPID) - assert.EqualValues(t, "Normal process", processes[1].Description) - assert.EqualValues(t, NormalProcessType, processes[1].Type) + assert.Equal(t, "Normal process", processes[1].Description) + assert.Equal(t, NormalProcessType, processes[1].Type) assert.Empty(t, processes[1].ParentPID) assert.Empty(t, processes[1].Children) }) @@ -43,21 +43,21 @@ func TestProcessStacktraces(t *testing.T) { t.Run("Flat with no system process", func(t *testing.T) { processes, processCount, _, err := GetManager().ProcessStacktraces(true, true) require.NoError(t, err) - assert.EqualValues(t, 4, processCount) + assert.Equal(t, 4, processCount) assert.Len(t, processes, 3) - assert.EqualValues(t, "Children process", processes[0].Description) - assert.EqualValues(t, NormalProcessType, processes[0].Type) - assert.EqualValues(t, processes[1].PID, processes[0].ParentPID) + assert.Equal(t, "Children process", processes[0].Description) + assert.Equal(t, NormalProcessType, processes[0].Type) + assert.Equal(t, processes[1].PID, processes[0].ParentPID) assert.Empty(t, processes[0].Children) - assert.EqualValues(t, "Children normal process", processes[1].Description) - assert.EqualValues(t, NormalProcessType, processes[1].Type) + assert.Equal(t, "Children normal process", processes[1].Description) + assert.Equal(t, NormalProcessType, processes[1].Type) assert.Empty(t, processes[1].ParentPID) assert.Empty(t, processes[1].Children) - assert.EqualValues(t, "Normal process", processes[2].Description) - assert.EqualValues(t, NormalProcessType, processes[2].Type) + assert.Equal(t, "Normal process", processes[2].Description) + assert.Equal(t, NormalProcessType, processes[2].Type) assert.Empty(t, processes[2].ParentPID) assert.Empty(t, processes[2].Children) }) @@ -65,27 +65,27 @@ func TestProcessStacktraces(t *testing.T) { t.Run("System process", func(t *testing.T) { processes, processCount, _, err := GetManager().ProcessStacktraces(false, false) require.NoError(t, err) - assert.EqualValues(t, 4, processCount) + assert.Equal(t, 4, processCount) assert.Len(t, processes, 4) - assert.EqualValues(t, "System process", processes[0].Description) - assert.EqualValues(t, SystemProcessType, processes[0].Type) + assert.Equal(t, "System process", processes[0].Description) + assert.Equal(t, SystemProcessType, processes[0].Type) assert.Empty(t, processes[0].ParentPID) assert.Empty(t, processes[0].Children) - assert.EqualValues(t, "Children normal process", processes[1].Description) - assert.EqualValues(t, NormalProcessType, processes[1].Type) + assert.Equal(t, "Children normal process", processes[1].Description) + assert.Equal(t, NormalProcessType, processes[1].Type) assert.Empty(t, processes[1].ParentPID) assert.Len(t, processes[1].Children, 1) - assert.EqualValues(t, "Normal process", processes[2].Description) - assert.EqualValues(t, NormalProcessType, processes[2].Type) + assert.Equal(t, "Normal process", processes[2].Description) + assert.Equal(t, NormalProcessType, processes[2].Type) assert.Empty(t, processes[2].ParentPID) assert.Empty(t, processes[2].Children) // This is the "main" pid, testing code always runs in a goroutine. - assert.EqualValues(t, "(unassociated)", processes[3].Description) - assert.EqualValues(t, NoneProcessType, processes[3].Type) + assert.Equal(t, "(unassociated)", processes[3].Description) + assert.Equal(t, NoneProcessType, processes[3].Type) assert.Empty(t, processes[3].ParentPID) assert.Empty(t, processes[3].Children) }) diff --git a/modules/queue/base_test.go b/modules/queue/base_test.go index d852a80b16..caa930158c 100644 --- a/modules/queue/base_test.go +++ b/modules/queue/base_test.go @@ -22,7 +22,7 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) _ = q.RemoveAll(ctx) cnt, err := q.Len(ctx) require.NoError(t, err) - assert.EqualValues(t, 0, cnt) + assert.Equal(t, 0, cnt) // push the first item err = q.PushItem(ctx, []byte("foo")) @@ -30,7 +30,7 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) cnt, err = q.Len(ctx) require.NoError(t, err) - assert.EqualValues(t, 1, cnt) + assert.Equal(t, 1, cnt) // push a duplicate item err = q.PushItem(ctx, []byte("foo")) @@ -46,10 +46,10 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) has, err := q.HasItem(ctx, []byte("foo")) require.NoError(t, err) if !isUnique { - assert.EqualValues(t, 2, cnt) + assert.Equal(t, 2, cnt) assert.False(t, has) // non-unique queues don't check for duplicates } else { - assert.EqualValues(t, 1, cnt) + assert.Equal(t, 1, cnt) assert.True(t, has) } @@ -60,18 +60,18 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) // pop the first item (and the duplicate if non-unique) it, err := q.PopItem(ctx) require.NoError(t, err) - assert.EqualValues(t, "foo", string(it)) + assert.Equal(t, "foo", string(it)) if !isUnique { it, err = q.PopItem(ctx) require.NoError(t, err) - assert.EqualValues(t, "foo", string(it)) + assert.Equal(t, "foo", string(it)) } // pop another item it, err = q.PopItem(ctx) require.NoError(t, err) - assert.EqualValues(t, "bar", string(it)) + assert.Equal(t, "bar", string(it)) // pop an empty queue (timeout, cancel) ctxTimed, cancel := context.WithTimeout(ctx, 10*time.Millisecond) @@ -108,13 +108,13 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) // remove all cnt, err = q.Len(ctx) require.NoError(t, err) - assert.EqualValues(t, cfg.Length, cnt) + assert.Equal(t, cfg.Length, cnt) _ = q.RemoveAll(ctx) cnt, err = q.Len(ctx) require.NoError(t, err) - assert.EqualValues(t, 0, cnt) + assert.Equal(t, 0, cnt) }) } @@ -127,7 +127,7 @@ func TestBaseDummy(t *testing.T) { cnt, err := q.Len(ctx) require.NoError(t, err) - assert.EqualValues(t, 0, cnt) + assert.Equal(t, 0, cnt) has, err := q.HasItem(ctx, []byte("foo")) require.NoError(t, err) diff --git a/modules/queue/manager_test.go b/modules/queue/manager_test.go index bd6e314493..f02d66dff4 100644 --- a/modules/queue/manager_test.go +++ b/modules/queue/manager_test.go @@ -48,7 +48,7 @@ CONN_STR = redis:// assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/common"), q.baseConfig.DataFullDir) assert.Equal(t, 100000, q.baseConfig.Length) assert.Equal(t, 20, q.batchLength) - assert.Equal(t, "", q.baseConfig.ConnStr) + assert.Empty(t, q.baseConfig.ConnStr) assert.Equal(t, "default_queue", q.baseConfig.QueueFullName) assert.Equal(t, "default_queue_unique", q.baseConfig.SetFullName) assert.NotZero(t, q.GetWorkerMaxNumber()) @@ -102,7 +102,7 @@ MAX_WORKERS = 123 assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/dir2"), q2.baseConfig.DataFullDir) assert.Equal(t, 102, q2.baseConfig.Length) assert.Equal(t, 22, q2.batchLength) - assert.Equal(t, "", q2.baseConfig.ConnStr) + assert.Empty(t, q2.baseConfig.ConnStr) assert.Equal(t, "sub_q2", q2.baseConfig.QueueFullName) assert.Equal(t, "sub_q2_u2", q2.baseConfig.SetFullName) assert.Equal(t, 123, q2.GetWorkerMaxNumber()) diff --git a/modules/queue/workerqueue_test.go b/modules/queue/workerqueue_test.go index 5ae1a701b2..8d907ed8cd 100644 --- a/modules/queue/workerqueue_test.go +++ b/modules/queue/workerqueue_test.go @@ -64,9 +64,9 @@ func TestWorkerPoolQueueUnhandled(t *testing.T) { ok := true for i := 0; i < queueSetting.Length; i++ { if i%2 == 0 { - ok = ok && assert.EqualValues(t, 2, m[i], "test %s: item %d", t.Name(), i) + ok = ok && assert.Equal(t, 2, m[i], "test %s: item %d", t.Name(), i) } else { - ok = ok && assert.EqualValues(t, 1, m[i], "test %s: item %d", t.Name(), i) + ok = ok && assert.Equal(t, 1, m[i], "test %s: item %d", t.Name(), i) } } if !ok { @@ -174,7 +174,7 @@ func testWorkerPoolQueuePersistence(t *testing.T, queueSetting setting.QueueSett assert.NotEmpty(t, tasksQ1) assert.NotEmpty(t, tasksQ2) - assert.EqualValues(t, testCount, len(tasksQ1)+len(tasksQ2)) + assert.Equal(t, testCount, len(tasksQ1)+len(tasksQ2)) } func TestWorkerPoolQueueActiveWorkers(t *testing.T) { @@ -192,13 +192,13 @@ func TestWorkerPoolQueueActiveWorkers(t *testing.T) { } time.Sleep(50 * time.Millisecond) - assert.EqualValues(t, 1, q.GetWorkerNumber()) - assert.EqualValues(t, 1, q.GetWorkerActiveNumber()) + assert.Equal(t, 1, q.GetWorkerNumber()) + assert.Equal(t, 1, q.GetWorkerActiveNumber()) time.Sleep(500 * time.Millisecond) - assert.EqualValues(t, 1, q.GetWorkerNumber()) - assert.EqualValues(t, 0, q.GetWorkerActiveNumber()) + assert.Equal(t, 1, q.GetWorkerNumber()) + assert.Equal(t, 0, q.GetWorkerActiveNumber()) time.Sleep(workerIdleDuration) - assert.EqualValues(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working + assert.Equal(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working stop() q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 3, Length: 100}, handler, false) @@ -208,13 +208,13 @@ func TestWorkerPoolQueueActiveWorkers(t *testing.T) { } time.Sleep(50 * time.Millisecond) - assert.EqualValues(t, 3, q.GetWorkerNumber()) - assert.EqualValues(t, 3, q.GetWorkerActiveNumber()) + assert.Equal(t, 3, q.GetWorkerNumber()) + assert.Equal(t, 3, q.GetWorkerActiveNumber()) time.Sleep(500 * time.Millisecond) - assert.EqualValues(t, 3, q.GetWorkerNumber()) - assert.EqualValues(t, 0, q.GetWorkerActiveNumber()) + assert.Equal(t, 3, q.GetWorkerNumber()) + assert.Equal(t, 0, q.GetWorkerActiveNumber()) time.Sleep(workerIdleDuration) - assert.EqualValues(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working + assert.Equal(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working stop() } @@ -241,13 +241,13 @@ func TestWorkerPoolQueueShutdown(t *testing.T) { } <-handlerCalled time.Sleep(200 * time.Millisecond) // wait for a while to make sure all workers are active - assert.EqualValues(t, 4, q.GetWorkerActiveNumber()) + assert.Equal(t, 4, q.GetWorkerActiveNumber()) stop() // stop triggers shutdown - assert.EqualValues(t, 0, q.GetWorkerActiveNumber()) + assert.Equal(t, 0, q.GetWorkerActiveNumber()) // no item was ever handled, so we still get all of them again q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", qs, handler, false) - assert.EqualValues(t, 20, q.GetQueueItemNumber()) + assert.Equal(t, 20, q.GetQueueItemNumber()) } func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) { diff --git a/modules/references/references.go b/modules/references/references.go index e1d4b47de0..81267f0065 100644 --- a/modules/references/references.go +++ b/modules/references/references.go @@ -464,11 +464,12 @@ func findAllIssueReferencesBytes(content []byte, links []string) []*rawReference continue } var sep string - if parts[3] == "issues" { + switch parts[3] { + case "issues": sep = "#" - } else if parts[3] == "pulls" { + case "pulls": sep = "!" - } else { + default: continue } // Note: closing/reopening keywords not supported with URLs diff --git a/modules/references/references_test.go b/modules/references/references_test.go index 996066c820..77f6bfbae3 100644 --- a/modules/references/references_test.go +++ b/modules/references/references_test.go @@ -46,7 +46,7 @@ owner/repo!123456789 contentBytes := []byte(test) convertFullHTMLReferencesToShortRefs(re, &contentBytes) result := string(contentBytes) - assert.EqualValues(t, expect, result) + assert.Equal(t, expect, result) } func TestFindAllIssueReferences(t *testing.T) { @@ -284,9 +284,9 @@ func testFixtures(t *testing.T, fixtures []testFixture, context string) { } expref := rawToIssueReferenceList(expraw) refs := FindAllIssueReferencesMarkdown(fixture.input) - assert.EqualValues(t, expref, refs, "[%s] Failed to parse: {%s}", context, fixture.input) + assert.Equal(t, expref, refs, "[%s] Failed to parse: {%s}", context, fixture.input) rawrefs := findAllIssueReferencesMarkdown(fixture.input) - assert.EqualValues(t, expraw, rawrefs, "[%s] Failed to parse: {%s}", context, fixture.input) + assert.Equal(t, expraw, rawrefs, "[%s] Failed to parse: {%s}", context, fixture.input) } // Restore for other tests that may rely on the original value @@ -295,7 +295,7 @@ func testFixtures(t *testing.T, fixtures []testFixture, context string) { func TestFindAllMentions(t *testing.T) { res := FindAllMentionsBytes([]byte("@tasha, @mike; @lucy: @john")) - assert.EqualValues(t, []RefSpan{ + assert.Equal(t, []RefSpan{ {Start: 0, End: 6}, {Start: 8, End: 13}, {Start: 15, End: 20}, @@ -558,7 +558,7 @@ func TestParseCloseKeywords(t *testing.T) { res := pat.FindAllStringSubmatch(test.match, -1) assert.Len(t, res, 1) assert.Len(t, res[0], 2) - assert.EqualValues(t, test.expected, res[0][1]) + assert.Equal(t, test.expected, res[0][1]) } } } diff --git a/modules/regexplru/regexplru_test.go b/modules/regexplru/regexplru_test.go index 8c0c722336..6e15e88e14 100644 --- a/modules/regexplru/regexplru_test.go +++ b/modules/regexplru/regexplru_test.go @@ -19,9 +19,9 @@ func TestRegexpLru(t *testing.T) { require.NoError(t, err) assert.True(t, r.MatchString("a")) - assert.EqualValues(t, 1, lruCache.Len()) + assert.Equal(t, 1, lruCache.Len()) _, err = GetCompiled("(") require.Error(t, err) - assert.EqualValues(t, 2, lruCache.Len()) + assert.Equal(t, 2, lruCache.Len()) } diff --git a/modules/repository/branch_test.go b/modules/repository/branch_test.go index deb6cd5d19..31e27f222f 100644 --- a/modules/repository/branch_test.go +++ b/modules/repository/branch_test.go @@ -28,5 +28,5 @@ func TestSyncRepoBranches(t *testing.T) { assert.Equal(t, "sha1", repo.ObjectFormatName) branch, err := git_model.GetBranch(db.DefaultContext, 1, "master") require.NoError(t, err) - assert.EqualValues(t, "master", branch.Name) + assert.Equal(t, "master", branch.Name) } diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go index f49b0d37c5..f5ba0adeba 100644 --- a/modules/repository/commits_test.go +++ b/modules/repository/commits_test.go @@ -63,9 +63,9 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { assert.Equal(t, "user2", payloadCommits[0].Committer.UserName) assert.Equal(t, "User2", payloadCommits[0].Author.Name) assert.Equal(t, "user2", payloadCommits[0].Author.UserName) - assert.EqualValues(t, []string{}, payloadCommits[0].Added) - assert.EqualValues(t, []string{}, payloadCommits[0].Removed) - assert.EqualValues(t, []string{"readme.md"}, payloadCommits[0].Modified) + assert.Equal(t, []string{}, payloadCommits[0].Added) + assert.Equal(t, []string{}, payloadCommits[0].Removed) + assert.Equal(t, []string{"readme.md"}, payloadCommits[0].Modified) assert.Equal(t, "27566bd", payloadCommits[1].ID) assert.Equal(t, "good signed commit (with not yet validated email)", payloadCommits[1].Message) @@ -74,9 +74,9 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { assert.Equal(t, "user2", payloadCommits[1].Committer.UserName) assert.Equal(t, "User2", payloadCommits[1].Author.Name) assert.Equal(t, "user2", payloadCommits[1].Author.UserName) - assert.EqualValues(t, []string{}, payloadCommits[1].Added) - assert.EqualValues(t, []string{}, payloadCommits[1].Removed) - assert.EqualValues(t, []string{"readme.md"}, payloadCommits[1].Modified) + assert.Equal(t, []string{}, payloadCommits[1].Added) + assert.Equal(t, []string{}, payloadCommits[1].Removed) + assert.Equal(t, []string{"readme.md"}, payloadCommits[1].Modified) assert.Equal(t, "5099b81", payloadCommits[2].ID) assert.Equal(t, "good signed commit", payloadCommits[2].Message) @@ -85,9 +85,9 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { assert.Equal(t, "user2", payloadCommits[2].Committer.UserName) assert.Equal(t, "User2", payloadCommits[2].Author.Name) assert.Equal(t, "user2", payloadCommits[2].Author.UserName) - assert.EqualValues(t, []string{"readme.md"}, payloadCommits[2].Added) - assert.EqualValues(t, []string{}, payloadCommits[2].Removed) - assert.EqualValues(t, []string{}, payloadCommits[2].Modified) + assert.Equal(t, []string{"readme.md"}, payloadCommits[2].Added) + assert.Equal(t, []string{}, payloadCommits[2].Removed) + assert.Equal(t, []string{}, payloadCommits[2].Modified) assert.Equal(t, "69554a6", headCommit.ID) assert.Equal(t, "not signed commit", headCommit.Message) @@ -96,9 +96,9 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { assert.Equal(t, "user2", headCommit.Committer.UserName) assert.Equal(t, "User2", headCommit.Author.Name) assert.Equal(t, "user2", headCommit.Author.UserName) - assert.EqualValues(t, []string{}, headCommit.Added) - assert.EqualValues(t, []string{}, headCommit.Removed) - assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified) + assert.Equal(t, []string{}, headCommit.Added) + assert.Equal(t, []string{}, headCommit.Removed) + assert.Equal(t, []string{"readme.md"}, headCommit.Modified) } func TestPushCommits_AvatarLink(t *testing.T) { diff --git a/modules/repository/create.go b/modules/repository/create.go index d76a5571c7..25c7edb425 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -73,7 +73,8 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re } units := make([]repo_model.RepoUnit, 0, len(defaultUnits)) for _, tp := range defaultUnits { - if tp == unit.TypeIssues { + switch tp { + case unit.TypeIssues: units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, Type: tp, @@ -83,7 +84,7 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re EnableDependencies: setting.Service.DefaultEnableDependencies, }, }) - } else if tp == unit.TypePullRequests { + case unit.TypePullRequests: units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, Type: tp, @@ -94,7 +95,7 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re AllowRebaseUpdate: true, }, }) - } else { + default: units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, Type: tp, @@ -240,6 +241,11 @@ func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibili e := db.GetEngine(ctx) + // If the repository was reported as abusive, a shadow copy should be created before first update. + if err := repo_model.IfNeededCreateShadowCopyForRepository(ctx, repo, true); err != nil { + return err + } + if _, err = e.ID(repo.ID).AllCols().Update(repo); err != nil { return fmt.Errorf("update: %w", err) } diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go index cb34143cef..45f7f8e853 100644 --- a/modules/repository/create_test.go +++ b/modules/repository/create_test.go @@ -42,5 +42,5 @@ func TestGetDirectorySize(t *testing.T) { size, err := getDirectorySize(repo.RepoPath()) require.NoError(t, err) - assert.EqualValues(t, size, repo.Size) + assert.Equal(t, size, repo.Size) } diff --git a/modules/repository/init_test.go b/modules/repository/init_test.go index 227efdc1db..1fa928105c 100644 --- a/modules/repository/init_test.go +++ b/modules/repository/init_test.go @@ -14,17 +14,17 @@ func TestMergeCustomLabels(t *testing.T) { all: []string{"a", "a.yaml", "a.yml"}, custom: nil, }) - assert.EqualValues(t, []string{"a.yaml"}, files, "yaml file should win") + assert.Equal(t, []string{"a.yaml"}, files, "yaml file should win") files = mergeCustomLabelFiles(optionFileList{ all: []string{"a", "a.yaml"}, custom: []string{"a"}, }) - assert.EqualValues(t, []string{"a"}, files, "custom file should win") + assert.Equal(t, []string{"a"}, files, "custom file should win") files = mergeCustomLabelFiles(optionFileList{ all: []string{"a", "a.yml", "a.yaml"}, custom: []string{"a", "a.yml"}, }) - assert.EqualValues(t, []string{"a.yml"}, files, "custom yml file should win if no yaml") + assert.Equal(t, []string{"a.yml"}, files, "custom yml file should win if no yaml") } diff --git a/modules/repository/license.go b/modules/repository/license.go index af75d463d2..9776f047af 100644 --- a/modules/repository/license.go +++ b/modules/repository/license.go @@ -99,7 +99,8 @@ func getLicensePlaceholder(name string) *licensePlaceholder { // Some special placeholders for specific licenses. // It's unsafe to apply them to all licenses. - if name == "0BSD" { + switch name { + case "0BSD": return &licensePlaceholder{ Owner: []string{"AUTHOR"}, Email: []string{"EMAIL"}, @@ -108,7 +109,7 @@ func getLicensePlaceholder(name string) *licensePlaceholder { } // Other special placeholders can be added here. - } else if name == "BSD-4-Clause" { + case "BSD-4-Clause": ret.Owner = append(ret.Owner, "COPYRIGHT HOLDER") ret.Owner = append(ret.Owner, "the organization") } diff --git a/modules/repository/repo_test.go b/modules/repository/repo_test.go index 278bdc2420..45a650ba42 100644 --- a/modules/repository/repo_test.go +++ b/modules/repository/repo_test.go @@ -63,7 +63,7 @@ func Test_calcSync(t *testing.T) { inserts, deletes, updates := calcSync(gitTags, dbReleases) if assert.Len(t, inserts, 1, "inserts") { - assert.EqualValues(t, *gitTags[2], *inserts[0], "inserts equal") + assert.Equal(t, *gitTags[2], *inserts[0], "inserts equal") } if assert.Len(t, deletes, 1, "deletes") { @@ -71,6 +71,6 @@ func Test_calcSync(t *testing.T) { } if assert.Len(t, updates, 1, "updates") { - assert.EqualValues(t, *gitTags[1], *updates[0], "updates equal") + assert.Equal(t, *gitTags[1], *updates[0], "updates equal") } } diff --git a/modules/setting/actions_test.go b/modules/setting/actions_test.go index 4bff6e02ad..a3cd5ced44 100644 --- a/modules/setting/actions_test.go +++ b/modules/setting/actions_test.go @@ -21,9 +21,9 @@ func Test_getStorageInheritNameSectionTypeForActions(t *testing.T) { require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "minio", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) iniStr = ` [storage.actions_log] @@ -34,9 +34,9 @@ STORAGE_TYPE = minio require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "minio", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) assert.EqualValues(t, "local", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) + assert.Equal(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) iniStr = ` [storage.actions_log] @@ -50,9 +50,9 @@ STORAGE_TYPE = minio require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "minio", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) assert.EqualValues(t, "local", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) + assert.Equal(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) iniStr = ` [storage.actions_artifacts] @@ -66,9 +66,9 @@ STORAGE_TYPE = minio require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "local", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) + assert.Equal(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) iniStr = ` [storage.actions_artifacts] @@ -82,9 +82,9 @@ STORAGE_TYPE = minio require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "local", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) + assert.Equal(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) iniStr = `` cfg, err = NewConfigProviderFromData(iniStr) @@ -92,9 +92,9 @@ STORAGE_TYPE = minio require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "local", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) + assert.Equal(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) assert.EqualValues(t, "local", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) + assert.Equal(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) } func Test_getDefaultActionsURLForActions(t *testing.T) { @@ -151,7 +151,7 @@ DEFAULT_ACTIONS_URL = https://example.com require.NoError(t, err) require.NoError(t, loadActionsFrom(cfg)) - assert.EqualValues(t, tt.wantURL, Actions.DefaultActionsURL.URL()) + assert.Equal(t, tt.wantURL, Actions.DefaultActionsURL.URL()) }) } } diff --git a/modules/setting/admin_test.go b/modules/setting/admin_test.go index 5473534521..744f069d82 100644 --- a/modules/setting/admin_test.go +++ b/modules/setting/admin_test.go @@ -26,8 +26,8 @@ func Test_loadAdminFrom(t *testing.T) { loadAdminFrom(cfg) assert.True(t, Admin.DisableRegularOrgCreation) - assert.EqualValues(t, "z", Admin.DefaultEmailNotification) + assert.Equal(t, "z", Admin.DefaultEmailNotification) assert.True(t, Admin.SendNotificationEmailOnNewUser) - assert.EqualValues(t, container.SetOf("a", "b"), Admin.UserDisabledFeatures) - assert.EqualValues(t, container.SetOf("x", "y"), Admin.ExternalUserDisableFeatures) + assert.Equal(t, container.SetOf("a", "b"), Admin.UserDisabledFeatures) + assert.Equal(t, container.SetOf("x", "y"), Admin.ExternalUserDisableFeatures) } diff --git a/modules/setting/attachment_test.go b/modules/setting/attachment_test.go index f8085c1657..a56fcf1c55 100644 --- a/modules/setting/attachment_test.go +++ b/modules/setting/attachment_test.go @@ -26,9 +26,9 @@ MINIO_ENDPOINT = my_minio:9000 require.NoError(t, loadAttachmentFrom(cfg)) assert.EqualValues(t, "minio", Attachment.Storage.Type) - assert.EqualValues(t, "my_minio:9000", Attachment.Storage.MinioConfig.Endpoint) - assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "my_minio:9000", Attachment.Storage.MinioConfig.Endpoint) + assert.Equal(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) } func Test_getStorageTypeSectionOverridesStorageSection(t *testing.T) { @@ -48,8 +48,8 @@ MINIO_BUCKET = gitea require.NoError(t, loadAttachmentFrom(cfg)) assert.EqualValues(t, "minio", Attachment.Storage.Type) - assert.EqualValues(t, "gitea-minio", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-minio", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) } func Test_getStorageSpecificOverridesStorage(t *testing.T) { @@ -70,8 +70,8 @@ STORAGE_TYPE = local require.NoError(t, loadAttachmentFrom(cfg)) assert.EqualValues(t, "minio", Attachment.Storage.Type) - assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) } func Test_getStorageGetDefaults(t *testing.T) { @@ -81,7 +81,7 @@ func Test_getStorageGetDefaults(t *testing.T) { require.NoError(t, loadAttachmentFrom(cfg)) // default storage is local, so bucket is empty - assert.EqualValues(t, "", Attachment.Storage.MinioConfig.Bucket) + assert.Empty(t, Attachment.Storage.MinioConfig.Bucket) } func Test_getStorageInheritNameSectionType(t *testing.T) { @@ -116,7 +116,7 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := Attachment.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) } func Test_AttachmentStorage1(t *testing.T) { @@ -129,6 +129,6 @@ STORAGE_TYPE = minio require.NoError(t, loadAttachmentFrom(cfg)) assert.EqualValues(t, "minio", Attachment.Storage.Type) - assert.EqualValues(t, "gitea", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) } diff --git a/modules/setting/config_env_test.go b/modules/setting/config_env_test.go index bec3e584ef..fed0f668aa 100644 --- a/modules/setting/config_env_test.go +++ b/modules/setting/config_env_test.go @@ -30,8 +30,8 @@ func TestDecodeEnvSectionKey(t *testing.T) { ok, section, key = decodeEnvSectionKey("SEC") assert.False(t, ok) - assert.Equal(t, "", section) - assert.Equal(t, "", key) + assert.Empty(t, section) + assert.Empty(t, key) } func TestDecodeEnvironmentKey(t *testing.T) { @@ -40,19 +40,19 @@ func TestDecodeEnvironmentKey(t *testing.T) { ok, section, key, file := decodeEnvironmentKey(prefix, suffix, "SEC__KEY") assert.False(t, ok) - assert.Equal(t, "", section) - assert.Equal(t, "", key) + assert.Empty(t, section) + assert.Empty(t, key) assert.False(t, file) ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA__SEC") assert.False(t, ok) - assert.Equal(t, "", section) - assert.Equal(t, "", key) + assert.Empty(t, section) + assert.Empty(t, key) assert.False(t, file) ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA____KEY") assert.True(t, ok) - assert.Equal(t, "", section) + assert.Empty(t, section) assert.Equal(t, "KEY", key) assert.False(t, file) @@ -72,8 +72,8 @@ func TestDecodeEnvironmentKey(t *testing.T) { // but it could be fixed in the future by adding a new suffix like "__VALUE" (no such key VALUE is used in Gitea either) ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA__SEC__FILE") assert.False(t, ok) - assert.Equal(t, "", section) - assert.Equal(t, "", key) + assert.Empty(t, section) + assert.Empty(t, key) assert.True(t, file) ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA__SEC__KEY__FILE") diff --git a/modules/setting/config_provider_test.go b/modules/setting/config_provider_test.go index 702be80861..3b99911f38 100644 --- a/modules/setting/config_provider_test.go +++ b/modules/setting/config_provider_test.go @@ -63,17 +63,17 @@ key = 123 // test default behavior assert.Equal(t, "123", ConfigSectionKeyString(sec, "key")) - assert.Equal(t, "", ConfigSectionKeyString(secSub, "key")) + assert.Empty(t, ConfigSectionKeyString(secSub, "key")) assert.Equal(t, "def", ConfigSectionKeyString(secSub, "key", "def")) assert.Equal(t, "123", ConfigInheritedKeyString(secSub, "key")) // Workaround for ini package's BuggyKeyOverwritten behavior - assert.Equal(t, "", ConfigSectionKeyString(sec, "empty")) - assert.Equal(t, "", ConfigSectionKeyString(secSub, "empty")) + assert.Empty(t, ConfigSectionKeyString(sec, "empty")) + assert.Empty(t, ConfigSectionKeyString(secSub, "empty")) assert.Equal(t, "def", ConfigInheritedKey(secSub, "empty").MustString("def")) assert.Equal(t, "def", ConfigInheritedKey(secSub, "empty").MustString("xyz")) - assert.Equal(t, "", ConfigSectionKeyString(sec, "empty")) + assert.Empty(t, ConfigSectionKeyString(sec, "empty")) assert.Equal(t, "def", ConfigSectionKeyString(secSub, "empty")) } diff --git a/modules/setting/cron_test.go b/modules/setting/cron_test.go index 32f8ecffd2..cabfb3a325 100644 --- a/modules/setting/cron_test.go +++ b/modules/setting/cron_test.go @@ -39,6 +39,6 @@ EXTEND = true _, err = getCronSettings(cfg, "test", extended) require.NoError(t, err) assert.True(t, extended.Base) - assert.EqualValues(t, "white rabbit", extended.Second) + assert.Equal(t, "white rabbit", extended.Second) assert.True(t, extended.Extend) } diff --git a/modules/setting/database.go b/modules/setting/database.go index 76fae27164..b5131d3782 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -10,8 +10,13 @@ import ( "net/url" "os" "path/filepath" + "strconv" "strings" "time" + + "forgejo.org/modules/log" + + "xorm.io/xorm" ) var ( @@ -24,35 +29,41 @@ var ( EnableSQLite3 bool // Database holds the database settings - Database = struct { - Type DatabaseType - Host string - Name string - User string - Passwd string - Schema string - SSLMode string - Path string - LogSQL bool - MysqlCharset string - CharsetCollation string - Timeout int // seconds - SQLiteJournalMode string - DBConnectRetries int - DBConnectBackoff time.Duration - MaxIdleConns int - MaxOpenConns int - ConnMaxIdleTime time.Duration - ConnMaxLifetime time.Duration - IterateBufferSize int - AutoMigration bool - SlowQueryThreshold time.Duration - }{ + Database = DatabaseSettings{ Timeout: 500, IterateBufferSize: 50, } ) +type DatabaseSettings struct { + Type DatabaseType + Host string + HostPrimary string + HostReplica string + LoadBalancePolicy string + LoadBalanceWeights string + Name string + User string + Passwd string + Schema string + SSLMode string + Path string + LogSQL bool + MysqlCharset string + CharsetCollation string + Timeout int // seconds + SQLiteJournalMode string + DBConnectRetries int + DBConnectBackoff time.Duration + MaxIdleConns int + MaxOpenConns int + ConnMaxIdleTime time.Duration + ConnMaxLifetime time.Duration + IterateBufferSize int + AutoMigration bool + SlowQueryThreshold time.Duration +} + // LoadDBSetting loads the database settings func LoadDBSetting() { loadDBSetting(CfgProvider) @@ -63,6 +74,10 @@ func loadDBSetting(rootCfg ConfigProvider) { Database.Type = DatabaseType(sec.Key("DB_TYPE").String()) Database.Host = sec.Key("HOST").String() + Database.HostPrimary = sec.Key("HOST_PRIMARY").String() + Database.HostReplica = sec.Key("HOST_REPLICA").String() + Database.LoadBalancePolicy = sec.Key("LOAD_BALANCE_POLICY").String() + Database.LoadBalanceWeights = sec.Key("LOAD_BALANCE_WEIGHTS").String() Database.Name = sec.Key("NAME").String() Database.User = sec.Key("USER").String() if len(Database.Passwd) == 0 { @@ -99,8 +114,93 @@ func loadDBSetting(rootCfg ConfigProvider) { } } -// DBConnStr returns database connection string -func DBConnStr() (string, error) { +// DBMasterConnStr returns the connection string for the master (primary) database. +// If a primary host is defined in the configuration, it is used; +// otherwise, it falls back to Database.Host. +// Returns an error if no master host is provided but a slave is defined. +func DBMasterConnStr() (string, error) { + var host string + if Database.HostPrimary != "" { + host = Database.HostPrimary + } else { + host = Database.Host + } + if host == "" && Database.HostReplica != "" { + return "", errors.New("master host is not defined while slave is defined; cannot proceed") + } + + // For SQLite, no host is needed + if host == "" && !Database.Type.IsSQLite3() { + return "", errors.New("no database host defined") + } + + return dbConnStrWithHost(host) +} + +// DBSlaveConnStrs returns one or more connection strings for the replica databases. +// If a replica host is defined (possibly as a comma-separated list) then those DSNs are returned. +// Otherwise, this function falls back to the master DSN (with a warning log). +func DBSlaveConnStrs() ([]string, error) { + var dsns []string + if Database.HostReplica != "" { + // support multiple replica hosts separated by commas + replicas := strings.SplitSeq(Database.HostReplica, ",") + for r := range replicas { + trimmed := strings.TrimSpace(r) + if trimmed == "" { + continue + } + dsn, err := dbConnStrWithHost(trimmed) + if err != nil { + return nil, err + } + dsns = append(dsns, dsn) + } + } + // Fall back to master if no slave DSN was provided. + if len(dsns) == 0 { + master, err := DBMasterConnStr() + if err != nil { + return nil, err + } + log.Debug("Database: No dedicated replica host defined; falling back to primary DSN for replica connections") + dsns = append(dsns, master) + } + return dsns, nil +} + +func BuildLoadBalancePolicy(settings *DatabaseSettings, slaveEngines []*xorm.Engine) xorm.GroupPolicy { + var policy xorm.GroupPolicy + switch settings.LoadBalancePolicy { // Use the settings parameter directly + case "WeightRandom": + var weights []int + if settings.LoadBalanceWeights != "" { // Use the settings parameter directly + for part := range strings.SplitSeq(settings.LoadBalanceWeights, ",") { + w, err := strconv.Atoi(strings.TrimSpace(part)) + if err != nil { + w = 1 // use a default weight if conversion fails + } + weights = append(weights, w) + } + } + // If no valid weights were provided, default each slave to weight 1 + if len(weights) == 0 { + weights = make([]int, len(slaveEngines)) + for i := range weights { + weights[i] = 1 + } + } + policy = xorm.WeightRandomPolicy(weights) + case "RoundRobin": + policy = xorm.RoundRobinPolicy() + default: + policy = xorm.RandomPolicy() + } + return policy +} + +// dbConnStrWithHost constructs the connection string, given a host value. +func dbConnStrWithHost(host string) (string, error) { var connStr string paramSep := "?" if strings.Contains(Database.Name, paramSep) { @@ -109,23 +209,25 @@ func DBConnStr() (string, error) { switch Database.Type { case "mysql": connType := "tcp" - if len(Database.Host) > 0 && Database.Host[0] == '/' { // looks like a unix socket + // if the host starts with '/' it is assumed to be a unix socket path + if len(host) > 0 && host[0] == '/' { connType = "unix" } tls := Database.SSLMode - if tls == "disable" { // allow (Postgres-inspired) default value to work in MySQL + // allow the "disable" value (borrowed from Postgres defaults) to behave as false + if tls == "disable" { tls = "false" } connStr = fmt.Sprintf("%s:%s@%s(%s)/%s%sparseTime=true&tls=%s", - Database.User, Database.Passwd, connType, Database.Host, Database.Name, paramSep, tls) + Database.User, Database.Passwd, connType, host, Database.Name, paramSep, tls) case "postgres": - connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, Database.SSLMode) + connStr = getPostgreSQLConnectionString(host, Database.User, Database.Passwd, Database.Name, Database.SSLMode) case "sqlite3": if !EnableSQLite3 { return "", errors.New("this Gitea binary was not built with SQLite3 support") } if err := os.MkdirAll(filepath.Dir(Database.Path), os.ModePerm); err != nil { - return "", fmt.Errorf("Failed to create directories: %w", err) + return "", fmt.Errorf("failed to create directories: %w", err) } journalMode := "" if Database.SQLiteJournalMode != "" { @@ -136,7 +238,6 @@ func DBConnStr() (string, error) { default: return "", fmt.Errorf("unknown database type: %s", Database.Type) } - return connStr, nil } diff --git a/modules/setting/database_test.go b/modules/setting/database_test.go index a742d54f8c..ce816d53e8 100644 --- a/modules/setting/database_test.go +++ b/modules/setting/database_test.go @@ -4,6 +4,7 @@ package setting import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -107,3 +108,104 @@ func Test_getPostgreSQLConnectionString(t *testing.T) { assert.Equal(t, test.Output, connStr) } } + +func getPostgreSQLEngineGroupConnectionStrings(primaryHost, replicaHosts, user, passwd, name, sslmode string) (string, []string) { + // Determine the primary connection string. + primary := primaryHost + if strings.TrimSpace(primary) == "" { + primary = "127.0.0.1:5432" + } + primaryConn := getPostgreSQLConnectionString(primary, user, passwd, name, sslmode) + + // Build the replica connection strings. + replicaConns := []string{} + if strings.TrimSpace(replicaHosts) != "" { + // Split comma-separated replica host values. + hosts := strings.Split(replicaHosts, ",") + for _, h := range hosts { + trimmed := strings.TrimSpace(h) + if trimmed != "" { + replicaConns = append(replicaConns, + getPostgreSQLConnectionString(trimmed, user, passwd, name, sslmode)) + } + } + } + + return primaryConn, replicaConns +} + +func Test_getPostgreSQLEngineGroupConnectionStrings(t *testing.T) { + tests := []struct { + primaryHost string // primary host setting (e.g. "localhost" or "[::1]:1234") + replicaHosts string // comma-separated replica hosts (e.g. "replica1,replica2:2345") + user string + passwd string + name string + sslmode string + outputPrimary string + outputReplicas []string + }{ + { + // No primary override (empty => default) and no replicas. + primaryHost: "", + replicaHosts: "", + user: "", + passwd: "", + name: "", + sslmode: "", + outputPrimary: "postgres://:@127.0.0.1:5432?sslmode=", + outputReplicas: []string{}, + }, + { + // Primary set and one replica. + primaryHost: "localhost", + replicaHosts: "replicahost", + user: "user", + passwd: "pass", + name: "gitea", + sslmode: "disable", + outputPrimary: "postgres://user:pass@localhost:5432/gitea?sslmode=disable", + outputReplicas: []string{"postgres://user:pass@replicahost:5432/gitea?sslmode=disable"}, + }, + { + // Primary with explicit port; multiple replicas (one without and one with an explicit port). + primaryHost: "localhost:5433", + replicaHosts: "replica1,replica2:5434", + user: "test", + passwd: "secret", + name: "db", + sslmode: "require", + outputPrimary: "postgres://test:secret@localhost:5433/db?sslmode=require", + outputReplicas: []string{ + "postgres://test:secret@replica1:5432/db?sslmode=require", + "postgres://test:secret@replica2:5434/db?sslmode=require", + }, + }, + { + // IPv6 addresses for primary and replica. + primaryHost: "[::1]:1234", + replicaHosts: "[::2]:2345", + user: "ipv6", + passwd: "ipv6pass", + name: "ipv6db", + sslmode: "disable", + outputPrimary: "postgres://ipv6:ipv6pass@[::1]:1234/ipv6db?sslmode=disable", + outputReplicas: []string{ + "postgres://ipv6:ipv6pass@[::2]:2345/ipv6db?sslmode=disable", + }, + }, + } + + for _, test := range tests { + primary, replicas := getPostgreSQLEngineGroupConnectionStrings( + test.primaryHost, + test.replicaHosts, + test.user, + test.passwd, + test.name, + test.sslmode, + ) + assert.Equal(t, test.outputPrimary, primary) + assert.Equal(t, test.outputReplicas, replicas) + } +} diff --git a/modules/setting/federation.go b/modules/setting/federation.go index a0fdec228e..510ac128ee 100644 --- a/modules/setting/federation.go +++ b/modules/setting/federation.go @@ -15,18 +15,20 @@ var ( Enabled bool ShareUserStatistics bool MaxSize int64 - Algorithms []string + SignatureAlgorithms []string DigestAlgorithm string GetHeaders []string PostHeaders []string + SignatureEnforced bool }{ Enabled: false, ShareUserStatistics: true, MaxSize: 4, - Algorithms: []string{"rsa-sha256", "rsa-sha512", "ed25519"}, + SignatureAlgorithms: []string{"rsa-sha256", "rsa-sha512", "ed25519"}, DigestAlgorithm: "SHA-256", GetHeaders: []string{"(request-target)", "Date", "Host"}, PostHeaders: []string{"(request-target)", "Date", "Host", "Digest"}, + SignatureEnforced: true, } ) @@ -44,8 +46,8 @@ func loadFederationFrom(rootCfg ConfigProvider) { // Get MaxSize in bytes instead of MiB Federation.MaxSize = 1 << 20 * Federation.MaxSize - HttpsigAlgs = make([]httpsig.Algorithm, len(Federation.Algorithms)) - for i, alg := range Federation.Algorithms { + HttpsigAlgs = make([]httpsig.Algorithm, len(Federation.SignatureAlgorithms)) + for i, alg := range Federation.SignatureAlgorithms { HttpsigAlgs[i] = httpsig.Algorithm(alg) } } diff --git a/modules/setting/forgejo_storage_test.go b/modules/setting/forgejo_storage_test.go index d91bff59e9..42c46beb77 100644 --- a/modules/setting/forgejo_storage_test.go +++ b/modules/setting/forgejo_storage_test.go @@ -259,6 +259,6 @@ func testStoragePathMatch(t *testing.T, iniStr string, storageType StorageType, cfg, err := NewConfigProviderFromData(iniStr) require.NoError(t, err, iniStr) require.NoError(t, loadCommonSettingsFrom(cfg), iniStr) - assert.EqualValues(t, testSectionToPath(storageType, section), testStorageGetPath(*storage), iniStr) - assert.EqualValues(t, storageType, (*storage).Type, iniStr) + assert.Equal(t, testSectionToPath(storageType, section), testStorageGetPath(*storage), iniStr) + assert.Equal(t, storageType, (*storage).Type, iniStr) } diff --git a/modules/setting/git_test.go b/modules/setting/git_test.go index 34427f908f..582f5d403e 100644 --- a/modules/setting/git_test.go +++ b/modules/setting/git_test.go @@ -24,8 +24,8 @@ a.b = 1 `) require.NoError(t, err) loadGitFrom(cfg) - assert.EqualValues(t, "1", GitConfig.Options["a.b"]) - assert.EqualValues(t, "histogram", GitConfig.Options["diff.algorithm"]) + assert.Equal(t, "1", GitConfig.Options["a.b"]) + assert.Equal(t, "histogram", GitConfig.Options["diff.algorithm"]) cfg, err = NewConfigProviderFromData(` [git.config] @@ -33,7 +33,7 @@ diff.algorithm = other `) require.NoError(t, err) loadGitFrom(cfg) - assert.EqualValues(t, "other", GitConfig.Options["diff.algorithm"]) + assert.Equal(t, "other", GitConfig.Options["diff.algorithm"]) } func TestGitReflog(t *testing.T) { @@ -49,8 +49,8 @@ func TestGitReflog(t *testing.T) { require.NoError(t, err) loadGitFrom(cfg) - assert.EqualValues(t, "true", GitConfig.GetOption("core.logAllRefUpdates")) - assert.EqualValues(t, "90", GitConfig.GetOption("gc.reflogExpire")) + assert.Equal(t, "true", GitConfig.GetOption("core.logAllRefUpdates")) + assert.Equal(t, "90", GitConfig.GetOption("gc.reflogExpire")) // custom reflog config by legacy options cfg, err = NewConfigProviderFromData(` @@ -61,6 +61,6 @@ EXPIRATION = 123 require.NoError(t, err) loadGitFrom(cfg) - assert.EqualValues(t, "false", GitConfig.GetOption("core.logAllRefUpdates")) - assert.EqualValues(t, "123", GitConfig.GetOption("gc.reflogExpire")) + assert.Equal(t, "false", GitConfig.GetOption("core.logAllRefUpdates")) + assert.Equal(t, "123", GitConfig.GetOption("gc.reflogExpire")) } diff --git a/modules/setting/incoming_email_test.go b/modules/setting/incoming_email_test.go index 0fdd44d333..6d181cae3c 100644 --- a/modules/setting/incoming_email_test.go +++ b/modules/setting/incoming_email_test.go @@ -31,8 +31,8 @@ func Test_loadIncomingEmailFrom(t *testing.T) { loadIncomingEmailFrom(cfg) - assert.EqualValues(t, "jane.doe@example.com", IncomingEmail.Username) - assert.EqualValues(t, "y0u'll n3v3r gUess th1S!!1", IncomingEmail.Password) + assert.Equal(t, "jane.doe@example.com", IncomingEmail.Username) + assert.Equal(t, "y0u'll n3v3r gUess th1S!!1", IncomingEmail.Password) }) t.Run("Port settings", func(t *testing.T) { @@ -45,7 +45,7 @@ func Test_loadIncomingEmailFrom(t *testing.T) { loadIncomingEmailFrom(cfg) - assert.EqualValues(t, 143, IncomingEmail.Port) + assert.Equal(t, 143, IncomingEmail.Port) }) t.Run("no port, with tls", func(t *testing.T) { @@ -56,7 +56,7 @@ func Test_loadIncomingEmailFrom(t *testing.T) { loadIncomingEmailFrom(cfg) - assert.EqualValues(t, 993, IncomingEmail.Port) + assert.Equal(t, 993, IncomingEmail.Port) }) t.Run("port overrides tls", func(t *testing.T) { @@ -68,7 +68,7 @@ func Test_loadIncomingEmailFrom(t *testing.T) { loadIncomingEmailFrom(cfg) - assert.EqualValues(t, 1993, IncomingEmail.Port) + assert.Equal(t, 1993, IncomingEmail.Port) }) }) } diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go index 3cd48c538b..452bfae737 100644 --- a/modules/setting/lfs.go +++ b/modules/setting/lfs.go @@ -80,10 +80,7 @@ func loadLFSFrom(rootCfg ConfigProvider) error { jwtSecretBase64 := loadSecret(rootCfg.Section("server"), "LFS_JWT_SECRET_URI", "LFS_JWT_SECRET") LFS.JWTSecretBytes, err = generate.DecodeJwtSecret(jwtSecretBase64) if err != nil { - LFS.JWTSecretBytes, jwtSecretBase64, err = generate.NewJwtSecret() - if err != nil { - return fmt.Errorf("error generating JWT Secret for custom config: %v", err) - } + LFS.JWTSecretBytes, jwtSecretBase64 = generate.NewJwtSecret() // Save secret saveCfg, err := rootCfg.PrepareSaving() diff --git a/modules/setting/lfs_test.go b/modules/setting/lfs_test.go index 2b204282a8..0abf401fa0 100644 --- a/modules/setting/lfs_test.go +++ b/modules/setting/lfs_test.go @@ -20,7 +20,7 @@ func Test_getStorageInheritNameSectionTypeForLFS(t *testing.T) { require.NoError(t, loadLFSFrom(cfg)) assert.EqualValues(t, "minio", LFS.Storage.Type) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) iniStr = ` [server] @@ -55,7 +55,7 @@ STORAGE_TYPE = minio require.NoError(t, loadLFSFrom(cfg)) assert.EqualValues(t, "minio", LFS.Storage.Type) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) iniStr = ` [lfs] @@ -69,7 +69,7 @@ STORAGE_TYPE = minio require.NoError(t, loadLFSFrom(cfg)) assert.EqualValues(t, "minio", LFS.Storage.Type) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) iniStr = ` [lfs] @@ -84,7 +84,7 @@ STORAGE_TYPE = minio require.NoError(t, loadLFSFrom(cfg)) assert.EqualValues(t, "minio", LFS.Storage.Type) - assert.EqualValues(t, "my_lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "my_lfs/", LFS.Storage.MinioConfig.BasePath) } func Test_LFSStorage1(t *testing.T) { @@ -97,8 +97,8 @@ STORAGE_TYPE = minio require.NoError(t, loadLFSFrom(cfg)) assert.EqualValues(t, "minio", LFS.Storage.Type) - assert.EqualValues(t, "gitea", LFS.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", LFS.Storage.MinioConfig.Bucket) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) } func Test_LFSClientServerConfigs(t *testing.T) { @@ -113,9 +113,9 @@ BATCH_SIZE = 0 assert.NoError(t, err) assert.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, 100, LFS.MaxBatchSize) - assert.EqualValues(t, 20, LFSClient.BatchSize) - assert.EqualValues(t, 8, LFSClient.BatchOperationConcurrency) + assert.Equal(t, 100, LFS.MaxBatchSize) + assert.Equal(t, 20, LFSClient.BatchSize) + assert.Equal(t, 8, LFSClient.BatchOperationConcurrency) iniStr = ` [lfs_client] @@ -126,6 +126,6 @@ BATCH_OPERATION_CONCURRENCY = 10 assert.NoError(t, err) assert.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, 50, LFSClient.BatchSize) - assert.EqualValues(t, 10, LFSClient.BatchOperationConcurrency) + assert.Equal(t, 50, LFSClient.BatchSize) + assert.Equal(t, 10, LFSClient.BatchOperationConcurrency) } diff --git a/modules/setting/mailer_test.go b/modules/setting/mailer_test.go index f8af4a78c1..685586d2c7 100644 --- a/modules/setting/mailer_test.go +++ b/modules/setting/mailer_test.go @@ -34,8 +34,8 @@ func Test_loadMailerFrom(t *testing.T) { // Check mailer setting loadMailerFrom(cfg) - assert.EqualValues(t, kase.SMTPAddr, MailService.SMTPAddr) - assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort) + assert.Equal(t, kase.SMTPAddr, MailService.SMTPAddr) + assert.Equal(t, kase.SMTPPort, MailService.SMTPPort) }) } @@ -48,7 +48,7 @@ func Test_loadMailerFrom(t *testing.T) { loadMailerFrom(cfg) - assert.EqualValues(t, "jane.doe@example.com", MailService.User) - assert.EqualValues(t, "y0u'll n3v3r gUess th1S!!1", MailService.Passwd) + assert.Equal(t, "jane.doe@example.com", MailService.User) + assert.Equal(t, "y0u'll n3v3r gUess th1S!!1", MailService.Passwd) }) } diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go index 72500cfc89..9e95e1c6a9 100644 --- a/modules/setting/oauth2.go +++ b/modules/setting/oauth2.go @@ -138,10 +138,7 @@ func loadOAuth2From(rootCfg ConfigProvider) { if InstallLock { jwtSecretBytes, err := generate.DecodeJwtSecret(jwtSecretBase64) if err != nil { - jwtSecretBytes, jwtSecretBase64, err = generate.NewJwtSecret() - if err != nil { - log.Fatal("error generating JWT secret: %v", err) - } + jwtSecretBytes, jwtSecretBase64 = generate.NewJwtSecret() saveCfg, err := rootCfg.PrepareSaving() if err != nil { log.Fatal("save oauth2.JWT_SECRET failed: %v", err) @@ -161,10 +158,7 @@ var generalSigningSecret atomic.Pointer[[]byte] func GetGeneralTokenSigningSecret() []byte { old := generalSigningSecret.Load() if old == nil || len(*old) == 0 { - jwtSecret, _, err := generate.NewJwtSecret() - if err != nil { - log.Fatal("Unable to generate general JWT secret: %v", err) - } + jwtSecret, _ := generate.NewJwtSecret() if generalSigningSecret.CompareAndSwap(old, &jwtSecret) { return jwtSecret } diff --git a/modules/setting/oauth2_test.go b/modules/setting/oauth2_test.go index 2fc5da6996..7a1f4842a4 100644 --- a/modules/setting/oauth2_test.go +++ b/modules/setting/oauth2_test.go @@ -32,7 +32,7 @@ JWT_SECRET = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB actual := GetGeneralTokenSigningSecret() expected, _ := generate.DecodeJwtSecret("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") assert.Len(t, actual, 32) - assert.EqualValues(t, expected, actual) + assert.Equal(t, expected, actual) } func TestGetGeneralSigningSecretSave(t *testing.T) { diff --git a/modules/setting/packages_test.go b/modules/setting/packages_test.go index 78eb4b4bbc..85a4656da0 100644 --- a/modules/setting/packages_test.go +++ b/modules/setting/packages_test.go @@ -42,7 +42,7 @@ STORAGE_TYPE = minio require.NoError(t, loadPackagesFrom(cfg)) assert.EqualValues(t, "minio", Packages.Storage.Type) - assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) + assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath) // we can also configure packages storage directly iniStr = ` @@ -54,7 +54,7 @@ STORAGE_TYPE = minio require.NoError(t, loadPackagesFrom(cfg)) assert.EqualValues(t, "minio", Packages.Storage.Type) - assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) + assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath) // or we can indicate the storage type in the packages section iniStr = ` @@ -69,7 +69,7 @@ STORAGE_TYPE = minio require.NoError(t, loadPackagesFrom(cfg)) assert.EqualValues(t, "minio", Packages.Storage.Type) - assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) + assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath) // or we can indicate the storage type and minio base path in the packages section iniStr = ` @@ -85,7 +85,7 @@ STORAGE_TYPE = minio require.NoError(t, loadPackagesFrom(cfg)) assert.EqualValues(t, "minio", Packages.Storage.Type) - assert.EqualValues(t, "my_packages/", Packages.Storage.MinioConfig.BasePath) + assert.Equal(t, "my_packages/", Packages.Storage.MinioConfig.BasePath) } func Test_PackageStorage1(t *testing.T) { @@ -110,8 +110,8 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := Packages.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) - assert.EqualValues(t, "packages/", storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "packages/", storage.MinioConfig.BasePath) assert.True(t, storage.MinioConfig.ServeDirect) } @@ -137,8 +137,8 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := Packages.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) - assert.EqualValues(t, "packages/", storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "packages/", storage.MinioConfig.BasePath) assert.True(t, storage.MinioConfig.ServeDirect) } @@ -165,8 +165,8 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := Packages.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) - assert.EqualValues(t, "my_packages/", storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "my_packages/", storage.MinioConfig.BasePath) assert.True(t, storage.MinioConfig.ServeDirect) } @@ -193,7 +193,7 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := Packages.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) - assert.EqualValues(t, "my_packages/", storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "my_packages/", storage.MinioConfig.BasePath) assert.True(t, storage.MinioConfig.ServeDirect) } diff --git a/modules/setting/repository_archive_test.go b/modules/setting/repository_archive_test.go index d3901b6e47..cff59f3663 100644 --- a/modules/setting/repository_archive_test.go +++ b/modules/setting/repository_archive_test.go @@ -21,7 +21,7 @@ STORAGE_TYPE = minio require.NoError(t, loadRepoArchiveFrom(cfg)) assert.EqualValues(t, "minio", RepoArchive.Storage.Type) - assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) // we can also configure packages storage directly iniStr = ` @@ -33,7 +33,7 @@ STORAGE_TYPE = minio require.NoError(t, loadRepoArchiveFrom(cfg)) assert.EqualValues(t, "minio", RepoArchive.Storage.Type) - assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) // or we can indicate the storage type in the packages section iniStr = ` @@ -48,7 +48,7 @@ STORAGE_TYPE = minio require.NoError(t, loadRepoArchiveFrom(cfg)) assert.EqualValues(t, "minio", RepoArchive.Storage.Type) - assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) // or we can indicate the storage type and minio base path in the packages section iniStr = ` @@ -64,7 +64,7 @@ STORAGE_TYPE = minio require.NoError(t, loadRepoArchiveFrom(cfg)) assert.EqualValues(t, "minio", RepoArchive.Storage.Type) - assert.EqualValues(t, "my_archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "my_archive/", RepoArchive.Storage.MinioConfig.BasePath) } func Test_RepoArchiveStorage(t *testing.T) { @@ -86,7 +86,7 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := RepoArchive.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) iniStr = ` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -108,5 +108,5 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage = RepoArchive.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) } diff --git a/modules/setting/server_test.go b/modules/setting/server_test.go index 4450f99546..3c6faa2311 100644 --- a/modules/setting/server_test.go +++ b/modules/setting/server_test.go @@ -84,5 +84,5 @@ func TestUnixSocketAbstractNamespace(t *testing.T) { require.NoError(t, err) loadServerFrom(cfg) - assert.EqualValues(t, "@forgejo", HTTPAddr) + assert.Equal(t, "@forgejo", HTTPAddr) } diff --git a/modules/setting/service.go b/modules/setting/service.go index 729b10839e..5717225578 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -206,7 +206,7 @@ func loadServiceFrom(rootCfg ConfigProvider) { } Service.EmailDomainBlockList = append(Service.EmailDomainBlockList, toAdd...) } - Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration)) + Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!Service.DisableRegistration && !Service.AllowOnlyExternalRegistration) Service.EnableInternalSignIn = sec.Key("ENABLE_INTERNAL_SIGNIN").MustBool(true) Service.ShowMilestonesDashboardPage = sec.Key("SHOW_MILESTONES_DASHBOARD_PAGE").MustBool(true) Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool() diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 97eec3df61..75c24580b2 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -12,8 +12,8 @@ import ( "time" "forgejo.org/modules/log" + "forgejo.org/modules/optional" "forgejo.org/modules/user" - "forgejo.org/modules/util" ) var ForgejoVersion = "1.0.0" @@ -162,7 +162,7 @@ func loadRunModeFrom(rootCfg ConfigProvider) { // The following is a purposefully undocumented option. Please do not run Forgejo as root. It will only cause future headaches. // Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly. unsafeAllowRunAsRoot := ConfigSectionKeyBool(rootSec, "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT") - unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || util.OptionalBoolParse(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value() + unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || optional.ParseBool(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value() RunMode = os.Getenv("GITEA_RUN_MODE") if RunMode == "" { RunMode = rootSec.Key("RUN_MODE").MustString("prod") diff --git a/modules/setting/storage.go b/modules/setting/storage.go index 8ee5c0f0ab..532842064c 100644 --- a/modules/setting/storage.go +++ b/modules/setting/storage.go @@ -170,8 +170,8 @@ func getStorageTargetSection(rootCfg ConfigProvider, name, typ string, sec Confi targetSec, _ := rootCfg.GetSection(storageSectionName + "." + name) if targetSec != nil { targetType := targetSec.Key("STORAGE_TYPE").String() - switch { - case targetType == "": + switch targetType { + case "": if targetSec.Key("PATH").String() == "" { // both storage type and path are empty, use default return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil } diff --git a/modules/setting/storage_test.go b/modules/setting/storage_test.go index 59135b5911..a1b687ba5b 100644 --- a/modules/setting/storage_test.go +++ b/modules/setting/storage_test.go @@ -27,16 +27,16 @@ MINIO_BUCKET = gitea-storage require.NoError(t, err) require.NoError(t, loadAttachmentFrom(cfg)) - assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) require.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, "gitea-lfs", LFS.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-lfs", LFS.Storage.MinioConfig.Bucket) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) require.NoError(t, loadAvatarsFrom(cfg)) - assert.EqualValues(t, "gitea-storage", Avatar.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "avatars/", Avatar.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-storage", Avatar.Storage.MinioConfig.Bucket) + assert.Equal(t, "avatars/", Avatar.Storage.MinioConfig.BasePath) } func Test_getStorageUseOtherNameAsType(t *testing.T) { @@ -52,12 +52,12 @@ MINIO_BUCKET = gitea-storage require.NoError(t, err) require.NoError(t, loadAttachmentFrom(cfg)) - assert.EqualValues(t, "gitea-storage", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-storage", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) require.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, "gitea-storage", LFS.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-storage", LFS.Storage.MinioConfig.Bucket) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) } func Test_getStorageInheritStorageType(t *testing.T) { @@ -70,32 +70,32 @@ STORAGE_TYPE = minio require.NoError(t, loadPackagesFrom(cfg)) assert.EqualValues(t, "minio", Packages.Storage.Type) - assert.EqualValues(t, "gitea", Packages.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", Packages.Storage.MinioConfig.Bucket) + assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath) require.NoError(t, loadRepoArchiveFrom(cfg)) assert.EqualValues(t, "minio", RepoArchive.Storage.Type) - assert.EqualValues(t, "gitea", RepoArchive.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", RepoArchive.Storage.MinioConfig.Bucket) + assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "minio", Actions.LogStorage.Type) - assert.EqualValues(t, "gitea", Actions.LogStorage.MinioConfig.Bucket) - assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) + assert.Equal(t, "gitea", Actions.LogStorage.MinioConfig.Bucket) + assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "gitea", Actions.ArtifactStorage.MinioConfig.Bucket) - assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) + assert.Equal(t, "gitea", Actions.ArtifactStorage.MinioConfig.Bucket) + assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) require.NoError(t, loadAvatarsFrom(cfg)) assert.EqualValues(t, "minio", Avatar.Storage.Type) - assert.EqualValues(t, "gitea", Avatar.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "avatars/", Avatar.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", Avatar.Storage.MinioConfig.Bucket) + assert.Equal(t, "avatars/", Avatar.Storage.MinioConfig.BasePath) require.NoError(t, loadRepoAvatarFrom(cfg)) assert.EqualValues(t, "minio", RepoAvatar.Storage.Type) - assert.EqualValues(t, "gitea", RepoAvatar.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "repo-avatars/", RepoAvatar.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", RepoAvatar.Storage.MinioConfig.Bucket) + assert.Equal(t, "repo-avatars/", RepoAvatar.Storage.MinioConfig.BasePath) } type testLocalStoragePathCase struct { @@ -114,7 +114,7 @@ func testLocalStoragePath(t *testing.T, appDataPath, iniStr string, cases []test assert.EqualValues(t, "local", storage.Type) assert.True(t, filepath.IsAbs(storage.Path)) - assert.EqualValues(t, filepath.Clean(c.expectedPath), filepath.Clean(storage.Path)) + assert.Equal(t, filepath.Clean(c.expectedPath), filepath.Clean(storage.Path)) } } @@ -352,8 +352,8 @@ MINIO_SECRET_ACCESS_KEY = my_secret_key require.NoError(t, loadRepoArchiveFrom(cfg)) cp := RepoArchive.Storage.ToShadowCopy() - assert.EqualValues(t, "******", cp.MinioConfig.AccessKeyID) - assert.EqualValues(t, "******", cp.MinioConfig.SecretAccessKey) + assert.Equal(t, "******", cp.MinioConfig.AccessKeyID) + assert.Equal(t, "******", cp.MinioConfig.SecretAccessKey) } func Test_getStorageConfiguration24(t *testing.T) { @@ -408,10 +408,10 @@ MINIO_USE_SSL = true `) require.NoError(t, err) require.NoError(t, loadRepoArchiveFrom(cfg)) - assert.EqualValues(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID) - assert.EqualValues(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey) + assert.Equal(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID) + assert.Equal(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey) assert.True(t, RepoArchive.Storage.MinioConfig.UseSSL) - assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) } func Test_getStorageConfiguration28(t *testing.T) { @@ -425,10 +425,10 @@ MINIO_BASE_PATH = /prefix `) require.NoError(t, err) require.NoError(t, loadRepoArchiveFrom(cfg)) - assert.EqualValues(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID) - assert.EqualValues(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey) + assert.Equal(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID) + assert.Equal(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey) assert.True(t, RepoArchive.Storage.MinioConfig.UseSSL) - assert.EqualValues(t, "/prefix/repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "/prefix/repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) cfg, err = NewConfigProviderFromData(` [storage] @@ -443,10 +443,10 @@ MINIO_BASE_PATH = /lfs `) require.NoError(t, err) require.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) - assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) + assert.Equal(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) + assert.Equal(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) assert.True(t, LFS.Storage.MinioConfig.UseSSL) - assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "/lfs", LFS.Storage.MinioConfig.BasePath) cfg, err = NewConfigProviderFromData(` [storage] @@ -461,8 +461,8 @@ MINIO_BASE_PATH = /lfs `) require.NoError(t, err) require.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) - assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) + assert.Equal(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) + assert.Equal(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) assert.True(t, LFS.Storage.MinioConfig.UseSSL) - assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "/lfs", LFS.Storage.MinioConfig.BasePath) } diff --git a/modules/storage/local_test.go b/modules/storage/local_test.go index d0dd3a6462..d74c6bbc41 100644 --- a/modules/storage/local_test.go +++ b/modules/storage/local_test.go @@ -50,7 +50,7 @@ func TestBuildLocalPath(t *testing.T) { t.Run(k.path, func(t *testing.T) { l := LocalStorage{dir: k.localDir} - assert.EqualValues(t, k.expected, l.buildLocalPath(k.path)) + assert.Equal(t, k.expected, l.buildLocalPath(k.path)) }) } } diff --git a/modules/storage/minio.go b/modules/storage/minio.go index ee545edc10..bf51a1642a 100644 --- a/modules/storage/minio.go +++ b/modules/storage/minio.go @@ -231,7 +231,7 @@ type minioFileInfo struct { } func (m minioFileInfo) Name() string { - return path.Base(m.ObjectInfo.Key) + return path.Base(m.Key) } func (m minioFileInfo) Size() int64 { @@ -243,7 +243,7 @@ func (m minioFileInfo) ModTime() time.Time { } func (m minioFileInfo) IsDir() bool { - return strings.HasSuffix(m.ObjectInfo.Key, "/") + return strings.HasSuffix(m.Key, "/") } func (m minioFileInfo) Mode() os.FileMode { diff --git a/modules/storage/minio_test.go b/modules/storage/minio_test.go index 99f70c4565..e168a2efbb 100644 --- a/modules/storage/minio_test.go +++ b/modules/storage/minio_test.go @@ -52,19 +52,19 @@ func TestVirtualHostMinioStorage(t *testing.T) { func TestMinioStoragePath(t *testing.T) { m := &MinioStorage{basePath: ""} - assert.Equal(t, "", m.buildMinioPath("/")) - assert.Equal(t, "", m.buildMinioPath(".")) + assert.Empty(t, m.buildMinioPath("/")) + assert.Empty(t, m.buildMinioPath(".")) assert.Equal(t, "a", m.buildMinioPath("/a")) assert.Equal(t, "a/b", m.buildMinioPath("/a/b/")) - assert.Equal(t, "", m.buildMinioDirPrefix("")) + assert.Empty(t, m.buildMinioDirPrefix("")) assert.Equal(t, "a/", m.buildMinioDirPrefix("/a/")) m = &MinioStorage{basePath: "/"} - assert.Equal(t, "", m.buildMinioPath("/")) - assert.Equal(t, "", m.buildMinioPath(".")) + assert.Empty(t, m.buildMinioPath("/")) + assert.Empty(t, m.buildMinioPath(".")) assert.Equal(t, "a", m.buildMinioPath("/a")) assert.Equal(t, "a/b", m.buildMinioPath("/a/b/")) - assert.Equal(t, "", m.buildMinioDirPrefix("")) + assert.Empty(t, m.buildMinioDirPrefix("")) assert.Equal(t, "a/", m.buildMinioDirPrefix("/a/")) m = &MinioStorage{basePath: "/base"} diff --git a/modules/structs/pull.go b/modules/structs/pull.go index ab627666c9..1ce7550e19 100644 --- a/modules/structs/pull.go +++ b/modules/structs/pull.go @@ -57,7 +57,8 @@ type PullRequest struct { // swagger:strfmt date-time Closed *time.Time `json:"closed_at"` - PinOrder int `json:"pin_order"` + PinOrder int `json:"pin_order"` + Flow int64 `json:"flow"` } // PRBranchInfo information about a branch diff --git a/modules/structs/repo.go b/modules/structs/repo.go index b5f54a2a7a..c9cd729cf3 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -224,10 +224,10 @@ type EditRepoOption struct { AllowRebaseUpdate *bool `json:"allow_rebase_update,omitempty"` // set to `true` to delete pr branch after merge by default DefaultDeleteBranchAfterMerge *bool `json:"default_delete_branch_after_merge,omitempty"` - // set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", "squash", or "fast-forward-only". - DefaultMergeStyle *string `json:"default_merge_style,omitempty"` + // set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", "squash", "fast-forward-only", "manually-merged", or "rebase-update-only". + DefaultMergeStyle *string `json:"default_merge_style,omitempty" binding:"In(merge,rebase,rebase-merge,squash,fast-forward-only,manually-merged,rebase-update-only)"` // set to a update style to be used by this repository: "rebase" or "merge" - DefaultUpdateStyle *string `json:"default_update_style,omitempty"` + DefaultUpdateStyle *string `json:"default_update_style,omitempty" binding:"In(merge,rebase)"` // set to `true` to allow edits from maintainers by default DefaultAllowMaintainerEdit *bool `json:"default_allow_maintainer_edit,omitempty"` // set to `true` to archive this repository. diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go index 00c804146a..65d9a2d490 100644 --- a/modules/structs/repo_file.go +++ b/modules/structs/repo_file.go @@ -31,7 +31,7 @@ type CreateFileOptions struct { // Branch returns branch name func (o *CreateFileOptions) Branch() string { - return o.FileOptions.BranchName + return o.BranchName } // DeleteFileOptions options for deleting files (used for other File structs below) @@ -45,7 +45,7 @@ type DeleteFileOptions struct { // Branch returns branch name func (o *DeleteFileOptions) Branch() string { - return o.FileOptions.BranchName + return o.BranchName } // UpdateFileOptions options for updating files @@ -61,7 +61,7 @@ type UpdateFileOptions struct { // Branch returns branch name func (o *UpdateFileOptions) Branch() string { - return o.FileOptions.BranchName + return o.BranchName } // ChangeFileOperation for creating, updating or deleting a file @@ -92,7 +92,7 @@ type ChangeFilesOptions struct { // Branch returns branch name func (o *ChangeFilesOptions) Branch() string { - return o.FileOptions.BranchName + return o.BranchName } // FileOptionInterface provides a unified interface for the different file options diff --git a/modules/system/appstate_test.go b/modules/system/appstate_test.go index 8a444aff0f..d19900c03d 100644 --- a/modules/system/appstate_test.go +++ b/modules/system/appstate_test.go @@ -43,8 +43,8 @@ func TestAppStateDB(t *testing.T) { item1 := new(testItem1) require.NoError(t, as.Get(db.DefaultContext, item1)) - assert.Equal(t, "", item1.Val1) - assert.EqualValues(t, 0, item1.Val2) + assert.Empty(t, item1.Val1) + assert.Equal(t, 0, item1.Val2) item1 = new(testItem1) item1.Val1 = "a" @@ -58,7 +58,7 @@ func TestAppStateDB(t *testing.T) { item1 = new(testItem1) require.NoError(t, as.Get(db.DefaultContext, item1)) assert.Equal(t, "a", item1.Val1) - assert.EqualValues(t, 2, item1.Val2) + assert.Equal(t, 2, item1.Val2) item2 = new(testItem2) require.NoError(t, as.Get(db.DefaultContext, item2)) diff --git a/modules/templates/htmlrenderer_test.go b/modules/templates/htmlrenderer_test.go index 7373605744..d7a4cd7161 100644 --- a/modules/templates/htmlrenderer_test.go +++ b/modules/templates/htmlrenderer_test.go @@ -66,7 +66,7 @@ func TestHandleError(t *testing.T) { _, err = tmpl.Parse(s) require.Error(t, err) msg := h(err) - assert.EqualValues(t, strings.TrimSpace(expect), strings.TrimSpace(msg)) + assert.Equal(t, strings.TrimSpace(expect), strings.TrimSpace(msg)) } test("{{", p.handleGenericTemplateError, ` @@ -103,5 +103,5 @@ god knows XXX ---------------------------------------------------------------------- ` actualMsg := p.handleExpectedEndError(errors.New("template: test:1: expected end; found XXX")) - assert.EqualValues(t, strings.TrimSpace(expectedMsg), strings.TrimSpace(actualMsg)) + assert.Equal(t, strings.TrimSpace(expectedMsg), strings.TrimSpace(actualMsg)) } diff --git a/modules/templates/scopedtmpl/scopedtmpl.go b/modules/templates/scopedtmpl/scopedtmpl.go index 2722ba97a2..41a8ca86e9 100644 --- a/modules/templates/scopedtmpl/scopedtmpl.go +++ b/modules/templates/scopedtmpl/scopedtmpl.go @@ -192,21 +192,21 @@ func newScopedTemplateSet(all *template.Template, name string) (*scopedTemplateS collectTemplates(nodeList.Nodes) } else if node.Type() == parse.NodeIf { nodeIf := node.(*parse.IfNode) - collectTemplates(nodeIf.BranchNode.List.Nodes) - if nodeIf.BranchNode.ElseList != nil { - collectTemplates(nodeIf.BranchNode.ElseList.Nodes) + collectTemplates(nodeIf.List.Nodes) + if nodeIf.ElseList != nil { + collectTemplates(nodeIf.ElseList.Nodes) } } else if node.Type() == parse.NodeRange { nodeRange := node.(*parse.RangeNode) - collectTemplates(nodeRange.BranchNode.List.Nodes) - if nodeRange.BranchNode.ElseList != nil { - collectTemplates(nodeRange.BranchNode.ElseList.Nodes) + collectTemplates(nodeRange.List.Nodes) + if nodeRange.ElseList != nil { + collectTemplates(nodeRange.ElseList.Nodes) } } else if node.Type() == parse.NodeWith { nodeWith := node.(*parse.WithNode) - collectTemplates(nodeWith.BranchNode.List.Nodes) - if nodeWith.BranchNode.ElseList != nil { - collectTemplates(nodeWith.BranchNode.ElseList.Nodes) + collectTemplates(nodeWith.List.Nodes) + if nodeWith.ElseList != nil { + collectTemplates(nodeWith.ElseList.Nodes) } } } diff --git a/modules/templates/util_misc.go b/modules/templates/util_misc.go index 12a65c87da..90b6195321 100644 --- a/modules/templates/util_misc.go +++ b/modules/templates/util_misc.go @@ -38,10 +38,11 @@ func SortArrow(normSort, revSort, urlSort string, isDefault bool) template.HTML } else { // if sort arg is in url test if it correlates with column header sort arguments // the direction of the arrow should indicate the "current sort order", up means ASC(normal), down means DESC(rev) - if urlSort == normSort { + switch urlSort { + case normSort: // the table is sorted with this header normal return svg.RenderHTML("octicon-triangle-up", 16) - } else if urlSort == revSort { + case revSort: // the table is sorted with this header reverse return svg.RenderHTML("octicon-triangle-down", 16) } diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 20c39eb417..b75b061218 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -47,12 +47,12 @@ var testMetas = map[string]string{ func TestApostrophesInMentions(t *testing.T) { rendered := RenderMarkdownToHtml(t.Context(), "@mention-user's comment") - assert.EqualValues(t, template.HTML("<p><a href=\"/mention-user\" class=\"mention\" rel=\"nofollow\">@mention-user</a>'s comment</p>\n"), rendered) + assert.Equal(t, template.HTML("<p><a href=\"/mention-user\" class=\"mention\" rel=\"nofollow\">@mention-user</a>'s comment</p>\n"), rendered) } func TestNonExistantUserMention(t *testing.T) { rendered := RenderMarkdownToHtml(t.Context(), "@ThisUserDoesNotExist @mention-user") - assert.EqualValues(t, template.HTML("<p>@ThisUserDoesNotExist <a href=\"/mention-user\" class=\"mention\" rel=\"nofollow\">@mention-user</a></p>\n"), rendered) + assert.Equal(t, template.HTML("<p>@ThisUserDoesNotExist <a href=\"/mention-user\" class=\"mention\" rel=\"nofollow\">@mention-user</a></p>\n"), rendered) } func TestRenderCommitBody(t *testing.T) { diff --git a/modules/templates/util_test.go b/modules/templates/util_test.go index 79aaba4a0e..e28da8090b 100644 --- a/modules/templates/util_test.go +++ b/modules/templates/util_test.go @@ -29,7 +29,7 @@ func TestDict(t *testing.T) { for _, c := range cases { got, err := dict(c.args...) require.NoError(t, err) - assert.EqualValues(t, c.want, got) + assert.Equal(t, c.want, got) } bads := []struct { diff --git a/modules/templates/vars/vars_test.go b/modules/templates/vars/vars_test.go index c54342204d..a0c3490c3a 100644 --- a/modules/templates/vars/vars_test.go +++ b/modules/templates/vars/vars_test.go @@ -61,7 +61,7 @@ func TestExpandVars(t *testing.T) { for _, kase := range kases { t.Run(kase.tmpl, func(t *testing.T) { res, err := Expand(kase.tmpl, kase.data) - assert.EqualValues(t, kase.out, res) + assert.Equal(t, kase.out, res) if kase.error { require.Error(t, err) } else { diff --git a/modules/test/distant_federation_server_mock.go b/modules/test/distant_federation_server_mock.go index fd68c88a40..9bd908e2b9 100644 --- a/modules/test/distant_federation_server_mock.go +++ b/modules/test/distant_federation_server_mock.go @@ -95,7 +95,7 @@ func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server { }) } for _, repository := range mock.Repositories { - federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox/", repository.ID), + federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox", repository.ID), func(res http.ResponseWriter, req *http.Request) { if req.Method != "POST" { t.Errorf("POST expected at: %q", req.URL.EscapedPath()) diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go index b5f196ad4b..772ae47e71 100644 --- a/modules/testlogger/testlogger.go +++ b/modules/testlogger/testlogger.go @@ -363,6 +363,12 @@ var ignoredErrorMessage = []string{ // TestDatabaseCollation `[E] [Error SQL Query] INSERT INTO test_collation_tbl (txt) VALUES ('main') []`, + + // Test_CmdForgejo_Actions + `DB: No dedicated replica host defined; falling back to primary DSN for replica connections`, + + // TestDevtestErrorpages + `ErrorPage() [E] Example error: Example error`, } func (w *testLoggerWriterCloser) recordError(msg string) { diff --git a/modules/timeutil/since.go b/modules/timeutil/since.go index b0bbe25f98..f296a2dc86 100644 --- a/modules/timeutil/since.go +++ b/modules/timeutil/since.go @@ -96,11 +96,7 @@ func timeSincePro(then, now time.Time, lang translation.Locale) string { } var timeStr, diffStr string - for { - if diff == 0 { - break - } - + for diff != 0 { diff, diffStr = computeTimeDiffFloor(diff, lang) timeStr += ", " + diffStr } diff --git a/modules/translation/i18n/i18n_test.go b/modules/translation/i18n/i18n_test.go index a0458f0b8e..798c0e14a4 100644 --- a/modules/translation/i18n/i18n_test.go +++ b/modules/translation/i18n/i18n_test.go @@ -166,7 +166,7 @@ commits = fallback value for commits found = lang1.HasKey("no-such") assert.False(t, found) - assert.EqualValues(t, "no-such", lang1.TrString("no-such")) + assert.Equal(t, "no-such", lang1.TrString("no-such")) require.NoError(t, ls.Close()) } diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go index 823cadf8f9..bf98b7b5da 100644 --- a/modules/translation/i18n/localestore.go +++ b/modules/translation/i18n/localestore.go @@ -8,9 +8,9 @@ import ( "html/template" "slices" - "forgejo.org/modules/json" "forgejo.org/modules/log" "forgejo.org/modules/setting" + "forgejo.org/modules/translation/localeiter" "forgejo.org/modules/util" ) @@ -94,54 +94,20 @@ func (store *localeStore) AddLocaleByIni(langName, langDesc string, pluralRule P return nil } -func RecursivelyAddTranslationsFromJSON(locale *locale, object map[string]any, prefix string) error { - for key, value := range object { - var fullkey string - if prefix != "" { - fullkey = prefix + "." + key - } else { - fullkey = key - } - - switch v := value.(type) { - case string: - // Check whether we are adding a plural form to the parent object, or a new nested JSON object. - - if key == "zero" || key == "one" || key == "two" || key == "few" || key == "many" { - locale.newStyleMessages[prefix+PluralFormSeparator+key] = v - } else if key == "other" { - locale.newStyleMessages[prefix] = v - } else { - locale.newStyleMessages[fullkey] = v - } - - case map[string]any: - err := RecursivelyAddTranslationsFromJSON(locale, v, fullkey) - if err != nil { - return err - } - - case nil: - default: - return fmt.Errorf("Unrecognized JSON value '%s'", value) - } - } - - return nil -} - func (store *localeStore) AddToLocaleFromJSON(langName string, source []byte) error { locale, ok := store.localeMap[langName] if !ok { return ErrLocaleDoesNotExist } - var result map[string]any - if err := json.Unmarshal(source, &result); err != nil { - return err - } - - return RecursivelyAddTranslationsFromJSON(locale, result, "") + return localeiter.IterateMessagesNextContent(source, func(key, pluralForm, value string) error { + msgKey := key + if pluralForm != "" { + msgKey = key + PluralFormSeparator + pluralForm + } + locale.newStyleMessages[msgKey] = value + return nil + }) } func (l *locale) LookupNewStyleMessage(trKey string) string { diff --git a/modules/translation/localeiter/utils.go b/modules/translation/localeiter/utils.go new file mode 100644 index 0000000000..de398258e2 --- /dev/null +++ b/modules/translation/localeiter/utils.go @@ -0,0 +1,89 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +// extracted from `/build/lint-locale.go`, `/build/lint-locale-usage.go` + +package localeiter + +import ( + "encoding/json" //nolint:depguard + "fmt" + + "forgejo.org/modules/setting" +) + +func IterateMessagesContent(localeContent []byte, onMsgid func(string, string) error) error { + cfg, err := setting.NewConfigProviderForLocale(localeContent) + if err != nil { + return err + } + + for _, section := range cfg.Sections() { + for _, key := range section.Keys() { + var trKey string + // see https://codeberg.org/forgejo/discussions/issues/104 + // https://github.com/WeblateOrg/weblate/issues/10831 + // for an explanation of why "common" is an alternative + if section.Name() == "" || section.Name() == "DEFAULT" || section.Name() == "common" { + trKey = key.Name() + } else { + trKey = section.Name() + "." + key.Name() + } + if err := onMsgid(trKey, key.Value()); err != nil { + return err + } + } + } + + return nil +} + +func iterateMessagesNextInner(onMsgid func(string, string, string) error, data map[string]any, trKey string) error { + for key, value := range data { + fullKey := key + if trKey != "" { + fullKey = trKey + "." + key + } + switch value := value.(type) { + case string: + // Check whether we are adding a plural form to the parent object, or a new nested JSON object. + realKey := trKey + pluralSuffix := "" + + switch key { + case "zero", "one", "two", "few", "many": + pluralSuffix = key + case "other": + // do nothing + default: + realKey = fullKey + } + + if err := onMsgid(realKey, pluralSuffix, value); err != nil { + return err + } + + case map[string]any: + if err := iterateMessagesNextInner(onMsgid, value, fullKey); err != nil { + return err + } + + case nil: + // do nothing + + default: + return fmt.Errorf("Unexpected JSON type: %s - %T", fullKey, value) + } + } + + return nil +} + +func IterateMessagesNextContent(localeContent []byte, onMsgid func(string, string, string) error) error { + var localeData map[string]any + if err := json.Unmarshal(localeContent, &localeData); err != nil { + return err + } + return iterateMessagesNextInner(onMsgid, localeData, "") +} diff --git a/modules/translation/translation_test.go b/modules/translation/translation_test.go index 356b85f946..7584490941 100644 --- a/modules/translation/translation_test.go +++ b/modules/translation/translation_test.go @@ -16,19 +16,19 @@ import ( func TestTrSize(t *testing.T) { l := NewLocale("") size := int64(1) - assert.EqualValues(t, "1 munits.data.b", l.TrSize(size).String()) + assert.Equal(t, "1 munits.data.b", l.TrSize(size).String()) size *= 2048 - assert.EqualValues(t, "2 munits.data.kib", l.TrSize(size).String()) + assert.Equal(t, "2 munits.data.kib", l.TrSize(size).String()) size *= 2048 - assert.EqualValues(t, "4 munits.data.mib", l.TrSize(size).String()) + assert.Equal(t, "4 munits.data.mib", l.TrSize(size).String()) size *= 2048 - assert.EqualValues(t, "8 munits.data.gib", l.TrSize(size).String()) + assert.Equal(t, "8 munits.data.gib", l.TrSize(size).String()) size *= 2048 - assert.EqualValues(t, "16 munits.data.tib", l.TrSize(size).String()) + assert.Equal(t, "16 munits.data.tib", l.TrSize(size).String()) size *= 2048 - assert.EqualValues(t, "32 munits.data.pib", l.TrSize(size).String()) + assert.Equal(t, "32 munits.data.pib", l.TrSize(size).String()) size *= 128 - assert.EqualValues(t, "4 munits.data.eib", l.TrSize(size).String()) + assert.Equal(t, "4 munits.data.eib", l.TrSize(size).String()) } func TestPrettyNumber(t *testing.T) { @@ -38,15 +38,15 @@ func TestPrettyNumber(t *testing.T) { allLangMap["id-ID"] = &LangType{Lang: "id-ID", Name: "Bahasa Indonesia"} l := NewLocale("id-ID") - assert.EqualValues(t, "1.000.000", l.PrettyNumber(1000000)) - assert.EqualValues(t, "1.000.000,1", l.PrettyNumber(1000000.1)) - assert.EqualValues(t, "1.000.000", l.PrettyNumber("1000000")) - assert.EqualValues(t, "1.000.000", l.PrettyNumber("1000000.0")) - assert.EqualValues(t, "1.000.000,1", l.PrettyNumber("1000000.1")) + assert.Equal(t, "1.000.000", l.PrettyNumber(1000000)) + assert.Equal(t, "1.000.000,1", l.PrettyNumber(1000000.1)) + assert.Equal(t, "1.000.000", l.PrettyNumber("1000000")) + assert.Equal(t, "1.000.000", l.PrettyNumber("1000000.0")) + assert.Equal(t, "1.000.000,1", l.PrettyNumber("1000000.1")) l = NewLocale("nosuch") - assert.EqualValues(t, "1,000,000", l.PrettyNumber(1000000)) - assert.EqualValues(t, "1,000,000.1", l.PrettyNumber(1000000.1)) + assert.Equal(t, "1,000,000", l.PrettyNumber(1000000)) + assert.Equal(t, "1,000,000.1", l.PrettyNumber(1000000.1)) } func TestGetPluralRule(t *testing.T) { diff --git a/modules/util/paginate_test.go b/modules/util/paginate_test.go index 6e69dd19cc..3dc5095071 100644 --- a/modules/util/paginate_test.go +++ b/modules/util/paginate_test.go @@ -13,23 +13,23 @@ func TestPaginateSlice(t *testing.T) { stringSlice := []string{"a", "b", "c", "d", "e"} result, ok := PaginateSlice(stringSlice, 1, 2).([]string) assert.True(t, ok) - assert.EqualValues(t, []string{"a", "b"}, result) + assert.Equal(t, []string{"a", "b"}, result) result, ok = PaginateSlice(stringSlice, 100, 2).([]string) assert.True(t, ok) - assert.EqualValues(t, []string{}, result) + assert.Equal(t, []string{}, result) result, ok = PaginateSlice(stringSlice, 3, 2).([]string) assert.True(t, ok) - assert.EqualValues(t, []string{"e"}, result) + assert.Equal(t, []string{"e"}, result) result, ok = PaginateSlice(stringSlice, 1, 0).([]string) assert.True(t, ok) - assert.EqualValues(t, []string{"a", "b", "c", "d", "e"}, result) + assert.Equal(t, []string{"a", "b", "c", "d", "e"}, result) result, ok = PaginateSlice(stringSlice, 1, -1).([]string) assert.True(t, ok) - assert.EqualValues(t, []string{"a", "b", "c", "d", "e"}, result) + assert.Equal(t, []string{"a", "b", "c", "d", "e"}, result) type Test struct { Val int @@ -38,9 +38,9 @@ func TestPaginateSlice(t *testing.T) { testVar := []*Test{{Val: 2}, {Val: 3}, {Val: 4}} testVar, ok = PaginateSlice(testVar, 1, 50).([]*Test) assert.True(t, ok) - assert.EqualValues(t, []*Test{{Val: 2}, {Val: 3}, {Val: 4}}, testVar) + assert.Equal(t, []*Test{{Val: 2}, {Val: 3}, {Val: 4}}, testVar) testVar, ok = PaginateSlice(testVar, 2, 2).([]*Test) assert.True(t, ok) - assert.EqualValues(t, []*Test{{Val: 4}}, testVar) + assert.Equal(t, []*Test{{Val: 4}}, testVar) } diff --git a/modules/util/path.go b/modules/util/path.go index 9039f27cbf..3ef3925c49 100644 --- a/modules/util/path.go +++ b/modules/util/path.go @@ -34,9 +34,10 @@ func PathJoinRel(elem ...string) string { elems[i] = path.Clean("/" + e) } p := path.Join(elems...) - if p == "" { + switch p { + case "": return "" - } else if p == "/" { + case "/": return "." } return p[1:] diff --git a/modules/util/util.go b/modules/util/util.go index da405c9c4b..548fd1e90b 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -14,22 +14,11 @@ import ( "strconv" "strings" - "forgejo.org/modules/optional" - "golang.org/x/crypto/ssh" "golang.org/x/text/cases" "golang.org/x/text/language" ) -// OptionalBoolParse get the corresponding optional.Option[bool] of a string using strconv.ParseBool -func OptionalBoolParse(s string) optional.Option[bool] { - v, e := strconv.ParseBool(s) - if e != nil { - return optional.None[bool]() - } - return optional.Some(v) -} - // IsEmptyString checks if the provided string is empty func IsEmptyString(s string) bool { return len(strings.TrimSpace(s)) == 0 @@ -99,10 +88,16 @@ func CryptoRandomString(length int64) (string, error) { // CryptoRandomBytes generates `length` crypto bytes // This differs from CryptoRandomString, as each byte in CryptoRandomString is generated by [0,61] range // This function generates totally random bytes, each byte is generated by [0,255] range -func CryptoRandomBytes(length int64) ([]byte, error) { +func CryptoRandomBytes(length int64) []byte { + // crypto/rand.Read is documented to never return a error. + // https://go.dev/issue/66821 buf := make([]byte, length) - _, err := rand.Read(buf) - return buf, err + n, err := rand.Read(buf) + if err != nil || n != int(length) { + panic(err) + } + + return buf } // ToUpperASCII returns s with all ASCII letters mapped to their upper case. diff --git a/modules/util/util_test.go b/modules/util/util_test.go index 5e0c4a9a0b..21988fd0f8 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -11,7 +11,6 @@ import ( "strings" "testing" - "forgejo.org/modules/optional" "forgejo.org/modules/test" "forgejo.org/modules/util" @@ -164,36 +163,21 @@ func Test_RandomString(t *testing.T) { } func Test_RandomBytes(t *testing.T) { - bytes1, err := util.CryptoRandomBytes(32) - require.NoError(t, err) - - bytes2, err := util.CryptoRandomBytes(32) - require.NoError(t, err) + bytes1 := util.CryptoRandomBytes(32) + bytes2 := util.CryptoRandomBytes(32) + assert.Len(t, bytes1, 32) + assert.Len(t, bytes2, 32) assert.NotEqual(t, bytes1, bytes2) - bytes3, err := util.CryptoRandomBytes(256) - require.NoError(t, err) - - bytes4, err := util.CryptoRandomBytes(256) - require.NoError(t, err) + bytes3 := util.CryptoRandomBytes(256) + bytes4 := util.CryptoRandomBytes(256) + assert.Len(t, bytes3, 256) + assert.Len(t, bytes4, 256) assert.NotEqual(t, bytes3, bytes4) } -func TestOptionalBoolParse(t *testing.T) { - assert.Equal(t, optional.None[bool](), util.OptionalBoolParse("")) - assert.Equal(t, optional.None[bool](), util.OptionalBoolParse("x")) - - assert.Equal(t, optional.Some(false), util.OptionalBoolParse("0")) - assert.Equal(t, optional.Some(false), util.OptionalBoolParse("f")) - assert.Equal(t, optional.Some(false), util.OptionalBoolParse("False")) - - assert.Equal(t, optional.Some(true), util.OptionalBoolParse("1")) - assert.Equal(t, optional.Some(true), util.OptionalBoolParse("t")) - assert.Equal(t, optional.Some(true), util.OptionalBoolParse("True")) -} - // Test case for any function which accepts and returns a single string. type StringTest struct { in, out string @@ -272,8 +256,8 @@ func TestGeneratingEd25519Keypair(t *testing.T) { publicKey, privateKey, err := util.GenerateSSHKeypair() require.NoError(t, err) - assert.EqualValues(t, testPublicKey, string(publicKey)) - assert.EqualValues(t, testPrivateKey, string(privateKey)) + assert.Equal(t, testPublicKey, string(publicKey)) + assert.Equal(t, testPrivateKey, string(privateKey)) } func TestOptionalArg(t *testing.T) { diff --git a/modules/validation/email_test.go b/modules/validation/email_test.go index ffdc6fd4ee..68864b2450 100644 --- a/modules/validation/email_test.go +++ b/modules/validation/email_test.go @@ -62,7 +62,7 @@ func TestEmailAddressValidate(t *testing.T) { } for kase, err := range kases { t.Run(kase, func(t *testing.T) { - assert.EqualValues(t, err, ValidateEmail(kase)) + assert.Equal(t, err, ValidateEmail(kase)) }) } } diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go index 9083e9b485..123eb29015 100644 --- a/modules/web/middleware/binding.go +++ b/modules/web/middleware/binding.go @@ -79,6 +79,11 @@ func GetInclude(field reflect.StructField) string { return getRuleBody(field, "Include(") } +func GetRange(field reflect.StructField) (string, string) { + min, max, _ := strings.Cut(getRuleBody(field, "Range("), ",") + return min, max +} + // Validate populates the data with validation error (if any). func Validate(errs binding.Errors, data map[string]any, f any, l translation.Locale) binding.Errors { if errs.Len() == 0 { @@ -131,6 +136,9 @@ func Validate(errs binding.Errors, data map[string]any, f any, l translation.Loc data["ErrorMsg"] = trName + l.TrString("form.url_error", errs[0].Message) case binding.ERR_INCLUDE: data["ErrorMsg"] = trName + l.TrString("form.include_error", GetInclude(field)) + case binding.ERR_RANGE: + min, max := GetRange(field) + data["ErrorMsg"] = trName + l.TrString("alert.range_error", l.PrettyNumber(min), l.PrettyNumber(max)) case validation.ErrGlobPattern: data["ErrorMsg"] = trName + l.TrString("form.glob_pattern_error", errs[0].Message) case validation.ErrRegexPattern: diff --git a/modules/web/route_test.go b/modules/web/route_test.go index d8015d6e0d..44626ec141 100644 --- a/modules/web/route_test.go +++ b/modules/web/route_test.go @@ -23,17 +23,17 @@ func TestRoute1(t *testing.T) { r := NewRoute() r.Get("/{username}/{reponame}/{type:issues|pulls}", func(resp http.ResponseWriter, req *http.Request) { username := chi.URLParam(req, "username") - assert.EqualValues(t, "gitea", username) + assert.Equal(t, "gitea", username) reponame := chi.URLParam(req, "reponame") - assert.EqualValues(t, "gitea", reponame) + assert.Equal(t, "gitea", reponame) tp := chi.URLParam(req, "type") - assert.EqualValues(t, "issues", tp) + assert.Equal(t, "issues", tp) }) req, err := http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) + assert.Equal(t, http.StatusOK, recorder.Code) } func TestRoute2(t *testing.T) { @@ -48,23 +48,23 @@ func TestRoute2(t *testing.T) { r.Group("", func() { r.Get("/{type:issues|pulls}", func(resp http.ResponseWriter, req *http.Request) { username := chi.URLParam(req, "username") - assert.EqualValues(t, "gitea", username) + assert.Equal(t, "gitea", username) reponame := chi.URLParam(req, "reponame") - assert.EqualValues(t, "gitea", reponame) + assert.Equal(t, "gitea", reponame) tp := chi.URLParam(req, "type") - assert.EqualValues(t, "issues", tp) + assert.Equal(t, "issues", tp) hit = 0 }) r.Get("/{type:issues|pulls}/{index}", func(resp http.ResponseWriter, req *http.Request) { username := chi.URLParam(req, "username") - assert.EqualValues(t, "gitea", username) + assert.Equal(t, "gitea", username) reponame := chi.URLParam(req, "reponame") - assert.EqualValues(t, "gitea", reponame) + assert.Equal(t, "gitea", reponame) tp := chi.URLParam(req, "type") - assert.EqualValues(t, "issues", tp) + assert.Equal(t, "issues", tp) index := chi.URLParam(req, "index") - assert.EqualValues(t, "1", index) + assert.Equal(t, "1", index) hit = 1 }) }, func(resp http.ResponseWriter, req *http.Request) { @@ -77,11 +77,11 @@ func TestRoute2(t *testing.T) { r.Group("/issues/{index}", func() { r.Get("/view", func(resp http.ResponseWriter, req *http.Request) { username := chi.URLParam(req, "username") - assert.EqualValues(t, "gitea", username) + assert.Equal(t, "gitea", username) reponame := chi.URLParam(req, "reponame") - assert.EqualValues(t, "gitea", reponame) + assert.Equal(t, "gitea", reponame) index := chi.URLParam(req, "index") - assert.EqualValues(t, "1", index) + assert.Equal(t, "1", index) hit = 2 }) }) @@ -90,26 +90,26 @@ func TestRoute2(t *testing.T) { req, err := http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 0, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 0, hit) req, err = http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues/1", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 1, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 1, hit) req, err = http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues/1?stop=100", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 100, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 100, hit) req, err = http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues/1/view", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 2, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 2, hit) } func TestRoute3(t *testing.T) { @@ -150,30 +150,30 @@ func TestRoute3(t *testing.T) { req, err := http.NewRequest("GET", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 0, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 0, hit) req, err = http.NewRequest("POST", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code, http.StatusOK) - assert.EqualValues(t, 1, hit) + assert.Equal(t, http.StatusOK, recorder.Code, http.StatusOK) + assert.Equal(t, 1, hit) req, err = http.NewRequest("GET", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 2, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 2, hit) req, err = http.NewRequest("PATCH", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 3, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 3, hit) req, err = http.NewRequest("DELETE", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 4, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 4, hit) } diff --git a/modules/web/routemock_test.go b/modules/web/routemock_test.go index 43d4b28830..46f0296046 100644 --- a/modules/web/routemock_test.go +++ b/modules/web/routemock_test.go @@ -35,9 +35,9 @@ func TestRouteMock(t *testing.T) { require.NoError(t, err) r.ServeHTTP(recorder, req) assert.Len(t, recorder.Header(), 3) - assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1")) - assert.EqualValues(t, "m2", recorder.Header().Get("X-Test-Middleware2")) - assert.EqualValues(t, "h", recorder.Header().Get("X-Test-Handler")) + assert.Equal(t, "m1", recorder.Header().Get("X-Test-Middleware1")) + assert.Equal(t, "m2", recorder.Header().Get("X-Test-Middleware2")) + assert.Equal(t, "h", recorder.Header().Get("X-Test-Handler")) RouteMockReset() // mock at "mock-point" @@ -50,8 +50,8 @@ func TestRouteMock(t *testing.T) { require.NoError(t, err) r.ServeHTTP(recorder, req) assert.Len(t, recorder.Header(), 2) - assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1")) - assert.EqualValues(t, "a", recorder.Header().Get("X-Test-MockPoint")) + assert.Equal(t, "m1", recorder.Header().Get("X-Test-Middleware1")) + assert.Equal(t, "a", recorder.Header().Get("X-Test-MockPoint")) RouteMockReset() // mock at MockAfterMiddlewares @@ -64,8 +64,8 @@ func TestRouteMock(t *testing.T) { require.NoError(t, err) r.ServeHTTP(recorder, req) assert.Len(t, recorder.Header(), 3) - assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1")) - assert.EqualValues(t, "m2", recorder.Header().Get("X-Test-Middleware2")) - assert.EqualValues(t, "b", recorder.Header().Get("X-Test-MockPoint")) + assert.Equal(t, "m1", recorder.Header().Get("X-Test-Middleware1")) + assert.Equal(t, "m2", recorder.Header().Get("X-Test-Middleware2")) + assert.Equal(t, "b", recorder.Header().Get("X-Test-MockPoint")) RouteMockReset() } diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index ae878acc15..caa3db3af9 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1652,7 +1652,7 @@ issues.comment_manually_pull_merged_at=ručně sloučena revize %[1]s do %[2]s % issues.close_comment_issue=Zavřít s komentářem issues.reopen_issue=Znovu otevřít issues.reopen_comment_issue=Znovu otevřít s komentářem -issues.create_comment=Okomentovat +issues.create_comment=Komentovat issues.closed_at=`uzavřel/a tento problém <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`znovu otevřel/a tento problém <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`odkázal/a na tento problém z revize <a id="%[1]s" href="#%[1]s">%[2]s</a>` @@ -2607,7 +2607,7 @@ diff.comment.reply=Odpovědět diff.review=Dokončit posouzení diff.review.header=Odeslat posouzení diff.review.placeholder=Posoudit komentář -diff.review.comment=Okomentovat +diff.review.comment=Komentovat diff.review.approve=Schválit diff.review.self_reject=Autoři požadavků na natažení nemohou požadovat změny na svém vlastním požadavku na natažení diff.review.reject=Požadovat změny @@ -2909,6 +2909,9 @@ issues.reopen.blocked_by_user = Tento problém nemůžete znovu otevřít, proto pulls.comment.blocked_by_user = Tuto žádost o sloučení nemůžete okomentovat, protože jste byli zablokováni majitelem repozitáře nebo autorem žádosti. issues.filter_no_results_placeholder = Zkuste upravit filtry vyhledávání. issues.filter_no_results = Žádné výsledky +migrate.repo_desc_helper = Ponechte prázdné pro importování existujícího popisu +archive.nocomment = Komentování není možné, protože je repozitář archivován. +comment.blocked_by_user = Komentování není možné, protože jste byli zablokováni majitelem repozitáře nebo autorem. [graphs] component_loading_info = Tohle může chvíli trvat… diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index 5bbd535b2a..3de53b2ebf 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -1451,7 +1451,7 @@ issues.filter_sort.leastcomment = Mindst kommenteret issues.filter_sort.nearduedate = Nærmeste forfaldsdato issues.filter_sort.farduedate = Længste forfaldsdato issues.filter_sort.moststars = Flest stjerner -editor.file_changed_while_editing = Filens indhold er ændret, siden du begyndte at redigere. <a target="_blank" rel="noopener noreferrer" href="%s">Klik her</a> for at se dem eller <strong>Bekræft ændringer igen</strong> for at overskrive dem. +editor.file_changed_while_editing = Filens indhold er ændret, siden du begyndte at åbne den. <a target="_blank" rel="noopener noreferrer" href="%s">Klik her</a> for at se dem eller <strong>Bekræft ændringer igen</strong> for at overskrive dem. editor.file_already_exists = En fil med navnet "%s" findes allerede i dette depot. editor.commit_id_not_matching = Filen blev ændret, mens du redigerede den. Forpligt dig til en ny gren og merge derefter. editor.push_out_of_date = Pushet ser ud til at være forældet. @@ -2722,6 +2722,9 @@ error.broken_git_hook = Git hooks af dette depot ser ud til at være brudt. Føl branch.create_new_branch = Opret en gren fra gren: branch.confirm_create_branch = Opret gren branch.rename_branch_to = Omdøb "%s" til: +migrate.repo_desc_helper = Lad være tom for at importere eksisterende beskrivelse +archive.nocomment = Det er ikke muligt at kommentere, fordi depotet er arkiveret. +comment.blocked_by_user = Det er ikke muligt at kommentere, fordi du er blokeret af depots ejer eller forfatteren. [notification] watching = Overvåger @@ -2735,9 +2738,40 @@ mark_all_as_read = Markér alle som læste mark_as_unread = Markér som ulæst subscriptions = Abonnementer no_subscriptions = Ingen abonnementer +pin = Fastgør notifikation [action] watched_repo = begyndte at overvåge <a href="%[1]s">%[2]s</a> +commit_repo = pushed til <a href="%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a> +comment_pull = `kommenterede pull request <a href="%[1]s">%[3]s#%[2]s</a>` +mirror_sync_push = synkroniserede commits til <a href="%[2]s">%[3]s</a> ved <a href="%[1]s">%[4]s</a> fra spejl +create_issue = `åbnet problem <a href="%[1]s">%[3]s#%[2]s</a>` +mirror_sync_create = synkroniseret ny reference <a href="%[2]s">%[3]s</a> til <a href="%[1]s">%[4]s</a> fra spejl +reopen_pull_request = `genåbnet pull request <a href="%[1]s">%[3]s#%[2]s</a>` +create_repo = oprettede depot <a href="%s">%s</a> +compare_branch = Sammenlign +rename_repo = omdøbt depot fra <code>%[1]s</code> til <a href="%[2]s">%[3]s</a> +reopen_issue = `genåbnet problem <a href="%[1]s">%[3]s#%[2]s</a>` +create_pull_request = `oprettet pull-anmodning <a href="%[1]s">%[3]s#%[2]s</a>` +close_pull_request = `lukket pull request <a href="%[1]s">%[3]s#%[2]s</a>` +starred_repo = stjernemarkerede <a href="%[1]s">%[2]s</a> +close_issue = `lukket problem <a href="%[1]s">%[3]s#%[2]s</a>` +comment_issue = `kommenterede problem <a href="%[1]s">%[3]s#%[2]s</a>` +delete_branch = slettede gren %[2]s fra <a href="%[1]s">%[3]s</a> +compare_commits = Sammenlign %d commits +compare_commits_general = Sammenlign commits +review_dismissed = `afvist anmeldelse fra <b>%[4]s</b> for <a href="%[1]s">%[3]s#%[2]s</a>` +review_dismissed_reason = Årsag: +create_branch = oprettede gren <a href="%[2]s">%[3]s</a> i <a href="%[1]s">%[4]s</a> +merge_pull_request = `sammenflettet pull request <a href="%[1]s">%[3]s#%[2]s</a>` +auto_merge_pull_request = `automatisk flettet pull-anmodning <a href="%[1]s">%[3]s#%[2]s</a>` +transfer_repo = overførte lager <code>%s</code> til <a href="%s">%s</a> +push_tag = skubbet tag <a href="%[2]s">%[3]s</a> til <a href="%[1]s">%[4]s</a> +delete_tag = slettede tag %[2]s fra <a href="%[1]s">%[3]s</a> +mirror_sync_delete = synkroniseret og slettet reference <code>%[2]s</code> ved <a href="%[1]s">%[3]s</a> fra spejl +approve_pull_request = `godkendt <a href="%[1]s">%[3]s#%[2]s</a>` +reject_pull_request = `foreslået ændringer for <a href="%[1]s">%[3]s#%[2]s</a>` +publish_release = `udgivet <a href="%[2]s">%[4]s</a> på <a href="%[1]s">%[3]s</a>` [org] repo_updated = Opdateret %s @@ -2914,7 +2948,7 @@ dashboard.sync_repo_branches = Synkroniser mistede grene fra Git-data til databa dashboard.sync_repo_tags = Synkroniser tags fra Git-data til database dashboard.archive_cleanup = Slet gamle depotarkiver dashboard.deleted_branches_cleanup = Ryd op i slettede grene -dashboard = Dashboard +dashboard = Instrumentpanel self_check = Selvtjek assets = Kode aktiver repositories = Depoter @@ -3095,6 +3129,280 @@ auths.type = Type auths.syncenabled = Slå brugersynkronisering til packages.published = Offentliggjort packages.type = Type +packages.cleanup = Ryd op i udløbne data +packages.cleanup.success = Der er ryddet op i udløbne data +defaulthooks.add_webhook = Tilføj standard webhook +defaulthooks.update_webhook = Opdater standardwebhook +systemhooks = System webhooks +systemhooks.desc = Webhooks foretager automatisk HTTP POST-anmodninger til en server, når visse Forgejo-hændelser udløses. Webhooks, der er defineret her, vil virke på alle repositories på systemet, så overvej venligst eventuelle præstationsimplikationer, dette kan have. Læs mere i <a target="_blank" rel="noopener" href="%s">webhooks-guiden</a>. +defaulthooks = Default webhooks +defaulthooks.desc = Webhooks foretager automatisk HTTP POST-anmodninger til en server, når visse Forgejo-hændelser udløses. Webhooks defineret her er standarder og vil blive kopieret til alle nye repositories. Læs mere i <a target="_blank" rel="noopener" href="%s">webhooks-guiden</a>. +packages.creator = Skaber +systemhooks.update_webhook = Opdater System Webhook +config.enable_openid_signin = Aktiver OpenID-logon +config.cache_test = Test cache +config.cache_item_ttl = Cache element TTL +self_check.database_inconsistent_collation_columns = Databasen bruger sortering %s, men disse kolonner bruger uoverensstemmende sorteringer. Det kan forårsage nogle uventede problemer. +auths.oauth2_group_claim_name = Gør krav på navn med gruppenavne for denne kilde. (Valgfri) +auths.oauth2_map_group_to_team_removal = Fjern brugere fra synkroniserede teams, hvis brugeren ikke tilhører den tilsvarende gruppe. +auths.tips.oauth2.general.tip = Når du registrerer en ny OAuth2-godkendelse, skal URL-adressen til tilbagekald/omdirigering være: +auths.tip.nextcloud = Registrer en ny OAuth-bruger på din instans ved hjælp af følgende menu "Indstillinger -> Sikkerhed -> OAuth 2.0-klient" +auths.tip.dropbox = Opret en ny applikation på %s +auths.tip.github = Registrer en ny OAuth-applikation på %s +auths.tip.gitlab_new = Registrer en ny applikation på %s +auths.tip.yandex = Opret en ny applikation på %s. Vælg følgende tilladelser fra afsnittet "Yandex.Passport API": "Adgang til e-mailadresse", "Adgang til brugeravatar" og "Adgang til brugernavn, fornavn og efternavn, køn" +config.run_mode = Kør tilstand +config.ssh_config = SSH konfiguration +config.allow_only_internal_registration = Tillad kun registrering gennem Forgejo selv +config.ssh_minimum_key_size_check = Minimum nøglestørrelse kontrol +config.enable_captcha = Aktiver CAPTCHA +config.ssh_minimum_key_sizes = Minimum nøglestørrelser +config.default_keep_email_private = Skjul e-mailadresser som standard +config.default_allow_create_organization = Tillad oprettelse af organisationer som standard +config.queue_length = Kø længde +config.default_enable_dependencies = Aktiver problemafhængigheder som standard +config.webhook_config = Webhook-konfiguration +config.deliver_timeout = Lever timeout +config.skip_tls_verify = Spring over TLS-bekræftelse +config.mailer_config = Mailer konfiguration +config.https_only = Kun HTTPS +monitor.next = Næste gang +monitor.processes_count = %d Processer +monitor.duration = Varighed (r) +config.session_life_time = Session levetid +monitor.start = Starttid +notices.deselect_all = Fravælg alle +notices.view_detail_header = Bemærk detaljer +notices.select_all = Vælg alle +self_check.database_collation_case_insensitive = Databasen bruger en sortering %s, som er en ufølsom sortering. Selvom Forgejo kunne arbejde med det, kan der være nogle sjældne tilfælde, som ikke fungerer som forventet. +config.git_max_diff_line_characters = Maks. diff-tegn pr. linje +config.access_log_template = Skabelon til adgangslog +monitor.process.children = Børn +monitor.queues = Køer +monitor.queue.settings.desc = Puljer vokser dynamisk som reaktion på deres blokering af arbejderkø. +auths.auth_manage_panel = Administrer godkendelseskilder +auths.auth_type = Godkendelsestype +auths.map_group_to_team_removal = Fjern brugere fra synkroniserede teams, hvis brugeren ikke tilhører den tilsvarende LDAP-gruppe +auths.attribute_name = Fornavn attribut +auths.map_group_to_team = Tilknyt LDAP-grupper til organisationsteams (lad feltet stå tomt for at springe over) +auths.helo_hostname = HELO værtsnavn +auths.helo_hostname_helper = Værtsnavn sendt med HELO. Lad stå tomt for at sende det nuværende værtsnavn. +auths.disable_helo = Deaktiver HELO +auths.oauth2_authURL = Godkend URL +auths.oauth2_profileURL = Profil-URL +auths.skip_local_two_fa_helper = Hvis du ikke er indstillet, betyder det, at lokale brugere med 2FA indstillet stadig skal bestå 2FA for at logge på +auths.oauth2_required_claim_name_helper = Indstil dette navn for at begrænse login fra denne kilde til brugere med et krav med dette navn +auths.tips.gmail_settings = Gmail-indstillinger: +auths.update_success = Godkendelseskilden er blevet opdateret. +auths.tip.mastodon = Indtast en brugerdefineret instans-URL for den mastodon-instans, du vil godkende med (eller brug standarden) +auths.activated = Denne godkendelseskilde er aktiveret +auths.new_success = Godkendelsen "%s" er blevet tilføjet. +config.enable_openid_signup = Aktiver OpenID-selvregistrering +config.show_registration_button = Vis registreringsknap +config.require_sign_in_view = Kræv at logge ind for at se indhold +config.cache_conn = Cacheforbindelse +config.xorm_log_sql = Log SQL +monitor.stacktrace = Stakspor +monitor.execute_time = Udførelsestid +monitor.last_execution_result = Resultat +monitor.process.cancel_notices = Annuller: <strong>%s</strong>? +monitor.queue.type = Type +monitor.queue.exemplar = Eksempler type +auths.auth_name = Godkendelsesnavn +auths.attribute_username = Brugernavn attribut +auths.attribute_username_placeholder = Lad stå tomt for at bruge brugernavnet indtastet i Forgejo. +auths.enable_ldap_groups = Aktiver LDAP-grupper +auths.ms_ad_sa = MS AD søgeattributter +auths.smtpport = SMTP port +auths.allowed_domains = Tilladte domæner +auths.skip_tls_verify = Spring over TLS-bekræftelse +auths.force_smtps = Tving SMTPS +auths.force_smtps_helper = SMTPS bruges altid på port 465. Indstil denne til at tvinge SMTPS på andre porte. (Ellers vil STARTTLS blive brugt på andre porte, hvis det understøttes af værten.) +auths.oauth2_emailURL = E-mail URL +auths.oauth2_scopes = Yderligere omfang +auths.oauth2_admin_group = Gruppekravværdi for administratorbrugere. (Valgfrit – kræver kravnavn ovenfor) +auths.oauth2_restricted_group = Gruppekravsværdi for begrænsede brugere. (Valgfrit – kræver kravnavn ovenfor) +auths.oauth2_map_group_to_team = Kortlæg hævdede grupper til organisationsteams. (Valgfrit – kræver kravnavn ovenfor) +auths.tip.google_plus = Hent OAuth2-klientlegitimationsoplysninger fra Google API-konsollen på %s +auths.tip.openid_connect = Brug OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) til at angive slutpunkterne +auths.tip.gitea = Registrer en ny OAuth2-applikation. Guide kan findes på %s +auths.edit = Rediger godkendelseskilden +auths.update = Opdater godkendelseskilden +config.offline_mode = Lokal tilstand +config.disable_router_log = Deaktiver routerlog +config.git_version = Git version +config.app_data_path = Appens datasti +config.repo_root_path = Depotets rodsti +config.log_file_root_path = Logsti +config.script_type = Script type +config.reverse_auth_user = Omvendt proxy-godkendelsesbruger +config.ssh_key_test_path = Nøgleteststi +config.ssh_keygen_path = Keygen ("ssh-keygen") sti +config.active_code_lives = Aktiveringskodens udløbstid +config.no_reply_address = Skjult e-mail-domæne +config.mailer_enable_helo = Aktiver HELO +config.mailer_protocol = Protokol +config.mailer_use_sendmail = Brug Sendmail +config.mailer_sendmail_path = Sendmail sti +config.mailer_sendmail_args = Ekstra argumenter til Sendmail +config.cache_test_failed = Kunne ikke undersøge cachen: %v. +config.cache_test_slow = Cachetest lykkedes, men svaret er langsomt: %s. +config.gc_interval_time = GC interval tid +config.git_max_diff_files = Max diff filer vist +config.git_gc_args = GC argumenter +monitor.queue.settings.maxnumberworkers.error = Max antal arbejdere skal være et tal +notices.inverse_selection = Omvendt valg +notices.delete_selected = Slet valgte +auths.allowed_domains_helper = Lad være tomt for at tillade alle domæner. Adskil flere domæner med et komma (","). +auths.skip_local_two_fa = Spring over lokal 2FA +auths.oauth2_required_claim_value_helper = Indstil denne værdi for at begrænse login fra denne kilde til brugere med et krav med dette navn og denne værdi +monitor.queue.settings.maxnumberworkers = Max antal arbejdere +notices.delete_all = Slet alle meddelelser +auths.smtp_auth = SMTP-godkendelsestype +config.default_visibility_organization = Standardsynlighed for nye organisationer +auths.tip.facebook = Registrer en ny applikation på %s og tilføj produktet "Facebook Login" +auths.tip.twitter = Gå til %s, opret en applikation og sørg for, at "Tillad, at denne applikation bruges til at logge på med Twitter" er aktiveret +self_check.database_fix_mysql = For MySQL/MariaDB-brugere kan du bruge kommandoen "forgejo doctor convert" til at løse sorteringsproblemerne, eller du kan også løse problemet ved at "ALTER ... COLLATE ..." SQLs manuelt. +auths.tips.oauth2.general = OAuth2-godkendelse +systemhooks.add_webhook = Tilføj System Webhook +auths.new = Tilføj godkendelseskilde +auths.unable_to_initialize_openid = Kan ikke initialisere OpenID Connect Provider: %s +auths.tip.oauth2_provider = OAuth2-udbyder +auths.tip.bitbucket = Registrer en ny OAuth-bruger på %s og tilføj tilladelsen "Konto" - "Læs" +auths.login_source_exist = Godkendelseskilden "%s" findes allerede. +auths.invalid_openIdConnectAutoDiscoveryURL = Ugyldig Auto Discovery URL (dette skal være en gyldig URL, der starter med http:// eller https://) +config.lfs_root_path = LFS rodsti +auths.login_source_of_type_exist = Der findes allerede en godkendelseskilde af denne type. +config.lfs_content_path = LFS indholdssti +config.lfs_http_auth_expiry = LFS HTTP-godkendelsesudløbstid +config.disable_register = Deaktiver selvregistrering +config.register_email_confirm = Kræv e-mail-bekræftelse for at tilmelde dig +config.mailer_user = Bruger +auths.openIdConnectAutoDiscoveryURL = OpenID Connect Auto Discovery URL +auths.oauth2_use_custom_url = Brug tilpassede webadresser i stedet for standardwebadresser +auths.pam_service_name = PAM-tjenestenavn +auths.pam_email_domain = PAM e-mail domæne (valgfrit) +auths.oauth2_provider = OAuth2-udbyder +auths.oauth2_clientID = Klient-id (nøgle) +auths.oauth2_clientSecret = Klientens hemmelighed +auths.oauth2_required_claim_value = Påkrævet kravværdi +config.db_config = Database konfiguration +config.db_host = Vært +config.ssh_start_builtin_server = Brug indbygget server +config.lfs_config = LFS-konfiguration +config.service_config = Tjenestekonfiguration +config.mailer_smtp_port = SMTP-port +config.disabled_logger = Deaktiveret +config.access_log_mode = Adgang til logtilstand +config.allow_only_external_registration = Tillad kun registrering via eksterne tjenester +config.mailer_smtp_addr = SMTP vært +config.mailer_sendmail_timeout = Sendmail timeout +auths.oauth2_tokenURL = Token URL +auths.oauth2_tenant = Lejer +config.custom_conf = Konfigurationsfilstien +config.custom_file_root_path = Brugerdefineret fil rodsti +config.app_ver = Forgejo version +config.app_url = Base URL +config.ssh_domain = SSH server domæne +config.ssh_port = Port +config.ssh_listen_port = Lyt port +config.ssh_root_path = Rodsti +config.db_type = Type +config.reset_password_code_lives = Gendannelseskodens udløbstid +config.mailer_use_dummy = Attrapp +config.test_mail_failed = Kunne ikke sende en test-e-mail til "%s": %v +config.cache_adapter = Cache adapter +config.test_mail_sent = En test-e-mail er blevet sendt til "%s". +config.oauth_config = OAuth-konfiguration +config.test_email_placeholder = E-mail (f.eks. test@example.com) +config.send_test_mail = Send test-e-mail +config.send_test_mail_submit = Send +config.cache_config = Cache konfiguration +config.cookie_name = Cookie navn +config.session_config = Sessionskonfiguration +config.session_provider = Session udbyder +config.provider_config = Udbyder konfig +notices.op = Op. +notices.delete_success = Systemmeddelelserne er blevet slettet. +self_check.no_problem_found = Intet problem fundet endnu. +self_check.database_collation_mismatch = Forvent, at databasen bruger sortering: %s +auths.user_dn = Bruger DN +auths.bind_dn = Bind DN +auths.bind_password = Bind adgangskode +auths.user_base = Bruger søgebase +auths.verify_group_membership = Bekræft gruppemedlemskab i LDAP (lad filteret stå tomt for at springe over) +auths.group_search_base = Gruppesøgningsbase DN +auths.smtphost = SMTP vært +auths.oauth2_required_claim_name = Påkrævet kravnavn +auths.tips = Tips +auths.delete = Slet godkendelseskilden +auths.delete_auth_title = Slet godkendelseskilden +auths.delete_auth_desc = Sletning af en godkendelseskilde forhindrer brugere i at bruge den til at logge ind. Vil du fortsætte? +auths.still_in_used = Godkendelseskilden er stadig i brug. Konverter eller slet alle brugere, der bruger denne godkendelseskilde først. +auths.deletion_success = Godkendelseskilden er blevet slettet. +config.run_user = Bruger at køre som +config.mail_notify = Aktiver e-mailmeddelelser +config.git_migrate_timeout = Migration timeout +config.log_config = Log konfiguration +auths.default_domain_name = Standard domænenavn, der bruges til e-mailadressen +auths.attribute_mail = E-mail attribut +auths.allow_deactivate_all = Tillad et tomt søgeresultat for at deaktivere alle brugere +auths.use_paged_search = Brug sidesøgning +auths.search_page_size = Sidestørrelse +auths.attribute_ssh_public_key = Offentlig SSH-nøgleattribut +auths.attribute_avatar = Avatar attribut +auths.attributes_in_bind = Hent attributter i bind DN-kontekst +config.app_name = Instans titel +config.app_slogan = instans slogan +config.cache_interval = Cache interval +monitor.queue.settings.title = Pool indstillinger +notices.type = Type +notices.type_2 = Opgave +config.db_schema = Skematisk +config.db_ssl_mode = SSL +config.db_path = Sti +notices.operations = Operationer +auths.restricted_filter_helper = Lad være tom for ikke at angive nogen brugere som begrænset. Brug en stjerne ("*") for at indstille alle brugere, der ikke matcher Admin-filteret, som begrænset. +auths.group_attribute_list_users = Gruppeattribut, der indeholder en liste over brugere +auths.tip.discord = Registrer en ny applikation på %s +config.server_config = Server konfiguration +config.domain = Server domæne +config.set_setting_failed = Indstilling af %s mislykkedes +monitor.previous = Tidligere tid +monitor.process = Kørende processer +monitor.execute_times = Udførelser +monitor.download_diagnosis_report = Hent diagnoserapport +monitor.process.cancel = Annuller processen +monitor.process.cancel_desc = Annullering af en proces kan medføre tab af data +monitor.queue = Kø: %s +monitor.queue.numberworkers = Antal arbejdere +monitor.queue.activeworkers = Aktive arbejdere +monitor.queue.maxnumberworkers = Max antal arbejdere +monitor.queue.numberinqueue = Nummer i kø +monitor.queue.review_add = Gennemgå / tilføj arbejdere +config.git_pull_timeout = Pull Operation timeout +config.git_clone_timeout = Klone Operation timeout +config.git_gc_timeout = GC Operation timeout +config.logger_name_fmt = Logger: %s +monitor.stats = Statistik +monitor.cron = Cron opgaver +monitor.schedule = Tidsplan +config.cache_test_succeeded = Cachetest gennemført, fik et svar i %s. +config.cookie_life_time = Cookie levetid +config.picture_config = Billede og avatar konfiguration +config.picture_service = Billedservice +config.disable_gravatar = Deaktiver Gravatar +config.enable_federated_avatar = Aktiver fødererede avatarer +config.open_with_editor_app_help = "Åbn med"-editorerne til klonmenuen. Hvis den efterlades tom, vil standarden blive brugt. Udvid for at se standarden. +config.git_config = Git konfiguration +config.git_disable_diff_highlight = Deaktiver diff-syntaksfremhævning +config.git_max_diff_lines = Max diff-linjer pr. fil +config.git_mirror_timeout = Spejlopdateringstimeout +auths.attribute_surname = Efternavn attribut +auths.filter = Bruger filter +auths.admin_filter = Admin filter +auths.restricted_filter = Begrænset filter +auths.user_attribute_in_group = Brugerattribut angivet i gruppen [packages] arch.version.description = Beskrivelse @@ -3146,6 +3454,134 @@ dependency.version = Version alpine.registry = Konfigurer dette register ved at tilføje url'en i din <code>/etc/apk/repositories</code> fil: alpine.registry.key = Download den offentlige RSA-nøgle til registreringsdatabasen i mappen <code>/etc/apk/keys/</code> for at bekræfte indekssignaturen: alpine.registry.info = Vælg $branch og $repository fra listen nedenfor. +empty = Der er ingen pakker endnu. +filter.type.all = Alle +filter.container.untagged = Umærket +about = Om denne pakke +filter.no_result = Dit filter gav ingen resultater. +dependencies = Afhængigheder +empty.documentation = For mere information om pakkeregistret, se <a target="_blank" rel="noopener noreferrer" href="%s">dokumentationen</a>. +filter.type = Type +registry.documentation = For mere information om %s registreringsdatabasen, se <a target="_blank" rel="noopener noreferrer" href="%s">dokumentationen</a>. +title = Pakker +desc = Administrer depotpakker. +empty.repo = Har du uploadet en pakke, men den vises ikke her? Gå til <a href="%[1]s">pakkeindstillinger</a> og link den til denne repo. +filter.container.tagged = Tagget +published_by = Udgivet %[1]s af <a href="%[2]s">%[3]s</a> +published_by_in = Udgivet %[1]s af <a href="%[2]s">%[3]s</a> i <a href="%[4]s"><strong>%[5]s</strong></a> +installation = Installation +requirements = Krav +cran.registry = Konfigurer dette register i din <code>Rprofile.site</code> fil: +rubygems.required.rubygems = Kræver RubyGem version +owner.settings.chef.title = Kokkeregister +owner.settings.chef.keypair = Generer nøglepar +arch.pacman.repo.multi.item = Konfiguration for %s +arch.pacman.sync = Synkroniser pakke med pacman: +arch.version.properties = Versionsegenskaber +arch.version.provides = Forsyner +arch.version.checkdepends = Check afhænger +arch.version.replaces = Erstatter +conan.install = For at installere pakken ved hjælp af Conan skal du køre følgende kommando: +conda.registry = Konfigurer dette register som et Conda-depot i din <code>.condarc</code>-fil: +conda.install = For at installere pakken ved hjælp af Conda skal du køre følgende kommando: +container.images.title = Billeder +container.details.type = Billedtype +container.details.platform = Platform +container.pull = Træk billedet fra kommandolinjen: +container.digest = Fordøje +alt.setup = Tilføj et depot til listen over tilsluttede arkiver (vælg den nødvendige arkitektur i stedet for "_arch_"): +vagrant.install = For at tilføje en Vagrant-boks skal du køre følgende kommando: +swift.install2 = og kør følgende kommando: +settings.link = Link denne pakke til et depot +settings.link.success = Depotlinket blev opdateret. +owner.settings.cargo.initialize.description = Et særligt indeks Git-depot er nødvendigt for at bruge Cargo-registret. Brug af denne mulighed vil (gen-)oprette depotet og konfigurere det automatisk. +settings.delete.notice = Du er ved at slette %s (%s). Denne operation er uigenkaldeligt, er du sikker? +owner.settings.cargo.title = Lastregisterindeks +owner.settings.cargo.initialize = Initialiser indeks +owner.settings.cleanuprules.preview.none = Oprydningsreglen matcher ikke nogen pakker. +owner.settings.cleanuprules.none = Der er endnu ingen oprydningsregler. +owner.settings.cleanuprules.keep.count.1 = 1 version pr. pakke +owner.settings.cleanuprules.preview.overview = %d pakker er planlagt til at blive fjernet. +owner.settings.cleanuprules.keep.pattern.container = Den <code>seneste</code> version bevares altid for containerpakker. +settings.delete = Slet pakke +settings.delete.description = Sletning af en pakke er permanent og kan ikke fortrydes. +owner.settings.cleanuprules.keep.count.n = %d versioner pr. pakke +arch.version.makedepends = Gør afhænger +alt.install = Installer pakken +composer.registry = Konfigurer dette register i din <code>~/.composer/config.json</code> fil: +composer.dependencies = Afhængigheder +settings.delete.success = Pakken er blevet slettet. +settings.delete.error = Kunne ikke slette pakken. +owner.settings.cargo.rebuild.error = Kunne ikke genopbygge Cargo-indeks: %v +owner.settings.cargo.rebuild = Genopbyg indeks +owner.settings.cleanuprules.preview = Forhåndsvisning af oprydningsregel +owner.settings.cleanuprules.keep.count = Behold den nyeste +owner.settings.cleanuprules.keep.pattern = Hold versionerne matchende +owner.settings.chef.keypair.description = Et nøglepar er nødvendigt for at autentificere til Chef-registret. Hvis du har genereret et nøglepar før, vil generering af et nyt nøglepar kassere det gamle nøglepar. +maven.install = For at bruge pakken skal du inkludere følgende i blokken <code>afhængigheder</code> i filen <code>pom.xml</code>: +details = Detaljer +cargo.registry = Konfigurer dette register i Cargo-konfigurationsfilen (for eksempel <code>~/.cargo/config.toml</code>): +cargo.install = For at installere pakken ved hjælp af Cargo skal du køre følgende kommando: +composer.install = For at installere pakken ved hjælp af Composer skal du køre følgende kommando: +container.multi_arch = OS / Arch +rubygems.required.ruby = Kræver Ruby version +swift.install = Tilføj pakken i din <code>Package.swift</code>-fil: +settings.link.select = Vælg Depot +settings.link.button = Opdater Depot Link +settings.link.error = Kunne ikke opdatere depotlinket. +owner.settings.cargo.initialize.success = Cargo-indekset blev oprettet. +owner.settings.cargo.rebuild.description = Genopbygning kan være nyttig, hvis indekset ikke er synkroniseret med de lagrede Cargo-pakker. +owner.settings.cargo.rebuild.success = Cargo-indekset blev genopbygget med succes. +owner.settings.cleanuprules.add = Tilføj oprydningsregel +owner.settings.cleanuprules.edit = Rediger oprydningsregel +owner.settings.cleanuprules.title = Oprydningsregler +maven.registry = Konfigurer denne registreringsdatabasen i din projekt <code>pom.xml</code> fil: +npm.install2 = eller føj det til filen package.json: +nuget.dependency.framework = Mål Framework +npm.registry = Konfigurer denne registreringsdatabase i din projekt-<code>.npmrc</code>-fil: +nuget.install = For at installere pakken ved hjælp af NuGet skal du køre følgende kommando: +npm.dependencies = Afhængigheder +settings.link.description = Hvis du forbinder en pakke med et depot, vises pakken i depotets pakkeliste. +owner.settings.cargo.initialize.error = Kunne ikke initialisere Cargo index: %v +owner.settings.cleanuprules.keep.title = Versioner, der matcher disse regler, bevares, selvom de matcher en fjernelsesregel nedenfor. +generic.download = Download pakken fra kommandolinjen: +go.install = Installer pakken fra kommandolinjen: +container.layers = Billedlag +container.labels.key = Nøgle +container.labels.value = Værdi +debian.registry.info = Vælg $distribution og $component fra listen nedenfor. +maven.download = For at downloade afhængigheden skal du køre via kommandolinjen: +rpm.distros.suse = på SUSE-baserede distributioner +rpm.distros.redhat = på RedHat-baserede distributioner +owner.settings.cleanuprules.pattern_full_match = Anvend mønster på det fulde pakkenavn +details.author = Forfatter +details.repository_site = Depots hjemmeside +details.documentation_site = Dokumentations hjemmeside +details.license = Licens +assets = Aktiver +versions = Versioner +details.project_site = Projektets hjemmeside +versions.view_all = Se alle +dependency.id = ID +alpine.repository.branches = Grene +arch.version.optdepends = Valgfri afhænger +owner.settings.cleanuprules.remove.title = Versioner, der matcher disse regler, fjernes, medmindre en regel ovenfor siger, at de skal beholdes. +owner.settings.cleanuprules.remove.days = Fjern versioner ældre end +owner.settings.cleanuprules.remove.pattern = Fjern matchende versioner +owner.settings.cleanuprules.success.update = Oprydningsreglen er blevet opdateret. +owner.settings.cleanuprules.success.delete = Oprydningsregel er blevet slettet. +arch.version.backup = Backup +chef.registry = Konfigurer dette register i din <code>~/.chef/config.rb</code> fil: +npm.install = For at installere pakken ved hjælp af npm skal du køre følgende kommando: +owner.settings.cargo.rebuild.no_index = Kan ikke genopbygge, intet indeks er initialiseret. +maven.install2 = Kør via kommandolinje: +keywords = Keywords +arch.pacman.helper.gpg = Tilføj tillidscertifikat til pacman: +arch.pacman.repo.multi = %s har den samme version i forskellige distributioner. +arch.pacman.conf = Tilføj server med relateret distribution og arkitektur til <code>/etc/pacman.conf</code>: +arch.version.groups = Gruppe +arch.version.depends = Afhænger +arch.version.conflicts = Konflikter [actions] runners.description = Beskrivelse @@ -3155,6 +3591,94 @@ runners.task_list.repository = Depot runners.status.active = Aktiv runners.status.offline = Offline runners.version = Version +runners.owner_type = Type +runners = Runners +unit.desc = Administrer integrerede CI/CD-pipelines med Forgejo Actions. +status.unknown = Ukendt +runners.runner_title = Runner +runners.task_list = Seneste opgaver på denne løber +runners.task_list.run = Kør +runners.task_list.commit = Commit +runners.edit_runner = Rediger Runner +runs.commit = Commit +runs.scheduled = Planlagt +runs.pushed_by = pushed af +status.running = Kører +status.waiting = Venter +runners.new_notice = Hvordan man starter en runner +status.success = Succes +variables.not_found = Variablen kunne ikke findes. +runs.workflow = Arbejdsgang +runners.last_online = Sidste online tid +runners.task_list.done_at = Udført kl +runners.update_runner = Opdater ændringer +runners.update_runner_success = Runner blev opdateret +runners.update_runner_failed = Løberen kunne ikke opdateres +runners.delete_runner_failed = Runner kunne ikke slettes +runners.delete_runner_header = Bekræft for at slette denne runner +runners.status.idle = Tomgang +runs.no_job_without_needs = Arbejdsgangen skal indeholde mindst ét job uden afhængigheder. +runs.no_job = Arbejdsgangen skal indeholde mindst ét job +runs.no_results = Ingen resultater matchede. +runs.no_workflows = Der er endnu ingen arbejdsgange. +workflow.enable = Aktiver arbejdsgang +workflow.enable_success = Arbejdsgangen "%s" blev aktiveret. +variables.none = Der er ingen variabler endnu. +variables.edit = Rediger variabel +variables.deletion.success = Variablen er blevet fjernet. +variables.creation.failed = Kunne ikke tilføje variabel. +runners.delete_runner_notice = Hvis en opgave kører på denne runner, vil den blive afsluttet og markeret som mislykket. Det kan bryde bygningens arbejdsgang. +runs.no_workflows.help_write_access = Ved du ikke, hvordan du starter med Forgejo Actions? Tjek <a target="_blank" rel="noopener noreferrer" href="%s">hurtigstarten i brugerdokumentationen</a> for at skrive dit første workflow, og <a target="_blank" rel="noopener noreferrer" href="%s">opsæt en Forgejo-løber</a> til at udføre dine opgaver. +runners.delete_runner_success = Runner blev slettet +variables.update.success = Variablen er blevet redigeret. +status.cancelled = Annulleret +status.skipped = Oversprunget +status.blocked = Blokeret +workflow.disable_success = Arbejdsgangen "%s" blev deaktiveret. +workflow.disable = Deaktiver arbejdsgang +workflow.dispatch.use_from = Brug arbejdsgangen fra +workflow.disabled = Arbejdsgangen er deaktiveret. +workflow.dispatch.trigger_found = Denne arbejdsgang har en <c>workflow_dispatch</c>-hændelsestrigger. +workflow.dispatch.warn_input_limit = Viser kun de første %d input. +workflow.dispatch.success = Kørsel af arbejdsgang blev anmodet om. +workflow.dispatch.input_required = Kræv værdi for input "%s". +workflow.dispatch.invalid_input_type = Ugyldig inputtype "%s". +variables.creation = Tilføj variabel +need_approval_desc = Har brug for godkendelse for at køre arbejdsgange for fork pull-anmodning. +runners.delete_runner = Slet denne runner +runners.status.unspecified = Ukendt +runners.reset_registration_token_success = Runner registreringstoken blev nulstillet +runs.all_workflows = Alle arbejdsgange +runners.reset_registration_token = Nulstil registreringstoken +runs.empty_commit_message = (tom commit besked) +runs.expire_log_message = Logfiler er blevet renset, fordi de var for gamle. +variables = Variabler +runs.actor = Aktør +actions = Handlinger +runners.status = Status +runners.task_list.status = Status +runners.id = ID +runners.task_list.no_tasks = Der er ingen opgave endnu. +runs.status = Status +runs.actors_no_select = Alle aktører +runs.status_no_select = Alle status +runners.none = Ingen runners tilgængelige +variables.management = Administrer variabler +variables.creation.success = Variablen "%s" er blevet tilføjet. +variables.update.failed = Variablen kunne ikke redigeres. +runs.no_workflows.help_no_write_access = For at lære om Forgejo Actions, se <a target="_blank" rel="noopener noreferrer" href="%s">dokumentationen</a>. +variables.deletion = Fjern variabel +variables.id_not_exist = Variabel med ID %d findes ikke. +variables.deletion.description = Fjernelse af en variabel er permanent og kan ikke fortrydes. Vil du fortsætte? +variables.description = Variabler vil blive videregivet til visse handlinger og kan ikke læses på anden vis. +variables.deletion.failed = Variablen kunne ikke fjernes. +runs.no_matching_online_runner_helper = Ingen matchende online-runner med etiket: %s +runners.runner_manage_panel = Administrer runners +runners.new = Opret ny runner +workflow.dispatch.run = Kør arbejdsgang +runs.invalid_workflow_helper = Workflow-konfigurationsfilen er ugyldig. Tjek venligst din konfigurationsfil: %s +status.failure = Fiasko +runs.no_runs = Workflowet har ingen kørsler endnu. [tool] 1d = 1 dag @@ -3174,6 +3698,7 @@ weeks = %d uger 1s = 1 sekund 1m = 1 minut days = %d dage +now = nu [repo.permissions] actions.read = <b>Læs:</b> Se integrerede CI/CD-pipelines og deres logfiler. @@ -3207,10 +3732,67 @@ component_loading_failed = Kunne ikke indlæse %s [secrets] creation.name_placeholder = uafhængig af store og små bogstaver, alfanumeriske tegn eller understregninger, kan ikke starte med GITEA_ eller GITHUB_ creation.value_placeholder = Indtast ethvert indhold. Mellemrum i starten og slutningen vil blive udeladt. +creation = Tilføj hemmelighed +description = Hemmeligheder vil blive videregivet til visse handlinger og kan ikke læses på anden vis. +secrets = Hemmeligheder +creation.success = Hemmeligheden "%s" er blevet tilføjet. +deletion = Fjern hemmelighed +deletion.success = Hemmeligheden er blevet fjernet. +deletion.failed = Hemmeligheden kunne ikke fjernes. +management = Håndter hemmeligheder +deletion.description = Fjernelse af en hemmelighed er permanent og kan ikke fortrydes. Vil du fortsætte? +none = Der er ingen hemmeligheder endnu. +creation.failed = Kunne ikke tilføje hemmelighed. [dropzone] invalid_input_type = Filer af denne type må ikke uploades. remove_file = Fjern fil +default_message = Slip filer eller klik her for at uploade. +file_too_big = Filstørrelsen ({{filesize}} MB) overstiger den maksimale størrelse på ({{maxFilesize}} MB). [gpg] -default_key = Underskrevet med standardnøglen \ No newline at end of file +default_key = Underskrevet med standardnøglen +error.generate_hash = Kunne ikke generere hash af commit +error.no_committer_account = Ingen konto knyttet til committers e-mailadresse +error.probable_bad_default_signature = ADVARSEL! Selvom standardnøglen har dette ID, bekræfter den ikke denne commit! Denne commit er MISTÆNLIG. +error.no_gpg_keys_found = Ingen kendt nøgle fundet for denne signatur i databasen +error.not_signed_commit = Ikke en underskrevet commit +error.failed_retrieval_gpg_keys = Kunne ikke hente nogen nøgle knyttet til committerens konto +error.probable_bad_signature = ADVARSEL! Selvom der er en nøgle med dette ID i databasen, bekræfter den ikke denne commit! Denne commit er MISTÆNLIG. +error.extract_sign = Kunne ikke udtrække signatur + +[munits.data] +kib = KiB +b = B +pib = PiB +mib = MiB +tib = TiB +eib = EiB +gib = GiB + +[units] +error.no_unit_allowed_repo = Du har ikke tilladelse til at få adgang til nogen sektion af dette depot. +unit = Enhed +error.unit_not_allowed = Du har ikke tilladelse til at få adgang til denne depotsektion. + +[projects] +type-3.display_name = Organisationsprojekt +type-2.display_name = Depotprojekt +deleted.display_name = Slettet projekt +type-1.display_name = Individuelt projekt + +[markup] +filepreview.line = Linje %[1]d i %[2]s +filepreview.lines = Linjer %[1]d til %[2]d i %[3]s +filepreview.truncated = Forhåndsvisningen er blevet afkortet + +[git.filemode] +symbolic_link = Symbolsk link +submodule = Submodule +changed_filemode = %[1]s → %[2]s +directory = Directory +normal_file = Normal fil +executable_file = Eksekverbar fil + +[translation_meta] +test = (OK) DA was translated by Tacaly \ No newline at end of file diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index f4eca32202..77c011677b 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -110,7 +110,7 @@ preview=Vorschau loading=Laden … error=Fehler -error404=Die Seite, die du versuchst aufzurufen, <strong>existiert nicht</strong> oder <strong>du bist nicht berechtigt</strong>, diese anzusehen. +error404=Die Seite, die du versuchst aufzurufen, <strong>existiert nicht</strong>, <strong>wurde entfernt</strong> oder <strong>du bist nicht berechtigt</strong>, diese anzusehen. go_back=Zurück never=Niemals @@ -1073,7 +1073,7 @@ quota.applies_to_user = Die folgenden Quota-Regeln greifen für deinen Account quota.sizes.assets.attachments.issues = Issue-Anhänge quota.rule.exceeded.helper = Die Gesamtgröße der Objekte für diese Regel hat die Quota überschritten. storage_overview = Speicherübersicht -quota = Quota +quota = Kontingent quota.sizes.assets.attachments.releases = Release-Anhänge quota.applies_to_org = Die folgenden Quota-Regeln greifen für diese Organisation quota.rule.exceeded = Überschritten @@ -2911,6 +2911,9 @@ issues.reopen.blocked_by_user = Du kannst dieses Issue nicht wieder eröffnen, w pulls.comment.blocked_by_user = Du kannst diesen Pull-Request nicht kommentieren, da du vom Repository-Besitzer oder Ersteller des Pull-Requests blockiert wurdest. issues.filter_no_results = Keine Ergebnisse issues.filter_no_results_placeholder = Versuche, deine Suchfilter anzupassen. +migrate.repo_desc_helper = Leer lassen, um vorhandene Beschreibung zu importieren +archive.nocomment = Kommentieren ist nicht möglich, da das Repository archiviert ist. +comment.blocked_by_user = Kommentieren ist nicht möglich, da du vom Repository-Besitzer oder vom Autor blockiert wurdest. [graphs] component_loading_failed = Konnte %s nicht laden diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index ead54967c0..c20a2f3172 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -2460,9 +2460,9 @@ settings.protect_branch_name_pattern=Μοτίβο προστατευμένου settings.protect_branch_name_pattern_desc=Μοτίβα ονόματος προστατευμένων κλάδων. Συμβολευτείτε <a href="%s">την τεκμηρίωση</a> για την σύνταξη ενός μοτίβου. Παραδείγματα: main, release/** settings.protect_patterns=Μοτίβα settings.protect_protected_file_patterns=Μοτίβα προστατευμένων αρχείων (διαχωρισμός με semicolon «;» και ΟΧΙ το ελληνικό ερωτηματικό): -settings.protect_protected_file_patterns_desc=Τα προστατευόμενα αρχεία δεν επιτρέπεται να αλλάξουν άμεσα, ακόμη και αν ο χρήστης έχει δικαιώματα να προσθέσει, να επεξεργαστεί ή να διαγράψει αρχεία σε αυτόν τον κλάδο. Επιπλέων μοτίβα μπορούν να διαχωριστούν με semicolon («;») (ΟΧΙ ερωτηματικό). Για να συντάξετε μοτίβα, συμβουλευτείται την τεκμηρίωση <a href='%s'>%s</a>. Παράδειγμα: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. +settings.protect_protected_file_patterns_desc=Τα προστατευόμενα αρχεία δεν επιτρέπεται να αλλάξουν άμεσα, ακόμη και αν ο χρήστης έχει δικαιώματα να προσθέσει, να επεξεργαστεί ή να διαγράψει αρχεία σε αυτόν τον κλάδο. Επιπλέων μοτίβα μπορούν να διαχωριστούν με semicolon («;») (ΟΧΙ ερωτηματικό). Για να συντάξετε μοτίβα, συμβουλευτείται την τεκμηρίωση <a href="%[1]s">%[2]s</a>. Παράδειγμα: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.protect_unprotected_file_patterns=Μοτίβα μη προστατευμένων αρχείων (διαχωρισμένα με semicolon «;» και ΟΧΙ το ελληνικό ερωτηματικό): -settings.protect_unprotected_file_patterns_desc=Μη προστατευμένα αρχεία που επιτρέπεται να αλλάξουν απευθείας εάν ο χρήστης έχει πρόσβαση εγγραφής, παρακάμπτοντας τον περιορισμό ώθησης. Επιπλέων μοτίβα μπορούν να διαχωριστούν με ερωτηματικό (';'). Δείτε την τεκμηρίωση <a href='%[1]s'>%[2]s</a> για τη σύνταξη του μοτίβου. Πχ: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. +settings.protect_unprotected_file_patterns_desc=Μη προστατευμένα αρχεία που επιτρέπεται να αλλάξουν απευθείας εάν ο χρήστης έχει πρόσβαση εγγραφής, παρακάμπτοντας τον περιορισμό ώθησης. Επιπλέων μοτίβα μπορούν να διαχωριστούν με ερωτηματικό (';'). Δείτε την τεκμηρίωση <a href="%[1]s">%[2]s</a> για τη σύνταξη του μοτίβου. Πχ: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.add_protected_branch=Ενεργοποίηση προστασίας settings.delete_protected_branch=Απενεργοποίηση προστασίας settings.update_protect_branch_success=Η προστασία κλάδου για τον κανόνα «%s» ενημερώθηκε. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 2a8c789385..d97f90ec9c 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1217,8 +1217,7 @@ template.invalid = Must select a template repository archive.title = This repository is archived. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments. archive.title_date = This repository has been archived on %s. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments. -archive.issue.nocomment = This repository is archived. You cannot comment on issues. -archive.pull.nocomment = This repository is archived. You cannot comment on pull requests. +archive.nocomment = Commenting is not possible because the repository is archived. archive.pull.noreview = This repository is archived. You cannot review pull requests. form.reach_limit_of_creation_1 = The owner has already reached the limit of %d repository. @@ -1245,6 +1244,7 @@ migrate_items_pullrequests = Pull requests migrate_items_merge_requests = Merge requests migrate_items_releases = Releases migrate_repo = Migrate repository +migrate.repo_desc_helper = Leave empty to import existing description migrate.clone_address = Migrate / Clone from URL migrate.clone_address_desc = The HTTP(S) or Git "clone" URL of an existing repository migrate.github_token_desc = You can put one or more tokens with comma separated here to make migrating faster because of GitHub API rate limit. WARN: Abusing this feature may violate the service provider's policy and lead to account blocking. @@ -1436,7 +1436,7 @@ editor.file_is_a_symlink = `"%s" is a symbolic link. Symbolic links cannot be ed editor.filename_is_a_directory = Filename "%s" is already used as a directory name in this repository. editor.file_editing_no_longer_exists = The file being edited, "%s", no longer exists in this repository. editor.file_deleting_no_longer_exists = The file being deleted, "%s", no longer exists in this repository. -editor.file_changed_while_editing = The file contents have changed since you started editing. <a target="_blank" rel="noopener noreferrer" href="%s">Click here</a> to see them or <strong>Commit changes again</strong> to overwrite them. +editor.file_changed_while_editing = The file contents have changed since you opened the file. <a target="_blank" rel="noopener noreferrer" href="%s">Click here</a> to see them or <strong>Commit changes again</strong> to overwrite them. editor.file_already_exists = A file named "%s" already exists in this repository. editor.commit_id_not_matching = The file was changed while you were editing it. Commit to a new branch and then merge. editor.push_out_of_date = The push appears to be out of date. @@ -1881,7 +1881,7 @@ issues.content_history.delete_from_history_confirm = Delete from history? issues.content_history.options = Options issues.reference_link = Reference: %s issues.blocked_by_user = You cannot create issues in this repository because you are blocked by the repository owner. -issues.comment.blocked_by_user = You cannot comment on this issue because you are blocked by the repository owner or the poster of the issue. +comment.blocked_by_user = Commenting is not possible because you are blocked by the repository owner or the author. issues.reopen.blocked_by_user = You cannot reopen this issue because you are blocked by the repository owner or the poster of this issue. issues.summary_card_alt = Summary card of an issue titled "%s" in repository %s @@ -1966,7 +1966,6 @@ pulls.waiting_count_1 = %d waiting review pulls.waiting_count_n = %d waiting reviews pulls.wrong_commit_id = commit id must be a commit id on the target branch pulls.blocked_by_user = You cannot create a pull request on this repository because you are blocked by the repository owner. -pulls.comment.blocked_by_user = You cannot comment on this pull request because you are blocked by the repository owner or the poster of the pull request. pulls.no_merge_desc = This pull request cannot be merged because all repository merge options are disabled. pulls.no_merge_helper = Enable merge options in the repository settings or merge the pull request manually. diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 797f4ac5bb..57e3369d67 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -209,6 +209,7 @@ table_modal.placeholder.content = Contenido link_modal.header = Añadir enlace link_modal.description = Descripción link_modal.paste_reminder = Pista: Con una URL en tu portapapeles, puedes pegar directamente en el editor para crear un enlace. +link_modal.url = URL [filter] string.asc=A - Z @@ -1062,6 +1063,18 @@ pronouns_custom_label = Pronombres personalizados user_block_yourself = No puede bloquearse a sí mismo. quota = Cuota quota.sizes.assets.attachments.all = Archivos adjuntos +quota.sizes.repos.private = Repositorios privados +quota.sizes.assets.packages.all = Paquetes +regenerate_token = Regenerar +quota.sizes.all = Todo +quota.sizes.repos.all = Repositorios +quota.sizes.repos.public = Repositorios públicos +quota.sizes.git.all = Contenido de Git +quota.sizes.wiki = Wiki +keep_pronouns_private = Mostrar pronombres solo a personas autenticadas +storage_overview = Resumen del almacenamiento +quota.sizes.assets.artifacts = Artefactos +quota.sizes.assets.attachments.releases = Archivos adjuntos del lanzamiento [repo] owner=Propietario @@ -1141,7 +1154,7 @@ forks=Forks reactions_more=y %d más unit_disabled=El administrador del sitio ha deshabilitado esta sección del repositorio. language_other=Otros -adopt_search=Introduzca el nombre de usuario para buscar repositorios no adoptados... (déjelo en blanco para encontrar todos) +adopt_search=Introduzca el nombre de usuario para buscar repositorios no adoptados… (déjelo en blanco para encontrar todos) adopt_preexisting_label=Adoptar archivos adopt_preexisting=Adoptar archivos preexistentes adopt_preexisting_content=Crear repositorio desde %s @@ -2348,7 +2361,7 @@ settings.event_pull_request_comment_desc=Comentario de pull request creado, edit settings.event_pull_request_review=Revisiones settings.event_pull_request_review_desc=Pull request aprobada, rechazada o comentarios de revisión añadidos. settings.event_pull_request_sync=Sincronizado -settings.event_pull_request_sync_desc=Pull request sincronizado. +settings.event_pull_request_sync_desc=La rama se actualizó automáticamente con la rama de destino. settings.event_pull_request_review_request=Solicitudes de revisión settings.event_pull_request_review_request_desc=La solicitud de Pull Request ha sido eliminada. settings.event_pull_request_approvals=Aprobaciones de pull request @@ -2450,9 +2463,9 @@ settings.require_signed_commits_desc=Rechazar push en esta rama si los commits n settings.protect_branch_name_pattern=Patrón de nombre de ramas protegidas settings.protect_patterns=Patrones settings.protect_protected_file_patterns=Patrones de archivos protegidos (separados con punto y coma ';') -settings.protect_protected_file_patterns_desc=No está permitido cambiar archivos directamente incluso si el usuario tiene permiso para agregar, editar o borrar archivos en esta rama. Múltiples patrones pueden separarse usando punto y coma (';'). Refvisa la documentación de <a href='%s'>%s</a> para la sintaxis de patrones. Ejemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. +settings.protect_protected_file_patterns_desc=No está permitido cambiar archivos directamente incluso si el usuario tiene permiso para agregar, editar o borrar archivos en esta rama. Múltiples patrones pueden separarse usando punto y coma (';'). Refvisa la documentación de <a href="%[1]s">%[2]s</a> para la sintaxis de patrones. Ejemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.protect_unprotected_file_patterns=Patrones de archivos sin protección (separados con punto y coma ";") -settings.protect_unprotected_file_patterns_desc=Los archivos sin protección se pueden cambiar directamente si el usuario tiene acceso de escritura, evitando la restricción push. Múltiples patrones pueden separarse usando punto y coma (';'). Vea la documentación de <a href='%[1]s'>%[2]s</a> para la sintaxis de patrones. Ejemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. +settings.protect_unprotected_file_patterns_desc=Los archivos sin protección se pueden cambiar directamente si el usuario tiene acceso de escritura, evitando la restricción push. Múltiples patrones pueden separarse usando punto y coma (';'). Vea la documentación de <a href="%[1]s">%[2]s</a> para la sintaxis de patrones. Ejemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.add_protected_branch=Activar protección settings.delete_protected_branch=Desactivar protección settings.update_protect_branch_success=Se ha actualizado la protección de la rama para la regla "%s". @@ -2542,10 +2555,10 @@ diff.parent=padre diff.commit=commit diff.git-notes=Notas diff.data_not_available=El contenido del diff no está disponible -diff.options_button=Opciones de diff +diff.options_button=Opciones de diferencias diff.show_diff_stats=Mostrar estadísticas diff.download_patch=Descargar archivo de parche -diff.download_diff=Descargar archivo de diff +diff.download_diff=Descargar archivo de diferencias diff.show_split_view=Dividir vista diff.show_unified_view=Vista unificada diff.whitespace_button=Espacio blanco @@ -2848,6 +2861,7 @@ pulls.cmd_instruction_checkout_title = Cambiar rama release.add_external_asset = Añadir un recurso externo settings.enforce_on_admins_desc = Los administradores del repositorio no pueden saltarse esta regla. pulls.editable = Editable +issues.filter_no_results = No hay resultados [graphs] component_loading = Cargando %s... @@ -3152,7 +3166,7 @@ emails.primary=Principal emails.activated=Activado emails.filter_sort.email=Correo electrónico emails.filter_sort.email_reverse=Email (invertir) -emails.filter_sort.name=Nombre de Usuario +emails.filter_sort.name=Nombre de usuario emails.filter_sort.name_reverse=Nombre de usuario (invertir) emails.updated=Email actualizado emails.not_updated=Error al actualizar la dirección de correo electrónico solicitada: %v @@ -3808,11 +3822,11 @@ unit.desc=Gestione procesos CI/CD integrados con Forgejo Actions. status.unknown=Desconocido status.waiting=Esperando -status.running=Ejecutando +status.running=Corriendo status.success=Éxito status.failure=Fallo status.cancelled=Cancelado -status.skipped=Omitido +status.skipped=Saltado status.blocked=Bloqueado runners=Nodos @@ -3864,8 +3878,8 @@ runs.status_no_select=Todo el estado runs.no_results=No hay resultados coincidentes. runs.no_runs=El flujo de trabajo no tiene ejecuciones todavía. -workflow.disable=Desactivar fllujo de trabajo -workflow.disable_success=Flujo de trabajo '%s' deshabilitado correctamente. +workflow.disable=Desactivar flujo de trabajo +workflow.disable_success=Flujo de trabajo "%s" desactivado exitosamente. workflow.enable=Activar flujo de trabajo workflow.enable_success=Flujo de trabajo '%s' habilitado con éxito. workflow.disabled=El flujo de trabajo está deshabilitado. @@ -3889,10 +3903,11 @@ variables.update.success=La variable ha sido editada. variables.id_not_exist = Variable con id %d no existe. runs.empty_commit_message = (mensaje de commit vacío) runs.expire_log_message = Los registros han sido eliminados porque eran demasiado antiguos. +runs.workflow = Flujo de trabajo [projects] type-1.display_name=Proyecto individual -type-2.display_name=Proyecto repositorio +type-2.display_name=Proyecto de repositorio type-3.display_name=Proyecto de organización deleted.display_name = Proyecto borrado @@ -3907,24 +3922,24 @@ submodule=Submódulo [search] -search = Buscar... +search = Buscar… type_tooltip = Tipo de búsqueda -project_kind = Buscar proyectos... -branch_kind = Buscar ramas... -commit_kind = Buscar confirmaciones... -repo_kind = Buscar repositorios... -user_kind = Buscar usuarios... -org_kind = Buscar organizaciones... -team_kind = Buscar equipos... -code_kind = Buscar código... -package_kind = Buscar paquetes... +project_kind = Buscar proyectos… +branch_kind = Buscar ramas… +commit_kind = Buscar confirmaciones… +repo_kind = Buscar repositorios… +user_kind = Buscar usuarios… +org_kind = Buscar organizaciones… +team_kind = Buscar equipos… +code_kind = Buscar código… +package_kind = Buscar paquetes… code_search_unavailable = La búsqueda de código no está disponible actualmente. Por favor contacta al administrador del sitio. code_search_by_git_grep = Los resultados actuales de la búsqueda de código son proporcionados por "git grep". Es posible que se obtengan mejores resultados si el administrador del sitio habilita el indexador de código. no_results = No se encontraron resultados coincidentes. keyword_search_unavailable = La búsqueda por palabra clave no está disponible actualmente. Por favor contacta al administrador del sitio. fuzzy_tooltip = Incluir resultados que también coincidan estrechamente con el término de búsqueda milestone_kind = Buscar hitos… -pull_kind = Buscar pulls… +pull_kind = Buscar extracciones… union = Unión union_tooltip = Incluir resultados correspondientes a cualquiera de las palabras clave separadas por espacios en blanco exact = Exacto @@ -3938,6 +3953,7 @@ regexp = Expresión Regular [markup] filepreview.lines = Líneas %[1]d a %[2]d en %[3]s filepreview.line = Línea %[1]d en %[2]s +filepreview.truncated = La vista previa se ha truncado [repo.permissions] pulls.read = <b>Lectura:</b> Leer y crear pull requests. @@ -3956,3 +3972,13 @@ ext_wiki = Acceder al enlace de la wiki externa. Los permisos se gestionan de fo code.write = <b>Escritura:</b> Push al repositorio, crear ramas y tags. issues.write = <b>Escritura:</b> Cerrar incidencias y gestion de metadatos como etiquetas, hitos, asignaciones, fechas de vencimiento y dependencias. packages.write = <b>Escritura:</b> Publicar y eliminar paquetes asignados al repositorio. + + +[munits.data] +eib = EiB +gib = GiB +pib = PiB +kib = KiB +tib = TiB +mib = MiB +b = B \ No newline at end of file diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index ec7a6405e1..efa93f5d2c 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -2767,6 +2767,9 @@ issues.reopen.blocked_by_user = Hindi mo maaaring buksan muli ang isyung ito dah pulls.comment.blocked_by_user = Hindi ka maaaring magkomento sa hiling sa paghila na ito dahil hinarang ka ng may-ari ng repositoryo o ng may-akda ng hiling sa paghila. issues.filter_no_results = Walang mga resulta issues.filter_no_results_placeholder = Subukang ayusin ang iyong mga filter sa paghahanap. +migrate.repo_desc_helper = Iwanang walang laman para i-import ang umiiral na paglalarawan +archive.nocomment = Hindi posible ang pagkomento dahil naka-archive ang repositoryo. +comment.blocked_by_user = Hindi posible ang pagkomento dahil hinarang ka ng may-ari ng repositoryo o ng may-akda. [search] commit_kind = Maghanap ng mga commit… diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 1e78443b89..218c9af843 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -207,6 +207,8 @@ table_modal.label.rows = Lignes table_modal.label.columns = Colonnes link_modal.header = Ajouter un lien link_modal.url = Url +link_modal.description = Description +link_modal.paste_reminder = Indice : avec une URL dans votre clipboard, vous pouvez copier directement dans l'éditeur pour créer un lien. [filter] string.asc=A - Z @@ -3982,7 +3984,7 @@ search = Rechercher… type_tooltip = Type de recherche fuzzy = Approximatif code_search_by_git_grep = Les résultats de recherche dans le code sont fournis par "git grep". Les résultats pourraient être plus pertinents si l'administrateur du site active les indexeurs de code source. -runner_kind = Chercher les runners... +runner_kind = Chercher les runners… no_results = Aucun résultat n'a été trouvé. keyword_search_unavailable = La recherche par mot-clé n'est pas disponible actuellement. Veuillez contacter l'administrateur du site. fuzzy_tooltip = Inclure les résultats proches des termes recherchés @@ -3996,14 +3998,14 @@ code_kind = Chercher le code… code_search_unavailable = La recherche dans le code n'est pas disponible. Veuillez contacter l'administrateur du site. package_kind = Chercher les paquets… project_kind = Chercher les projets… -branch_kind = Chercher les branches... -commit_kind = Chercher les commits... +branch_kind = Chercher les branches… +commit_kind = Chercher les commits… exact = Exact exact_tooltip = Inclure uniquement les résultats qui correspondent exactement au terme recherché -issue_kind = Rechercher dans les tickets... +issue_kind = Rechercher dans les tickets… union = Union union_tooltip = Inclus les résultats contenant au moins un des mots clé séparés par des espaces -pull_kind = Rechercher dans les demande d'ajout... +pull_kind = Rechercher dans les demande d'ajout milestone_kind = Recherche dans les jalons... regexp_tooltip = Interpréter le terme de recherche comme une expression régulière regexp = RegExp diff --git a/options/locale/locale_hi.ini b/options/locale/locale_hi.ini index 8baaad5224..2f75c8d13e 100644 --- a/options/locale/locale_hi.ini +++ b/options/locale/locale_hi.ini @@ -21,4 +21,22 @@ signed_in_as = साइन इन किया गया है link_account = खाता लिंक करें notifications = सूचनाएँ tracked_time_summary = समस्याओं की सूची के फ़िल्टर के आधार पर ट्रैक किए गए समय का सारांश -user_profile_and_more = प्रोफ़ाइल और सेटिंग्स… \ No newline at end of file +user_profile_and_more = प्रोफ़ाइल और सेटिंग्स… +enable_javascript = इस पन्ने के लिये जावा चाहिए +toc = विषय सूची +licenses = लाइसेंस +more_items = और वस्तुएं +username = यूजरनाम +email = ईमेल पता +password = पासवर्ड +re_type = पासवर्ड पक्का करें +captcha = कैप्चा +twofa = टू-फैक्टर ऑथेंटिकेशन +twofa_scratch = टू-फैक्टर स्क्रैच कोड +passcode = पास कोड +repository = भंडार +organization = संगठन +mirror = छवि +settings = सेटिंग्स +your_settings = आपकी सेटिंग्स +return_to_forgejo = फ़ोर्जेगो पे वापस जाएं \ No newline at end of file diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index ace0decdbb..c4083f0ce8 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -1418,7 +1418,7 @@ issues.new.assignees=Assegnatari issues.new.clear_assignees=Cancella assegnatari issues.new.no_assignees=Nessun assegnatario issues.new.no_reviewers=Nessun revisore -issues.choose.get_started=Cominciare +issues.choose.get_started=Comincia issues.choose.open_external_link=Apri issues.choose.blank=Default issues.choose.blank_about=Crea una segnalazione dal modello predefinito. diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 67e96cdc0c..d4d7024f5d 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -2448,9 +2448,9 @@ settings.protect_branch_name_pattern=保護ブランチ名のパターン settings.protect_branch_name_pattern_desc=保護ブランチ名のパターン。書き方については <a href="%s">ドキュメント</a> を参照してください。例: main, release/** settings.protect_patterns=パターン settings.protect_protected_file_patterns=保護されるファイルのパターン (セミコロン';'で区切る): -settings.protect_protected_file_patterns_desc=保護されたファイルは、このブランチにファイルを追加・編集・削除する権限を持つユーザーであっても、直接変更することができなくなります。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については <a href='%s'>%s</a> を参照してください。 例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code> +settings.protect_protected_file_patterns_desc=保護されたファイルは、このブランチにファイルを追加・編集・削除する権限を持つユーザーであっても、直接変更することができなくなります。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については <a href="%[1]s">%[2]s</a> を参照してください。 例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code> settings.protect_unprotected_file_patterns=保護しないファイルのパターン (セミコロン';'で区切る): -settings.protect_unprotected_file_patterns_desc=保護しないファイルは、ユーザーに書き込み権限があればプッシュ制限をバイパスして直接変更できます。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については <a href='%[1]s'>%[2]s</a> を参照してください。 例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code> +settings.protect_unprotected_file_patterns_desc=保護しないファイルは、ユーザーに書き込み権限があればプッシュ制限をバイパスして直接変更できます。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については <a href="%[1]s">%[2]s</a> を参照してください。 例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code> settings.add_protected_branch=保護を有効にする settings.delete_protected_branch=保護を無効にする settings.update_protect_branch_success=ルール "%s" に対するブランチ保護を更新しました。 diff --git a/options/locale/locale_ka.ini b/options/locale/locale_ka.ini new file mode 100644 index 0000000000..bebd021965 --- /dev/null +++ b/options/locale/locale_ka.ini @@ -0,0 +1,839 @@ + + + +[common] +register = რეგისტრაცია +pin = ამაგრება +copy = კოპირება +filter = ფილტრი +edit = ჩასწორება +home = საწყისი +dashboard = სამუშაო მაგიდა +explore = დათვალიერება +help = დახმარება +logo = ლოგო +sign_in_or = ან +sign_up = რეგისტრაცია +language = ენა +username = მომხმარებლის სახელი +version = ვერსია +page = გვერდი +template = ნიმუში +notifications = გაფრთხილებები +create_new = შექმნა… +licenses = ლიცენზიები +password = პაროლი +captcha = კაპჩა +passcode = საკვანძო კოდი +repository = რეპოზიტორია +organization = ორგანიზაცია +mirror = სარკე +settings = მორგება +your_profile = პროფილი +your_starred = ვარსკვლავიანი +your_settings = მორგება +all = ყველა +sources = წყაროები +mirrors = სარკეები +collaborative = საერთო +forks = ფორკები +activities = აქტივობები +issues = პრობლემები +milestones = მისაღწევი გეგმები +ok = დიახ +cancel = გაუქმება +retry = თავიდან ცდა +rerun = თავიდან გაშვება +save = შენახვა +add = დამატება +remove = წაშლა +view = ხედი +test = შემოწმება +enabled = ჩართულია +disabled = გამორთულია +locked = ჩაკეტილია +write = ჩაწერა +preview = მინიატურა +loading = ჩატვირთვა… +error = შეცდომა +never = არასდროს +unknown = უცნობი +unpin = ჩამოხსნა +artifacts = არტეფაქტები +concept_user_individual = ინდივიდუალური +concept_code_repository = რეპოზიტორია +concept_user_organization = ორგანიზაცია +name = სახელი +value = მნიშვნელობა +archived = დაარქივებული +filter.is_archived = დაარქივებული +filter.is_fork = ფორკები +filter.is_mirror = სარკეები +filter.is_template = ნიმუშები +filter.private = პირადი +copy_success = დაკოპირებულია! +concept_system_global = გლობალური +filter.public = საჯარო +sign_in = შესვლა +sign_out = გასვლა +link_account = ანგარიშის მიბმა +toggle_menu = მენიუს გადართვა +more_items = მეტი ელემენტი +email = ელფოსტის მისამართი +access_token = წვდომის ტოკენი +re_type = დაადასტურეთ პაროლი +twofa = 2FA +new_mirror = ახალი სარკე +new_project = ახალი პროექტი +new_project_column = ახალი სვეტი +admin_panel = საიტის ადმინისტრირება +new_repo.title = ახალი რეპოზიტორია +new_migrate.title = ახალი მიგრაცია +new_org.title = ახალი ორგანიზაცია +new_repo.link = ახალი რეპოზიტორია +new_migrate.link = ახალი მიგრაცია +new_org.link = ახალი ორგანიზაცია +pull_requests = შერწყმის მოთხოვნები +add_all = ყველას დამატება +remove_all = ყველას წაშლა +copy_url = ბმულის კოპირება +copy_hash = ჰეშის კოპირება +copy_path = ბილიკის კოპირება +copy_content = შემცველობის კოპირება +copy_error = კოპირება ჩავარდა + +[install] +path = ბილიკი +install = დაყენება +host = ჰოსტი +user = მომხმარებლის სახელი +password = პაროლი +db_schema = სქემა +ssl_mode = SSL +admin_password = პაროლი + +[search] +search = ძებნა… +fuzzy = გაურკვეველი +union = გაერთიანება +exact = ზუსტი +regexp = რეგგამოსი + +[aria] +footer = ქვედა კოლონტიტული +footer.links = ბმულები + +[heatmap] +contributions_one = შეწირულობა +contributions_few = შეწირულობები +less = ნაკლები +more = მეტი + +[editor] +table_modal.placeholder.header = ზედა კოლონტიტული +table_modal.placeholder.content = შემცველობა +table_modal.label.rows = მწკრივი +table_modal.label.columns = სვეტი +link_modal.url = Url +link_modal.description = აღწერა + +[home] +my_repos = რეპოზიტორიები +show_archived = დაარქივებული +my_orgs = ორგანიზაციები +show_private = პირადი + +[explore] +repos = რეპოზიტორიები +organizations = ორგანიზაციები +code = კოდი +users = მომხმარებლები + +[auth] +openid_connect_submit = მიერთება +verify = გადამოწმება + +[mail] +release.downloads = გადმოწერები: +repo.transfer.to_you = თქვენ +release.note = შენიშვნა: + +[modal] +yes = დიახ +no = არა +confirm = დადასტურება +cancel = გაუქმება +modify = განახლება + +[form] +Description = აღწერა +Pronouns = ნაცვალსახელები +Biography = ბიოგრაფია +Website = ვებგვერდი +Location = მდებარეობა +Content = შემცველობა +UserName = მომხმარებლის სახელი +Password = პაროლი + +[user] +repositories = რეპოზიტორიები +followers.title.one = მომყოლი +followers.title.few = მომყოლები +following.title.one = მიჰყვებით +following.title.few = მიჰყვებით +follow = მიყოლა +unfollow = მიყოლის გაუქმება +code = კოდი +projects = პროექტები +overview = გადახედვა +block = დაბლოკვა +unblock = განბლოკვა +user_bio = ბიოგრაფია + +[settings] +profile = პროფილი +account = ანგარიში +appearance = გარეგნობა +password = პაროლი +security = უსაფრთხოება +avatar = ავატარი +applications = აპები +orgs = ორგანიზაციები +repos = რეპოზიტორიები +organization = ორგანიზაციები +uid = UID +quota = კვოტა +location = მდებარეობა +pronouns = ნაცვალსახელები +continue = გაგრძელება +cancel = გაუქმება +language = ენა +ui = თემა +hints = მინიშნებები +comment_type_group_reference = მიმართვა +comment_type_group_milestone = მისაღწევი მიზანი +comment_type_group_assignee = მიმნიჭებელი +comment_type_group_title = სათაური +comment_type_group_branch = ბრენჩი +comment_type_group_deadline = პროექტის ვადა +comment_type_group_dependency = დამოკიდებულება +comment_type_group_project = პროექტი +privacy = კონფიდენციალობა +primary = ძირითადი +activated = გააქტიურებულია +delete_email = წაშლა +gpg_key_verify = გადამოწმება +gpg_token = ტოკენი +ssh_key_verify = გადამოწმება +ssh_token = ტოკენი +subkeys = ქვეგასაღებები +key_content = შემცველობა +principal_content = შემცველობა +delete_key = წაშლა +can_read_info = წაკითხვა +can_write_info = ჩაწერა +permission_read = წაკითხვა +permissions_list = წვდომები: +pronouns_unspecified = მითითებული არაა +regenerate_token = რეგენერაცია +website = ვებგვერდი +comment_type_group_label = ჭდე +delete_token = წაშლა +save_application = შენახვა +oauth2_application_edit = ჩასწორება +revoke_key = გაუქმება +visibility.public = საჯარო +visibility.limited = შეზღუდული +quota.rule.exceeded = გადაცილებულია +quota.rule.no_limit = შეუზღუდავი +quota.sizes.all = ყველა +quota.sizes.repos.all = რეპოზიტორიები +quota.sizes.assets.artifacts = არტეფაქტები +quota.sizes.assets.packages.all = პაკეტები +quota.sizes.wiki = ვიკი +visibility.private = პირადი +quota.sizes.assets.all = ობიექტები +webauthn_nickname = მეტსახელი +quota.sizes.assets.attachments.all = მიმაგრებული ფაილები + +[startpage] +lightweight = მსუბუქი +platform = პლატფორმათაშორისი + +[repo] +owner = მფლობელი +template = ნიმუში +visibility = ხილვადობა +repo_desc = აღწერა +repo_lang = ენა +issue_labels = ჭდეები +license = ლიცენზია +readme = README +default_branch_label = ნაგულისხმევი +mirror_prune = გასუფთავება +mirror_sync = სინქრონიზებულია +mirror_password_placeholder = (არ შეცვლილა) +mirror_password_blank_placeholder = (დაყენება გაუქმებულია) +watchers = მაყურებლები +stargazers = ვარსკვლავთმრიცხველები +forks = ფორკები +stars = ვარსკვლავები +language_other = სხვა +delete_preexisting_label = წაშლა +desc.public = საჯარო +desc.template = ნიმუში +desc.internal = შიდა +desc.sha256 = SHA256 +template.webhooks = ვებჰუკები +template.topics = თემები +template.avatar = ავატარი +need_auth = ავტორიზაცია +migrate_items_wiki = ვიკი +migrate_items_labels = ჭდეები +migrate_items_issues = პრობლემები +migrate_items_releases = რელიზები +star = ვარსკვლავი +unstar = ვარსკვლავის მოხსნა +fork = ფორკი +code = კოდი +branch = ბრენჩი +branches = ბრენჩები +tag = ჭდე +tags = ჭდეები +issues = პრობლემები +packages = პაკეტები +actions = ქმედებები +release = რელიზი +releases = რელიზები +labels = ჭდეები +org_labels_desc_manage = მართვა +commits = კომიტები +commit = კომიტი +file_raw = დაუმუშავებელი +file_history = ისტორია +escape_control_characters = დაეკრანება +unescape_control_characters = დაეკრანების მოხსნა +vendored = გარედან მოწოდებული +generated = დაგენერირებული +commit_graph.monochrome = მონო +commit_graph.color = ფერი +blame = საავტორო უფლებები +line = ხაზი +lines = ხაზები +from_comment = (კომენტარი) +editor.or = ან +editor.cancel_lower = გაუქმება +editor.add_tmpl.filename = ფაილის სახელი +editor.patching = პაჩები: +editor.cancel = გაუქმება +commits.commits = კომიტები +commits.author = ავტორი +commits.message = შეტყობინება +commits.date = თარიღი +commits.older = უფრო ძველი +commits.newer = უფრო ახალი +commit.revert = დაბრუნება +commitstatus.error = შეცდომა +commitstatus.failure = ჩავარდნა +commitstatus.pending = რიგშია +commitstatus.success = წარმატება +projects = პროექტები +projects.description_placeholder = აღწერა +projects.title = სათაური +projects.type.none = არცერთი +projects.column.edit_title = სახელი +projects.column.new_title = სახელი +projects.column.color = ფერი +projects.open = გახსნა +projects.close = დახურვა +issues.new.labels = ჭდეები +issues.new.projects = პროექტები +issues.new.milestone = მისაღწევი მიზანი +issues.new.assignees = მიმნიჭებლები +issues.choose.open_external_link = გახსნა +issues.choose.blank = ნაგულისხმევი +issues.new_label_desc_placeholder = აღწერა +issues.deleted_project = `(წაშლილია)` +issues.filter_label = ჭდე +issues.filter_milestone = მისაღწევი მიზანი +issues.filter_project = პროექტი +issues.filter_poster = ავტორი +issues.filter_type = ტიპი +issues.filter_sort = დალაგება +issues.filter_sort.relevance = შესაბამისობა +issues.filter_sort.latest = უახლესი +issues.filter_sort.oldest = უძველესი +issues.action_open = გახსნა +issues.action_close = დახურვა +issues.action_label = ჭდე +issues.action_milestone = მისაღწევი მიზანი +issues.action_assignee = მიმნიჭებელი +issues.action_check = ჩართვა/გამორთვა +issues.previous = წინა +issues.next = შემდეგი +issues.open_title = გახსნა +issues.closed_title = დახურული +issues.all_title = ყველა +issues.draft_title = მონახაზი +issues.context.delete = წაშლა +issues.create_comment = კომენტარი +issues.author = ავტორი +issues.role.owner = მფლობელი +issues.role.member = წევრი +issues.role.collaborator = თანამონაწილე +issues.role.contributor = მოხალისე +issues.edit = ჩასწორება +issues.cancel = გაუქმება +issues.label_title = სახელი +issues.label_description = აღწერა +issues.label_color = ფერი +issues.label_exclusive = ექსკლუზიური +issues.label_delete = წაშლა +issues.label.filter_sort.alphabetically = ანბანის მიხედვით +issues.unsubscribe = გამოწერის გაუქმება +issues.lock_confirm = ჩაკეტვა +issues.delete = წაშლა +issues.cancel_tracking = მოცილება +issues.add_time_cancel = გაუქმება +issues.add_time_hours = საათი +issues.add_time_minutes = წუთი +issues.force_push_compare = შედარება +issues.due_date_form = წწწწ-თთ-დდ +issues.due_date_form_edit = ჩასწორება +issues.due_date_form_remove = წაშლა +issues.due_date_overdue = გადაცილებული +issues.dependency.title = დამოკიდებულებები +issues.dependency.cancel = გაუქმება +issues.dependency.remove = წაშლა +issues.review.dismissed_label = მოცილებულია +issues.review.pending = რიგშია +issues.review.reviewers = გადამხედავები +issues.reference_issue.body = სხეული +issues.content_history.deleted = წაშლილია +issues.content_history.edited = ჩასწორებულია +issues.content_history.created = შეიქმნა +compare.compare_base = ბაზა +compare.compare_head = შედარება +pulls.has_viewed_file = ნანახია +pulls.tab_conversation = საუბარი +pulls.tab_commits = კომიტები +pulls.merged = შერწყმულია +pulls.status_checks_requested = აუცილებელია +pulls.status_checks_details = დეტალები +pulls.cmd_instruction_merge_title = შერწყმა +pulls.made_using_agit = AGit +pulls.editable = ჩასწორებადი +milestones.open = გახსნა +milestones.close = დახურვა +milestones.title = სათაური +milestones.desc = აღწერა +milestones.clear = გასუფთავება +milestones.cancel = გაუქმება +milestones.filter_sort.name = სახელი +wiki = ვიკი +wiki.page = გვერდი +wiki.new_page = გვერდი +wiki.edit_page_button = ჩასწორება +wiki.pages = გვერდები +activity.navbar.pulse = უახლესი აქტივობები +activity.navbar.contributors = მოხალისეები +activity.period.filter_label = პერიოდი: +activity.overview = გადახედვა +activity.opened_prs_label = შეთავაზებულია +activity.closed_issue_label = დახურულია +activity.new_issue_label = ღიაა +activity.unresolved_conv_label = გახსნა +activity.published_release_label = რელიზი +activity.published_prerelease_label = პრე-რელიზი +activity.published_tag_label = ჭდე +activity.git_stats_and_deletions = და +contributors.contribution_type.commits = კომიტები +contributors.contribution_type.additions = დამატებები +contributors.contribution_type.deletions = წაშლები +settings = მორგება +settings.options = რეპოზიტორია +settings.collaboration = თანამონაწილეები +settings.collaboration.admin = ადმინისტრატორი +settings.collaboration.write = ჩაწერა +settings.collaboration.read = წაკითხვა +settings.collaboration.owner = მფლობელი +settings.collaboration.undefined = აღუწერელი +settings.hooks = ვებჰუკები +settings.mirror_settings.direction.push = გაგზავნა +settings.mirror_settings.push_mirror.none_ssh = არცერთი +settings.units.units = ერთეულები +settings.units.overview = გადახედვა +settings.site = ვებგვერდი +settings.tracker_issue_style.alphanumeric = ალფარიცხვითი +settings.admin_indexer_unindexed = არადაინდექსებული +settings.trust_model.collaborator = თანამონაწილე +settings.trust_model.committer = გადამცემი +settings.teams = გუნდები +settings.webhook.headers = თავსართები +settings.webhook.body = სხეული +settings.secret = საიდუმლო +settings.slack_username = მომხმარებლის სახელი +settings.slack_color = ფერი +settings.event_create = შექმნა +settings.event_delete = წაშლა +settings.event_fork = ფორკი +settings.event_wiki = ვიკი +settings.event_release = რელიზი +settings.event_push = გაგზავნა +settings.event_repository = რეპოზიტორია +settings.webhook.response = გამოხმაურება +settings.event_issue_assign = მინიჭება +settings.event_issue_label = ჭდეები +settings.event_issue_comment = კომენტარები +settings.event_pull_request = ცვლილება +settings.event_pull_request_assign = მინიჭება +settings.event_pull_request_label = ჭდეები +settings.event_pull_request_milestone = მისაღწევი გეგმები +settings.event_pull_request_review = გადახედვები +settings.event_pull_request_sync = სინქრონიზებულია +settings.event_pull_request_enforcement = ფორსირება +settings.event_package = პაკეტი +settings.active = აქტიურია +settings.slack_token = ტოკენი +settings.slack_domain = დომენი +settings.slack_channel = არხი +settings.web_hook_name_gitea = Gitea +settings.web_hook_name_forgejo = Forgejo +settings.web_hook_name_gogs = Gogs +settings.web_hook_name_slack = Slack +settings.web_hook_name_discord = Discord +settings.web_hook_name_dingtalk = DingTalk +settings.web_hook_name_matrix = Matrix +settings.web_hook_name_feishu_only = Feishu +settings.web_hook_name_packagist = Packagist +settings.sourcehut_builds.secrets = საიდუმლოები +settings.title = სათაური +settings.branches = ბრენჩები +settings.protect_patterns = ნიმუშები +settings.tags = ჭდეები +settings.lfs_lock = ჩაკეტვა +settings.lfs_pointers.oid = OID +diff.parent = მშობელი +diff.commit = კომიტი +diff.git-notes = შენიშვნები +diff.bin = BIN +diff.file_after = შემდეგ +diff.file_image_width = სიგანე +diff.file_image_height = სიმაღლე +diff.file_byte_size = ზომა +diff.generated = გენერირებულია +diff.comment.reply = პასუხი +diff.review.comment = კომენტარი +diff.review.approve = დადასტურება +diff.protected = დაცულია +diff.image.swipe = გაუსვით +diff.image.overlay = ზემოდან დადება +release.releases = რელიზები +release.draft = მონახაზი +release.prerelease = პრე-რელიზი +release.stable = სტაბილური +release.compare = შედარება +release.target = სამიზნე +release.cancel = გაუქმება +release.downloads = გადმოწერები +branch.delete_head = წაშლა +branch.included = ჩასმულია +commit.cherry-pick = ზუსტი არჩევა +issues.deleted_milestone = `(წაშლილია)` +issues.content_history.options = მორგება +settings.mirror_settings.direction = მიმართულება +settings.tracker_issue_style.numeric = რიცხვითი +settings.delete_collaborator = წაშლა +release.tags = ჭდეები +unwatch = თვალყურის დევნების გაუქმება +tree = ხე +issues.context.edit = ჩასწორება +wiki.cancel = გაუქმება +settings.event_issue_milestone = მისაღწევი გეგმები +diff.file_before = მანამდე +commit.operations = ოპერაციები +issues.label_edit = ჩასწორება +issues.unlock_confirm = ჩაკეტვის გაუქმება +issues.dependency.blocks_short = ბლოკები +pulls.cmd_instruction_checkout_title = გამოთხოვა +pull.deleted_branch = (წაშლილია):%s +desc.private = პირადი +desc.archived = დაარქივებული +watch = თვალყურის დევნება +project = პროექტები +milestones = მისაღწევი გეგმები +file_permalink = მუდმივი ბმული +projects.template.desc = ნიმუში +issues.filter_assignee = მიმნიჭებელი +issues.reopen_issue = თავიდან გახსნა +issues.save = შენახვა +activity = აქტივობა +settings.mirror_settings.direction.pull = მიღება +settings.trust_model.collaboratorcommitter = თანამონაწილე+გადამცემი +settings.webhook.request = მოთხოვნა +settings.discord_username = მომხმარებლის სახელი +settings.event_issues = ცვლილება +settings.web_hook_name_telegram = Telegram +settings.protect_status_check_matched = დამთხვეული +settings.edit_protected_branch = ჩასწორება +settings.tags.protection.allowed = დაშვებულია +diff.whitespace_button = ჰარე +diff.vendored = გარედან შემოტანილია +release.edit = ჩასწორება +release.type_attachment = მიმაგრებული ფაილი +topic.done = დასრულება +migrate_items_milestones = მისაღწევი გეგმები +issues.subscribe = გამოწერა +issues.review.outdated = ვადაგადაცილებული +activity.merged_prs_label = შერწყმულია +settings.webhook.payload = შემცველობა +settings.event_pull_request_comment = კომენტარები +settings.deploy_key_content = შემცველობა +settings.lfs = LFS +settings.lfs_locks = დაბლოკვა + +[org] +members = წევრები +teams = გუნდები +code = კოდი +lower_members = წევრები +lower_repositories = რეპოზიტორიები +org_desc = აღწერა +team_unit_disabled = (გათიშულია) +settings = მორგება +settings.options = ორგანიზაცია +settings.website = ვებგვერდი +settings.permission = წვდომები +settings.visibility = ხილვადობა +settings.visibility.public = საჯარო +settings.visibility.limited_shortname = შეზღუდული +settings.visibility.private_shortname = პირადი +members.public = ხილული +members.owner = მფლობელი +members.member = წევრი +members.remove = წაშლა +members.leave = გასვლა +teams.join = შეერთება +teams.read_access = წაკითხვა +teams.write_access = ჩაწერა +teams.settings = მორგება +team_desc = აღწერა +members.private = დამალული +team_permission_desc = წვდომა +settings.location = მდებარეობა +teams.leave = გასვლა + +[admin] +dashboard = სამუშაო მაგიდა +organizations = ორგანიზაციები +repositories = რეპოზიტორიები +hooks = ვებჰუკები +integrations = ინტეგრაციები +config = მორგება +config_summary = შეჯამება +config_settings = მორგება +monitor = მონიტორინგი +first_page = პირველი +last_page = ბოლო +dashboard.statistic = შეჯამება +dashboard.operation_run = გაშვება +users.name = მომხმარებლის სახელი +users.activated = გააქტიურებულია +users.admin = ადმინი +users.restricted = შეზღუდული +users.reserved = დაცული +users.bot = ბოტი +users.remote = დაშორებული +users.2fa = 2FA +users.repos = რეპოები +users.created = შეიქმნა +users.edit = ჩასწორება +users.local = ლოკალური +users.list_status_filter.reset = ჩამოყრა +users.list_status_filter.is_active = აქტიურია +users.list_status_filter.is_admin = ადმინი +users.list_status_filter.is_restricted = შეზღუდული +emails.primary = ძირითადი +emails.activated = გააქტიურებულია +emails.filter_sort.email = ელფოსტა +emails.filter_sort.name = მომხმარებლის სახელი +orgs.name = სახელი +orgs.teams = გუნდები +repos.owner = მფლობელი +repos.name = სახელი +repos.private = პირადი +repos.issues = პრობლემები +repos.size = ზომა +packages.owner = მფლობელი +packages.name = სახელი +packages.version = ვერსია +packages.type = ტიპი +packages.repository = რეპოზიტორია +packages.size = ზომა +auths.name = სახელი +auths.type = ტიპი +auths.enabled = ჩართულია +auths.updated = განახლებულია +auths.domain = დომენი +auths.host = ჰოსტი +auths.port = პორტი +auths.oauth2_tenant = ტენანტი +auths.tips = რჩევები +config.ssh_enabled = ჩართულია +config.ssh_port = პორტი +config.lfs_enabled = ჩართულია +config.db_type = ტიპი +config.db_host = ჰოსტი +config.db_name = სახელი +config.db_user = მომხმარებლის სახელი +config.db_schema = სქემა +config.db_ssl_mode = SSL +config.db_path = ბილიკი +config.mailer_name = სახელი +config.mailer_protocol = პროტოკოლი +config.mailer_user = მომხმარებელი +config.mailer_use_dummy = სულელი +config.oauth_enabled = ჩართულია +config.disabled_logger = გამორთულია +monitor.stats = სტატისტიკა +monitor.name = სახელი +monitor.schedule = გეგმა +monitor.execute_times = შესრულებები +monitor.stacktrace = სტეკის დატრეისება +monitor.desc = აღწერა +monitor.last_execution_result = შედეგი +monitor.process.children = შვილები +monitor.queues = რიგები +monitor.queue.name = სახელი +monitor.queue.type = ტიპი +notices.operations = ოპერაციები +notices.type = ტიპი +notices.type_1 = რეპოზიტორია +notices.type_2 = ამოცანა +notices.desc = აღწერა +notices.op = ოპ. +users.list_status_filter.menu_text = ფილტრი +users.list_status_filter.not_active = არააქტიურია +config.send_test_mail_submit = გაგზავნა +packages.creator = შემქმნელი +dashboard.operation_switch = გადართვა +orgs.members = წევრები +packages.published = გამოქვეყნებულია +config.mailer_enabled = ჩართულია + +[action] +review_dismissed_reason = მიზეზი: +compare_branch = შედარება + +[tool] +now = ახლა +raw_seconds = წამი +raw_minutes = წუთი +future = მომავალში + +[munits.data] +b = ბ +kib = კიბ +mib = მიბ +gib = გიბ +tib = ტიბ +pib = პიბ +eib = ეიბ + +[notification] +notifications = გაფრთხილებები +read = წაკითხვა +subscriptions = გამოწერები +watching = უყურებთ +unread = წაკითხულობის გაუქმება + +[units] +unit = ერთეული + +[packages] +title = პაკეტები +filter.type = ტიპი +filter.type.all = ყველა +filter.container.tagged = ჭდით +filter.container.untagged = ჭდემოხსნილი +installation = დაყენება +requirements = მოთხოვნები +dependencies = დამოკიდებულებები +keywords = საკვანძო სიტყვები +details = დეტალები +details.author = ავტორი +details.license = ლიცენზია +assets = ობიექტები +versions = ვერსიები +dependency.id = ID +dependency.version = ვერსია +alpine.repository.branches = ბრენჩები +alpine.repository.repositories = რეპოზიტორიები +alpine.repository.architectures = არქიტექტურები +arch.version.provides = მოგაწვდით +arch.version.depends = დამოკიდებულია +arch.version.conflicts = კონფლიქტშია +arch.version.replaces = ანაცვლებს +arch.version.backup = მარქაფი +conan.details.repository = რეპოზიტორია +container.images.title = ასლის ფაილები +container.details.platform = პლატფორმა +container.digest = დაიჯესტი +container.labels = ჭდეები +container.labels.key = გასაღები +debian.repository.distributions = დისტრიბუტივები +debian.repository.components = კომპონენტები +debian.repository.architectures = არქიტექტურები +npm.dependencies = დამოკიდებულებები +npm.details.tag = ჭდე +rpm.repository.architectures = არქიტექტურები +alt.repository.architectures = არქიტექტურები +owner.settings.cleanuprules.enabled = ჩართულია +arch.version.description = აღწერა +arch.version.groups = ჯგუფი +composer.dependencies = დამოკიდებულებები +container.labels.value = მნიშვნელობა + +[secrets] +secrets = საიდუმლოები + +[actions] +actions = ქმედებები +status.unknown = უცნობი +status.waiting = ველოდები +status.running = მიმდინარეობს შესრულება +status.success = წარმატება +status.failure = ჩავარდნა +status.cancelled = გაუქმებულია +status.skipped = გამოტოვებულია +status.blocked = დაბლოკილია +runners = გამსვებები +runners.status = სტატუსი +runners.id = ID +runners.owner_type = ტიპი +runners.description = აღწერა +runners.labels = ჭდეები +runners.runner_title = გამშვები +runners.task_list.run = გაშვება +runners.task_list.status = სტატუსი +runners.task_list.repository = რეპოზიტორია +runners.task_list.commit = კომიტი +runners.status.unspecified = უცნობი +runners.status.idle = უქმე +runners.status.active = აქტიურია +runners.status.offline = ქსელგარეშე +runners.version = ვერსია +runs.commit = კომიტი +runs.scheduled = დაგეგმილია +runs.workflow = შრომის პროცესი +runs.actor = ავტორი +runs.status = სტატუსი +variables = ცვლადები +runners.name = სახელი + +[git.filemode] +directory = საქაღალდე +submodule = ქვემოდული + +[graphs] +contributors.what = მოხალისეები \ No newline at end of file diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 7f4ae4262b..e59ee89f7e 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -2488,9 +2488,9 @@ settings.protect_branch_name_pattern=Aizsargātā zara nosaukuma paraugs settings.protect_branch_name_pattern_desc=Aizsargāto zaru nosaukumu paraugi. Paraugu pierakstu skatīt <a href="%s">dokumentācijā</a>. Piemēri: main, release/** settings.protect_patterns=Paraugi settings.protect_protected_file_patterns=Aizsargāto datņu paraugs (vairākus atdala ar semikolu ";") -settings.protect_protected_file_patterns_desc=Aizsargātās datnes nav ļauts tiešā veidā mainīt, pat ja lietotājam šajā zarā ir tiesības pievienot, labot vai izdzēst datnes. Vairākus paraugus var atdalīt ar semikolu (";"). Paraugu pieraksts ir skatāms <a href='%[1]s'>%[2]s</a> dokumentācijā. Piemēri: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. +settings.protect_protected_file_patterns_desc=Aizsargātās datnes nav ļauts tiešā veidā mainīt, pat ja lietotājam šajā zarā ir tiesības pievienot, labot vai izdzēst datnes. Vairākus paraugus var atdalīt ar semikolu (";"). Paraugu pieraksts ir skatāms <a href="%[1]s">%[2]s</a> dokumentācijā. Piemēri: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.protect_unprotected_file_patterns=Neaizsargāto datņu paraugs (vairākus atdala ar semikolu ";") -settings.protect_unprotected_file_patterns_desc=Neaizsargātās datnes, kuras ir ļauts izmainīt tiešā veidā, apejot aizgādāšanas ierobežojumu, ja lietotājam ir rakstīšanas piekļuve. Vairāki paraugi ir atdalāmi ar semikolu (";"). Paraugu pierakstu skatīt <a href='%[1]s'>%[2]s</a> dokumentācijā. Piemēri: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. +settings.protect_unprotected_file_patterns_desc=Neaizsargātās datnes, kuras ir ļauts izmainīt tiešā veidā, apejot aizgādāšanas ierobežojumu, ja lietotājam ir rakstīšanas piekļuve. Vairāki paraugi ir atdalāmi ar semikolu (";"). Paraugu pierakstu skatīt <a href="%[1]s">%[2]s</a> dokumentācijā. Piemēri: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.add_protected_branch=Iespējot aizsargāšanu settings.delete_protected_branch=Atspējot aizsargāšanu settings.update_protect_branch_success=Zara aizsargāšanas kārtula "%s" tika atjaunināta. @@ -2907,6 +2907,9 @@ issues.reopen.blocked_by_user = Tu nevari atkārtoti atvērt šo pieteikumu, jo pulls.comment.blocked_by_user = Tu šim izmaiņu pieprasījumam nevari pievienot piebildi, jo tā izveidotājs vai glabātavas īpašnieks ir liedzis Tevi. issues.filter_no_results = Nav vienumu issues.filter_no_results_placeholder = Jāmēģina pielāgot meklēšanas atlasītāji. +migrate.repo_desc_helper = Atstāt tukšu, lai ievietotu esošo aprakstu +archive.nocomment = Piebilžu pievienošana nav iespējama, jo glabātava ir arhivēta. +comment.blocked_by_user = Piebilžu pievienošana nav iespējama, jo glabātavas īpašnieks vai autors ir nolieguši Tevi. [graphs] component_loading=Ielādē %s… @@ -3896,7 +3899,7 @@ status.unknown=Nezināms status.waiting=Gaida status.running=Izpildās status.success=Sekmīgi -status.failure=Atteice +status.failure=Nesekmīgi status.cancelled=Atcelts status.skipped=Izlaists status.blocked=Aizturēts diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index 2be67a97da..c0701cd018 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -1305,7 +1305,7 @@ issues.new.clear_assignees = Towiesens leegmaken editor.file_editing_no_longer_exists = De Datei, wat bewarkt word, »%s«, gifft dat in deesem Repositorium nich mehr. editor.user_no_push_to_branch = Bruker kann nich to Twieg schuven editor.directory_is_a_file = Verteeknisnaam »%s« word in deesem Repositorium al as Dateinaam bruukt. -editor.file_changed_while_editing = De Datei-Inhollens hebben sik ännert, siet du mit de Bewarken begunnen hest. <a target="_blank" rel="noopener noreferrer" href="%s">Klick hier</a>, um se to sehn, of <strong>kommitteer de Änners weer</strong>, um se to överschrieven. +editor.file_changed_while_editing = De Datei-Inhollens hebben sik ännert, siet du de Datei opmaakt hest. <a target="_blank" rel="noopener noreferrer" href="%s">Klick hier</a>, um se to sehn, of <strong>kommitteer de Änners weer</strong>, um se to överschrieven. editor.push_rejected_no_message = De Ännern is vun de Server sünner Naricht oflehnt worden. Bidde överprüüf de Git-Hakens. commits.signed_by = Unnerschrieven vun commit.revert-content = Twieg utkören, up wat du dat torüggnehmen willst: @@ -2608,6 +2608,9 @@ issues.reopen.blocked_by_user = Du kannst deeses Gefall nich weer opmaken, denn pulls.comment.blocked_by_user = Du kannst up deesem Haalvörslag nich kommenteren, denn de Repositoriums-Eegner of de Autor vun de Haalvörslag hett di blockeert. issues.filter_no_results = Keene Resultaten issues.filter_no_results_placeholder = Versöök, diene Söök-Filters antopassen. +migrate.repo_desc_helper = Leeg laten, um de bestahn Beschrieven to importeren +comment.blocked_by_user = Kommenteren gaht hier nich, denn du büst vun de Repositoriums-Eegner of de Autor blockeert worden. +archive.nocomment = Kommenteren gaht hier nich, denn dat Repositorium is archiveert. [repo.permissions] code.read = <b>Lesen:</b> De Quelltext vun deesem Repositorium ankieken un klonen. diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 6bc3dcecdc..f4e046b79b 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -1361,7 +1361,7 @@ editor.propose_file_change=Stel bestandswijziging voor editor.new_branch_name_desc=Nieuwe branch naam… editor.cancel=Annuleer editor.filename_cannot_be_empty=Bestandsnaam mag niet leeg zijn. -editor.file_changed_while_editing=De bestandsinhoud is veranderd sinds je bent begonnen met bewerken. <a target="_blank" rel="noopener noreferrer" href="%s">Klik hier</a> om ze te zien, of <strong>commit de veranderingen opnieuw</strong> om ze te overschrijven. +editor.file_changed_while_editing=De inhoud van het bestand is gewijzigd sinds u het bestand hebt geopend. <a target="_blank" rel="noopener noreferrer" href="%s">Klik hier</a> om ze te zien, of <strong>commit de veranderingen opnieuw</strong> om ze te overschrijven. editor.commit_empty_file_header=Commit een leeg bestand editor.commit_empty_file_text=Het bestand dat u wilt committen is leeg. Doorgaan? editor.no_changes_to_show=Er zijn geen wijzigingen om weer te geven. @@ -2906,6 +2906,9 @@ issues.reopen.blocked_by_user = U kunt deze issue niet heropenen omdat u geblokk pulls.comment.blocked_by_user = U kunt niet reageren op deze pull request omdat u geblokkeerd bent door de eigenaar van de repository of de poster van de issue. issues.filter_no_results_placeholder = Probeer uw zoekfilters aan te passen. issues.filter_no_results = Geen resultaten +migrate.repo_desc_helper = Leeg laten om bestaande beschrijving te importeren +archive.nocomment = Commentaar geven is niet mogelijk omdat de repository gearchiveerd is. +comment.blocked_by_user = Commentaar geven is niet mogelijk omdat u geblokkeerd bent door de eigenaar van de repository of door de auteur. @@ -2924,7 +2927,7 @@ org_name_holder=Organisatienaam org_full_name_holder=Volledige naam organisatie org_name_helper=Organisatienamen horen kort en memorabel zijn. create_org=Nieuwe organisatie aanmaken -repo_updated=Geupdate %s +repo_updated=Geüpdatet %s members=Leden teams=Teams lower_members=leden diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 0ae55b98e1..66f4a9f51b 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -2432,9 +2432,9 @@ settings.require_signed_commits_desc=Rejeitar pushes para este branch se não es settings.protect_branch_name_pattern=Padrão de nome de branch protegido settings.protect_patterns=Padrões settings.protect_protected_file_patterns=Padrões de arquivo protegidos (separados usando ponto e vírgula ";") -settings.protect_protected_file_patterns_desc=Arquivos protegidos não podem ser alterados diretamente, mesmo que o usuário tenha direitos para adicionar, editar ou excluir arquivos neste branch. Vários padrões podem ser separados usando ponto e vírgula (';'). Consulte a documentação <a href='%s'>%s</a> para a sintaxe padrão. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. +settings.protect_protected_file_patterns_desc=Arquivos protegidos não podem ser alterados diretamente, mesmo que o usuário tenha direitos para adicionar, editar ou excluir arquivos neste branch. Vários padrões podem ser separados usando ponto e vírgula (';'). Consulte a documentação <a href="%[1]s">%[2]s</a> para a sintaxe padrão. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.protect_unprotected_file_patterns=Padrões de arquivo desprotegidos (separados usando ponto e vírgula ";") -settings.protect_unprotected_file_patterns_desc=Arquivos não protegidos que podem ser alterados diretamente se o usuário tiver acesso de gravação, ignorando as restrições de push. Vários padrões podem ser separados usando ponto e vírgula (\;'). Veja <a href='%[1]s'>%[2]s</a> documentação para sintaxe de padrões. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. +settings.protect_unprotected_file_patterns_desc=Arquivos não protegidos que podem ser alterados diretamente se o usuário tiver acesso de gravação, ignorando as restrições de push. Vários padrões podem ser separados usando ponto e vírgula (\;'). Veja <a href="%[1]s">%[2]s</a> documentação para sintaxe de padrões. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.add_protected_branch=Habilitar proteção settings.delete_protected_branch=Desabilitar proteção settings.update_protect_branch_success=Proteção do branch "%s" foi atualizada. @@ -2907,6 +2907,9 @@ issues.reopen.blocked_by_user = Você não pode reabrir este issue porque você pulls.comment.blocked_by_user = Você não pode comentar neste pull request porque você foi bloqueado pelo dono do repositório ou pelo autor do pull request. issues.filter_no_results = Nenhum resultado issues.filter_no_results_placeholder = Tente ajustar seus filtros de pesquisa. +archive.nocomment = Não é possível comentar pois o repositório foi arquivado. +migrate.repo_desc_helper = Deixe em branco para importar a descrição existente +comment.blocked_by_user = Não é possível comentar pois você foi bloqueado pelo dono ou autor do repositório. [graphs] component_loading = Carregando %s… diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 05613030b5..2b096a894e 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -2909,6 +2909,9 @@ pulls.editable = Editável pulls.editable_explanation = Este pedido de integração permite edições dos responsáveis. Pode contribuir diretamente para ele. issues.filter_no_results = Nenhum resultado issues.filter_no_results_placeholder = Tente ajustar os seus filtros de pesquisa. +migrate.repo_desc_helper = Deixe em branco para importar a descrição existente +archive.nocomment = Não é possível fazer comentários porque o repositório está arquivado. +comment.blocked_by_user = Não é possível comentar porque está bloqueado pelo proprietário do repositório ou pelo autor. [graphs] component_loading=A carregar %s… diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index e0f8065063..a11cf4d1f0 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1042,7 +1042,7 @@ user_block_success = Пользователь заблокирован. oauth2_application_locked = Forgejo предварительно регистрирует некоторые приложения OAuth2 при запуске, если это включено в конфигурации. Для избежания неожиданного поведения их нельзя удалять или редактировать. Ознакомиться с подробностями можно в документации OAuth2. hooks.desc = Добавьте веб-хуки, которые будут срабатывать <strong>во всех</strong> ваших репозиториях. webauthn_alternative_tip = Возможно, стоит настроить дополнительный метод аутентификации. -blocked_since = Заблокирован с %s +blocked_since = Заблокирован %s user_unblock_success = Пользователь разблокирован. twofa_scratch_token_regenerated = Ваш одноразовый ключ восстановления: %s. Сохраните его в надёжном месте. Больше он показан не будет. blocked_users = Заблокированные пользователи @@ -2910,6 +2910,9 @@ issues.reopen.blocked_by_user = Повторное открытие задачи pulls.comment.blocked_by_user = Вы не можете комментировать под этим запросом слияния, т.к. вы заблокированы владельцем репозитория или автором задачи. issues.filter_no_results = Ничего не нашлось issues.filter_no_results_placeholder = Попробуйте поискать по-другому. +migrate.repo_desc_helper = Оставьте пустым, чтобы скопировать описание из источника +archive.nocomment = Комментирование невозможно, потому что этот репозиторий архивирован. +comment.blocked_by_user = Комментирование невозможно, потому что вы заблокированы его владельцем или автором обсуждения. [graphs] component_loading_failed = Не удалось загрузить %s diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 186f8b3b50..2743f63a41 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -2429,9 +2429,9 @@ settings.protect_branch_name_pattern=Korunmuş Dal Adı Deseni settings.protect_branch_name_pattern_desc=Korunmuş dal isim desenleri. Desen sözdizimi için <a href="%s">belgelere</a> bakabilirsiniz. Örnekler: main, release/** settings.protect_patterns=Desenler settings.protect_protected_file_patterns=Korumalı dosya kalıpları (noktalı virgülle ayrılmış ';'): -settings.protect_protected_file_patterns_desc=Kullanıcının bu dalda dosya ekleme, düzenleme veya silme hakları olsa bile doğrudan değiştirilmesine izin verilmeyen korumalı dosyalar. Birden çok desen noktalı virgül (';') kullanılarak ayrılabilir. Desen sözdizimi için <a href='%s'>%s</a> belgelerine bakın. Örnekler: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. +settings.protect_protected_file_patterns_desc=Kullanıcının bu dalda dosya ekleme, düzenleme veya silme hakları olsa bile doğrudan değiştirilmesine izin verilmeyen korumalı dosyalar. Birden çok desen noktalı virgül (';') kullanılarak ayrılabilir. Desen sözdizimi için <a href="%[1]s">%[2]s</a> belgelerine bakın. Örnekler: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.protect_unprotected_file_patterns=Korunmasız dosya desenleri (noktalı virgülle ayrılmış ';'): -settings.protect_unprotected_file_patterns_desc=Kullanıcının yazma erişimi, itme kısıtlamasını atlama hakkı olduğunda doğrudan değiştirmesine izin verilen korunmasız dosyalar. Birden çok desen noktalı virgül (';') kullanılarak ayrılabilir. Desen söz dizimi için <a href='%[1]s'>%[2]s</a> belgelerine bakın. Örnekler: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. +settings.protect_unprotected_file_patterns_desc=Kullanıcının yazma erişimi, itme kısıtlamasını atlama hakkı olduğunda doğrudan değiştirmesine izin verilen korunmasız dosyalar. Birden çok desen noktalı virgül (';') kullanılarak ayrılabilir. Desen söz dizimi için <a href="%[1]s">%[2]s</a> belgelerine bakın. Örnekler: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.add_protected_branch=Korumayı etkinleştir settings.delete_protected_branch=Korumayı devre dışı bırak settings.update_protect_branch_success=Dal koruma kuralı "%s" güncellendi. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 9e10c3f6ec..7e51b27fc6 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1075,6 +1075,7 @@ quota.applies_to_org = До цієї організації застосован quota.rule.exceeded = Перевищено regenerate_token = Згенерувати знову access_token_regeneration = Згенерувати новий токен доступу +quota.sizes.assets.all = Ресурси [repo] owner=Власник @@ -1323,7 +1324,7 @@ editor.propose_file_change=Запропонувати зміну файлу editor.new_branch_name_desc=Ім'я нової гілки… editor.cancel=Відмінити editor.filename_cannot_be_empty=Ім'я файлу не може бути порожнім. -editor.file_changed_while_editing=Зміст файлу змінився з моменту початку редагування. <a target="_blank" rel="noopener" href="%s"> Натисніть тут </a>, щоб переглянути що було змінено, або <strong>закомітьте зміни ще раз</strong>, щоб переписати їх. +editor.file_changed_while_editing=Вміст файлу змінився відтоді, як ви його відкрили. <a target="_blank" rel="noopener noreferrer" href="%s">Натисніть тут</a>, щоб переглянути, що було змінено, або <strong>закомітьте зміни ще раз</strong>, щоб переписати їх. editor.commit_empty_file_header=Закомітити порожній файл editor.commit_empty_file_text=Файл, в комміті порожній. Продовжити? editor.no_changes_to_show=Нема змін для показу. @@ -1585,8 +1586,8 @@ issues.due_date=Дата завершення issues.invalid_due_date_format=Дата закінчення має бути в форматі 'ррр-мм-дд'. issues.error_modifying_due_date=Не вдалося змінити дату завершення. issues.error_removing_due_date=Не вдалося видалити дату завершення. -issues.push_commit_1=додав %d коміт %s -issues.push_commits_n=додав %d коміти(-ів) %s +issues.push_commit_1=додає %d коміт %s +issues.push_commits_n=додає %d коміти(-ів) %s issues.force_push_codes=`примусово залито %[1]s з <a class="%[7]s" href="%[3]s"><code>%[2]s</code></a> до <a class="%[7]s" href="%[5]s"><code>%[4]s</code></a> %[6]s` issues.force_push_compare=Порівняти issues.due_date_form=рррр-мм-дд @@ -2650,6 +2651,14 @@ release.asset_external_url = Зовнішня URL-адреса branch.download = Завантажити гілку «%s» pulls.delete_after_merge.head_branch.insufficient_branch = У вас немає дозволу видаляти головну гілку. activity.published_tag_label = Тег +migrate.repo_desc_helper = Залиште порожнім, щоб імпортувати існуючий опис +comment.blocked_by_user = Ви не можете коментувати, оскільки вас заблокував власник репозиторію або автор. +archive.nocomment = Ви не можете коментувати, оскільки цей репозиторій архівовано. +release.type_external_asset = Зовнішній ресурс +release.asset_name = Назва ресурсу +release.add_external_asset = Додати зовнішній ресурс +find_file.no_matching = Не знайдено відповідного файлу +commits.search.tooltip = До ключових слів можна додавати префікси «author:», «committer:», «after:» або «before:», наприклад, «revert author:Alice before:2019-01-13». [graphs] contributors.what = внески @@ -2777,6 +2786,8 @@ settings.change_orgname_redirect_prompt.with_cooldown.few = Стара назв teams.none_access = Немає доступу teams.invite_team_member = Запросити до %s teams.write_access = Запис +teams.invite.by = Вас запрошує %s +teams.invite_team_member.list = Запрошення в очікуванні [admin] dashboard=Панель управління @@ -3282,6 +3293,7 @@ dashboard.task.cancelled = Завдання: %[1]s скасовано: %[3]s config.mailer_protocol = Протокол dashboard.cron.cancelled = Cron: %[1]s скасовано: %[3]s defaulthooks.desc = Вебхуки автоматично сповіщають HTTP-сервер POST-запитами, коли в Forgejo відбуваються певні події. Вказані тут вебхуки є типовими і будуть скопійовані до всіх нових репозиторіїв. Докладніше — в <a target="_blank" rel="noopener" href="%s">посібнику з вебхуків</a>. +assets = Ресурси коду [action] @@ -3364,7 +3376,7 @@ error.generate_hash=Не вдалося згенерувати хеш коміт error.no_committer_account=Аккаунт користувача з таким Email не знайдено error.no_gpg_keys_found=Не вдалося знайти GPG-ключ, що відповідає даному підпису error.not_signed_commit=Непідписаний коміт -error.failed_retrieval_gpg_keys=Не вдалося отримати відповідний GPG-ключ користувача +error.failed_retrieval_gpg_keys=Не вдалося отримати ключ, пов'язаний з обліковим записом комітера error.probable_bad_signature=УВАГА! Хоча ключ із таким ID і є в базі, коміт неможливо ним перевірити! Цей коміт ПІДОЗРІЛИЙ. error.probable_bad_default_signature=УВАГА! Хоча типовий ключ має цей ID, коміт неможливо ним перевірити! Цей коміт ПІДОЗРІЛИЙ. @@ -3491,6 +3503,16 @@ published_by = %[1]s опубліковано <a href="%[2]s">%[3]s</a> swift.install = Додайте пакунок у свій файл <code>Package.swift</code>: settings.link.select = Виберіть репозиторій alpine.registry.info = Виберіть $branch і $repository зі списку нижче. +conda.registry = Встановіть цей реєстр як репозиторій Conda у своєму файлі <code>.condarc</code>: +owner.settings.chef.title = Реєстр Chef +arch.pacman.repo.multi.item = Конфігурація %s +maven.registry = Налаштуйте цей реєстр у файлі <code>pom.xml</code> свого проєкту: +cargo.registry = Налаштуйте цей реєстр у файлі конфігурації Cargo (наприклад, <code>~/.cargo/config.toml</code>): +composer.registry = Налаштуйте цей реєстр у файлі <code>~/.composer/config.json</code>: +alpine.registry = Налаштуйте цей реєстр, додавши URL у файл <code>/etc/apk/repositories</code>: +cran.registry = Налаштуйте цей реєстр у файлі <code>Rprofile.site</code>: +npm.registry = Налаштуйте цей реєстр у файлі <code>.npmrc</code> свого проєкту: +chef.registry = Налаштуйте цей реєстр у файлі <code>~/.chef/config.rb</code>: [secrets] deletion = Видалити секрет @@ -3505,6 +3527,7 @@ none = Секретів ще немає. creation.name_placeholder = без урахування регістру, тільки літерно-цифрові символи або підкреслення, не може починатися з GITEA_ або GITHUB_ secrets = Секрети creation.value_placeholder = Уведіть довільний вміст. Пробіли на початку та в кінці будуть пропущені. +description = Секрети передаються певним діям і не можуть бути прочитані інакше. [actions] runners.name=Назва @@ -3649,8 +3672,16 @@ filepreview.lines = Рядки з %[1]d по %[2]d в %[3]s test = Це тестовий текст. Він не відображається в інтерфейсі користувача Forgejo, а використовується з метою тестування [repo.permissions] -packages.read = <b>Читати:</b> Дивитись та завантажити пакунки призначені до репозиторію. -packages.write = <b>Писати:</b> Публікувати та видаляти пакунки призначені до репозиторію. +packages.read = <b>Читати:</b> дивитись та завантажити пакунки, призначені до репозиторію. +packages.write = <b>Писати:</b> публікувати та видаляти пакунки, призначені до репозиторію. +releases.write = <b>Писати:</b> публікувати, змінювати і видаляти випуски та їхні ресурси. +pulls.read = <b>Читати:</b> дивитись і створювати запити на злиття. +releases.read = <b>Читати:</b> дивитись і завантажувати випуски. +actions.read = <b>Читати:</b> дивитися вбудовані конвеєри CI/CD та їхні журнали. +wiki.write = <b>Писати:</b> створювати, оновлювати та видаляти сторінки вбудованої вікі. +issues.read = <b>Читати:</b> дивитись і створювати задачі та коментарі. +wiki.read = <b>Читати:</b> переглядати вбудовану вікі та її історію. +actions.write = <b>Писати:</b> вручну запускати, перезапускати, скасовувати або схвалювати конвеєри CI/CD в очікуванні. [munits.data] pib = ПіБ diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 53e25afb95..fe2bcde489 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1076,7 +1076,7 @@ quota.sizes.repos.all = 仓库 quota.sizes.repos.private = 私有仓库 quota.sizes.git.all = Git 内容 quota.sizes.git.lfs = Git LFS -quota.sizes.assets.all = 资产 +quota.sizes.assets.all = 资源 quota.sizes.assets.attachments.all = 附件 quota.sizes.assets.attachments.issues = 工单附件 quota.sizes.assets.attachments.releases = 版本发布附件 @@ -1416,7 +1416,7 @@ editor.file_is_a_symlink=`"%s" 是一个符号链接,无法在 web 编辑器 editor.filename_is_a_directory=此仓库中已存在名为“%s” 的目录。 editor.file_editing_no_longer_exists=正在编辑的文件 %s 已不存在。 editor.file_deleting_no_longer_exists=正在删除的文件 %s 已不存在。 -editor.file_changed_while_editing=文件内容在您进行编辑时被更改。<a target="_blank" rel="noopener noreferrer" href="%s">单击此处</a> 查看具体被更改的内容,或者 <strong>再次提交</strong> 覆盖其它在编辑时产生的更改。 +editor.file_changed_while_editing=文件内容在您打开文件后被更改。<a target="_blank" rel="noopener noreferrer" href="%s">单击此处</a> 查看具体被更改的内容,或者 <strong>再次提交</strong> 覆盖其它在编辑时产生的更改。 editor.file_already_exists=此仓库已经存在名为 %s 的文件。 editor.commit_empty_file_header=提交一个空文件 editor.commit_empty_file_text=您要提交的文件是空的,继续吗? @@ -2500,7 +2500,7 @@ settings.protect_branch_name_pattern=受保护的分支名称正则 settings.protect_branch_name_pattern_desc=受保护的分支名称正则。语法请参阅<a href="%s">文档</a> 。如:main, release/** settings.protect_patterns=规则 settings.protect_protected_file_patterns=受保护的文件模式(使用半角分号“;”分隔) -settings.protect_protected_file_patterns_desc=即使用户有权添加、编辑或删除此分支中的文件,也不允许直接更改受保护的文件。 可以使用半角分号(“;”)分隔多个模式。 见<a href='%s'>%s</a>文档了解模式语法。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。 +settings.protect_protected_file_patterns_desc=即使用户有权添加、编辑或删除此分支中的文件,也不允许直接更改受保护的文件。 可以使用半角分号(“;”)分隔多个模式。 见<a href="%[1]s">%[2]s</a>文档了解模式语法。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。 settings.protect_unprotected_file_patterns=不受保护的文件模式(使用半角分号“;”分隔) settings.protect_unprotected_file_patterns_desc=在用户有写权限的情况下允许绕过限制,直接修改设为不保护的文件。如有多个匹配模式,则可用半角分号(“;”)分隔开。见 <a href="%[1]s">%[2]s</a> 的文档以了解匹配模式的格式。例子: <code>.drone.yml</code>、<code>/docs/**/*.txt</code>。 settings.add_protected_branch=启用保护 @@ -2846,10 +2846,10 @@ settings.federation_not_enabled = 当前实例未启用邦联功能。 issues.author.tooltip.issue = 此用户是本工单的作者。 issues.author.tooltip.pr = 此用户是此合并请求的作者。 release.type_attachment = 附件 -release.type_external_asset = 外部资产 -release.asset_name = 资产名称 +release.type_external_asset = 外部资源 +release.asset_name = 资源名称 release.asset_external_url = 外部 URL -release.add_external_asset = 添加外部资产 +release.add_external_asset = 添加外部资源 release.invalid_external_url = 无效的外部 URL:“%s” milestones.filter_sort.name = 名称 settings.pull_mirror_sync_quota_exceeded = 超出配额,未拉取更改。 @@ -2909,6 +2909,9 @@ issues.reopen.blocked_by_user = 由于你已被仓库所有者或工单作者屏 pulls.comment.blocked_by_user = 由于你已被仓库所有者或合并请求作者屏蔽,你不能在此合并请求发表评论。 issues.filter_no_results = 无结果 issues.filter_no_results_placeholder = 尝试调整搜索筛选条件。 +migrate.repo_desc_helper = 留空以导入现有描述 +archive.nocomment = 您无法评论,因为此仓库已存档。 +comment.blocked_by_user = 您无法评论,因为您已被仓库所有者或作者屏蔽。 [graphs] component_loading=正在加载 %s… @@ -3053,7 +3056,7 @@ self_check = 自检 identity_access=身份及认证 users=用户帐户 organizations=组织管理 -assets=代码资产 +assets=代码资源 repositories=仓库管理 hooks=Web 钩子 integrations=集成 @@ -3660,7 +3663,7 @@ raw_minutes=分钟 [dropzone] default_message=拖放文件或点击此处上传。 -invalid_input_type=您不能上传该类型的文件 +invalid_input_type=您不能上传该类型的文件。 file_too_big=文件体积({{filesize}} MB)超过了最大允许体积({{maxFilesize}} MB) remove_file=移除文件 @@ -4064,7 +4067,7 @@ code.write = <b>写入:</b>推送到仓库,创建分支和标签。 code.read = <b>读取:</b>访问并克隆仓库的代码。 actions.read = <b>读取:</b>查看集成的 CI/CD 管道及其日志。 issues.write = <b>写入:</b>关闭工单并管理元数据,如标签、里程碑、指派成员、截止日期和依赖。 -releases.write = <b>写入:</b>发布、编辑和删除版本发布及其资产。 +releases.write = <b>写入:</b>发布、编辑和删除版本发布及其资源。 issues.read = <b>读取:</b>阅读并创建工单和评论。 pulls.read = <b>读取:</b>阅读并创建合并请求。 releases.read = <b>读取:</b>查看并下载版本发布。 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index d0d3b15427..ea8c1bc2b1 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -2317,9 +2317,9 @@ settings.require_signed_commits=僅接受經簽署的提交 settings.require_signed_commits_desc=拒絕未經簽署或未經驗證的提交推送到此分支。 settings.protect_branch_name_pattern=受保護的分支名稱式樣 settings.protect_protected_file_patterns=受保護檔案的式樣(以半形分號區隔「;」) -settings.protect_protected_file_patterns_desc=即便使用者有權限新增、修改、刪除此分支的檔案,仍不允許直接修改受保護的檔案。可以用半形分號「;」分隔多個式樣。請於 <a href='%s'>%s</a> 文件查看模式格式。範例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。 +settings.protect_protected_file_patterns_desc=即便使用者有權限新增、修改、刪除此分支的檔案,仍不允許直接修改受保護的檔案。可以用半形分號「;」分隔多個式樣。請於 <a href="%[1]s">%[2]s</a> 文件查看模式格式。範例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。 settings.protect_unprotected_file_patterns=未受保護檔案的式樣(以半形分號區隔「;」) -settings.protect_unprotected_file_patterns_desc=當使用者有寫入權限時,可繞過推送限制,直接修改未受保護的檔案。可以用半形分號「;」分隔多個模式。請於 <a href='%[1]s'>%[2]s</a> 文件查看模式格式。範例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。 +settings.protect_unprotected_file_patterns_desc=當使用者有寫入權限時,可繞過推送限制,直接修改未受保護的檔案。可以用半形分號「;」分隔多個模式。請於 <a href="%[1]s">%[2]s</a> 文件查看模式格式。範例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。 settings.add_protected_branch=啟用保護 settings.delete_protected_branch=停用保護 settings.protected_branch_deletion=刪除分支保護 diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 4e9600a460..c297196777 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -18,5 +18,7 @@ "incorrect_root_url": "Tato instance Forgejo je nastavena tak, aby běžela na adrese „%s“. Vy si momentálně prohlížíte Forgejo na jiné adrese, což může způsobit rozbití některých částí aplikace. Správná adresa je ovládána správci Forgejo pomocí nastavení ROOT_URL v souboru app.ini.", "themes.names.forgejo-auto": "Forgejo (podle motivu systému)", "themes.names.forgejo-light": "Forgejo – světlé", - "themes.names.forgejo-dark": "Forgejo – tmavé" + "themes.names.forgejo-dark": "Forgejo – tmavé", + "error.not_found.title": "Stránka nenalezena", + "alert.asset_load_failed": "Nepodařilo se načíst soubory příloh z {path}. Ujistěte se, že jsou dané soubory přístupné." } diff --git a/options/locale_next/locale_da.json b/options/locale_next/locale_da.json index 96b829770c..32d44fbf22 100644 --- a/options/locale_next/locale_da.json +++ b/options/locale_next/locale_da.json @@ -16,5 +16,11 @@ "home.explore_orgs": "Udforsk organisationer", "themes.names.forgejo-light": "Forgejo lys", "themes.names.forgejo-dark": "Forgejo mørk", - "themes.names.forgejo-auto": "Forgejo (følg systemtema)" + "themes.names.forgejo-auto": "Forgejo (følg systemtema)", + "error.not_found.title": "Siden blev ikke fundet", + "alert.asset_load_failed": "Kunne ikke indlæse aktivfiler fra {path}. Sørg for, at aktivfilerne er tilgængelige.", + "install.invalid_lfs_path": "Kan ikke oprette LFS-roden på den angivne sti: %[1]s", + "install.lfs_jwt_secret_failed": "Kan ikke generere en LFS JWT-hemmelighed: %[1]s", + "settings.adopt": "Adoptere", + "alert.range_error": " skal være et tal mellem %[1]s og %[2]s." } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index e77315c7be..2fdd0b528f 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -16,5 +16,7 @@ "incorrect_root_url": "Diese Forgejo-Instanz ist konfiguriert, auf „%s“ bereitgestellt zu werden. Du rufst Forgejo über einen anderen URL auf, was dazu führen könnte, dass einige Bereiche nicht funktionieren. Der anerkannte URL wird durch die Forgejo-Admins mittels der Einstellung ROOT_URL in der app.ini kontrolliert.", "themes.names.forgejo-auto": "Forgejo (folge Systemthema)", "themes.names.forgejo-light": "Forgejo hell", - "themes.names.forgejo-dark": "Forgejo dunkel" + "themes.names.forgejo-dark": "Forgejo dunkel", + "error.not_found.title": "Seite nicht gefunden", + "alert.asset_load_failed": "Konnte Asset-Dateien nicht von {path} laden. Bitte stelle sicher, dass auf die Asset-Dateien zugegriffen werden kann." } diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index 5dbb354513..5e9e717d68 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -17,6 +17,10 @@ "themes.names.forgejo-auto": "Forgejo (follow system theme)", "themes.names.forgejo-light": "Forgejo light", "themes.names.forgejo-dark": "Forgejo dark", + "error.not_found.title": "Page not found", + "alert.asset_load_failed": "Failed to load asset files from {path}. Please make sure the asset files can be accessed.", + "alert.range_error": " must be a number between %[1]s and %[2]s.", + "install.invalid_lfs_path": "Unable to create the LFS root at the specified path: %[1]s", "admin.config.moderation_config": "Moderation configuration", "moderation.report_abuse": "Report abuse", "moderation.report_content": "Report content", diff --git a/options/locale_next/locale_es-ES.json b/options/locale_next/locale_es-ES.json index 8261f05240..5e7cb34634 100644 --- a/options/locale_next/locale_es-ES.json +++ b/options/locale_next/locale_es-ES.json @@ -9,5 +9,10 @@ "many": "quiere fusionar %[1]d commits de <code>%[2]s</code> en <code id=\"%[4]s\">%[3]s</code>", "other": "" }, - "search.milestone_kind": "Buscar hitos…" + "search.milestone_kind": "Buscar hitos…", + "error.not_found.title": "Página no encontrada", + "themes.names.forgejo-auto": "Forgejo (seguir el tema del sistema)", + "themes.names.forgejo-dark": "Forgejo oscuro", + "themes.names.forgejo-light": "Forgejo claro", + "home.welcome.no_activity": "Sin actividad" } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index f4cc2bb24a..5ee4c3bb7a 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -16,5 +16,6 @@ "home.welcome.activity_hint": "Wala pang laman ang iyong feed. Makikita dito ang iyong mga aksyon at aktibidad mula sa mga repositoryo na pinapanood mo.", "home.explore_repos": "Tuklasin ang mga repositoryo", "home.explore_users": "Tuklasin ang mga user", - "home.explore_orgs": "Tuklasin ang mga organisasyon" + "home.explore_orgs": "Tuklasin ang mga organisasyon", + "error.not_found.title": "Hindi nahanap ang pahina" } diff --git a/options/locale_next/locale_it-IT.json b/options/locale_next/locale_it-IT.json index b19d948cea..8bd6d811a0 100644 --- a/options/locale_next/locale_it-IT.json +++ b/options/locale_next/locale_it-IT.json @@ -9,5 +9,11 @@ "many": "vuole unire %[1]d commit da <code>%[2]s</code> a <code id=\"%[4]s\">%[3]s</code>", "other": "" }, - "search.milestone_kind": "Ricerca tappe..." + "search.milestone_kind": "Ricerca traguardi…", + "themes.names.forgejo-light": "Forgejo chiaro", + "themes.names.forgejo-dark": "Forgejo scuro", + "home.welcome.no_activity": "Nessun'attività", + "home.explore_repos": "Esplora i repositori", + "home.explore_users": "Esplora l'utenza", + "home.explore_orgs": "Esplora le organizzazioni" } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index 4482b9993a..6891c84f15 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -18,5 +18,7 @@ "incorrect_root_url": "Šis Forgejo serveris ir konfigurēts darboties \"%s\". Pašlaik Forgejo tiek apmeklēta ar atšķirīgu URL, kas var radīt atsevišķu lietotnes daļu salūšanu. Kanonisko URL pārrauga Forgejo pārvaldītāji ar app.ini iestatījumu ROOT_URL.", "themes.names.forgejo-auto": "Forgejo (ievērot sistēmas izskatu)", "themes.names.forgejo-light": "Forgejo gaišais", - "themes.names.forgejo-dark": "Forgejo tumšais" + "themes.names.forgejo-dark": "Forgejo tumšais", + "error.not_found.title": "Lapa nav atrasta", + "alert.asset_load_failed": "Neizdevās ielādēt līdzekļu datnes no {path}. Lūgums pārliecināties, ka līdzekļu datnēm var piekļūt." } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index c9e30df345..f637cd31c7 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -16,5 +16,11 @@ "incorrect_root_url": "Deese Forgejo-Instanz is inricht, unner »%s« besöcht to worden. Du bekiekst Forgejo jüüst dör een anner URL, wat daarto föhren kann, dat ’t deelwies nich richtig warkt. De kanonisk URL word vun de Forgejo-Chefs över de ROOT_URL-Instellen in de app.ini kuntrolleert.", "themes.names.forgejo-light": "Forgejo Hell", "themes.names.forgejo-dark": "Forgejo Dunker", - "themes.names.forgejo-auto": "Forgejo (Systeem-Thema nagahn)" + "themes.names.forgejo-auto": "Forgejo (Systeem-Thema nagahn)", + "error.not_found.title": "Sied nich funnen", + "alert.asset_load_failed": "Kunn Objekt-Dateien ut {path} nich laden. Bidde wees wiss, dat up de Objekt-Dateien togriepen worden kann.", + "install.lfs_jwt_secret_failed": "Kunn dat LFS-JWT-Geheemst nich maken: %[1]s", + "settings.adopt": "Övernehmen", + "install.invalid_lfs_path": "Kunn de LFS-Ruut an de angeven Padd nich maken: %[1]s", + "alert.range_error": " mutt eene Tahl tüsken %[1]s un %[2]s wesen." } diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index dcd0b6625e..cb2d250006 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -4,17 +4,22 @@ "other": "heeft %[1]d commits samengevoegd van <code>%[2]s</code> naar <code>%[3]s</code> %[4]s" }, "repo.pulls.title_desc": { - "one": "wilt %[1]d commit van <code>%[2]s</code> samenvoegen in <code id=\"%[4]s\">%[3]s</code>", - "other": "wilt %[1]d commits van <code>%[2]s</code> samenvoegen met <code id=\"%[4]s\">%[3]s</code>" + "one": "wil %[1]d commit van <code>%[2]s</code> samenvoegen in <code id=\"%[4]s\">%[3]s</code>", + "other": "wil %[1]d commits van <code>%[2]s</code> samenvoegen met <code id=\"%[4]s\">%[3]s</code>" }, "search.milestone_kind": "Zoek mijlpalen…", "home.welcome.no_activity": "Geen activiteit", - "home.welcome.activity_hint": "Er staat nog niets in uw feed. Uw acties en activiteiten van repositories die u bekijkt zullen hier verschijnen.", - "home.explore_repos": "Ontdek repositories", - "home.explore_users": "Ontdek gebruikers", - "home.explore_orgs": "Ontdek organisaties", + "home.welcome.activity_hint": "Er staat nog niets in uw feed. Uw acties en activiteiten van repositories die u monitort zullen hier verschijnen.", + "home.explore_repos": "Verken repositories", + "home.explore_users": "Verken gebruikers", + "home.explore_orgs": "Verken organisaties", "incorrect_root_url": "Deze Forgejo-instantie is geconfigureerd om geserveerd te worden op \"%s\". U bekijkt Forgejo momenteel via een andere URL, waardoor onderdelen van de applicatie kunnen breken. De canonieke URL kan worden gewijzigd door Forgejo admins via de ROOT_URL instelling in de app.ini.", "themes.names.forgejo-auto": "Forgejo (volg het systeemthema)", "themes.names.forgejo-light": "Forgejo licht", - "themes.names.forgejo-dark": "Forgejo donker" + "themes.names.forgejo-dark": "Forgejo donker", + "error.not_found.title": "Pagina niet gevonden", + "alert.asset_load_failed": "Het laden van asset-bestanden van {path} is mislukt. Controleer of de asset-bestanden toegankelijk zijn.", + "settings.adopt": "Adopteer", + "install.invalid_lfs_path": "Kan de LFS-root niet aanmaken op het opgegeven pad: %[1]s", + "install.lfs_jwt_secret_failed": "Kan geen LFS JWT-geheim genereren: %[1]s" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 218669787d..1f941d86b4 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -18,5 +18,7 @@ "incorrect_root_url": "Esta instância do Forgejo está configurada para o endereço \"%s\". Você está atualmente vendo o Forgejo através de uma URL diferente, o que pode causar erros em algumas partes da aplicação. A URL oficial é controlada pela administração do Forgejo através da configuração ROOT_URL no arquivo app.ini.", "themes.names.forgejo-auto": "Forgejo (usar o tema do sistema)", "themes.names.forgejo-light": "Forgejo claro", - "themes.names.forgejo-dark": "Forgejo escuro" + "themes.names.forgejo-dark": "Forgejo escuro", + "error.not_found.title": "Página não encontrada", + "alert.asset_load_failed": "Não foi possível carregar arquivos de assets de {path}. Por favor, certifique-se que os arquivos podem ser acessados." } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index 40748e8e2d..157f48e14b 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -18,5 +18,6 @@ "incorrect_root_url": "Esta instância do Forgejo está configurada para ser servida em “%s”. Atualmente, está a visualizar o Forgejo através de um URL diferente, o que pode causar a quebra de partes da aplicação. O URL official é controlado pelos administradores do Forgejo através da configuração ROOT_URL no ficheiro app.ini.", "themes.names.forgejo-auto": "Forgejo (segue o tema do sistema)", "themes.names.forgejo-light": "Forgejo claro", - "themes.names.forgejo-dark": "Forgejo escuro" + "themes.names.forgejo-dark": "Forgejo escuro", + "error.not_found.title": "Página não encontrada" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index cf46cb1300..0fae237134 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -18,5 +18,6 @@ "incorrect_root_url": "Этот сервер Forgejo расположен по адресу «%s», но вы просматриваете страницу с другого адреса. Это может приводить к поломкам частей приложения. Канонический адрес указывается администратором сервера в файле конфигурации app.ini - ROOT_URL.", "themes.names.forgejo-light": "Forgejo – светлая", "themes.names.forgejo-auto": "Forgejo – как в системе", - "themes.names.forgejo-dark": "Forgejo – тёмная" + "themes.names.forgejo-dark": "Forgejo – тёмная", + "error.not_found.title": "Страница не найдена" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index f9481c79dc..54b5b8532f 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -18,5 +18,11 @@ "incorrect_root_url": "Цей екземпляр Forgejo налаштовано на відвідування з «%s». Зараз ви переглядаєте Forgejo за іншою URL-адресою, що може призвести до збоїв деяких частин програми. Канонічна URL-адреса встановлюється адміністраторами Forgejo за допомогою параметра ROOT_URL у файлі app.ini.", "themes.names.forgejo-light": "Forgejo світла", "themes.names.forgejo-dark": "Forgejo темна", - "themes.names.forgejo-auto": "Forgejo (як у системі)" + "themes.names.forgejo-auto": "Forgejo (як у системі)", + "error.not_found.title": "Сторінку не знайдено", + "alert.asset_load_failed": "Не вдалося завантажити файли ресурсів з {path}. Переконайтеся, що до файлів ресурсів є доступ.", + "install.invalid_lfs_path": "Не вдалося створити корінь LFS за вказаним шляхом: %[1]s", + "settings.adopt": "Прийняти", + "install.lfs_jwt_secret_failed": "Не вдалося створити секрет LFS JWT: %[1]s", + "alert.range_error": " має бути числом від %[1]s до %[2]s." } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 90faba85d2..77cbd7ab0f 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -10,5 +10,10 @@ "incorrect_root_url": "此 Forgejo 实例配置为在“%s”上提供服务。您当前正在通过不同的网址查看 Forgejo,这可能会导致应用程序的某些部分损坏。Forgejo 管理员可以通过 app.ini 中的 ROOT_URL 设置控制规范网址。", "themes.names.forgejo-auto": "Forgejo(遵循系统主题)", "themes.names.forgejo-light": "Forgejo 浅色", - "themes.names.forgejo-dark": "Forgejo 深色" + "themes.names.forgejo-dark": "Forgejo 深色", + "error.not_found.title": "页面不存在", + "alert.asset_load_failed": "无法从 {path} 加载资源文件。请确保可以访问资源文件。", + "install.lfs_jwt_secret_failed": "无法生成 LFS JWT 密钥:%[1]s", + "install.invalid_lfs_path": "无法在指定路径创建 LFS 根目录:%[1]s", + "alert.range_error": " 必须是一个介于 %[1]s 和 %[2]s 之间的数字。" } diff --git a/package-lock.json b/package-lock.json index 6e597bc511..58ae7c5880 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@github/text-expander-element": "2.8.0", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.14.0", - "ansi_up": "6.0.2", + "ansi_up": "6.0.3", "asciinema-player": "3.8.2", "chart.js": "4.4.5", "chartjs-adapter-dayjs-4": "1.0.4", @@ -67,22 +67,22 @@ "@stoplight/spectral-cli": "6.14.3", "@stylistic/eslint-plugin-js": "4.2.0", "@stylistic/stylelint-plugin": "3.1.2", - "@typescript-eslint/parser": "8.26.1", + "@typescript-eslint/parser": "8.29.0", "@vitejs/plugin-vue": "5.2.3", - "@vitest/coverage-v8": "3.0.8", - "@vitest/eslint-plugin": "1.1.25", + "@vitest/coverage-v8": "3.1.1", + "@vitest/eslint-plugin": "1.1.38", "@vue/test-utils": "2.4.6", - "eslint": "9.22.0", - "eslint-import-resolver-typescript": "4.1.1", + "eslint": "9.23.0", + "eslint-import-resolver-typescript": "4.3.1", "eslint-plugin-array-func": "5.0.2", - "eslint-plugin-import-x": "4.7.0", + "eslint-plugin-import-x": "4.9.4", "eslint-plugin-no-jquery": "3.1.1", "eslint-plugin-no-use-extend-native": "0.7.2", "eslint-plugin-playwright": "2.2.0", "eslint-plugin-regexp": "2.7.0", "eslint-plugin-sonarjs": "3.0.2", "eslint-plugin-toml": "0.12.0", - "eslint-plugin-unicorn": "57.0.0", + "eslint-plugin-unicorn": "58.0.0", "eslint-plugin-vitest-globals": "1.5.0", "eslint-plugin-vue": "10.0.0", "eslint-plugin-vue-scoped-css": "2.9.0", @@ -92,15 +92,15 @@ "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.44.0", "postcss-html": "1.8.0", - "stylelint": "16.16.0", + "stylelint": "16.17.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", "typescript": "5.7.3", - "typescript-eslint": "8.26.1", + "typescript-eslint": "8.29.0", "vite-string-plugin": "1.3.4", - "vitest": "3.0.8" + "vitest": "3.1.1" }, "engines": { "node": ">= 20.0.0" @@ -216,12 +216,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", - "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.10" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -231,9 +231,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -243,9 +243,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -528,9 +528,9 @@ } }, "node_modules/@emnapi/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz", - "integrity": "sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.0.tgz", + "integrity": "sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg==", "dev": true, "license": "MIT", "optional": true, @@ -540,9 +540,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", - "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.0.tgz", + "integrity": "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==", "dev": true, "license": "MIT", "optional": true, @@ -562,9 +562,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", - "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", "cpu": [ "ppc64" ], @@ -578,9 +578,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", - "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", "cpu": [ "arm" ], @@ -594,9 +594,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", - "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", "cpu": [ "arm64" ], @@ -610,9 +610,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", - "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", - "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", "cpu": [ "arm64" ], @@ -642,9 +642,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", - "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", "cpu": [ "x64" ], @@ -658,9 +658,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", - "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", "cpu": [ "arm64" ], @@ -674,9 +674,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", - "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", "cpu": [ "x64" ], @@ -690,9 +690,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", - "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", "cpu": [ "arm" ], @@ -706,9 +706,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", - "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", "cpu": [ "arm64" ], @@ -722,9 +722,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", - "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", "cpu": [ "ia32" ], @@ -738,9 +738,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", - "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", "cpu": [ "loong64" ], @@ -754,9 +754,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", - "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", "cpu": [ "mips64el" ], @@ -770,9 +770,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", - "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", "cpu": [ "ppc64" ], @@ -786,9 +786,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", - "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", "cpu": [ "riscv64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", - "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", "cpu": [ "s390x" ], @@ -818,9 +818,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", - "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", "cpu": [ "x64" ], @@ -834,9 +834,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", - "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", "cpu": [ "arm64" ], @@ -850,9 +850,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", - "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", "cpu": [ "x64" ], @@ -866,9 +866,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", - "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", "cpu": [ "arm64" ], @@ -882,9 +882,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", - "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", "cpu": [ "x64" ], @@ -898,9 +898,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", - "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", "cpu": [ "x64" ], @@ -914,9 +914,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", - "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", "cpu": [ "arm64" ], @@ -930,9 +930,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", - "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", "cpu": [ "ia32" ], @@ -946,9 +946,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", - "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", "cpu": [ "x64" ], @@ -1063,9 +1063,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz", - "integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", + "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1171,9 +1171,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.22.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz", - "integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==", + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", + "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", "dev": true, "license": "MIT", "engines": { @@ -1710,163 +1710,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-5.0.1.tgz", - "integrity": "sha512-lY5mi+6ztYGM9tiyMhHbzZteE7LFaexu9PjDL2N0stCNuGkSHO1eeeabi2ckxGWBbu9EW01XovbQqir12NbA6A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-5.0.1.tgz", - "integrity": "sha512-wHKdPB2Q0dMVQy6b5depG8IGI0Y37CIUTZYrZDYEGMT3X481DAJjp6AIVVHQeolEZzy+lWrpUQdGPy0dQnt7IA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-5.0.1.tgz", - "integrity": "sha512-TchNkHMASh2vZ2/FH71zqgAUNYuAlP8Bm1ocSnt7UVV43jZ9kG8HtQFc3AsYeVGzw06LZoeE3mVdSQVXUZhgzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-5.0.1.tgz", - "integrity": "sha512-JhqYi6uTSYflXSDNG/ZPTvBUcvcPR+vr/09XLdWJpZfEYzpPEEr7YQ2ogiOfZVUmCZOJWVOP8jJdv735j46a5w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-5.0.1.tgz", - "integrity": "sha512-W2OXq25tYiFVhi0Eioc9b+KWnaK2Hj7x/eW2u6r6u4afd1cQ09U+o5u98jrj1rxowSOTfFyiKqMqQOrS4PAlFg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-5.0.1.tgz", - "integrity": "sha512-dYaXmy90L5fTpH0mWmWvsf5PC9me5Lyjcj5vOQYNVedSQUavGMbQ+sKOWOTAFIpS8xzzIhYRdLI7oj4WV9S2bQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-5.0.1.tgz", - "integrity": "sha512-lOgcxgtV7nM4TjolWgW9qk05iDQE2GGZTx2Zr+Jj+4GqdIhkkGpbqyGPJx2FRsY6kXhyaA9eOad4Bldfo8vAaw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-5.0.1.tgz", - "integrity": "sha512-3Pg8eSolVs3hy3xoqkjCgoIC1uHKPAUY0rs4QX+KtsPMOZ7iO5b3JBbJHl+ZDIeuzE8dkSRG7aIuCNVonp5G1Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-5.0.1.tgz", - "integrity": "sha512-rBEgV5AW5obb6ATeNUSUouW0FzpoADOweTYblprko+Cv6rW1NSQGzGxD6o6jhIk4yV7/CZBwBWAkz9xpL5JSUQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.7" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-5.0.1.tgz", - "integrity": "sha512-okHg9C+z+2tCGKPszYVJDTZKhPHta7xKvhw0WMWPLTz9wbBMmHTuY9vVpgvFGhTU3N5iQ56LPollnSPtjvObCw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-5.0.1.tgz", - "integrity": "sha512-ODlCn4Pbd0HEWMixonax1uJtNCG4lEne6Jq98iUsmwSibQYcBzutxPed1qhLKur6KtCsYYa4LtRxys7e/5lZwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1960,9 +1803,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.37.0.tgz", - "integrity": "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.38.0.tgz", + "integrity": "sha512-ldomqc4/jDZu/xpYU+aRxo3V4mGCV9HeTgUBANI3oIQMOL+SsxB+S2lxMpkFp5UamSS3XuTMQVbsS24R4J4Qjg==", "cpu": [ "arm" ], @@ -1974,9 +1817,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.37.0.tgz", - "integrity": "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.38.0.tgz", + "integrity": "sha512-VUsgcy4GhhT7rokwzYQP+aV9XnSLkkhlEJ0St8pbasuWO/vwphhZQxYEKUP3ayeCYLhk6gEtacRpYP/cj3GjyQ==", "cpu": [ "arm64" ], @@ -1988,9 +1831,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.37.0.tgz", - "integrity": "sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.38.0.tgz", + "integrity": "sha512-buA17AYXlW9Rn091sWMq1xGUvWQFOH4N1rqUxGJtEQzhChxWjldGCCup7r/wUnaI6Au8sKXpoh0xg58a7cgcpg==", "cpu": [ "arm64" ], @@ -2002,9 +1845,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.37.0.tgz", - "integrity": "sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.38.0.tgz", + "integrity": "sha512-Mgcmc78AjunP1SKXl624vVBOF2bzwNWFPMP4fpOu05vS0amnLcX8gHIge7q/lDAHy3T2HeR0TqrriZDQS2Woeg==", "cpu": [ "x64" ], @@ -2016,9 +1859,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.37.0.tgz", - "integrity": "sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.38.0.tgz", + "integrity": "sha512-zzJACgjLbQTsscxWqvrEQAEh28hqhebpRz5q/uUd1T7VTwUNZ4VIXQt5hE7ncs0GrF+s7d3S4on4TiXUY8KoQA==", "cpu": [ "arm64" ], @@ -2030,9 +1873,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.37.0.tgz", - "integrity": "sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.38.0.tgz", + "integrity": "sha512-hCY/KAeYMCyDpEE4pTETam0XZS4/5GXzlLgpi5f0IaPExw9kuB+PDTOTLuPtM10TlRG0U9OSmXJ+Wq9J39LvAg==", "cpu": [ "x64" ], @@ -2044,9 +1887,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.37.0.tgz", - "integrity": "sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.38.0.tgz", + "integrity": "sha512-mimPH43mHl4JdOTD7bUMFhBdrg6f9HzMTOEnzRmXbOZqjijCw8LA5z8uL6LCjxSa67H2xiLFvvO67PT05PRKGg==", "cpu": [ "arm" ], @@ -2058,9 +1901,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.37.0.tgz", - "integrity": "sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.38.0.tgz", + "integrity": "sha512-tPiJtiOoNuIH8XGG8sWoMMkAMm98PUwlriOFCCbZGc9WCax+GLeVRhmaxjJtz6WxrPKACgrwoZ5ia/uapq3ZVg==", "cpu": [ "arm" ], @@ -2072,9 +1915,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.37.0.tgz", - "integrity": "sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.38.0.tgz", + "integrity": "sha512-wZco59rIVuB0tjQS0CSHTTUcEde+pXQWugZVxWaQFdQQ1VYub/sTrNdY76D1MKdN2NB48JDuGABP6o6fqos8mA==", "cpu": [ "arm64" ], @@ -2086,9 +1929,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.37.0.tgz", - "integrity": "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.38.0.tgz", + "integrity": "sha512-fQgqwKmW0REM4LomQ+87PP8w8xvU9LZfeLBKybeli+0yHT7VKILINzFEuggvnV9M3x1Ed4gUBmGUzCo/ikmFbQ==", "cpu": [ "arm64" ], @@ -2100,9 +1943,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.37.0.tgz", - "integrity": "sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.38.0.tgz", + "integrity": "sha512-hz5oqQLXTB3SbXpfkKHKXLdIp02/w3M+ajp8p4yWOWwQRtHWiEOCKtc9U+YXahrwdk+3qHdFMDWR5k+4dIlddg==", "cpu": [ "loong64" ], @@ -2114,9 +1957,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.37.0.tgz", - "integrity": "sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.38.0.tgz", + "integrity": "sha512-NXqygK/dTSibQ+0pzxsL3r4Xl8oPqVoWbZV9niqOnIHV/J92fe65pOir0xjkUZDRSPyFRvu+4YOpJF9BZHQImw==", "cpu": [ "ppc64" ], @@ -2128,9 +1971,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.37.0.tgz", - "integrity": "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.38.0.tgz", + "integrity": "sha512-GEAIabR1uFyvf/jW/5jfu8gjM06/4kZ1W+j1nWTSSB3w6moZEBm7iBtzwQ3a1Pxos2F7Gz+58aVEnZHU295QTg==", "cpu": [ "riscv64" ], @@ -2142,9 +1985,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.37.0.tgz", - "integrity": "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.38.0.tgz", + "integrity": "sha512-9EYTX+Gus2EGPbfs+fh7l95wVADtSQyYw4DfSBcYdUEAmP2lqSZY0Y17yX/3m5VKGGJ4UmIH5LHLkMJft3bYoA==", "cpu": [ "riscv64" ], @@ -2156,9 +1999,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.37.0.tgz", - "integrity": "sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.38.0.tgz", + "integrity": "sha512-Mpp6+Z5VhB9VDk7RwZXoG2qMdERm3Jw07RNlXHE0bOnEeX+l7Fy4bg+NxfyN15ruuY3/7Vrbpm75J9QHFqj5+Q==", "cpu": [ "s390x" ], @@ -2170,9 +2013,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.37.0.tgz", - "integrity": "sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.38.0.tgz", + "integrity": "sha512-vPvNgFlZRAgO7rwncMeE0+8c4Hmc+qixnp00/Uv3ht2x7KYrJ6ERVd3/R0nUtlE6/hu7/HiiNHJ/rP6knRFt1w==", "cpu": [ "x64" ], @@ -2184,9 +2027,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.37.0.tgz", - "integrity": "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.38.0.tgz", + "integrity": "sha512-q5Zv+goWvQUGCaL7fU8NuTw8aydIL/C9abAVGCzRReuj5h30TPx4LumBtAidrVOtXnlB+RZkBtExMsfqkMfb8g==", "cpu": [ "x64" ], @@ -2198,9 +2041,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.37.0.tgz", - "integrity": "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.38.0.tgz", + "integrity": "sha512-u/Jbm1BU89Vftqyqbmxdq14nBaQjQX1HhmsdBWqSdGClNaKwhjsg5TpW+5Ibs1mb8Es9wJiMdl86BcmtUVXNZg==", "cpu": [ "arm64" ], @@ -2212,9 +2055,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.37.0.tgz", - "integrity": "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.38.0.tgz", + "integrity": "sha512-mqu4PzTrlpNHHbu5qleGvXJoGgHpChBlrBx/mEhTPpnAL1ZAYFlvHD7rLK839LLKQzqEQMFJfGrrOHItN4ZQqA==", "cpu": [ "ia32" ], @@ -2226,9 +2069,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.37.0.tgz", - "integrity": "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.38.0.tgz", + "integrity": "sha512-jjqy3uWlecfB98Psxb5cD6Fny9Fupv9LrDSPTQZUROqjvZmcCqNu4UMl7qqhlUUGpwiAkotj6GYu4SZdcr/nLw==", "cpu": [ "x64" ], @@ -3185,9 +3028,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.11.tgz", - "integrity": "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==", + "version": "22.13.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz", + "integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -3238,17 +3081,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz", - "integrity": "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz", + "integrity": "sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.26.1", - "@typescript-eslint/type-utils": "8.26.1", - "@typescript-eslint/utils": "8.26.1", - "@typescript-eslint/visitor-keys": "8.26.1", + "@typescript-eslint/scope-manager": "8.29.0", + "@typescript-eslint/type-utils": "8.29.0", + "@typescript-eslint/utils": "8.29.0", + "@typescript-eslint/visitor-keys": "8.29.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3268,16 +3111,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz", - "integrity": "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.0.tgz", + "integrity": "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.26.1", - "@typescript-eslint/types": "8.26.1", - "@typescript-eslint/typescript-estree": "8.26.1", - "@typescript-eslint/visitor-keys": "8.26.1", + "@typescript-eslint/scope-manager": "8.29.0", + "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/typescript-estree": "8.29.0", + "@typescript-eslint/visitor-keys": "8.29.0", "debug": "^4.3.4" }, "engines": { @@ -3293,14 +3136,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz", - "integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz", + "integrity": "sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.26.1", - "@typescript-eslint/visitor-keys": "8.26.1" + "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/visitor-keys": "8.29.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3311,14 +3154,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz", - "integrity": "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.0.tgz", + "integrity": "sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.26.1", - "@typescript-eslint/utils": "8.26.1", + "@typescript-eslint/typescript-estree": "8.29.0", + "@typescript-eslint/utils": "8.29.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -3335,9 +3178,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz", - "integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.0.tgz", + "integrity": "sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==", "dev": true, "license": "MIT", "engines": { @@ -3349,14 +3192,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz", - "integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.0.tgz", + "integrity": "sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.26.1", - "@typescript-eslint/visitor-keys": "8.26.1", + "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/visitor-keys": "8.29.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3392,16 +3235,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", - "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.0.tgz", + "integrity": "sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.26.1", - "@typescript-eslint/types": "8.26.1", - "@typescript-eslint/typescript-estree": "8.26.1" + "@typescript-eslint/scope-manager": "8.29.0", + "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/typescript-estree": "8.29.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3416,13 +3259,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz", - "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.0.tgz", + "integrity": "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/types": "8.29.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3433,10 +3276,10 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@unrs/rspack-resolver-binding-darwin-arm64": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-darwin-arm64/-/rspack-resolver-binding-darwin-arm64-1.2.2.tgz", - "integrity": "sha512-i7z0B+C0P8Q63O/5PXJAzeFtA1ttY3OR2VSJgGv18S+PFNwD98xHgAgPOT1H5HIV6jlQP8Avzbp09qxJUdpPNw==", + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-EpRILdWr3/xDa/7MoyfO7JuBIJqpBMphtu4+80BK1bRfFcniVT74h3Z7q1+WOc92FuIAYatB1vn9TJR67sORGw==", "cpu": [ "arm64" ], @@ -3447,10 +3290,10 @@ "darwin" ] }, - "node_modules/@unrs/rspack-resolver-binding-darwin-x64": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-darwin-x64/-/rspack-resolver-binding-darwin-x64-1.2.2.tgz", - "integrity": "sha512-YEdFzPjIbDUCfmehC6eS+AdJYtFWY35YYgWUnqqTM2oe/N58GhNy5yRllxYhxwJ9GcfHoNc6Ubze1yjkNv+9Qg==", + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.3.3.tgz", + "integrity": "sha512-ntj/g7lPyqwinMJWZ+DKHBse8HhVxswGTmNgFKJtdgGub3M3zp5BSZ3bvMP+kBT6dnYJLSVlDqdwOq1P8i0+/g==", "cpu": [ "x64" ], @@ -3461,10 +3304,10 @@ "darwin" ] }, - "node_modules/@unrs/rspack-resolver-binding-freebsd-x64": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-freebsd-x64/-/rspack-resolver-binding-freebsd-x64-1.2.2.tgz", - "integrity": "sha512-TU4ntNXDgPN2giQyyzSnGWf/dVCem5lvwxg0XYvsvz35h5H19WrhTmHgbrULMuypCB3aHe1enYUC9rPLDw45mA==", + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.3.3.tgz", + "integrity": "sha512-l6BT8f2CU821EW7U8hSUK8XPq4bmyTlt9Mn4ERrfjJNoCw0/JoHAh9amZZtV3cwC3bwwIat+GUnrcHTG9+qixw==", "cpu": [ "x64" ], @@ -3475,10 +3318,10 @@ "freebsd" ] }, - "node_modules/@unrs/rspack-resolver-binding-linux-arm-gnueabihf": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-linux-arm-gnueabihf/-/rspack-resolver-binding-linux-arm-gnueabihf-1.2.2.tgz", - "integrity": "sha512-ik3w4/rU6RujBvNWiDnKdXi1smBhqxEDhccNi/j2rHaMjm0Fk49KkJ6XKsoUnD2kZ5xaMJf9JjailW/okfUPIw==", + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.3.3.tgz", + "integrity": "sha512-8ScEc5a4y7oE2BonRvzJ+2GSkBaYWyh0/Ko4Q25e/ix6ANpJNhwEPZvCR6GVRmsQAYMIfQvYLdM6YEN+qRjnAQ==", "cpu": [ "arm" ], @@ -3489,10 +3332,24 @@ "linux" ] }, - "node_modules/@unrs/rspack-resolver-binding-linux-arm64-gnu": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-linux-arm64-gnu/-/rspack-resolver-binding-linux-arm64-gnu-1.2.2.tgz", - "integrity": "sha512-fp4Azi8kHz6TX8SFmKfyScZrMLfp++uRm2srpqRjsRZIIBzH74NtSkdEUHImR4G7f7XJ+sVZjCc6KDDK04YEpQ==", + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.3.3.tgz", + "integrity": "sha512-8qQ6l1VTzLNd3xb2IEXISOKwMGXDCzY/UNy/7SovFW2Sp0K3YbL7Ao7R18v6SQkLqQlhhqSBIFRk+u6+qu5R5A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.3.3.tgz", + "integrity": "sha512-v81R2wjqcWXJlQY23byqYHt9221h4anQ6wwN64oMD/WAE+FmxPHFZee5bhRkNVtzqO/q7wki33VFWlhiADwUeQ==", "cpu": [ "arm64" ], @@ -3503,10 +3360,10 @@ "linux" ] }, - "node_modules/@unrs/rspack-resolver-binding-linux-arm64-musl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-linux-arm64-musl/-/rspack-resolver-binding-linux-arm64-musl-1.2.2.tgz", - "integrity": "sha512-gMiG3DCFioJxdGBzhlL86KcFgt9HGz0iDhw0YVYPsShItpN5pqIkNrI+L/Q/0gfDiGrfcE0X3VANSYIPmqEAlQ==", + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.3.3.tgz", + "integrity": "sha512-cAOx/j0u5coMg4oct/BwMzvWJdVciVauUvsd+GQB/1FZYKQZmqPy0EjJzJGbVzFc6gbnfEcSqvQE6gvbGf2N8Q==", "cpu": [ "arm64" ], @@ -3517,10 +3374,38 @@ "linux" ] }, - "node_modules/@unrs/rspack-resolver-binding-linux-x64-gnu": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-linux-x64-gnu/-/rspack-resolver-binding-linux-x64-gnu-1.2.2.tgz", - "integrity": "sha512-n/4n2CxaUF9tcaJxEaZm+lqvaw2gflfWQ1R9I7WQgYkKEKbRKbpG/R3hopYdUmLSRI4xaW1Cy0Bz40eS2Yi4Sw==", + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.3.3.tgz", + "integrity": "sha512-mq2blqwErgDJD4gtFDlTX/HZ7lNP8YCHYFij2gkXPtMzrXxPW1hOtxL6xg4NWxvnj4bppppb0W3s/buvM55yfg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.3.3.tgz", + "integrity": "sha512-u0VRzfFYysarYHnztj2k2xr+eu9rmgoTUUgCCIT37Nr+j0A05Xk2c3RY8Mh5+DhCl2aYibihnaAEJHeR0UOFIQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.3.3.tgz", + "integrity": "sha512-OrVo5ZsG29kBF0Ug95a2KidS16PqAMmQNozM6InbquOfW/udouk063e25JVLqIBhHLB2WyBnixOQ19tmeC/hIg==", "cpu": [ "x64" ], @@ -3531,10 +3416,10 @@ "linux" ] }, - "node_modules/@unrs/rspack-resolver-binding-linux-x64-musl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-linux-x64-musl/-/rspack-resolver-binding-linux-x64-musl-1.2.2.tgz", - "integrity": "sha512-cHyhAr6rlYYbon1L2Ag449YCj3p6XMfcYTP0AQX+KkQo025d1y/VFtPWvjMhuEsE2lLvtHm7GdJozj6BOMtzVg==", + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.3.3.tgz", + "integrity": "sha512-PYnmrwZ4HMp9SkrOhqPghY/aoL+Rtd4CQbr93GlrRTjK6kDzfMfgz3UH3jt6elrQAfupa1qyr1uXzeVmoEAxUA==", "cpu": [ "x64" ], @@ -3545,10 +3430,10 @@ "linux" ] }, - "node_modules/@unrs/rspack-resolver-binding-wasm32-wasi": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-wasm32-wasi/-/rspack-resolver-binding-wasm32-wasi-1.2.2.tgz", - "integrity": "sha512-eogDKuICghDLGc32FtP+WniG38IB1RcGOGz0G3z8406dUdjJvxfHGuGs/dSlM9YEp/v0lEqhJ4mBu6X2nL9pog==", + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.3.3.tgz", + "integrity": "sha512-81AnQY6fShmktQw4hWDUIilsKSdvr/acdJ5azAreu2IWNlaJOKphJSsUVWE+yCk6kBMoQyG9ZHCb/krb5K0PEA==", "cpu": [ "wasm32" ], @@ -3562,10 +3447,10 @@ "node": ">=14.0.0" } }, - "node_modules/@unrs/rspack-resolver-binding-win32-arm64-msvc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-win32-arm64-msvc/-/rspack-resolver-binding-win32-arm64-msvc-1.2.2.tgz", - "integrity": "sha512-7sWRJumhpXSi2lccX8aQpfFXHsSVASdWndLv8AmD8nDRA/5PBi8IplQVZNx2mYRx6+Bp91Z00kuVqpXO9NfCTg==", + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.3.3.tgz", + "integrity": "sha512-X/42BMNw7cW6xrB9syuP5RusRnWGoq+IqvJO8IDpp/BZg64J1uuIW6qA/1Cl13Y4LyLXbJVYbYNSKwR/FiHEng==", "cpu": [ "arm64" ], @@ -3576,10 +3461,24 @@ "win32" ] }, - "node_modules/@unrs/rspack-resolver-binding-win32-x64-msvc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-win32-x64-msvc/-/rspack-resolver-binding-win32-x64-msvc-1.2.2.tgz", - "integrity": "sha512-hewo/UMGP1a7O6FG/ThcPzSJdm/WwrYDNkdGgWl6M18H6K6MSitklomWpT9MUtT5KGj++QJb06va/14QBC4pvw==", + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.3.3.tgz", + "integrity": "sha512-EGNnNGQxMU5aTN7js3ETYvuw882zcO+dsVjs+DwO2j/fRVKth87C8e2GzxW1L3+iWAXMyJhvFBKRavk9Og1Z6A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.3.3.tgz", + "integrity": "sha512-GraLbYqOJcmW1qY3osB+2YIiD62nVf2/bVLHZmrb4t/YSUwE03l7TwcDJl08T/Tm3SVhepX8RQkpzWbag/Sb4w==", "cpu": [ "x64" ], @@ -3605,9 +3504,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.8.tgz", - "integrity": "sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.1.tgz", + "integrity": "sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3620,7 +3519,7 @@ "istanbul-reports": "^3.1.7", "magic-string": "^0.30.17", "magicast": "^0.3.5", - "std-env": "^3.8.0", + "std-env": "^3.8.1", "test-exclude": "^7.0.1", "tinyrainbow": "^2.0.0" }, @@ -3628,8 +3527,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.0.8", - "vitest": "3.0.8" + "@vitest/browser": "3.1.1", + "vitest": "3.1.1" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -3648,13 +3547,13 @@ } }, "node_modules/@vitest/eslint-plugin": { - "version": "1.1.25", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.25.tgz", - "integrity": "sha512-u8DpDnMbPcqBmJOB4PeEtn6q7vKmLVTLFMpzoxSAo0hjYdl4iYSHRleqwPQo0ywc7UV0S6RKIahYRQ3BnZdMVw==", + "version": "1.1.38", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.38.tgz", + "integrity": "sha512-KcOTZyVz8RiM5HyriiDVrP1CyBGuhRxle+lBsmSs6NTJEO/8dKVAq+f5vQzHj1/Kc7bYXSDO6yBe62Zx0t5iaw==", "dev": true, "license": "MIT", "peerDependencies": { - "@typescript-eslint/utils": ">= 8.0", + "@typescript-eslint/utils": "^8.24.0", "eslint": ">= 8.57.0", "typescript": ">= 5.0.0", "vitest": "*" @@ -3669,14 +3568,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz", - "integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", + "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.0.8", - "@vitest/utils": "3.0.8", + "@vitest/spy": "3.1.1", + "@vitest/utils": "3.1.1", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -3685,13 +3584,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz", - "integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", + "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.0.8", + "@vitest/spy": "3.1.1", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -3712,9 +3611,9 @@ } }, "node_modules/@vitest/mocker/node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true, "license": "MIT" }, @@ -3739,9 +3638,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz", - "integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", + "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", "dev": true, "license": "MIT", "dependencies": { @@ -3752,13 +3651,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz", - "integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", + "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.0.8", + "@vitest/utils": "3.1.1", "pathe": "^2.0.3" }, "funding": { @@ -3766,13 +3665,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz", - "integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", + "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.8", + "@vitest/pretty-format": "3.1.1", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -3791,9 +3690,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz", - "integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", + "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3804,13 +3703,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz", - "integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", + "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.0.8", + "@vitest/pretty-format": "3.1.1", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -4256,9 +4155,9 @@ } }, "node_modules/ansi_up": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.2.tgz", - "integrity": "sha512-3G3vKvl1ilEp7J1u6BmULpMA0xVoW/f4Ekqhl8RTrJrhEBkonKn5k3bUc5Xt+qDayA6iDX0jyUh3AbZjB/l0tw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.3.tgz", + "integrity": "sha512-V1t1pcsMkgdqwbtZGbqI732rb8u9X2GzcT3PAThzo/zMQSkoT4AGnRUnI/7fIOWMaL5c/8EE00VywATsjtIDbw==", "license": "MIT", "engines": { "node": "*" @@ -6328,9 +6227,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.123", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz", - "integrity": "sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==", + "version": "1.5.128", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz", + "integrity": "sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -6579,9 +6478,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", - "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -6591,31 +6490,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.1", - "@esbuild/android-arm": "0.25.1", - "@esbuild/android-arm64": "0.25.1", - "@esbuild/android-x64": "0.25.1", - "@esbuild/darwin-arm64": "0.25.1", - "@esbuild/darwin-x64": "0.25.1", - "@esbuild/freebsd-arm64": "0.25.1", - "@esbuild/freebsd-x64": "0.25.1", - "@esbuild/linux-arm": "0.25.1", - "@esbuild/linux-arm64": "0.25.1", - "@esbuild/linux-ia32": "0.25.1", - "@esbuild/linux-loong64": "0.25.1", - "@esbuild/linux-mips64el": "0.25.1", - "@esbuild/linux-ppc64": "0.25.1", - "@esbuild/linux-riscv64": "0.25.1", - "@esbuild/linux-s390x": "0.25.1", - "@esbuild/linux-x64": "0.25.1", - "@esbuild/netbsd-arm64": "0.25.1", - "@esbuild/netbsd-x64": "0.25.1", - "@esbuild/openbsd-arm64": "0.25.1", - "@esbuild/openbsd-x64": "0.25.1", - "@esbuild/sunos-x64": "0.25.1", - "@esbuild/win32-arm64": "0.25.1", - "@esbuild/win32-ia32": "0.25.1", - "@esbuild/win32-x64": "0.25.1" + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" } }, "node_modules/esbuild-loader": { @@ -6671,19 +6570,19 @@ } }, "node_modules/eslint": { - "version": "9.22.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz", - "integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==", + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", + "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", - "@eslint/config-helpers": "^0.1.0", + "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.0", - "@eslint/js": "9.22.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.23.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -6770,24 +6669,24 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.1.1.tgz", - "integrity": "sha512-91As8PwH6xjPwndAbvhTuZ3DUfdl4HttocWRyaLOd5T4uUo6km8EDO7Sve4jzH50V1wbgql2nOFbE3GGfARJag==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.3.1.tgz", + "integrity": "sha512-/dR9YMomeBlvfuvX5q0C3Y/2PHC9OCRdT2ijFwdfq/4Bq+4m5/lqstEp9k3P6ocha1pCbhoY9fkwVYLmOqR0VQ==", "dev": true, "license": "ISC", "dependencies": { "debug": "^4.4.0", "get-tsconfig": "^4.10.0", - "is-bun-module": "^1.3.0", - "rspack-resolver": "^1.1.2", + "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", - "tinyglobby": "^0.2.12" + "tinyglobby": "^0.2.12", + "unrs-resolver": "^1.3.3" }, "engines": { "node": "^16.17.0 || >=18.6.0" }, "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + "url": "https://opencollective.com/eslint-import-resolver-typescript" }, "peerDependencies": { "eslint": "*", @@ -6817,24 +6716,24 @@ } }, "node_modules/eslint-plugin-import-x": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.7.0.tgz", - "integrity": "sha512-LHxq8V6SJ99hSFYAexxUKk3gVsjb8fuNRGsbMinwlJGvcuREP9SVzCCNKJ3POdDowEHdExy/bPN6YfjraueIXA==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.9.4.tgz", + "integrity": "sha512-IPWbN0KBgBCpAiSlUcS17zc1eqMzRlYz15AzsFrw2Qfqt+e0IupxYbvYD96bGLKVlNdkNwa4ggv1skztpaZR/g==", "dev": true, "license": "MIT", "dependencies": { "@types/doctrine": "^0.0.9", - "@typescript-eslint/utils": "^8.26.1", + "@typescript-eslint/utils": "^8.28.0", "debug": "^4.4.0", "doctrine": "^3.0.0", "eslint-import-resolver-node": "^0.3.9", "get-tsconfig": "^4.10.0", "is-glob": "^4.0.3", "minimatch": "^10.0.1", - "oxc-resolver": "^5.0.0", "semver": "^7.7.1", "stable-hash": "^0.0.5", - "tslib": "^2.8.1" + "tslib": "^2.8.1", + "unrs-resolver": "^1.3.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7002,21 +6901,22 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "57.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-57.0.0.tgz", - "integrity": "sha512-zUYYa6zfNdTeG9BISWDlcLmz16c+2Ck2o5ZDHh0UzXJz3DEP7xjmlVDTzbyV0W+XksgZ0q37WEWzN2D2Ze+g9Q==", + "version": "58.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-58.0.0.tgz", + "integrity": "sha512-fc3iaxCm9chBWOHPVjn+Czb/wHS0D2Mko7wkOdobqo9R2bbFObc4LyZaLTNy0mhZOP84nKkLhTUQxlLOZ7EjKw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.4.1", - "ci-info": "^4.1.0", + "@eslint-community/eslint-utils": "^4.5.1", + "@eslint/plugin-kit": "^0.2.7", + "ci-info": "^4.2.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.40.0", + "core-js-compat": "^3.41.0", "esquery": "^1.6.0", - "globals": "^15.15.0", + "globals": "^16.0.0", "indent-string": "^5.0.0", - "is-builtin-module": "^4.0.0", + "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "read-package-up": "^11.0.0", @@ -7026,26 +6926,13 @@ "strip-indent": "^4.0.0" }, "engines": { - "node": ">=18.18" + "node": "^18.20.0 || ^20.10.0 || >=21.0.0" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=9.20.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "eslint": ">=9.22.0" } }, "node_modules/eslint-plugin-vitest-globals": { @@ -7149,9 +7036,9 @@ } }, "node_modules/eslint/node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true, "license": "MIT" }, @@ -8449,13 +8336,13 @@ } }, "node_modules/is-builtin-module": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-4.0.0.tgz", - "integrity": "sha512-rWP3AMAalQSesXO8gleROyL2iKU73SX5Er66losQn9rWOWL4Gef0a/xOEOVqjWGMuR2vHG3FJ8UUmT700O8oFg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", + "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", "dev": true, "license": "MIT", "dependencies": { - "builtin-modules": "^4.0.0" + "builtin-modules": "^5.0.0" }, "engines": { "node": ">=18.20" @@ -8465,9 +8352,9 @@ } }, "node_modules/is-builtin-module/node_modules/builtin-modules": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-4.0.0.tgz", - "integrity": "sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", + "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", "dev": true, "license": "MIT", "engines": { @@ -8478,13 +8365,13 @@ } }, "node_modules/is-bun-module": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.3.0.tgz", - "integrity": "sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, "license": "MIT", "dependencies": { - "semver": "^7.6.3" + "semver": "^7.7.1" } }, "node_modules/is-callable": { @@ -10902,29 +10789,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/oxc-resolver": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-5.0.1.tgz", - "integrity": "sha512-BbclyCSxgnqO5mo05RGcwp8rkVdZL7sf0ugEnFWK67DIBAMq5wR0/GQlQCdPiPkpiv9GESAVX2cbh1DMFux/TQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - }, - "optionalDependencies": { - "@oxc-resolver/binding-darwin-arm64": "5.0.1", - "@oxc-resolver/binding-darwin-x64": "5.0.1", - "@oxc-resolver/binding-freebsd-x64": "5.0.1", - "@oxc-resolver/binding-linux-arm-gnueabihf": "5.0.1", - "@oxc-resolver/binding-linux-arm64-gnu": "5.0.1", - "@oxc-resolver/binding-linux-arm64-musl": "5.0.1", - "@oxc-resolver/binding-linux-x64-gnu": "5.0.1", - "@oxc-resolver/binding-linux-x64-musl": "5.0.1", - "@oxc-resolver/binding-wasm32-wasi": "5.0.1", - "@oxc-resolver/binding-win32-arm64-msvc": "5.0.1", - "@oxc-resolver/binding-win32-x64-msvc": "5.0.1" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -11165,9 +11029,9 @@ } }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "license": "MIT", "engines": { "node": ">= 6" @@ -12325,29 +12189,6 @@ "points-on-path": "^0.2.1" } }, - "node_modules/rspack-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/rspack-resolver/-/rspack-resolver-1.2.2.tgz", - "integrity": "sha512-Fwc19jMBA3g+fxDJH2B4WxwZjE0VaaOL7OX/A4Wn5Zv7bOD/vyPZhzXfaO73Xc2GAlfi96g5fGUa378WbIGfFw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/JounQin" - }, - "optionalDependencies": { - "@unrs/rspack-resolver-binding-darwin-arm64": "1.2.2", - "@unrs/rspack-resolver-binding-darwin-x64": "1.2.2", - "@unrs/rspack-resolver-binding-freebsd-x64": "1.2.2", - "@unrs/rspack-resolver-binding-linux-arm-gnueabihf": "1.2.2", - "@unrs/rspack-resolver-binding-linux-arm64-gnu": "1.2.2", - "@unrs/rspack-resolver-binding-linux-arm64-musl": "1.2.2", - "@unrs/rspack-resolver-binding-linux-x64-gnu": "1.2.2", - "@unrs/rspack-resolver-binding-linux-x64-musl": "1.2.2", - "@unrs/rspack-resolver-binding-wasm32-wasi": "1.2.2", - "@unrs/rspack-resolver-binding-win32-arm64-msvc": "1.2.2", - "@unrs/rspack-resolver-binding-win32-x64-msvc": "1.2.2" - } - }, "node_modules/run-con": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", @@ -13135,9 +12976,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.16.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.16.0.tgz", - "integrity": "sha512-40X5UOb/0CEFnZVEHyN260HlSSUxPES+arrUphOumGWgXERHfwCD0kNBVILgQSij8iliYVwlc0V7M5bcLP9vPg==", + "version": "16.17.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.17.0.tgz", + "integrity": "sha512-I9OwVIWRMqVm2Br5iTbrfSqGRPWQUlvm6oXO1xZuYYu0Gpduy67N8wXOZv15p6E/JdlZiAtQaIoLKZEWk5hrjw==", "dev": true, "funding": [ { @@ -14063,9 +13904,9 @@ } }, "node_modules/type-fest": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.37.0.tgz", - "integrity": "sha512-S/5/0kFftkq27FPNye0XM1e2NsnoD/3FS+pBmbjmmtLT6I+i344KoOf7pvXreaFsDamWeaJX55nczA1m5PsBDg==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.38.0.tgz", + "integrity": "sha512-2dBz5D5ycHIoliLYLi0Q2V7KRaDlH0uWIvmk7TYlAg5slqwiPv1ezJdZm1QEM0xgk29oYWMCbIG7E6gHpvChlg==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -14168,15 +14009,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.26.1.tgz", - "integrity": "sha512-t/oIs9mYyrwZGRpDv3g+3K6nZ5uhKEMt2oNmAPwaY4/ye0+EH4nXIPYNtkYFS6QHm+1DFg34DbglYBz5P9Xysg==", + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.0.tgz", + "integrity": "sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.26.1", - "@typescript-eslint/parser": "8.26.1", - "@typescript-eslint/utils": "8.26.1" + "@typescript-eslint/eslint-plugin": "8.29.0", + "@typescript-eslint/parser": "8.29.0", + "@typescript-eslint/utils": "8.29.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -14263,6 +14104,33 @@ "node": ">= 10.0.0" } }, + "node_modules/unrs-resolver": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.3.3.tgz", + "integrity": "sha512-PFLAGQzYlyjniXdbmQ3dnGMZJXX5yrl2YS4DLRfR3BhgUsE1zpRIrccp9XMOGRfIHpdFvCn/nr5N1KMVda4x3A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/JounQin" + }, + "optionalDependencies": { + "@unrs/resolver-binding-darwin-arm64": "1.3.3", + "@unrs/resolver-binding-darwin-x64": "1.3.3", + "@unrs/resolver-binding-freebsd-x64": "1.3.3", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.3.3", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.3.3", + "@unrs/resolver-binding-linux-arm64-gnu": "1.3.3", + "@unrs/resolver-binding-linux-arm64-musl": "1.3.3", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.3.3", + "@unrs/resolver-binding-linux-s390x-gnu": "1.3.3", + "@unrs/resolver-binding-linux-x64-gnu": "1.3.3", + "@unrs/resolver-binding-linux-x64-musl": "1.3.3", + "@unrs/resolver-binding-wasm32-wasi": "1.3.3", + "@unrs/resolver-binding-win32-arm64-msvc": "1.3.3", + "@unrs/resolver-binding-win32-ia32-msvc": "1.3.3", + "@unrs/resolver-binding-win32-x64-msvc": "1.3.3" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -14367,9 +14235,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz", - "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz", + "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==", "dev": true, "license": "MIT", "dependencies": { @@ -14439,9 +14307,9 @@ } }, "node_modules/vite-node": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz", - "integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", + "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", "dev": true, "license": "MIT", "dependencies": { @@ -14469,9 +14337,9 @@ "license": "BSD-2-Clause" }, "node_modules/vite/node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true, "license": "MIT" }, @@ -14520,13 +14388,13 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.37.0.tgz", - "integrity": "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg==", + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.38.0.tgz", + "integrity": "sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.7" }, "bin": { "rollup": "dist/bin/rollup" @@ -14536,55 +14404,55 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.37.0", - "@rollup/rollup-android-arm64": "4.37.0", - "@rollup/rollup-darwin-arm64": "4.37.0", - "@rollup/rollup-darwin-x64": "4.37.0", - "@rollup/rollup-freebsd-arm64": "4.37.0", - "@rollup/rollup-freebsd-x64": "4.37.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.37.0", - "@rollup/rollup-linux-arm-musleabihf": "4.37.0", - "@rollup/rollup-linux-arm64-gnu": "4.37.0", - "@rollup/rollup-linux-arm64-musl": "4.37.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.37.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.37.0", - "@rollup/rollup-linux-riscv64-gnu": "4.37.0", - "@rollup/rollup-linux-riscv64-musl": "4.37.0", - "@rollup/rollup-linux-s390x-gnu": "4.37.0", - "@rollup/rollup-linux-x64-gnu": "4.37.0", - "@rollup/rollup-linux-x64-musl": "4.37.0", - "@rollup/rollup-win32-arm64-msvc": "4.37.0", - "@rollup/rollup-win32-ia32-msvc": "4.37.0", - "@rollup/rollup-win32-x64-msvc": "4.37.0", + "@rollup/rollup-android-arm-eabi": "4.38.0", + "@rollup/rollup-android-arm64": "4.38.0", + "@rollup/rollup-darwin-arm64": "4.38.0", + "@rollup/rollup-darwin-x64": "4.38.0", + "@rollup/rollup-freebsd-arm64": "4.38.0", + "@rollup/rollup-freebsd-x64": "4.38.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.38.0", + "@rollup/rollup-linux-arm-musleabihf": "4.38.0", + "@rollup/rollup-linux-arm64-gnu": "4.38.0", + "@rollup/rollup-linux-arm64-musl": "4.38.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.38.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.38.0", + "@rollup/rollup-linux-riscv64-gnu": "4.38.0", + "@rollup/rollup-linux-riscv64-musl": "4.38.0", + "@rollup/rollup-linux-s390x-gnu": "4.38.0", + "@rollup/rollup-linux-x64-gnu": "4.38.0", + "@rollup/rollup-linux-x64-musl": "4.38.0", + "@rollup/rollup-win32-arm64-msvc": "4.38.0", + "@rollup/rollup-win32-ia32-msvc": "4.38.0", + "@rollup/rollup-win32-x64-msvc": "4.38.0", "fsevents": "~2.3.2" } }, "node_modules/vitest": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz", - "integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", + "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.0.8", - "@vitest/mocker": "3.0.8", - "@vitest/pretty-format": "^3.0.8", - "@vitest/runner": "3.0.8", - "@vitest/snapshot": "3.0.8", - "@vitest/spy": "3.0.8", - "@vitest/utils": "3.0.8", + "@vitest/expect": "3.1.1", + "@vitest/mocker": "3.1.1", + "@vitest/pretty-format": "^3.1.1", + "@vitest/runner": "3.1.1", + "@vitest/snapshot": "3.1.1", + "@vitest/spy": "3.1.1", + "@vitest/utils": "3.1.1", "chai": "^5.2.0", "debug": "^4.4.0", - "expect-type": "^1.1.0", + "expect-type": "^1.2.0", "magic-string": "^0.30.17", "pathe": "^2.0.3", - "std-env": "^3.8.0", + "std-env": "^3.8.1", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.0.8", + "vite-node": "3.1.1", "why-is-node-running": "^2.3.0" }, "bin": { @@ -14600,8 +14468,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.0.8", - "@vitest/ui": "3.0.8", + "@vitest/browser": "3.1.1", + "@vitest/ui": "3.1.1", "happy-dom": "*", "jsdom": "*" }, @@ -14932,9 +14800,9 @@ } }, "node_modules/webpack/node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "license": "MIT" }, "node_modules/webpack/node_modules/eslint-scope": { @@ -15270,9 +15138,9 @@ } }, "node_modules/yaml": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", - "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index a903935464..34486e8d5a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "@github/text-expander-element": "2.8.0", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.14.0", - "ansi_up": "6.0.2", + "ansi_up": "6.0.3", "asciinema-player": "3.8.2", "chart.js": "4.4.5", "chartjs-adapter-dayjs-4": "1.0.4", @@ -66,21 +66,21 @@ "@stoplight/spectral-cli": "6.14.3", "@stylistic/eslint-plugin-js": "4.2.0", "@stylistic/stylelint-plugin": "3.1.2", - "@typescript-eslint/parser": "8.26.1", + "@typescript-eslint/parser": "8.29.0", "@vitejs/plugin-vue": "5.2.3", - "@vitest/coverage-v8": "3.0.8", - "@vitest/eslint-plugin": "1.1.25", + "@vitest/coverage-v8": "3.1.1", + "@vitest/eslint-plugin": "1.1.38", "@vue/test-utils": "2.4.6", - "eslint": "9.22.0", - "eslint-import-resolver-typescript": "4.1.1", + "eslint": "9.23.0", + "eslint-import-resolver-typescript": "4.3.1", "eslint-plugin-array-func": "5.0.2", - "eslint-plugin-import-x": "4.7.0", + "eslint-plugin-import-x": "4.9.4", "eslint-plugin-no-jquery": "3.1.1", "eslint-plugin-no-use-extend-native": "0.7.2", "eslint-plugin-playwright": "2.2.0", "eslint-plugin-regexp": "2.7.0", "eslint-plugin-sonarjs": "3.0.2", - "eslint-plugin-unicorn": "57.0.0", + "eslint-plugin-unicorn": "58.0.0", "eslint-plugin-toml": "0.12.0", "eslint-plugin-vitest-globals": "1.5.0", "eslint-plugin-vue": "10.0.0", @@ -91,15 +91,15 @@ "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.44.0", "postcss-html": "1.8.0", - "stylelint": "16.16.0", + "stylelint": "16.17.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", "typescript": "5.7.3", - "typescript-eslint": "8.26.1", + "typescript-eslint": "8.29.0", "vite-string-plugin": "1.3.4", - "vitest": "3.0.8" + "vitest": "3.1.1" }, "browserslist": [ "defaults" diff --git a/poetry.lock b/poetry.lock index 5cda33c903..4f0a372ef5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -393,24 +393,24 @@ telegram = ["requests"] [[package]] name = "typing-extensions" -version = "4.12.2" +version = "4.13.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5"}, + {file = "typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b"}, ] [[package]] name = "yamllint" -version = "1.36.2" +version = "1.37.0" description = "A linter for YAML files." optional = false python-versions = ">=3.9" files = [ - {file = "yamllint-1.36.2-py3-none-any.whl", hash = "sha256:6ba19800513d86c987ed3cf48c0aba4e82b3624596e1dab55723b3733e11f35c"}, - {file = "yamllint-1.36.2.tar.gz", hash = "sha256:c9ccc818659736e7b13f7e2f9c3c9bb9ac77445be13e789e7d843e92cb8428ef"}, + {file = "yamllint-1.37.0-py3-none-any.whl", hash = "sha256:c03ab4e79ab4af964c8eb16ac9746880fc76a3bb0ffb14925b9a55220ae7dda0"}, + {file = "yamllint-1.37.0.tar.gz", hash = "sha256:ead81921d4d87216b2528b7a055664708f9fb8267beb0c427cb706ac6ab93580"}, ] [package.dependencies] @@ -423,4 +423,4 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "6e4e2a74db7e165f0aa8cae9bb8932cbe1e84ea9bef64db42371e6d620d77c93" +content-hash = "e1f441dfab1cb658912d98d3ee43b55111e6fc12e04f0388a2aa9fc01a6f583b" diff --git a/pyproject.toml b/pyproject.toml index eb3b338794..5c4bd26e27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ python = "^3.10" [tool.poetry.group.dev.dependencies] djlint = "1.36.4" -yamllint = "1.36.2" +yamllint = "1.37.0" codespell = "2.4.1" [tool.djlint] diff --git a/routers/api/actions/artifactsv4.go b/routers/api/actions/artifactsv4.go index c6bc39e7d2..dee5f1b2f3 100644 --- a/routers/api/actions/artifactsv4.go +++ b/routers/api/actions/artifactsv4.go @@ -166,8 +166,8 @@ func (r artifactV4Routes) buildSignature(endp, expires, artifactName string, tas mac.Write([]byte(endp)) mac.Write([]byte(expires)) mac.Write([]byte(artifactName)) - mac.Write([]byte(fmt.Sprint(taskID))) - mac.Write([]byte(fmt.Sprint(artifactID))) + fmt.Fprint(mac, taskID) + fmt.Fprint(mac, artifactID) return mac.Sum(nil) } diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index ebd081ae77..79e61cf352 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -48,13 +48,14 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) { if ok { // it's a personal access token but not oauth2 token scopeMatched := false var err error - if accessMode == perm.AccessModeRead { + switch accessMode { + case perm.AccessModeRead: scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopeReadPackage) if err != nil { ctx.Error(http.StatusInternalServerError, "HasScope", err.Error()) return } - } else if accessMode == perm.AccessModeWrite { + case perm.AccessModeWrite: scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopeWritePackage) if err != nil { ctx.Error(http.StatusInternalServerError, "HasScope", err.Error()) diff --git a/routers/api/packages/chef/auth.go b/routers/api/packages/chef/auth.go index fc552c1f1b..bbd5ce860f 100644 --- a/routers/api/packages/chef/auth.go +++ b/routers/api/packages/chef/auth.go @@ -147,7 +147,7 @@ func getSignVersion(req *http.Request) (string, error) { version := m[1] m = algorithmPattern.FindStringSubmatch(hdr) - if len(m) == 2 && m[1] != "sha1" && !(m[1] == "sha256" && version == "1.3") { + if len(m) == 2 && m[1] != "sha1" && (m[1] != "sha256" || version != "1.3") { return "", util.NewInvalidArgumentErrorf("unsupported algorithm") } diff --git a/routers/api/packages/chef/chef.go b/routers/api/packages/chef/chef.go index 909817861e..13419b9a95 100644 --- a/routers/api/packages/chef/chef.go +++ b/routers/api/packages/chef/chef.go @@ -139,7 +139,7 @@ func EnumeratePackages(ctx *context.Context) { }) } - skip, _ := opts.Paginator.GetSkipTake() + skip, _ := opts.GetSkipTake() ctx.JSON(http.StatusOK, &Result{ Start: skip, diff --git a/routers/api/packages/nuget/auth.go b/routers/api/packages/nuget/auth.go index bab08bb1b8..92868bdef5 100644 --- a/routers/api/packages/nuget/auth.go +++ b/routers/api/packages/nuget/auth.go @@ -25,7 +25,7 @@ func (a *Auth) Name() string { func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) { token, err := auth_model.GetAccessTokenBySHA(req.Context(), req.Header.Get("X-NuGet-ApiKey")) if err != nil { - if !(auth_model.IsErrAccessTokenNotExist(err) || auth_model.IsErrAccessTokenEmpty(err)) { + if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) { log.Error("GetAccessTokenBySHA: %v", err) return nil, err } diff --git a/routers/api/v1/activitypub/actor.go b/routers/api/v1/activitypub/actor.go index 7568a2a7c8..e49f277842 100644 --- a/routers/api/v1/activitypub/actor.go +++ b/routers/api/v1/activitypub/actor.go @@ -28,7 +28,7 @@ func Actor(ctx *context.APIContext) { // "200": // "$ref": "#/responses/ActivityPub" - link := user_model.APActorUserAPActorID() + link := user_model.APServerActorID() actor := ap.ActorNew(ap.IRI(link), ap.ApplicationType) actor.PreferredUsername = ap.NaturalLanguageValuesNew() @@ -46,7 +46,7 @@ func Actor(ctx *context.APIContext) { actor.PublicKey.ID = ap.IRI(link + "#main-key") actor.PublicKey.Owner = ap.IRI(link) - publicKeyPem, err := activitypub.GetPublicKey(ctx, user_model.NewAPActorUser()) + publicKeyPem, err := activitypub.GetPublicKey(ctx, user_model.NewAPServerActor()) if err != nil { ctx.ServerError("GetPublicKey", err) return diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index a9bb4bd868..5872d951cf 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -6,59 +6,141 @@ package activitypub import ( "crypto" "crypto/x509" + "database/sql" "encoding/pem" "fmt" - "io" "net/http" "net/url" + "forgejo.org/models/db" + "forgejo.org/models/forgefed" + "forgejo.org/models/user" "forgejo.org/modules/activitypub" - "forgejo.org/modules/httplib" + fm "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/modules/setting" gitea_context "forgejo.org/services/context" + "forgejo.org/services/federation" "github.com/42wim/httpsig" ap "github.com/go-ap/activitypub" ) -func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err error) { - person := ap.PersonNew(ap.IRI(keyID.String())) - err = person.UnmarshalJSON(b) - if err != nil { - return nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err) - } - pubKey := person.PublicKey - if pubKey.ID.String() != keyID.String() { - return nil, fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b)) - } - pubKeyPem := pubKey.PublicKeyPem +func decodePublicKeyPem(pubKeyPem string) ([]byte, error) { block, _ := pem.Decode([]byte(pubKeyPem)) if block == nil || block.Type != "PUBLIC KEY" { return nil, fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type") } - p, err = x509.ParsePKIXPublicKey(block.Bytes) - return p, err + + return block.Bytes, nil } -func fetch(iri *url.URL) (b []byte, err error) { - req := httplib.NewRequest(iri.String(), http.MethodGet) - req.Header("Accept", activitypub.ActivityStreamsContentType) - req.Header("User-Agent", "Gitea/"+setting.AppVer) - resp, err := req.Response() +func getFederatedUser(ctx *gitea_context.APIContext, person *ap.Person, federationHost *forgefed.FederationHost) (*user.FederatedUser, error) { + dbUser, err := user.GetUserByFederatedURI(ctx, person.ID.String()) if err != nil { return nil, err } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("url IRI fetch [%s] failed with status (%d): %s", iri, resp.StatusCode, resp.Status) + if dbUser != nil { + federatedUser, err := user.GetFederatedUserByUserID(ctx, dbUser.ID) + if err != nil { + return nil, err + } + + if federatedUser != nil { + return federatedUser, nil + } } - b, err = io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize)) - return b, err + + personID, err := fm.NewPersonID(person.ID.String(), string(federationHost.NodeInfo.SoftwareName)) + if err != nil { + return nil, err + } + + _, federatedUser, err := federation.CreateUserFromAP(ctx, personID, federationHost.ID) + if err != nil { + return nil, err + } + + return federatedUser, nil +} + +func storePublicKey(ctx *gitea_context.APIContext, person *ap.Person, pubKeyBytes []byte) error { + federationHost, err := federation.GetFederationHostForURI(ctx, person.ID.String()) + if err != nil { + return err + } + + if person.Type == ap.ActivityVocabularyType("Application") { + federationHost.KeyID = sql.NullString{ + String: person.PublicKey.ID.String(), + Valid: true, + } + + federationHost.PublicKey = sql.Null[sql.RawBytes]{ + V: pubKeyBytes, + Valid: true, + } + + _, err = db.GetEngine(ctx).ID(federationHost.ID).Update(federationHost) + if err != nil { + return err + } + } else if person.Type == ap.ActivityVocabularyType("Person") { + federatedUser, err := getFederatedUser(ctx, person, federationHost) + if err != nil { + return err + } + + federatedUser.KeyID = sql.NullString{ + String: person.PublicKey.ID.String(), + Valid: true, + } + + federatedUser.PublicKey = sql.Null[sql.RawBytes]{ + V: pubKeyBytes, + Valid: true, + } + + _, err = db.GetEngine(ctx).ID(federatedUser.ID).Update(federatedUser) + if err != nil { + return err + } + } + + return nil +} + +func getPublicKeyFromResponse(b []byte, keyID *url.URL) (person *ap.Person, pubKeyBytes []byte, p crypto.PublicKey, err error) { + person = ap.PersonNew(ap.IRI(keyID.String())) + err = person.UnmarshalJSON(b) + if err != nil { + return nil, nil, nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err) + } + + pubKey := person.PublicKey + if pubKey.ID.String() != keyID.String() { + return nil, nil, nil, fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b)) + } + + pubKeyBytes, err = decodePublicKeyPem(pubKey.PublicKeyPem) + if err != nil { + return nil, nil, nil, err + } + + p, err = x509.ParsePKIXPublicKey(pubKeyBytes) + if err != nil { + return nil, nil, nil, err + } + + return person, pubKeyBytes, p, err } func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, err error) { + if !setting.Federation.SignatureEnforced { + return true, nil + } + r := ctx.Req // 1. Figure out what key we need to verify @@ -66,23 +148,78 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er if err != nil { return false, err } + ID := v.KeyId() idIRI, err := url.Parse(ID) if err != nil { return false, err } + + signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0]) + // 2. Fetch the public key of the other actor - b, err := fetch(idIRI) + // Try if the signing actor is an already known federated user + federationUser, err := user.GetFederatedUserByKeyID(ctx, idIRI.String()) if err != nil { return false, err } - pubKey, err := getPublicKeyFromResponse(b, idIRI) + + if federationUser != nil && federationUser.PublicKey.Valid { + pubKey, err := x509.ParsePKIXPublicKey(federationUser.PublicKey.V) + if err != nil { + return false, err + } + + authenticated = v.Verify(pubKey, signatureAlgorithm) == nil + return authenticated, err + } + + // Try if the signing actor is an already known federation host + federationHost, err := forgefed.FindFederationHostByKeyID(ctx, idIRI.String()) if err != nil { return false, err } - // 3. Verify the other actor's key - algo := httpsig.Algorithm(setting.Federation.Algorithms[0]) - authenticated = v.Verify(pubKey, algo) == nil + + if federationHost != nil && federationHost.PublicKey.Valid { + pubKey, err := x509.ParsePKIXPublicKey(federationHost.PublicKey.V) + if err != nil { + return false, err + } + + authenticated = v.Verify(pubKey, signatureAlgorithm) == nil + return authenticated, err + } + + // Fetch missing public key + actionsUser := user.NewAPServerActor() + clientFactory, err := activitypub.GetClientFactory(ctx) + if err != nil { + return false, err + } + + apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID()) + if err != nil { + return false, err + } + + b, err := apClient.GetBody(idIRI.String()) + if err != nil { + return false, err + } + + person, pubKeyBytes, pubKey, err := getPublicKeyFromResponse(b, idIRI) + if err != nil { + return false, err + } + + authenticated = v.Verify(pubKey, signatureAlgorithm) == nil + if authenticated { + err = storePublicKey(ctx, person, pubKeyBytes) + if err != nil { + return false, err + } + } + return authenticated, err } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 2eb1ee4229..551fcf7a43 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -203,19 +203,19 @@ func repoAssignment() func(ctx *context.APIContext) { } if task.IsForkPullRequest { - ctx.Repo.Permission.AccessMode = perm.AccessModeRead + ctx.Repo.AccessMode = perm.AccessModeRead } else { - ctx.Repo.Permission.AccessMode = perm.AccessModeWrite + ctx.Repo.AccessMode = perm.AccessModeWrite } if err := ctx.Repo.Repository.LoadUnits(ctx); err != nil { ctx.Error(http.StatusInternalServerError, "LoadUnits", err) return } - ctx.Repo.Permission.Units = ctx.Repo.Repository.Units - ctx.Repo.Permission.UnitsMode = make(map[unit.Type]perm.AccessMode) + ctx.Repo.Units = ctx.Repo.Repository.Units + ctx.Repo.UnitsMode = make(map[unit.Type]perm.AccessMode) for _, u := range ctx.Repo.Repository.Units { - ctx.Repo.Permission.UnitsMode[u.Type] = ctx.Repo.Permission.AccessMode + ctx.Repo.UnitsMode[u.Type] = ctx.Repo.AccessMode } } else { ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) @@ -692,7 +692,7 @@ func mustEnableIssues(ctx *context.APIContext) { } func mustAllowPulls(ctx *context.APIContext) { - if !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(unit.TypePullRequests)) { + if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) { if ctx.Repo.Repository.CanEnablePulls() && log.IsTrace() { if ctx.IsSigned { log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+ @@ -716,7 +716,7 @@ func mustAllowPulls(ctx *context.APIContext) { func mustEnableIssuesOrPulls(ctx *context.APIContext) { if !ctx.Repo.CanRead(unit.TypeIssues) && - !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(unit.TypePullRequests)) { + (!ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests)) { if ctx.Repo.Repository.CanEnablePulls() && log.IsTrace() { if ctx.IsSigned { log.Trace("Permission Denied: User %-v cannot read %-v and %-v in Repo %-v\n"+ @@ -777,13 +777,13 @@ func bind[T any](_ T) any { func individualPermsChecker(ctx *context.APIContext) { // org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked. if ctx.ContextUser.IsIndividual() { - switch { - case ctx.ContextUser.Visibility == api.VisibleTypePrivate: + switch ctx.ContextUser.Visibility { + case api.VisibleTypePrivate: if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) { ctx.NotFound("Visit Project", nil) return } - case ctx.ContextUser.Visibility == api.VisibleTypeLimited: + case api.VisibleTypeLimited: if ctx.Doer == nil { ctx.NotFound("Visit Project", nil) return @@ -840,22 +840,22 @@ func Routes() *web.Route { m.Group("/activitypub", func() { // deprecated, remove in 1.20, use /user-id/{user-id} instead m.Group("/user/{username}", func() { - m.Get("", activitypub.Person) + m.Get("", activitypub.ReqHTTPSignature(), activitypub.Person) m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) }, context.UserAssignmentAPI(), checkTokenPublicOnly()) m.Group("/user-id/{user-id}", func() { - m.Get("", activitypub.Person) + m.Get("", activitypub.ReqHTTPSignature(), activitypub.Person) m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) }, context.UserIDAssignmentAPI(), checkTokenPublicOnly()) m.Group("/actor", func() { m.Get("", activitypub.Actor) - m.Post("/inbox", activitypub.ActorInbox) + m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.ActorInbox) }) m.Group("/repository-id/{repository-id}", func() { - m.Get("", activitypub.Repository) + m.Get("", activitypub.ReqHTTPSignature(), activitypub.Repository) m.Post("/inbox", bind(forgefed.ForgeLike{}), - // TODO: activitypub.ReqHTTPSignature(), + activitypub.ReqHTTPSignature(), activitypub.RepositoryInbox) }, context.RepositoryIDAssignmentAPI()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub)) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 6263360a8e..7474df4fd9 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -231,9 +231,9 @@ func CreateBranch(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "GetCommit", err) return } - } else if len(opt.OldBranchName) > 0 { //nolint - if ctx.Repo.GitRepo.IsBranchExist(opt.OldBranchName) { //nolint - oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(opt.OldBranchName) //nolint + } else if len(opt.OldBranchName) > 0 { //nolint:staticcheck + if ctx.Repo.GitRepo.IsBranchExist(opt.OldBranchName) { //nolint:staticcheck + oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(opt.OldBranchName) //nolint:staticcheck if err != nil { ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err) return diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index fb71d76388..dd1d7b3a9a 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -437,7 +437,7 @@ func canWriteFiles(ctx *context.APIContext, branch string) bool { // canReadFiles returns true if repository is readable and user has proper access level. func canReadFiles(r *context.Repository) bool { - return r.Permission.CanRead(unit.TypeCode) + return r.CanRead(unit.TypeCode) } func base64Reader(s string) (io.ReadSeeker, error) { diff --git a/routers/api/v1/repo/hook_test.go b/routers/api/v1/repo/hook_test.go index 77c86388f5..52d2245f03 100644 --- a/routers/api/v1/repo/hook_test.go +++ b/routers/api/v1/repo/hook_test.go @@ -25,7 +25,7 @@ func TestTestHook(t *testing.T) { defer ctx.Repo.GitRepo.Close() contexttest.LoadRepoCommit(t, ctx) TestHook(ctx) - assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status()) + assert.Equal(t, http.StatusNoContent, ctx.Resp.Status()) unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{ HookID: 1, diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go index bed1e7ecf9..7bf1d3c67c 100644 --- a/routers/api/v1/repo/issue_dependency.go +++ b/routers/api/v1/repo/issue_dependency.go @@ -72,7 +72,7 @@ func GetIssueDependencies(ctx *context.APIContext) { } // 1. We must be able to read this issue - if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) { + if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) { ctx.NotFound() return } @@ -88,7 +88,7 @@ func GetIssueDependencies(ctx *context.APIContext) { limit = setting.API.MaxResponseItems } - canWrite := ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull) + canWrite := ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) blockerIssues := make([]*issues_model.Issue, 0, limit) @@ -123,7 +123,7 @@ func GetIssueDependencies(ctx *context.APIContext) { } // check permission - if !perm.CanReadIssuesOrPulls(blocker.Issue.IsPull) { + if !perm.CanReadIssuesOrPulls(blocker.IsPull) { if !canWrite { hiddenBlocker := &issues_model.DependencyInfo{ Issue: issues_model.Issue{ @@ -134,19 +134,19 @@ func GetIssueDependencies(ctx *context.APIContext) { } else { confidentialBlocker := &issues_model.DependencyInfo{ Issue: issues_model.Issue{ - RepoID: blocker.Issue.RepoID, + RepoID: blocker.RepoID, Index: blocker.Index, Title: blocker.Title, IsClosed: blocker.IsClosed, IsPull: blocker.IsPull, }, Repository: repo_model.Repository{ - ID: blocker.Issue.Repo.ID, - Name: blocker.Issue.Repo.Name, - OwnerName: blocker.Issue.Repo.OwnerName, + ID: blocker.Repo.ID, + Name: blocker.Repo.Name, + OwnerName: blocker.Repo.OwnerName, }, } - confidentialBlocker.Issue.Repo = &confidentialBlocker.Repository + confidentialBlocker.Repo = &confidentialBlocker.Repository blocker = confidentialBlocker } } @@ -323,7 +323,7 @@ func GetIssueBlocks(ctx *context.APIContext) { return } - if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) { + if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) { ctx.NotFound() return } @@ -373,11 +373,11 @@ func GetIssueBlocks(ctx *context.APIContext) { repoPerms[depMeta.RepoID] = perm } - if !perm.CanReadIssuesOrPulls(depMeta.Issue.IsPull) { + if !perm.CanReadIssuesOrPulls(depMeta.IsPull) { continue } - depMeta.Issue.Repo = &depMeta.Repository + depMeta.Repo = &depMeta.Repository issues = append(issues, &depMeta.Issue) } diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go index 69eeb1cfdf..024376c146 100644 --- a/routers/api/v1/repo/repo_test.go +++ b/routers/api/v1/repo/repo_test.go @@ -58,7 +58,7 @@ func TestRepoEdit(t *testing.T) { web.SetForm(ctx, &opts) Edit(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ ID: 1, }, unittest.Cond("name = ? AND is_archived = 1", *opts.Name)) @@ -78,7 +78,7 @@ func TestRepoEditNameChange(t *testing.T) { web.SetForm(ctx, &opts) Edit(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ ID: 1, diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index 3aecf5fc3a..7119de9b2e 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -24,9 +24,10 @@ import ( // appendPrivateInformation appends the owner and key type information to api.PublicKey func appendPrivateInformation(ctx std_ctx.Context, apiKey *api.PublicKey, key *asymkey_model.PublicKey, defaultUser *user_model.User) (*api.PublicKey, error) { - if key.Type == asymkey_model.KeyTypeDeploy { + switch key.Type { + case asymkey_model.KeyTypeDeploy: apiKey.KeyType = "deploy" - } else if key.Type == asymkey_model.KeyTypeUser { + case asymkey_model.KeyTypeUser: apiKey.KeyType = "user" if defaultUser.ID == key.OwnerID { @@ -38,7 +39,7 @@ func appendPrivateInformation(ctx std_ctx.Context, apiKey *api.PublicKey, key *a } apiKey.Owner = convert.ToUser(ctx, user, user) } - } else { + default: apiKey.KeyType = "unknown" } apiKey.ReadOnly = key.Mode == perm.AccessModeRead diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index 7b326812a7..94dd3931e4 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -155,7 +155,7 @@ func ListMyRepos(ctx *context.APIContext) { results[i] = convert.ToRepo(ctx, repo, permission) } - ctx.SetLinkHeader(int(count), opts.ListOptions.PageSize) + ctx.SetLinkHeader(int(count), opts.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, &results) } diff --git a/routers/common/db.go b/routers/common/db.go index 0646071264..0f78d8debc 100644 --- a/routers/common/db.go +++ b/routers/common/db.go @@ -28,7 +28,7 @@ func InitDBEngine(ctx context.Context) (err error) { default: } log.Info("ORM engine initialization attempt #%d/%d...", i+1, setting.Database.DBConnectRetries) - if err = db.InitEngineWithMigration(ctx, migrateWithSetting); err == nil { + if err = db.InitEngineWithMigration(ctx, func(eng db.Engine) error { return migrateWithSetting(eng.(*xorm.Engine)) }); err == nil { break } else if i == setting.Database.DBConnectRetries-1 { return err diff --git a/routers/install/install.go b/routers/install/install.go index b9333a9e16..f64f395a7f 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -361,7 +361,8 @@ func SubmitInstall(ctx *context.Context) { } // Init the engine with migration - if err = db.InitEngineWithMigration(ctx, migrations.Migrate); err != nil { + // Wrap migrations.Migrate into a function of type func(db.Engine) error to fix diagnostics. + if err = db.InitEngineWithMigration(ctx, migrations.WrapperMigrate); err != nil { db.UnsetDefaultEngine() ctx.Data["Err_DbSetting"] = true ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, &form) @@ -407,11 +408,7 @@ func SubmitInstall(ctx *context.Context) { if form.LFSRootPath != "" { cfg.Section("server").Key("LFS_START_SERVER").SetValue("true") cfg.Section("lfs").Key("PATH").SetValue(form.LFSRootPath) - var lfsJwtSecret string - if _, lfsJwtSecret, err = generate.NewJwtSecret(); err != nil { - ctx.RenderWithErr(ctx.Tr("install.lfs_jwt_secret_failed", err), tplInstall, &form) - return - } + _, lfsJwtSecret := generate.NewJwtSecret() cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(lfsJwtSecret) } else { cfg.Section("server").Key("LFS_START_SERVER").SetValue("false") @@ -482,11 +479,7 @@ func SubmitInstall(ctx *context.Context) { // FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET" // see the "loadOAuth2From" in "setting/oauth2.go" if !cfg.Section("oauth2").HasKey("JWT_SECRET") && !cfg.Section("oauth2").HasKey("JWT_SECRET_URI") { - _, jwtSecretBase64, err := generate.NewJwtSecret() - if err != nil { - ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form) - return - } + _, jwtSecretBase64 := generate.NewJwtSecret() cfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64) } @@ -587,7 +580,7 @@ func SubmitInstall(ctx *context.Context) { go func() { // Sleep for a while to make sure the user's browser has loaded the post-install page and its assets (images, css, js) - // What if this duration is not long enough? That's impossible -- if the user can't load the simple page in time, how could they install or use Gitea in the future .... + // What if this duration is not long enough? That's impossible -- if the user can't load the simple page in time, how could they install or use Forgejo in the future .... time.Sleep(3 * time.Second) // Now get the http.Server from this request and shut it down diff --git a/routers/install/routes_test.go b/routers/install/routes_test.go index a504cf1baa..7324e912c8 100644 --- a/routers/install/routes_test.go +++ b/routers/install/routes_test.go @@ -19,18 +19,18 @@ func TestRoutes(t *testing.T) { w := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) r.ServeHTTP(w, req) - assert.EqualValues(t, 200, w.Code) + assert.Equal(t, 200, w.Code) assert.Contains(t, w.Body.String(), `class="page-content install"`) w = httptest.NewRecorder() req = httptest.NewRequest("GET", "/no-such", nil) r.ServeHTTP(w, req) - assert.EqualValues(t, 404, w.Code) + assert.Equal(t, 404, w.Code) w = httptest.NewRecorder() req = httptest.NewRequest("GET", "/assets/img/gitea.svg", nil) r.ServeHTTP(w, req) - assert.EqualValues(t, 200, w.Code) + assert.Equal(t, 200, w.Code) } func TestMain(m *testing.M) { diff --git a/routers/private/hook_post_receive_test.go b/routers/private/hook_post_receive_test.go index bbd0c45769..dde4ec08f4 100644 --- a/routers/private/hook_post_receive_test.go +++ b/routers/private/hook_post_receive_test.go @@ -44,7 +44,7 @@ func TestHandlePullRequestMerging(t *testing.T) { pr, err = issues_model.GetPullRequestByID(db.DefaultContext, pr.ID) require.NoError(t, err) assert.True(t, pr.HasMerged) - assert.EqualValues(t, "01234567", pr.MergedCommitID) + assert.Equal(t, "01234567", pr.MergedCommitID) unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{ID: autoMerge.ID}) } diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 191273209e..4c0e9a8551 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -155,7 +155,7 @@ func (ctx *preReceiveContext) checkQuota() error { return nil } - ok, err := quota_model.EvaluateForUser(ctx, ctx.PrivateContext.Repo.Repository.OwnerID, quota_model.LimitSubjectSizeReposAll) + ok, err := quota_model.EvaluateForUser(ctx, ctx.Repo.Repository.OwnerID, quota_model.LimitSubjectSizeReposAll) if err != nil { log.Error("quota_model.EvaluateForUser: %v", err) ctx.JSON(http.StatusInternalServerError, private.Response{ @@ -531,10 +531,7 @@ func preReceiveFor(ctx *preReceiveContext, oldCommitID, newCommitID string, refF baseBranchName := refFullName.ForBranchName() - baseBranchExist := false - if ctx.Repo.GitRepo.IsBranchExist(baseBranchName) { - baseBranchExist = true - } + baseBranchExist := ctx.Repo.GitRepo.IsBranchExist(baseBranchName) if !baseBranchExist { for p, v := range baseBranchName { diff --git a/routers/web/admin/admin_test.go b/routers/web/admin/admin_test.go index d0c3c2b56f..0bad4402aa 100644 --- a/routers/web/admin/admin_test.go +++ b/routers/web/admin/admin_test.go @@ -69,7 +69,7 @@ func TestShadowPassword(t *testing.T) { } for _, k := range kases { - assert.EqualValues(t, k.Result, shadowPassword(k.Provider, k.CfgItem)) + assert.Equal(t, k.Result, shadowPassword(k.Provider, k.CfgItem)) } } diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index f53a0197cb..cefdfadc53 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -22,7 +22,6 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/optional" "forgejo.org/modules/setting" - "forgejo.org/modules/util" "forgejo.org/modules/validation" "forgejo.org/modules/web" "forgejo.org/routers/web/explore" @@ -77,11 +76,11 @@ func Users(ctx *context.Context) { PageSize: setting.UI.Admin.UserPagingNum, }, SearchByEmail: true, - IsActive: util.OptionalBoolParse(statusFilterMap["is_active"]), - IsAdmin: util.OptionalBoolParse(statusFilterMap["is_admin"]), - IsRestricted: util.OptionalBoolParse(statusFilterMap["is_restricted"]), - IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]), - IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]), + IsActive: optional.ParseBool(statusFilterMap["is_active"]), + IsAdmin: optional.ParseBool(statusFilterMap["is_admin"]), + IsRestricted: optional.ParseBool(statusFilterMap["is_restricted"]), + IsTwoFactorEnabled: optional.ParseBool(statusFilterMap["is_2fa_enabled"]), + IsProhibitLogin: optional.ParseBool(statusFilterMap["is_prohibit_login"]), IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones Load2FAStatus: true, ExtraParamStrings: extraParamStrings, diff --git a/routers/web/auth/2fa.go b/routers/web/auth/2fa.go index 7acf9a87d3..ff769ffd5d 100644 --- a/routers/web/auth/2fa.go +++ b/routers/web/auth/2fa.go @@ -133,11 +133,7 @@ func TwoFactorScratchPost(ctx *context.Context) { // Validate the passcode with the stored TOTP secret. if twofa.VerifyScratchToken(form.Token) { // Invalidate the scratch token. - _, err = twofa.GenerateScratchToken() - if err != nil { - ctx.ServerError("UserSignIn", err) - return - } + twofa.GenerateScratchToken() if err = auth.UpdateTwoFactor(ctx, twofa); err != nil { ctx.ServerError("UserSignIn", err) return diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 755126b8e0..722091a606 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -512,7 +512,8 @@ func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form any func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) (ok bool) { if err := user_model.CreateUser(ctx, u, overwrites); err != nil { if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) { - if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto { + switch setting.OAuth2Client.AccountLinking { + case setting.OAuth2AccountLinkingAuto: var user *user_model.User user = &user_model.User{Name: u.Name} hasUser, err := user_model.GetUser(ctx, user) @@ -528,7 +529,7 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *us // TODO: probably we should respect 'remember' user's choice... linkAccount(ctx, user, *gothUser, true) return false // user is already created here, all redirects are handled - } else if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingLogin { + case setting.OAuth2AccountLinkingLogin: showLinkingLogin(ctx, *gothUser) return false // user will be created only after linking login } @@ -783,11 +784,7 @@ func ActivatePost(ctx *context.Context) { func handleAccountActivation(ctx *context.Context, user *user_model.User) { user.IsActive = true - var err error - if user.Rands, err = user_model.GetUserSalt(); err != nil { - ctx.ServerError("UpdateUser", err) - return - } + user.Rands = user_model.GetUserSalt() if err := user_model.UpdateUserCols(ctx, user, "is_active", "rands"); err != nil { if user_model.IsErrUserNotExist(err) { ctx.NotFound("UpdateUserCols", err) diff --git a/routers/web/auth/oauth_test.go b/routers/web/auth/oauth_test.go index 6275d63382..487b551d6c 100644 --- a/routers/web/auth/oauth_test.go +++ b/routers/web/auth/oauth_test.go @@ -71,7 +71,7 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) { assert.Equal(t, "user5", oidcToken.PreferredUsername) assert.Equal(t, "https://try.gitea.io/user5", oidcToken.Profile) assert.Equal(t, "https://try.gitea.io/assets/img/avatar_default.png", oidcToken.Picture) - assert.Equal(t, "", oidcToken.Website) + assert.Empty(t, oidcToken.Website) assert.Equal(t, timeutil.TimeStamp(0), oidcToken.UpdatedAt) assert.Equal(t, "user5@example.com", oidcToken.Email) assert.True(t, oidcToken.EmailVerified) diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go index 82c2d4e9d3..cb6b22e5b7 100644 --- a/routers/web/auth/password.go +++ b/routers/web/auth/password.go @@ -242,12 +242,8 @@ func ResetPasswdPost(ctx *context.Context) { if regenerateScratchToken { // Invalidate the scratch token. - _, err := twofa.GenerateScratchToken() - if err != nil { - ctx.ServerError("UserSignIn", err) - return - } - if err = auth.UpdateTwoFactor(ctx, twofa); err != nil { + twofa.GenerateScratchToken() + if err := auth.UpdateTwoFactor(ctx, twofa); err != nil { ctx.ServerError("UserSignIn", err) return } diff --git a/routers/web/devtest/devtest.go b/routers/web/devtest/devtest.go index 37496ca117..2ef030cb92 100644 --- a/routers/web/devtest/devtest.go +++ b/routers/web/devtest/devtest.go @@ -1,9 +1,11 @@ // Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package devtest import ( + "errors" "net/http" "path" "strings" @@ -42,6 +44,17 @@ func FetchActionTest(ctx *context.Context) { ctx.JSONRedirect("") } +func ErrorPage(ctx *context.Context) { + if ctx.Params("errcode") == "404" { + ctx.NotFound("Example error", errors.New("Example error")) + return + } else if ctx.Params("errcode") == "413" { + ctx.HTML(http.StatusRequestEntityTooLarge, base.TplName("status/413")) + return + } + ctx.ServerError("Example error", errors.New("Example error")) +} + func Tmpl(ctx *context.Context) { now := time.Now() ctx.Data["TimeNow"] = now diff --git a/routers/web/home.go b/routers/web/home.go index e0a466a81c..bd9942748a 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -1,5 +1,6 @@ // Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package web @@ -112,9 +113,3 @@ func HomeSitemap(ctx *context.Context) { log.Error("Failed writing sitemap: %v", err) } } - -// NotFound render 404 page -func NotFound(ctx *context.Context) { - ctx.Data["Title"] = "Page Not Found" - ctx.NotFound("home.NotFound", nil) -} diff --git a/routers/web/misc/misc.go b/routers/web/misc/misc.go index 306b15e3d5..2f1438b9d0 100644 --- a/routers/web/misc/misc.go +++ b/routers/web/misc/misc.go @@ -33,17 +33,96 @@ func DummyOK(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusOK) } -func RobotsTxt(w http.ResponseWriter, req *http.Request) { - robotsTxt := util.FilePathJoinAbs(setting.CustomPath, "public/robots.txt") - if ok, _ := util.IsExist(robotsTxt); !ok { - robotsTxt = util.FilePathJoinAbs(setting.CustomPath, "robots.txt") // the legacy "robots.txt" - } - httpcache.SetCacheControlInHeader(w.Header(), setting.StaticCacheTime) - http.ServeFile(w, req, robotsTxt) -} - func StaticRedirect(target string) func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) { http.Redirect(w, req, path.Join(setting.StaticURLPrefix, target), http.StatusMovedPermanently) } } + +var defaultRobotsTxt = []byte(`# The default Forgejo robots.txt +# For more information: https://forgejo.org/docs/latest/admin/search-engines-indexation/ + +User-agent: * +Disallow: /api/ +Disallow: /avatars/ +Disallow: /user/ +Disallow: /swagger.*.json +Disallow: /explore/*?* + +Disallow: /repo/create +Disallow: /repo/migrate +Disallow: /org/create +Disallow: /*/*/fork + +Disallow: /*/*/watchers +Disallow: /*/*/stargazers +Disallow: /*/*/forks + +Disallow: /*/*/src/ +Disallow: /*/*/blame/ +Disallow: /*/*/commit/ +Disallow: /*/*/commits/ +Disallow: /*/*/raw/ +Disallow: /*/*/media/ +Disallow: /*/*/tags +Disallow: /*/*/graph +Disallow: /*/*/branches +Disallow: /*/*/compare +Disallow: /*/*/lastcommit/ +Disallow: /*/*/rss/branch/ +Disallow: /*/*/atom/branch/ + +Disallow: /*/*/activity +Disallow: /*/*/activity_author_data + +Disallow: /*/*/actions +Disallow: /*/*/projects +Disallow: /*/*/labels +Disallow: /*/*/milestones + +Disallow: /*/*/find/ +Disallow: /*/*/tree-list/ +Disallow: /*/*/search/ +Disallow: /*/-/code + +Disallow: /*/*/issues/new +Disallow: /*/*/pulls/*/files +Disallow: /*/*/pulls/*/commits + +Disallow: /attachments/ +Disallow: /*/*/attachments/ +Disallow: /*/*/issues/*/attachments/ +Disallow: /*/*/pulls/*/attachments/ +Disallow: /*/*/releases/attachments +Disallow: /*/*/releases/download + +Disallow: /*/*/archive/ +Disallow: /*.bundle$ +Disallow: /*.patch$ +Disallow: /*.diff$ +Disallow: /*.atom$ +Disallow: /*.rss$ + +Disallow: /*lang=* +Disallow: /*redirect_to=* +Disallow: /*tab=* +Disallow: /*q=* +Disallow: /*sort=* +Disallow: /*repo-search-archived=* +`) + +func RobotsTxt(w http.ResponseWriter, req *http.Request) { + httpcache.SetCacheControlInHeader(w.Header(), setting.StaticCacheTime) + w.Header().Set("Content-Type", "text/plain") + + robotsTxt := util.FilePathJoinAbs(setting.CustomPath, "public/robots.txt") + if ok, _ := util.IsExist(robotsTxt); ok { + http.ServeFile(w, req, robotsTxt) + return + } + + _, err := w.Write(defaultRobotsTxt) + if err != nil { + log.Error("failed to write robots.txt: %v", err) + } +} diff --git a/routers/web/org/members.go b/routers/web/org/members.go index 51ac566e1a..65e2b032e8 100644 --- a/routers/web/org/members.go +++ b/routers/web/org/members.go @@ -60,8 +60,8 @@ func Members(ctx *context.Context) { } pager := context.NewPagination(int(total), setting.UI.MembersPagingNum, page, 5) - opts.ListOptions.Page = page - opts.ListOptions.PageSize = setting.UI.MembersPagingNum + opts.Page = page + opts.PageSize = setting.UI.MembersPagingNum members, membersIsPublic, err := organization.FindOrgMembers(ctx, opts) if err != nil { ctx.ServerError("GetMembers", err) diff --git a/routers/web/repo/action_aggregator_test.go b/routers/web/repo/action_aggregator_test.go index 7da14f1912..e1c6d37fd0 100644 --- a/routers/web/repo/action_aggregator_test.go +++ b/routers/web/repo/action_aggregator_test.go @@ -184,7 +184,7 @@ func (kase *testCase) doTest(t *testing.T) { cmt := issue.Comments[c] t.Logf("%v %v %v\n", cmt.Type, cmt.CreatedUnix, cmt.Content) } - assert.EqualValues(t, len(after), len(issue.Comments)) + assert.Len(t, issue.Comments, len(after)) t.Fail() return } @@ -214,7 +214,7 @@ func (kase *testCase) doTest(t *testing.T) { l.AssigneeTeamID = 0 } - assert.EqualValues(t, (after)[c], issue.Comments[c], + assert.Equal(t, (after)[c], issue.Comments[c], "Comment %v is not equal", c, ) } diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go index e46c08fef8..5b2eaef889 100644 --- a/routers/web/repo/attachment.go +++ b/routers/web/repo/attachment.go @@ -106,7 +106,7 @@ func ServeAttachment(ctx *context.Context, uuid string) { } if repository == nil { // If not linked - if !(ctx.IsSigned && attach.UploaderID == ctx.Doer.ID) { // We block if not the uploader + if !ctx.IsSigned || attach.UploaderID != ctx.Doer.ID { // We block if not the uploader ctx.Error(http.StatusNotFound) return } diff --git a/routers/web/repo/code_frequency.go b/routers/web/repo/code_frequency.go index 04009b4afa..44c07e617e 100644 --- a/routers/web/repo/code_frequency.go +++ b/routers/web/repo/code_frequency.go @@ -34,7 +34,7 @@ func CodeFrequencyData(ctx *context.Context) { ctx.Status(http.StatusAccepted) return } - ctx.ServerError("GetCodeFrequencyData", err) + ctx.ServerError("GetContributorStats", err) } else { ctx.JSON(http.StatusOK, contributorStats["total"].Weeks) } diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index db65e889e0..cd49a33b99 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -597,7 +597,7 @@ func PrepareCompareDiff( config := unit.PullRequestsConfig() if !config.AutodetectManualMerge { - allowEmptyPr := !(ci.BaseBranch == ci.HeadBranch && ctx.Repo.Repository.Name == ci.HeadRepo.Name) + allowEmptyPr := ci.BaseBranch != ci.HeadBranch || ctx.Repo.Repository.Name != ci.HeadRepo.Name ctx.Data["AllowEmptyPr"] = allowEmptyPr return !allowEmptyPr @@ -660,9 +660,9 @@ func PrepareCompareDiff( if len(commits) == 1 { c := commits[0] - title = strings.TrimSpace(c.UserCommit.Summary()) + title = strings.TrimSpace(c.Summary()) - body := strings.Split(strings.TrimSpace(c.UserCommit.Message()), "\n") + body := strings.Split(strings.TrimSpace(c.Message()), "\n") if len(body) > 1 { ctx.Data["content"] = strings.Join(body[1:], "\n") } @@ -952,9 +952,10 @@ func ExcerptBlob(ctx *context.Context) { RightHunkSize: rightHunkSize, }, } - if direction == "up" { + switch direction { + case "up": section.Lines = append([]*gitdiff.DiffLine{lineSection}, section.Lines...) - } else if direction == "down" { + case "down": section.Lines = append(section.Lines, lineSection) } } @@ -966,7 +967,7 @@ func ExcerptBlob(ctx *context.Context) { } func getExcerptLines(commit *git.Commit, filePath string, idxLeft, idxRight, chunkSize int) ([]*gitdiff.DiffLine, error) { - blob, err := commit.Tree.GetBlobByPath(filePath) + blob, err := commit.GetBlobByPath(filePath) if err != nil { return nil, err } diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 77f71fd2b1..5114cc9c05 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -585,7 +585,7 @@ func DeleteFilePost(ctx *context.Context) { ctx.Error(http.StatusInternalServerError, err.Error()) } } else if models.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) { - ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form) + ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form) } else if git.IsErrPushRejected(err) { errPushRej := err.(*git.ErrPushRejected) if len(errPushRej.Message) == 0 { diff --git a/routers/web/repo/editor_test.go b/routers/web/repo/editor_test.go index 5b893cf258..b5d40abdab 100644 --- a/routers/web/repo/editor_test.go +++ b/routers/web/repo/editor_test.go @@ -37,7 +37,7 @@ func TestCleanUploadName(t *testing.T) { "..a.dotty../.folder../.name...": "..a.dotty../.folder../.name...", } for k, v := range kases { - assert.EqualValues(t, cleanUploadFileName(k), v) + assert.Equal(t, cleanUploadFileName(k), v) } } diff --git a/routers/web/repo/githttp_test.go b/routers/web/repo/githttp_test.go index 5ba8de3d63..0164b11f66 100644 --- a/routers/web/repo/githttp_test.go +++ b/routers/web/repo/githttp_test.go @@ -37,6 +37,6 @@ func TestContainsParentDirectorySeparator(t *testing.T) { } for i := range tests { - assert.EqualValues(t, tests[i].b, containsParentDirectorySeparator(tests[i].v)) + assert.Equal(t, tests[i].b, containsParentDirectorySeparator(tests[i].v)) } } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 2df8eb0a02..b97c268ae2 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -187,9 +187,10 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt // 0 means issues with no label // blank means labels will not be filtered for issues selectLabels := ctx.FormString("labels") - if selectLabels == "" { + switch selectLabels { + case "": ctx.Data["AllLabels"] = true - } else if selectLabels == "0" { + case "0": ctx.Data["NoLabel"] = true } if len(selectLabels) > 0 { @@ -426,9 +427,10 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt return 0 } reviewTyp := issues_model.ReviewTypeApprove - if typ == "reject" { + switch typ { + case "reject": reviewTyp = issues_model.ReviewTypeReject - } else if typ == "waiting" { + case "waiting": reviewTyp = issues_model.ReviewTypeRequest } for _, count := range counts { @@ -1311,7 +1313,7 @@ func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *use } // Special user that can't have associated contributions and permissions in the repo. - if poster.IsGhost() || poster.IsActions() || poster.IsAPActor() { + if poster.IsGhost() || poster.IsActions() || poster.IsAPServerActor() { return roleDescriptor, nil } @@ -2129,7 +2131,7 @@ func checkBlockedByIssues(ctx *context.Context, blockers []*issues_model.Depende } repoPerms[blocker.RepoID] = perm } - if perm.CanReadIssuesOrPulls(blocker.Issue.IsPull) { + if perm.CanReadIssuesOrPulls(blocker.IsPull) { canRead = append(canRead, blocker) } else { notPermitted = append(notPermitted, blocker) @@ -3118,7 +3120,7 @@ func NewComment(ctx *context.Context) { // Check if issue admin/poster changes the status of issue. if (ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) || (ctx.IsSigned && issue.IsPoster(ctx.Doer.ID))) && (form.Status == "reopen" || form.Status == "close") && - !(issue.IsPull && issue.PullRequest.HasMerged) { + (!issue.IsPull || !issue.PullRequest.HasMerged) { // Duplication and conflict check should apply to reopen pull request. var pr *issues_model.PullRequest @@ -3254,11 +3256,7 @@ func NewComment(ctx *context.Context) { comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Content, attachments) if err != nil { if errors.Is(err, user_model.ErrBlockedByUser) { - if issue.IsPull { - ctx.JSONError(ctx.Tr("repo.pulls.comment.blocked_by_user")) - } else { - ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_by_user")) - } + ctx.JSONError(ctx.Tr("repo.comment.blocked_by_user")) } else { ctx.ServerError("CreateIssueComment", err) } @@ -3633,7 +3631,7 @@ func GetCommentAttachments(ctx *context.Context) { return } - if !ctx.Repo.Permission.CanReadIssuesOrPulls(comment.Issue.IsPull) { + if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) { ctx.NotFound("CanReadIssuesOrPulls", issues_model.ErrCommentNotExist{}) return } diff --git a/routers/web/repo/issue_content_history.go b/routers/web/repo/issue_content_history.go index 5c71d75f80..11d0de90de 100644 --- a/routers/web/repo/issue_content_history.go +++ b/routers/web/repo/issue_content_history.go @@ -160,15 +160,16 @@ func GetContentHistoryDetail(ctx *context.Context) { diffHTMLBuf := bytes.Buffer{} diffHTMLBuf.WriteString("<pre class='chroma'>") for _, it := range diff { - if it.Type == diffmatchpatch.DiffInsert { + switch it.Type { + case diffmatchpatch.DiffInsert: diffHTMLBuf.WriteString("<span class='gi'>") diffHTMLBuf.WriteString(html.EscapeString(it.Text)) diffHTMLBuf.WriteString("</span>") - } else if it.Type == diffmatchpatch.DiffDelete { + case diffmatchpatch.DiffDelete: diffHTMLBuf.WriteString("<span class='gd'>") diffHTMLBuf.WriteString(html.EscapeString(it.Text)) diffHTMLBuf.WriteString("</span>") - } else { + default: diffHTMLBuf.WriteString(html.EscapeString(it.Text)) } } diff --git a/routers/web/repo/issue_label_test.go b/routers/web/repo/issue_label_test.go index 406ab4918c..0adcc39499 100644 --- a/routers/web/repo/issue_label_test.go +++ b/routers/web/repo/issue_label_test.go @@ -39,7 +39,7 @@ func TestInitializeLabels(t *testing.T) { contexttest.LoadRepo(t, ctx, 2) web.SetForm(ctx, &forms.InitializeLabelsForm{TemplateName: "Default"}) InitializeLabels(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ RepoID: 2, Name: "enhancement", @@ -69,7 +69,7 @@ func TestRetrieveLabels(t *testing.T) { assert.True(t, ok) if assert.Len(t, labels, len(testCase.ExpectedLabelIDs)) { for i, label := range labels { - assert.EqualValues(t, testCase.ExpectedLabelIDs[i], label.ID) + assert.Equal(t, testCase.ExpectedLabelIDs[i], label.ID) } } } @@ -85,7 +85,7 @@ func TestNewLabel(t *testing.T) { Color: "#abcdef", }) NewLabel(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ Name: "newlabel", Color: "#abcdef", @@ -105,7 +105,7 @@ func TestUpdateLabel(t *testing.T) { IsArchived: true, }) UpdateLabel(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ ID: 2, Name: "newnameforlabel", @@ -121,7 +121,7 @@ func TestDeleteLabel(t *testing.T) { contexttest.LoadRepo(t, ctx, 1) ctx.Req.Form.Set("id", "2") DeleteLabel(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) unittest.AssertNotExistsBean(t, &issues_model.Label{ID: 2}) unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{LabelID: 2}) assert.EqualValues(t, ctx.Tr("repo.issues.label_deletion_success"), ctx.Flash.SuccessMsg) @@ -135,7 +135,7 @@ func TestUpdateIssueLabel_Clear(t *testing.T) { ctx.Req.Form.Set("issue_ids", "1,3") ctx.Req.Form.Set("action", "clear") UpdateIssueLabel(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: 1}) unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: 3}) unittest.CheckConsistencyFor(t, &issues_model.Label{}) @@ -161,7 +161,7 @@ func TestUpdateIssueLabel_Toggle(t *testing.T) { ctx.Req.Form.Set("action", testCase.Action) ctx.Req.Form.Set("id", strconv.Itoa(int(testCase.LabelID))) UpdateIssueLabel(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) for _, issueID := range testCase.IssueIDs { unittest.AssertExistsIf(t, testCase.ExpectedAdd, &issues_model.IssueLabel{ IssueID: issueID, diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 80f699787c..e5bd06e987 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -120,7 +120,7 @@ func Projects(ctx *context.Context) { pager.AddParam(ctx, "state", "State") ctx.Data["Page"] = pager - ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) + ctx.Data["CanWriteProjects"] = ctx.Repo.CanWrite(unit.TypeProjects) ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["IsProjectsPage"] = true ctx.Data["SortType"] = sortType @@ -146,7 +146,7 @@ func RenderNewProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") ctx.Data["TemplateConfigs"] = project_model.GetTemplateConfigs() ctx.Data["CardTypes"] = project_model.GetCardConfig() - ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) + ctx.Data["CanWriteProjects"] = ctx.Repo.CanWrite(unit.TypeProjects) ctx.Data["CancelLink"] = ctx.Repo.Repository.Link() + "/projects" ctx.HTML(http.StatusOK, tplProjectsNew) } @@ -228,7 +228,7 @@ func DeleteProject(ctx *context.Context) { func RenderEditProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.edit") ctx.Data["PageIsEditProjects"] = true - ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) + ctx.Data["CanWriteProjects"] = ctx.Repo.CanWrite(unit.TypeProjects) ctx.Data["CardTypes"] = project_model.GetCardConfig() p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) @@ -262,7 +262,7 @@ func EditProjectPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.edit") ctx.Data["PageIsEditProjects"] = true - ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) + ctx.Data["CanWriteProjects"] = ctx.Repo.CanWrite(unit.TypeProjects) ctx.Data["CardTypes"] = project_model.GetCardConfig() ctx.Data["CancelLink"] = project_model.ProjectLinkForRepo(ctx.Repo.Repository, projectID) @@ -378,7 +378,7 @@ func ViewProject(ctx *context.Context) { ctx.Data["Title"] = project.Title ctx.Data["IsProjectsPage"] = true - ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) + ctx.Data["CanWriteProjects"] = ctx.Repo.CanWrite(unit.TypeProjects) ctx.Data["Project"] = project ctx.Data["IssuesMap"] = issuesMap ctx.Data["Columns"] = columns diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index a54a31ac36..7ae7fd416c 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -892,7 +892,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi foundStartCommit := len(specifiedStartCommit) == 0 foundEndCommit := len(specifiedEndCommit) == 0 - if !(foundStartCommit && foundEndCommit) { + if !foundStartCommit || !foundEndCommit { for _, commit := range prInfo.Commits { if commit.ID.String() == specifiedStartCommit { foundStartCommit = true @@ -907,7 +907,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi } } - if !(foundStartCommit && foundEndCommit) { + if !foundStartCommit || !foundEndCommit { ctx.NotFound("Given SHA1 not found for this PR", nil) return } @@ -1321,8 +1321,8 @@ func MergePullRequest(ctx *context.Context) { } else if models.IsErrMergeConflicts(err) { conflictError := err.(models.ErrMergeConflicts) flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ - "Message": ctx.Tr("repo.editor.merge_conflict"), - "Summary": ctx.Tr("repo.editor.merge_conflict_summary"), + "Message": ctx.Tr("repo.pulls.merge_conflict"), + "Summary": ctx.Tr("repo.pulls.merge_conflict_summary"), "Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "<br>" + utils.SanitizeFlashErrorString(conflictError.StdOut), }) if err != nil { diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index 18a5b872f1..941e428039 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -211,9 +211,10 @@ func renderConversation(ctx *context.Context, comment *issues_model.Comment, ori return } ctx.Data["AfterCommitID"] = pullHeadCommitID - if origin == "diff" { + switch origin { + case "diff": ctx.HTML(http.StatusOK, tplDiffConversation) - } else if origin == "timeline" { + case "timeline": ctx.HTML(http.StatusOK, tplTimelineConversation) } } diff --git a/routers/web/repo/recent_commits.go b/routers/web/repo/recent_commits.go index 6154de7377..211b1b2b12 100644 --- a/routers/web/repo/recent_commits.go +++ b/routers/web/repo/recent_commits.go @@ -4,12 +4,10 @@ package repo import ( - "errors" "net/http" "forgejo.org/modules/base" "forgejo.org/services/context" - contributors_service "forgejo.org/services/repository" ) const ( @@ -26,16 +24,3 @@ func RecentCommits(ctx *context.Context) { ctx.HTML(http.StatusOK, tplRecentCommits) } - -// RecentCommitsData returns JSON of recent commits data -func RecentCommitsData(ctx *context.Context) { - if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil { - if errors.Is(err, contributors_service.ErrAwaitGeneration) { - ctx.Status(http.StatusAccepted) - return - } - ctx.ServerError("RecentCommitsData", err) - } else { - ctx.JSON(http.StatusOK, contributorStats["total"].Weeks) - } -} diff --git a/routers/web/repo/setting/avatar.go b/routers/web/repo/setting/avatar.go index abbb12cacb..84d7cccdb8 100644 --- a/routers/web/repo/setting/avatar.go +++ b/routers/web/repo/setting/avatar.go @@ -46,7 +46,7 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error { return fmt.Errorf("io.ReadAll: %w", err) } st := typesniffer.DetectContentType(data) - if !(st.IsImage() && !st.IsSvgImage()) { + if !st.IsImage() || st.IsSvgImage() { return errors.New(ctx.Locale.TrString("settings.uploaded_avatar_not_a_image")) } if err = repo_service.UploadAvatar(ctx, ctxRepo, data); err != nil { diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 2af2eef4a5..083cc4ae82 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -105,6 +105,10 @@ func Units(ctx *context.Context) { func UnitsPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.RepoUnitSettingForm) + if ctx.HasError() { + ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/units") + return + } repo := ctx.Repo.Repository diff --git a/routers/web/repo/setting/settings_test.go b/routers/web/repo/setting/settings_test.go index 6f05953bfb..8b37eb92cb 100644 --- a/routers/web/repo/setting/settings_test.go +++ b/routers/web/repo/setting/settings_test.go @@ -55,7 +55,7 @@ func TestAddReadOnlyDeployKey(t *testing.T) { } web.SetForm(ctx, &addKeyForm) DeployKeysPost(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) unittest.AssertExistsAndLoadBean(t, &asymkey_model.DeployKey{ Name: addKeyForm.Title, @@ -85,7 +85,7 @@ func TestAddReadWriteOnlyDeployKey(t *testing.T) { } web.SetForm(ctx, &addKeyForm) DeployKeysPost(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) unittest.AssertExistsAndLoadBean(t, &asymkey_model.DeployKey{ Name: addKeyForm.Title, @@ -124,7 +124,7 @@ func TestCollaborationPost(t *testing.T) { CollaborationPost(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) exists, err := repo_model.IsCollaborator(ctx, re.ID, 4) require.NoError(t, err) @@ -150,7 +150,7 @@ func TestCollaborationPost_InactiveUser(t *testing.T) { CollaborationPost(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) assert.NotEmpty(t, ctx.Flash.ErrorMsg) } @@ -184,7 +184,7 @@ func TestCollaborationPost_AddCollaboratorTwice(t *testing.T) { CollaborationPost(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) exists, err := repo_model.IsCollaborator(ctx, re.ID, 4) require.NoError(t, err) @@ -193,7 +193,7 @@ func TestCollaborationPost_AddCollaboratorTwice(t *testing.T) { // Try adding the same collaborator again CollaborationPost(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) assert.NotEmpty(t, ctx.Flash.ErrorMsg) } @@ -215,7 +215,7 @@ func TestCollaborationPost_NonExistentUser(t *testing.T) { CollaborationPost(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) assert.NotEmpty(t, ctx.Flash.ErrorMsg) } @@ -255,7 +255,7 @@ func TestAddTeamPost(t *testing.T) { AddTeamPost(ctx) assert.True(t, repo_service.HasRepository(db.DefaultContext, team, re.ID)) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) assert.Empty(t, ctx.Flash.ErrorMsg) } @@ -295,7 +295,7 @@ func TestAddTeamPost_NotAllowed(t *testing.T) { AddTeamPost(ctx) assert.False(t, repo_service.HasRepository(db.DefaultContext, team, re.ID)) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) assert.NotEmpty(t, ctx.Flash.ErrorMsg) } @@ -336,7 +336,7 @@ func TestAddTeamPost_AddTeamTwice(t *testing.T) { AddTeamPost(ctx) assert.True(t, repo_service.HasRepository(db.DefaultContext, team, re.ID)) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) assert.NotEmpty(t, ctx.Flash.ErrorMsg) } @@ -369,7 +369,7 @@ func TestAddTeamPost_NonExistentTeam(t *testing.T) { ctx.Repo = repo AddTeamPost(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) assert.NotEmpty(t, ctx.Flash.ErrorMsg) } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index b7a7a3f042..bea002f690 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -1044,7 +1044,15 @@ func renderHomeCode(ctx *context.Context) { return } - if entry.IsDir() { + if entry.IsSubModule() { + subModuleURL, err := ctx.Repo.Commit.GetSubModule(entry.Name()) + if err != nil { + HandleGitError(ctx, "Repo.Commit.GetSubModule", err) + return + } + subModuleFile := git.NewSubModuleFile(ctx.Repo.Commit, subModuleURL, entry.ID.String()) + ctx.Redirect(subModuleFile.RefURL(setting.AppURL, ctx.Repo.Repository.FullName(), setting.SSH.Domain)) + } else if entry.IsDir() { renderDirectory(ctx) } else { renderFile(ctx, entry) diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go index cba416fc92..5709b32257 100644 --- a/routers/web/repo/wiki_test.go +++ b/routers/web/repo/wiki_test.go @@ -73,7 +73,7 @@ func assertPagesMetas(t *testing.T, expectedNames []string, metas any) { return } for i, pageMeta := range pageMetas { - assert.EqualValues(t, expectedNames[i], pageMeta.Name) + assert.Equal(t, expectedNames[i], pageMeta.Name) } } @@ -84,7 +84,7 @@ func TestWiki(t *testing.T) { ctx.SetParams("*", "Home") contexttest.LoadRepo(t, ctx, 1) Wiki(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, "Home", ctx.Data["Title"]) assertPagesMetas(t, []string{"Home", "Long Page", "Page With Image", "Page With Spaced Name", "Unescaped File", "XSS"}, ctx.Data["Pages"]) } @@ -95,7 +95,7 @@ func TestWikiPages(t *testing.T) { ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki/?action=_pages") contexttest.LoadRepo(t, ctx, 1) WikiPages(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assertPagesMetas(t, []string{"Home", "Long Page", "Page With Image", "Page With Spaced Name", "Unescaped File", "XSS"}, ctx.Data["Pages"]) } @@ -106,7 +106,7 @@ func TestNewWiki(t *testing.T) { contexttest.LoadUser(t, ctx, 2) contexttest.LoadRepo(t, ctx, 1) NewWiki(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, ctx.Tr("repo.wiki.new_page"), ctx.Data["Title"]) } @@ -126,7 +126,7 @@ func TestNewWikiPost(t *testing.T) { Message: message, }) NewWikiPost(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) assertWikiExists(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title)) assert.Equal(t, content, wikiContent(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title))) } @@ -144,7 +144,7 @@ func TestNewWikiPost_ReservedName(t *testing.T) { Message: message, }) NewWikiPost(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, ctx.Tr("repo.wiki.reserved_page"), ctx.Flash.ErrorMsg) assertWikiNotExists(t, ctx.Repo.Repository, "_edit") } @@ -157,7 +157,7 @@ func TestEditWiki(t *testing.T) { contexttest.LoadUser(t, ctx, 2) contexttest.LoadRepo(t, ctx, 1) EditWiki(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, "Home", ctx.Data["Title"]) assert.Equal(t, wikiContent(t, ctx.Repo.Repository, "Home"), ctx.Data["content"]) } @@ -178,7 +178,7 @@ func TestEditWikiPost(t *testing.T) { Message: message, }) EditWikiPost(ctx) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) assertWikiExists(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title)) assert.Equal(t, content, wikiContent(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title))) if title != "Home" { @@ -194,7 +194,7 @@ func TestDeleteWikiPagePost(t *testing.T) { contexttest.LoadUser(t, ctx, 2) contexttest.LoadRepo(t, ctx, 1) DeleteWikiPagePost(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assertWikiNotExists(t, ctx.Repo.Repository, "Home") } @@ -215,10 +215,10 @@ func TestWikiRaw(t *testing.T) { contexttest.LoadRepo(t, ctx, 1) WikiRaw(ctx) if filetype == "" { - assert.EqualValues(t, http.StatusNotFound, ctx.Resp.Status(), "filepath: %s", filepath) + assert.Equal(t, http.StatusNotFound, ctx.Resp.Status(), "filepath: %s", filepath) } else { - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status(), "filepath: %s", filepath) - assert.EqualValues(t, filetype, ctx.Resp.Header().Get("Content-Type"), "filepath: %s", filepath) + assert.Equal(t, http.StatusOK, ctx.Resp.Status(), "filepath: %s", filepath) + assert.Equal(t, filetype, ctx.Resp.Header().Get("Content-Type"), "filepath: %s", filepath) } } } diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 9f22cebaba..2a93221c8f 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -655,9 +655,10 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { return 0 } reviewTyp := issues_model.ReviewTypeApprove - if typ == "reject" { + switch typ { + case "reject": reviewTyp = issues_model.ReviewTypeReject - } else if typ == "waiting" { + case "waiting": reviewTyp = issues_model.ReviewTypeRequest } for _, count := range counts { diff --git a/routers/web/user/home_test.go b/routers/web/user/home_test.go index af9d50538d..f3a2f12ae6 100644 --- a/routers/web/user/home_test.go +++ b/routers/web/user/home_test.go @@ -40,15 +40,15 @@ func TestArchivedIssues(t *testing.T) { NumIssues[repo.ID] = repo.NumIssues } assert.False(t, IsArchived[50]) - assert.EqualValues(t, 1, NumIssues[50]) + assert.Equal(t, 1, NumIssues[50]) assert.True(t, IsArchived[51]) - assert.EqualValues(t, 1, NumIssues[51]) + assert.Equal(t, 1, NumIssues[51]) // Act Issues(ctx) // Assert: One Issue (ID 30) from one Repo (ID 50) is retrieved, while nothing from archived Repo 51 is retrieved - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assert.Len(t, ctx.Data["Issues"], 1) } @@ -61,7 +61,7 @@ func TestIssues(t *testing.T) { contexttest.LoadUser(t, ctx, 2) ctx.Req.Form.Set("state", "closed") Issues(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, true, ctx.Data["IsShowClosed"]) assert.Len(t, ctx.Data["Issues"], 1) @@ -76,7 +76,7 @@ func TestPulls(t *testing.T) { ctx.Req.Form.Set("state", "open") ctx.Req.Form.Set("type", "your_repositories") Pulls(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assert.Len(t, ctx.Data["Issues"], 5) } @@ -91,15 +91,15 @@ func TestMilestones(t *testing.T) { ctx.Req.Form.Set("state", "closed") ctx.Req.Form.Set("sort", "furthestduedate") Milestones(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"]) assert.EqualValues(t, true, ctx.Data["IsShowClosed"]) assert.EqualValues(t, "furthestduedate", ctx.Data["SortType"]) assert.EqualValues(t, 1, ctx.Data["Total"]) assert.Len(t, ctx.Data["Milestones"], 1) assert.Len(t, ctx.Data["Repos"], 2) // both repo 42 and 1 have milestones and both are owned by user 2 - assert.EqualValues(t, "user2/glob", ctx.Data["Repos"].(repo_model.RepositoryList)[0].FullName()) - assert.EqualValues(t, "user2/repo1", ctx.Data["Repos"].(repo_model.RepositoryList)[1].FullName()) + assert.Equal(t, "user2/glob", ctx.Data["Repos"].(repo_model.RepositoryList)[0].FullName()) + assert.Equal(t, "user2/repo1", ctx.Data["Repos"].(repo_model.RepositoryList)[1].FullName()) } func TestMilestonesForSpecificRepo(t *testing.T) { @@ -113,7 +113,7 @@ func TestMilestonesForSpecificRepo(t *testing.T) { ctx.Req.Form.Set("state", "closed") ctx.Req.Form.Set("sort", "furthestduedate") Milestones(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"]) assert.EqualValues(t, true, ctx.Data["IsShowClosed"]) assert.EqualValues(t, "furthestduedate", ctx.Data["SortType"]) @@ -144,7 +144,7 @@ func TestOrgLabels(t *testing.T) { contexttest.LoadUser(t, ctx, 2) contexttest.LoadOrganization(t, ctx, 3) Issues(ctx) - assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.Equal(t, http.StatusOK, ctx.Resp.Status()) assert.True(t, ctx.Data["PageIsOrgIssues"].(bool)) @@ -163,9 +163,9 @@ func TestOrgLabels(t *testing.T) { if assert.Len(t, labels, len(orgLabels)) { for i, label := range labels { - assert.EqualValues(t, orgLabels[i].OrgID, label.OrgID) - assert.EqualValues(t, orgLabels[i].ID, label.ID) - assert.EqualValues(t, orgLabels[i].Name, label.Name) + assert.Equal(t, orgLabels[i].OrgID, label.OrgID) + assert.Equal(t, orgLabels[i].ID, label.ID) + assert.Equal(t, orgLabels[i].Name, label.Name) } } } diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 296951b2ff..9fa71add57 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -340,9 +340,10 @@ func NotificationSubscriptions(ctx *context.Context) { return 0 } reviewTyp := issues_model.ReviewTypeApprove - if typ == "reject" { + switch typ { + case "reject": reviewTyp = issues_model.ReviewTypeReject - } else if typ == "waiting" { + case "waiting": reviewTyp = issues_model.ReviewTypeRequest } for _, count := range counts { diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index a0cdb25f44..b421d28424 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -178,10 +178,10 @@ func EmailPost(ctx *context.Context) { // Set Email Notification Preference if ctx.FormString("_method") == "NOTIFICATION" { preference := ctx.FormString("preference") - if !(preference == user_model.EmailNotificationsEnabled || - preference == user_model.EmailNotificationsOnMention || - preference == user_model.EmailNotificationsDisabled || - preference == user_model.EmailNotificationsAndYourOwn) { + if preference != user_model.EmailNotificationsEnabled && + preference != user_model.EmailNotificationsOnMention && + preference != user_model.EmailNotificationsDisabled && + preference != user_model.EmailNotificationsAndYourOwn { log.Error("Email notifications preference change returned unrecognized option %s: %s", preference, ctx.Doer.Name) ctx.ServerError("SetEmailPreference", errors.New("option unrecognized")) return diff --git a/routers/web/user/setting/account_test.go b/routers/web/user/setting/account_test.go index 82e00bbf7c..3f7e1c13bc 100644 --- a/routers/web/user/setting/account_test.go +++ b/routers/web/user/setting/account_test.go @@ -95,7 +95,7 @@ func TestChangePassword(t *testing.T) { AccountPost(ctx) assert.Contains(t, ctx.Flash.ErrorMsg, req.Message) - assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) + assert.Equal(t, http.StatusSeeOther, ctx.Resp.Status()) }) } } diff --git a/routers/web/user/setting/adopt.go b/routers/web/user/setting/adopt.go index f7fd1c3803..59ff31162b 100644 --- a/routers/web/user/setting/adopt.go +++ b/routers/web/user/setting/adopt.go @@ -16,12 +16,8 @@ import ( // AdoptOrDeleteRepository adopts or deletes a repository func AdoptOrDeleteRepository(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("settings.adopt") - ctx.Data["PageIsSettingsRepos"] = true allowAdopt := ctx.IsUserSiteAdmin() || setting.Repository.AllowAdoptionOfUnadoptedRepositories - ctx.Data["allowAdopt"] = allowAdopt allowDelete := ctx.IsUserSiteAdmin() || setting.Repository.AllowDeleteOfUnadoptedRepositories - ctx.Data["allowDelete"] = allowDelete dir := ctx.FormString("id") action := ctx.FormString("action") diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index 173550ad19..fe6ffb802d 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -146,7 +146,7 @@ func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser * } st := typesniffer.DetectContentType(data) - if !(st.IsImage() && !st.IsSvgImage()) { + if !st.IsImage() || st.IsSvgImage() { return errors.New(ctx.Locale.TrString("settings.uploaded_avatar_not_a_image")) } if err = user_service.UploadAvatar(ctx, ctxUser, data); err != nil { diff --git a/routers/web/user/setting/security/2fa.go b/routers/web/user/setting/security/2fa.go index f1271c8370..8b362c4f08 100644 --- a/routers/web/user/setting/security/2fa.go +++ b/routers/web/user/setting/security/2fa.go @@ -40,11 +40,7 @@ func RegenerateScratchTwoFactor(ctx *context.Context) { return } - token, err := t.GenerateScratchToken() - if err != nil { - ctx.ServerError("SettingsTwoFactor: Failed to GenerateScratchToken", err) - return - } + token := t.GenerateScratchToken() if err = auth.UpdateTwoFactor(ctx, t); err != nil { ctx.ServerError("SettingsTwoFactor: Failed to UpdateTwoFactor", err) @@ -220,11 +216,7 @@ func EnrollTwoFactorPost(ctx *context.Context) { t = &auth.TwoFactor{ UID: ctx.Doer.ID, } - token, err := t.GenerateScratchToken() - if err != nil { - ctx.ServerError("SettingsTwoFactor: Failed to generate scratch token", err) - return - } + token := t.GenerateScratchToken() // Now we have to delete the secrets - because if we fail to insert then it's highly likely that they have already been used // If we can detect the unique constraint failure below we can move this to after the NewTwoFactor diff --git a/routers/web/web.go b/routers/web/web.go index fc58ebda94..79228800de 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1,5 +1,5 @@ // Copyright 2017 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. +// Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package web @@ -818,13 +818,13 @@ func registerRoutes(m *web.Route) { individualPermsChecker := func(ctx *context.Context) { // org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked. if ctx.ContextUser.IsIndividual() { - switch { - case ctx.ContextUser.Visibility == structs.VisibleTypePrivate: + switch ctx.ContextUser.Visibility { + case structs.VisibleTypePrivate: if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) { ctx.NotFound("Visit Project", nil) return } - case ctx.ContextUser.Visibility == structs.VisibleTypeLimited: + case structs.VisibleTypeLimited: if ctx.Doer == nil { ctx.NotFound("Visit Project", nil) return @@ -1461,7 +1461,7 @@ func registerRoutes(m *web.Route) { }, repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypeCode)) m.Group("/recent-commits", func() { m.Get("", repo.RecentCommits) - m.Get("/data", repo.RecentCommitsData) + m.Get("/data", repo.CodeFrequencyData) }, repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypeCode)) }, context.RepoRef(), context.RequireRepoReaderOr(unit.TypeCode, unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases)) @@ -1668,6 +1668,7 @@ func registerRoutes(m *web.Route) { m.Any("/devtest", devtest.List) m.Any("/devtest/fetch-action-test", devtest.FetchActionTest) m.Any("/devtest/{sub}", devtest.Tmpl) + m.Get("/devtest/error/{errcode}", devtest.ErrorPage) } m.NotFound(func(w http.ResponseWriter, req *http.Request) { diff --git a/services/actions/auth_test.go b/services/actions/auth_test.go index 93a5980bc5..d9f0437e1b 100644 --- a/services/actions/auth_test.go +++ b/services/actions/auth_test.go @@ -19,7 +19,7 @@ func TestCreateAuthorizationToken(t *testing.T) { var taskID int64 = 23 token, err := CreateAuthorizationToken(taskID, 1, 2) require.NoError(t, err) - assert.NotEqual(t, "", token) + assert.NotEmpty(t, token) claims := jwt.MapClaims{} _, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) { return setting.GetGeneralTokenSigningSecret(), nil @@ -45,7 +45,7 @@ func TestParseAuthorizationToken(t *testing.T) { var taskID int64 = 23 token, err := CreateAuthorizationToken(taskID, 1, 2) require.NoError(t, err) - assert.NotEqual(t, "", token) + assert.NotEmpty(t, token) headers := http.Header{} headers.Set("Authorization", "Bearer "+token) rTaskID, err := ParseAuthorizationToken(&http.Request{ diff --git a/services/actions/cleanup_test.go b/services/actions/cleanup_test.go index 67f68d4de9..4a847ced23 100644 --- a/services/actions/cleanup_test.go +++ b/services/actions/cleanup_test.go @@ -24,7 +24,7 @@ func TestCleanup(t *testing.T) { require.NoError(t, CleanupLogs(db.DefaultContext)) task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 1001}) - assert.EqualValues(t, "does-not-exist", task.LogFilename) + assert.Equal(t, "does-not-exist", task.LogFilename) assert.True(t, task.LogExpired) assert.Nil(t, task.LogIndexes) }) diff --git a/services/attachment/attachment_test.go b/services/attachment/attachment_test.go index 70b1e80d6a..ef002bf16c 100644 --- a/services/attachment/attachment_test.go +++ b/services/attachment/attachment_test.go @@ -43,6 +43,6 @@ func TestUploadAttachment(t *testing.T) { attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, attach.UUID) require.NoError(t, err) - assert.EqualValues(t, user.ID, attachment.UploaderID) + assert.Equal(t, user.ID, attachment.UploaderID) assert.Equal(t, int64(0), attachment.DownloadCount) } diff --git a/services/auth/reverseproxy_test.go b/services/auth/reverseproxy_test.go index 70ce1f8b0b..cdcd845148 100644 --- a/services/auth/reverseproxy_test.go +++ b/services/auth/reverseproxy_test.go @@ -38,10 +38,10 @@ func TestReverseProxyAuth(t *testing.T) { require.EqualValues(t, 1, user_model.CountUsers(db.DefaultContext, nil)) unittest.AssertExistsAndLoadBean(t, &user_model.User{Email: "edgar@example.org", Name: "Edgar", LowerName: "edgar", FullName: "Edgar Allan Poe", IsAdmin: true}) - require.EqualValues(t, "edgar@example.org", user.Email) - require.EqualValues(t, "Edgar", user.Name) - require.EqualValues(t, "edgar", user.LowerName) - require.EqualValues(t, "Edgar Allan Poe", user.FullName) + require.Equal(t, "edgar@example.org", user.Email) + require.Equal(t, "Edgar", user.Name) + require.Equal(t, "edgar", user.LowerName) + require.Equal(t, "Edgar Allan Poe", user.FullName) require.True(t, user.IsAdmin) }) @@ -58,10 +58,10 @@ func TestReverseProxyAuth(t *testing.T) { require.EqualValues(t, 2, user_model.CountUsers(db.DefaultContext, nil)) unittest.AssertExistsAndLoadBean(t, &user_model.User{Email: "gusted@example.org", Name: "Gusted", LowerName: "gusted", FullName: "❤‿❤"}, "is_admin = false") - require.EqualValues(t, "gusted@example.org", user.Email) - require.EqualValues(t, "Gusted", user.Name) - require.EqualValues(t, "gusted", user.LowerName) - require.EqualValues(t, "❤‿❤", user.FullName) + require.Equal(t, "gusted@example.org", user.Email) + require.Equal(t, "Gusted", user.Name) + require.Equal(t, "gusted", user.LowerName) + require.Equal(t, "❤‿❤", user.FullName) require.False(t, user.IsAdmin) }) } diff --git a/services/auth/source/oauth2/jwtsigningkey_test.go b/services/auth/source/oauth2/jwtsigningkey_test.go index 7cf2833696..9b07b022df 100644 --- a/services/auth/source/oauth2/jwtsigningkey_test.go +++ b/services/auth/source/oauth2/jwtsigningkey_test.go @@ -30,7 +30,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) { block, _ := pem.Decode(fileContent) assert.NotNil(t, block) - assert.EqualValues(t, "PRIVATE KEY", block.Type) + assert.Equal(t, "PRIVATE KEY", block.Type) parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) require.NoError(t, err) @@ -44,14 +44,14 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) { parsedKey := loadKey(t) rsaPrivateKey := parsedKey.(*rsa.PrivateKey) - assert.EqualValues(t, 2048, rsaPrivateKey.N.BitLen()) + assert.Equal(t, 2048, rsaPrivateKey.N.BitLen()) t.Run("Load key with differ specified algorithm", func(t *testing.T) { defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "EdDSA")() parsedKey := loadKey(t) rsaPrivateKey := parsedKey.(*rsa.PrivateKey) - assert.EqualValues(t, 2048, rsaPrivateKey.N.BitLen()) + assert.Equal(t, 2048, rsaPrivateKey.N.BitLen()) }) }) @@ -62,7 +62,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) { parsedKey := loadKey(t) rsaPrivateKey := parsedKey.(*rsa.PrivateKey) - assert.EqualValues(t, 3072, rsaPrivateKey.N.BitLen()) + assert.Equal(t, 3072, rsaPrivateKey.N.BitLen()) }) t.Run("RSA-4096", func(t *testing.T) { @@ -72,7 +72,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) { parsedKey := loadKey(t) rsaPrivateKey := parsedKey.(*rsa.PrivateKey) - assert.EqualValues(t, 4096, rsaPrivateKey.N.BitLen()) + assert.Equal(t, 4096, rsaPrivateKey.N.BitLen()) }) t.Run("ECDSA-256", func(t *testing.T) { @@ -82,7 +82,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) { parsedKey := loadKey(t) ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey) - assert.EqualValues(t, 256, ecdsaPrivateKey.Params().BitSize) + assert.Equal(t, 256, ecdsaPrivateKey.Params().BitSize) }) t.Run("ECDSA-384", func(t *testing.T) { @@ -92,7 +92,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) { parsedKey := loadKey(t) ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey) - assert.EqualValues(t, 384, ecdsaPrivateKey.Params().BitSize) + assert.Equal(t, 384, ecdsaPrivateKey.Params().BitSize) }) t.Run("ECDSA-512", func(t *testing.T) { @@ -102,7 +102,7 @@ func TestLoadOrCreateAsymmetricKey(t *testing.T) { parsedKey := loadKey(t) ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey) - assert.EqualValues(t, 521, ecdsaPrivateKey.Params().BitSize) + assert.Equal(t, 521, ecdsaPrivateKey.Params().BitSize) }) t.Run("EdDSA", func(t *testing.T) { diff --git a/services/context/api.go b/services/context/api.go index 37f0e0f559..0a3cb04399 100644 --- a/services/context/api.go +++ b/services/context/api.go @@ -186,7 +186,7 @@ func (ctx *APIContext) Error(status int, title string, obj any) { if status == http.StatusInternalServerError { log.ErrorWithSkip(1, "%s: %s", title, message) - if setting.IsProd && !(ctx.Doer != nil && ctx.Doer.IsAdmin) { + if setting.IsProd && (ctx.Doer == nil || !ctx.Doer.IsAdmin) { message = "" } } @@ -285,8 +285,8 @@ func APIContexter() func(http.Handler) http.Handler { } defer baseCleanUp() - ctx.Base.AppendContextValue(apiContextKey, ctx) - ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo }) + ctx.AppendContextValue(apiContextKey, ctx) + ctx.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo }) // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { @@ -334,7 +334,7 @@ func (ctx *APIContext) NotFound(objs ...any) { func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context.CancelFunc) { return func(ctx *APIContext) (cancel context.CancelFunc) { // Empty repository does not have reference information. - if ctx.Repo.Repository.IsEmpty && !(len(allowEmpty) != 0 && allowEmpty[0]) { + if ctx.Repo.Repository.IsEmpty && (len(allowEmpty) == 0 || !allowEmpty[0]) { return nil } diff --git a/services/context/api_test.go b/services/context/api_test.go index 90e4d5ec65..610f15ed5a 100644 --- a/services/context/api_test.go +++ b/services/context/api_test.go @@ -46,6 +46,6 @@ func TestGenAPILinks(t *testing.T) { links := genAPILinks(u, 100, 20, curPage) - assert.EqualValues(t, links, response) + assert.Equal(t, links, response) } } diff --git a/services/context/context.go b/services/context/context.go index 91484c5ba3..116f0c7f2c 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -100,7 +100,7 @@ func GetValidateContext(req *http.Request) (ctx *ValidateContext) { func NewTemplateContextForWeb(ctx *Context) TemplateContext { tmplCtx := NewTemplateContext(ctx) - tmplCtx["Locale"] = ctx.Base.Locale + tmplCtx["Locale"] = ctx.Locale tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx) return tmplCtx } @@ -151,8 +151,8 @@ func Contexter() func(next http.Handler) http.Handler { ctx.PageData = map[string]any{} ctx.Data["PageData"] = ctx.PageData - ctx.Base.AppendContextValue(WebContextKey, ctx) - ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo }) + ctx.AppendContextValue(WebContextKey, ctx) + ctx.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo }) ctx.Csrf = NewCSRFProtector(csrfOpts) diff --git a/services/context/context_response.go b/services/context/context_response.go index e20e7dd852..e64f478420 100644 --- a/services/context/context_response.go +++ b/services/context/context_response.go @@ -1,4 +1,5 @@ // Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package context @@ -66,7 +67,10 @@ func (ctx *Context) RedirectToFirst(location ...string) string { return setting.AppSubURL + "/" } -const tplStatus500 base.TplName = "status/500" +const ( + tplStatus404 base.TplName = "status/404" + tplStatus500 base.TplName = "status/500" +) // HTML calls Context.HTML and renders the template to HTTP response func (ctx *Context) HTML(status int, name base.TplName) { @@ -153,8 +157,8 @@ func (ctx *Context) notFoundInternal(logMsg string, logErr error) { } ctx.Data["IsRepo"] = ctx.Repo.Repository != nil - ctx.Data["Title"] = "Page Not Found" - ctx.HTML(http.StatusNotFound, base.TplName("status/404")) + ctx.Data["Title"] = ctx.Locale.TrString("error.not_found.title") + ctx.HTML(http.StatusNotFound, tplStatus404) } // ServerError displays a 500 (Internal Server Error) page and prints the given error, if any. @@ -177,7 +181,6 @@ func (ctx *Context) serverErrorInternal(logMsg string, logErr error) { } } - ctx.Data["Title"] = "Internal Server Error" ctx.HTML(http.StatusInternalServerError, tplStatus500) } diff --git a/services/context/package.go b/services/context/package.go index e597249e2a..b95e02a882 100644 --- a/services/context/package.go +++ b/services/context/package.go @@ -158,7 +158,7 @@ func PackageContexter() func(next http.Handler) http.Handler { // it is still needed when rendering 500 page in a package handler ctx := NewWebContext(base, renderer, nil) - ctx.Base.AppendContextValue(WebContextKey, ctx) + ctx.AppendContextValue(WebContextKey, ctx) next.ServeHTTP(ctx.Resp, ctx.Req) }) } diff --git a/services/context/private.go b/services/context/private.go index 3d7ed694f1..94ee31876a 100644 --- a/services/context/private.go +++ b/services/context/private.go @@ -67,7 +67,7 @@ func PrivateContexter() func(http.Handler) http.Handler { base, baseCleanUp := NewBaseContext(w, req) ctx := &PrivateContext{Base: base} defer baseCleanUp() - ctx.Base.AppendContextValue(privateContextKey, ctx) + ctx.AppendContextValue(privateContextKey, ctx) next.ServeHTTP(ctx.Resp, ctx.Req) }) diff --git a/services/context/quota.go b/services/context/quota.go index f6e79e1ebe..502a316107 100644 --- a/services/context/quota.go +++ b/services/context/quota.go @@ -64,7 +64,7 @@ func QuotaRuleAssignmentAPI() func(ctx *APIContext) { // ctx.CheckQuota checks whether the user in question is within quota limits (web context) func (ctx *Context) CheckQuota(subject quota_model.LimitSubject, userID int64, username string) bool { - ok, err := checkQuota(ctx.Base.originCtx, subject, userID, username, func(userID int64, username string) { + ok, err := checkQuota(ctx.originCtx, subject, userID, username, func(userID int64, username string) { showHTML := false for _, part := range ctx.Req.Header["Accept"] { if strings.Contains(part, "text/html") { @@ -91,7 +91,7 @@ func (ctx *Context) CheckQuota(subject quota_model.LimitSubject, userID int64, u // ctx.CheckQuota checks whether the user in question is within quota limits (API context) func (ctx *APIContext) CheckQuota(subject quota_model.LimitSubject, userID int64, username string) bool { - ok, err := checkQuota(ctx.Base.originCtx, subject, userID, username, func(userID int64, username string) { + ok, err := checkQuota(ctx.originCtx, subject, userID, username, func(userID int64, username string) { ctx.JSON(http.StatusRequestEntityTooLarge, APIQuotaExceeded{ Message: "quota exceeded", UserID: userID, diff --git a/services/context/repo.go b/services/context/repo.go index 9c38bdb6c5..cce3a5fa70 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -83,7 +83,7 @@ func (r *Repository) CanEnableEditor(ctx context.Context, user *user_model.User) // CanCreateBranch returns true if repository is editable and user has proper access level. func (r *Repository) CanCreateBranch() bool { - return r.Permission.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch() + return r.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch() } func (r *Repository) GetObjectFormat() git.ObjectFormat { @@ -160,12 +160,12 @@ func (r *Repository) CanUseTimetracker(ctx context.Context, issue *issues_model. // 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this? isAssigned, _ := issues_model.IsUserAssignedToIssue(ctx, issue, user) return r.Repository.IsTimetrackerEnabled(ctx) && (!r.Repository.AllowOnlyContributorsToTrackTime(ctx) || - r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned) + r.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned) } // CanCreateIssueDependencies returns whether or not a user can create dependencies. func (r *Repository) CanCreateIssueDependencies(ctx context.Context, user *user_model.User, isPull bool) bool { - return r.Repository.IsDependenciesEnabled(ctx) && r.Permission.CanWriteIssuesOrPulls(isPull) + return r.Repository.IsDependenciesEnabled(ctx) && r.CanWriteIssuesOrPulls(isPull) } // GetCommitsCount returns cached commit count for current view @@ -361,7 +361,9 @@ func RedirectToRepo(ctx *Base, redirectRepoID int64) { if ctx.Req.URL.RawQuery != "" { redirectPath += "?" + ctx.Req.URL.RawQuery } - ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusTemporaryRedirect) + // Git client needs a 301 redirect by default to follow the new location + // It's not documentated in git documentation, but it's the behavior of git client + ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusMovedPermanently) } func repoAssignment(ctx *Context, repo *repo_model.Repository) { @@ -378,7 +380,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) { } // Check access. - if !ctx.Repo.Permission.HasAccess() { + if !ctx.Repo.HasAccess() { if ctx.FormString("go-get") == "1" { EarlyResponseForGoGetMeta(ctx) return diff --git a/services/context/upload/upload.go b/services/context/upload/upload.go index 2fa177e604..e71fc50c1f 100644 --- a/services/context/upload/upload.go +++ b/services/context/upload/upload.go @@ -76,14 +76,15 @@ func Verify(buf []byte, fileName, allowedTypesStr string) error { // AddUploadContext renders template values for dropzone func AddUploadContext(ctx *context.Context, uploadType string) { - if uploadType == "release" { + switch uploadType { + case "release": ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/releases/attachments" ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/releases/attachments/remove" ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/releases/attachments" ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Release.AllowedTypes, "|", ",") ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize - } else if uploadType == "comment" { + case "comment": ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/issues/attachments" ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/issues/attachments/remove" if len(ctx.Params(":index")) > 0 { @@ -94,7 +95,7 @@ func AddUploadContext(ctx *context.Context, uploadType string) { ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Attachment.AllowedTypes, "|", ",") ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize - } else if uploadType == "repo" { + case "repo": ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/upload-file" ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/upload-remove" ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/upload-file" diff --git a/services/contexttest/context_tests.go b/services/contexttest/context_tests.go index ebab04f620..a4e674a896 100644 --- a/services/contexttest/context_tests.go +++ b/services/contexttest/context_tests.go @@ -68,7 +68,7 @@ func MockContext(t *testing.T, reqPath string, opts ...MockContextOption) (*cont ctx.PageData = map[string]any{} ctx.Data["PageStartTime"] = time.Now() chiCtx := chi.NewRouteContext() - ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx) + ctx.AppendContextValue(chi.RouteCtxKey, chiCtx) return ctx, resp } @@ -83,7 +83,7 @@ func MockAPIContext(t *testing.T, reqPath string) (*context.APIContext, *httptes _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later chiCtx := chi.NewRouteContext() - ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx) + ctx.AppendContextValue(chi.RouteCtxKey, chiCtx) return ctx, resp } @@ -96,7 +96,7 @@ func MockPrivateContext(t *testing.T, reqPath string) (*context.PrivateContext, ctx := &context.PrivateContext{Base: base} _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later chiCtx := chi.NewRouteContext() - ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx) + ctx.AppendContextValue(chi.RouteCtxKey, chiCtx) return ctx, resp } diff --git a/services/convert/git_commit_test.go b/services/convert/git_commit_test.go index 463b93aac3..97dff365e6 100644 --- a/services/convert/git_commit_test.go +++ b/services/convert/git_commit_test.go @@ -34,7 +34,7 @@ func TestToCommitMeta(t *testing.T) { commitMeta := ToCommitMeta(headRepo, tag) assert.NotNil(t, commitMeta) - assert.EqualValues(t, &api.CommitMeta{ + assert.Equal(t, &api.CommitMeta{ SHA: sha1.EmptyObjectID().String(), URL: util.URLJoin(headRepo.APIURL(), "git/commits", sha1.EmptyObjectID().String()), Created: time.Unix(0, 0), diff --git a/services/convert/notification.go b/services/convert/notification.go index 3a4239e0fe..2a69b62e4b 100644 --- a/services/convert/notification.go +++ b/services/convert/notification.go @@ -17,7 +17,7 @@ import ( func ToNotificationThread(ctx context.Context, n *activities_model.Notification) *api.NotificationThread { result := &api.NotificationThread{ ID: n.ID, - Unread: !(n.Status == activities_model.NotificationStatusRead || n.Status == activities_model.NotificationStatusPinned), + Unread: n.Status != activities_model.NotificationStatusRead && n.Status != activities_model.NotificationStatusPinned, Pinned: n.Status == activities_model.NotificationStatusPinned, UpdatedAt: n.UpdatedUnix.AsTime(), URL: n.APIURL(), diff --git a/services/convert/pull.go b/services/convert/pull.go index 7bee2b4c44..ca965a0d18 100644 --- a/services/convert/pull.go +++ b/services/convert/pull.go @@ -66,33 +66,36 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u } apiPullRequest := &api.PullRequest{ - ID: pr.ID, - URL: pr.Issue.HTMLURL(), - Index: pr.Index, - Poster: apiIssue.Poster, - Title: apiIssue.Title, - Body: apiIssue.Body, - Labels: apiIssue.Labels, - Milestone: apiIssue.Milestone, - Assignee: apiIssue.Assignee, - Assignees: apiIssue.Assignees, - State: apiIssue.State, - Draft: pr.IsWorkInProgress(ctx), - IsLocked: apiIssue.IsLocked, - Comments: apiIssue.Comments, - ReviewComments: pr.GetReviewCommentsCount(ctx), - HTMLURL: pr.Issue.HTMLURL(), - DiffURL: pr.Issue.DiffURL(), - PatchURL: pr.Issue.PatchURL(), - HasMerged: pr.HasMerged, - MergeBase: pr.MergeBase, - Mergeable: pr.Mergeable(ctx), - Deadline: apiIssue.Deadline, - Created: pr.Issue.CreatedUnix.AsTimePtr(), - Updated: pr.Issue.UpdatedUnix.AsTimePtr(), - PinOrder: apiIssue.PinOrder, + ID: pr.ID, + URL: pr.Issue.HTMLURL(), + Index: pr.Index, + Poster: apiIssue.Poster, + Title: apiIssue.Title, + Body: apiIssue.Body, + Labels: apiIssue.Labels, + Milestone: apiIssue.Milestone, + Assignee: apiIssue.Assignee, + Assignees: apiIssue.Assignees, + State: apiIssue.State, + Draft: pr.IsWorkInProgress(ctx), + IsLocked: apiIssue.IsLocked, + Comments: apiIssue.Comments, + ReviewComments: pr.GetReviewCommentsCount(ctx), + HTMLURL: pr.Issue.HTMLURL(), + DiffURL: pr.Issue.DiffURL(), + PatchURL: pr.Issue.PatchURL(), + HasMerged: pr.HasMerged, + MergeBase: pr.MergeBase, + Mergeable: pr.Mergeable(ctx), + Deadline: apiIssue.Deadline, + Created: pr.Issue.CreatedUnix.AsTimePtr(), + Updated: pr.Issue.UpdatedUnix.AsTimePtr(), + PinOrder: apiIssue.PinOrder, + RequestedReviewers: []*api.User{}, + RequestedReviewersTeams: []*api.Team{}, AllowMaintainerEdit: pr.AllowMaintainerEdit, + Flow: int64(pr.Flow), Base: &api.PRBranchInfo{ Name: pr.BaseBranch, diff --git a/services/convert/pull_review.go b/services/convert/pull_review.go index 08ccc0e1fc..97be118a83 100644 --- a/services/convert/pull_review.go +++ b/services/convert/pull_review.go @@ -66,7 +66,7 @@ func ToPullReviewList(ctx context.Context, rl []*issues_model.Review, doer *user result := make([]*api.PullReview, 0, len(rl)) for i := range rl { // show pending reviews only for the user who created them - if rl[i].Type == issues_model.ReviewTypePending && (doer == nil || !(doer.IsAdmin || doer.ID == rl[i].ReviewerID)) { + if rl[i].Type == issues_model.ReviewTypePending && (doer == nil || (!doer.IsAdmin && doer.ID != rl[i].ReviewerID)) { continue } r, err := ToPullReview(ctx, rl[i], doer) diff --git a/services/convert/pull_test.go b/services/convert/pull_test.go index 3e4875fc60..c0c69fd9ad 100644 --- a/services/convert/pull_test.go +++ b/services/convert/pull_test.go @@ -29,7 +29,7 @@ func TestPullRequest_APIFormat(t *testing.T) { require.NoError(t, pr.LoadIssue(db.DefaultContext)) apiPullRequest := ToAPIPullRequest(git.DefaultContext, pr, nil) assert.NotNil(t, apiPullRequest) - assert.EqualValues(t, &structs.PRBranchInfo{ + assert.Equal(t, &structs.PRBranchInfo{ Name: "branch1", Ref: "refs/pull/2/head", Sha: "4a357436d925b5c974181ff12a994538ddc5a269", diff --git a/services/convert/release_test.go b/services/convert/release_test.go index 3abd2ff3ef..1d214f0222 100644 --- a/services/convert/release_test.go +++ b/services/convert/release_test.go @@ -24,6 +24,6 @@ func TestRelease_ToRelease(t *testing.T) { apiRelease := ToAPIRelease(db.DefaultContext, repo1, release1) assert.NotNil(t, apiRelease) assert.EqualValues(t, 1, apiRelease.ID) - assert.EqualValues(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1", apiRelease.URL) - assert.EqualValues(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1/assets", apiRelease.UploadURL) + assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1", apiRelease.URL) + assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1/assets", apiRelease.UploadURL) } diff --git a/services/convert/user_test.go b/services/convert/user_test.go index 01ce8101da..8a42a9d97d 100644 --- a/services/convert/user_test.go +++ b/services/convert/user_test.go @@ -31,11 +31,11 @@ func TestUser_ToUser(t *testing.T) { apiUser = toUser(db.DefaultContext, user1, false, false) assert.False(t, apiUser.IsAdmin) - assert.EqualValues(t, api.VisibleTypePublic.String(), apiUser.Visibility) + assert.Equal(t, api.VisibleTypePublic.String(), apiUser.Visibility) user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31, IsAdmin: false, Visibility: api.VisibleTypePrivate}) apiUser = toUser(db.DefaultContext, user31, true, true) assert.False(t, apiUser.IsAdmin) - assert.EqualValues(t, api.VisibleTypePrivate.String(), apiUser.Visibility) + assert.Equal(t, api.VisibleTypePrivate.String(), apiUser.Visibility) } diff --git a/services/convert/utils_test.go b/services/convert/utils_test.go index b464d8bb68..6c3bf7d938 100644 --- a/services/convert/utils_test.go +++ b/services/convert/utils_test.go @@ -10,10 +10,10 @@ import ( ) func TestToCorrectPageSize(t *testing.T) { - assert.EqualValues(t, 30, ToCorrectPageSize(0)) - assert.EqualValues(t, 30, ToCorrectPageSize(-10)) - assert.EqualValues(t, 20, ToCorrectPageSize(20)) - assert.EqualValues(t, 50, ToCorrectPageSize(100)) + assert.Equal(t, 30, ToCorrectPageSize(0)) + assert.Equal(t, 30, ToCorrectPageSize(-10)) + assert.Equal(t, 20, ToCorrectPageSize(20)) + assert.Equal(t, 50, ToCorrectPageSize(100)) } func TestToGitServiceType(t *testing.T) { diff --git a/services/doctor/dbconsistency.go b/services/doctor/dbconsistency.go index 6fcbd90940..6fe4c9c5e6 100644 --- a/services/doctor/dbconsistency.go +++ b/services/doctor/dbconsistency.go @@ -78,7 +78,14 @@ func genericOrphanCheck(name, subject, refobject, joincond string) consistencyCh func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) error { // make sure DB version is up-to-date - if err := db.InitEngineWithMigration(ctx, migrations.EnsureUpToDate); err != nil { + ensureUpToDateWrapper := func(e db.Engine) error { + engine, err := db.GetMasterEngine(e) + if err != nil { + return err + } + return migrations.EnsureUpToDate(engine) + } + if err := db.InitEngineWithMigration(ctx, ensureUpToDateWrapper); err != nil { logger.Critical("Model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded") return err } diff --git a/services/doctor/dbversion.go b/services/doctor/dbversion.go index 9c02c732e5..c0ff22915d 100644 --- a/services/doctor/dbversion.go +++ b/services/doctor/dbversion.go @@ -9,11 +9,15 @@ import ( "forgejo.org/models/db" "forgejo.org/models/migrations" "forgejo.org/modules/log" + + "xorm.io/xorm" ) func checkDBVersion(ctx context.Context, logger log.Logger, autofix bool) error { logger.Info("Expected database version: %d", migrations.ExpectedDBVersion()) - if err := db.InitEngineWithMigration(ctx, migrations.EnsureUpToDate); err != nil { + if err := db.InitEngineWithMigration(ctx, func(eng db.Engine) error { + return migrations.EnsureUpToDate(eng.(*xorm.Engine)) + }); err != nil { if !autofix { logger.Critical("Error: %v during ensure up to date", err) return err @@ -21,7 +25,9 @@ func checkDBVersion(ctx context.Context, logger log.Logger, autofix bool) error logger.Warn("Got Error: %v during ensure up to date", err) logger.Warn("Attempting to migrate to the latest DB version to fix this.") - err = db.InitEngineWithMigration(ctx, migrations.Migrate) + err = db.InitEngineWithMigration(ctx, func(eng db.Engine) error { + return migrations.Migrate(eng.(*xorm.Engine)) + }) if err != nil { logger.Critical("Error: %v during migration", err) } diff --git a/services/doctor/fix16961_test.go b/services/doctor/fix16961_test.go index 7a83c808c3..75f9f206ab 100644 --- a/services/doctor/fix16961_test.go +++ b/services/doctor/fix16961_test.go @@ -221,7 +221,7 @@ func Test_fixPullRequestsConfig_16961(t *testing.T) { if gotFixed != tt.wantFixed { t.Errorf("fixPullRequestsConfig_16961() = %v, want %v", gotFixed, tt.wantFixed) } - assert.EqualValues(t, &tt.expected, cfg) + assert.Equal(t, &tt.expected, cfg) }) } } @@ -265,7 +265,7 @@ func Test_fixIssuesConfig_16961(t *testing.T) { if gotFixed != tt.wantFixed { t.Errorf("fixIssuesConfig_16961() = %v, want %v", gotFixed, tt.wantFixed) } - assert.EqualValues(t, &tt.expected, cfg) + assert.Equal(t, &tt.expected, cfg) }) } } diff --git a/services/f3/util/logger_test.go b/services/f3/util/logger_test.go index 4afd5dd57f..f62d9e2e82 100644 --- a/services/f3/util/logger_test.go +++ b/services/f3/util/logger_test.go @@ -23,7 +23,7 @@ func TestF3UtilMessage(t *testing.T) { actual = fmt.Sprintf(message, args...) }, nil) logger.Message("EXPECTED %s", "MESSAGE") - assert.EqualValues(t, expected, actual) + assert.Equal(t, expected, actual) } func TestF3UtilLogger(t *testing.T) { diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go index 21c7be855b..b8c471bfbb 100644 --- a/services/federation/federation_service.go +++ b/services/federation/federation_service.go @@ -98,39 +98,47 @@ func ProcessLikeActivity(ctx context.Context, form any, repositoryID int64) (int } func CreateFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forgefed.FederationHost, error) { - actionsUser := user.NewActionsUser() + actionsUser := user.NewAPServerActor() clientFactory, err := activitypub.GetClientFactory(ctx) if err != nil { return nil, err } - client, err := clientFactory.WithKeys(ctx, actionsUser, "no idea where to get key material.") + + client, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID()) if err != nil { return nil, err } + body, err := client.GetBody(actorID.AsWellKnownNodeInfoURI()) if err != nil { return nil, err } + nodeInfoWellKnown, err := forgefed.NewNodeInfoWellKnown(body) if err != nil { return nil, err } + body, err = client.GetBody(nodeInfoWellKnown.Href) if err != nil { return nil, err } + nodeInfo, err := forgefed.NewNodeInfo(body) if err != nil { return nil, err } + result, err := forgefed.NewFederationHost(nodeInfo, actorID.Host) if err != nil { return nil, err } + err = forgefed.CreateFederationHost(ctx, &result) if err != nil { return nil, err } + return &result, nil } @@ -155,18 +163,18 @@ func GetFederationHostForURI(ctx context.Context, actorURI string) (*forgefed.Fe } func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID int64) (*user.User, *user.FederatedUser, error) { - // ToDo: Do we get a publicKeyId from server, repo or owner or repo? - actionsUser := user.NewActionsUser() + actionsUser := user.NewAPServerActor() clientFactory, err := activitypub.GetClientFactory(ctx) if err != nil { return nil, nil, err } - client, err := clientFactory.WithKeys(ctx, actionsUser, "no idea where to get key material.") + + apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID()) if err != nil { return nil, nil, err } - body, err := client.GetBody(personID.AsURI()) + body, err := apClient.GetBody(personID.AsURI()) if err != nil { return nil, nil, err } @@ -176,26 +184,32 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI if err != nil { return nil, nil, err } + if res, err := validation.IsValid(person); !res { return nil, nil, err } + log.Info("Fetched valid person:%q", person) localFqdn, err := url.ParseRequestURI(setting.AppURL) if err != nil { return nil, nil, err } + email := fmt.Sprintf("f%v@%v", uuid.New().String(), localFqdn.Hostname()) loginName := personID.AsLoginName() name := fmt.Sprintf("%v%v", person.PreferredUsername.String(), personID.HostSuffix()) fullName := person.Name.String() + if len(person.Name) == 0 { fullName = name } + password, err := password.Generate(32) if err != nil { return nil, nil, err } + newUser := user.User{ LowerName: strings.ToLower(name), Name: name, @@ -209,16 +223,18 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI IsAdmin: false, NormalizedFederatedURI: personID.AsURI(), } + federatedUser := user.FederatedUser{ ExternalID: personID.ID, FederationHostID: federationHostID, } + err = user.CreateFederatedUser(ctx, &newUser, &federatedUser) if err != nil { return nil, nil, err } - log.Info("Created federatedUser:%q", federatedUser) + log.Info("Created federatedUser:%q", federatedUser) return &newUser, &federatedUser, nil } @@ -274,7 +290,8 @@ func SendLikeActivities(ctx context.Context, doer user.User, repoID int64) error if err != nil { return err } - apclient, err := apclientFactory.WithKeys(ctx, &doer, doer.APActorID()) + + apclient, err := apclientFactory.WithKeys(ctx, &doer, doer.APActorKeyID()) if err != nil { return err } @@ -285,7 +302,7 @@ func SendLikeActivities(ctx context.Context, doer user.User, repoID int64) error return err } - _, err = apclient.Post(json, fmt.Sprintf("%v/inbox/", activity.Object)) + _, err = apclient.Post(json, fmt.Sprintf("%s/inbox", activity.Object)) if err != nil { log.Error("error %v while sending activity: %q", err, activity) } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index bd2eb769c6..4a46c9cc5f 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -188,8 +188,8 @@ type RepoUnitSettingForm struct { PullsAllowSquash bool PullsAllowFastForwardOnly bool PullsAllowManualMerge bool - PullsDefaultMergeStyle string - PullsDefaultUpdateStyle string + PullsDefaultMergeStyle string `binding:"In(merge,rebase,rebase-merge,squash,fast-forward-only,manually-merged,rebase-update-only)"` + PullsDefaultUpdateStyle string `binding:"In(merge,rebase)"` EnableAutodetectManualMerge bool PullsAllowRebaseUpdate bool DefaultDeleteBranchAfterMerge bool @@ -725,8 +725,8 @@ func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) bi // AddTimeManuallyForm form that adds spent time manually. type AddTimeManuallyForm struct { - Hours int `binding:"Range(0,1000)"` - Minutes int `binding:"Range(0,1000)"` + Hours int `binding:"Range(0,1000)" locale:"repo.issues.add_time_hours"` + Minutes int `binding:"Range(0,1000)" locale:"repo.issues.add_time_minutes"` } // Validate validates the fields diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 2e1fecda2a..01de76d8b5 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -1060,7 +1060,7 @@ func readFileName(rd *strings.Reader) (string, bool) { _, _ = fmt.Fscanf(rd, "%s ", &name) char, _ := rd.ReadByte() _ = rd.UnreadByte() - for !(char == 0 || char == '"' || char == 'b') { + for char != 0 && char != '"' && char != 'b' { var suffix string _, _ = fmt.Fscanf(rd, "%s ", &suffix) name += " " + suffix diff --git a/services/gitdiff/highlightdiff.go b/services/gitdiff/highlightdiff.go index 08681b8617..61d52d91e6 100644 --- a/services/gitdiff/highlightdiff.go +++ b/services/gitdiff/highlightdiff.go @@ -14,13 +14,14 @@ import ( // token is a html tag or entity, eg: "<span ...>", "</span>", "<" func extractHTMLToken(s string) (before, token, after string, valid bool) { for pos1 := 0; pos1 < len(s); pos1++ { - if s[pos1] == '<' { + switch s[pos1] { + case '<': pos2 := strings.IndexByte(s[pos1:], '>') if pos2 == -1 { return "", "", s, false } return s[:pos1], s[pos1 : pos1+pos2+1], s[pos1+pos2+1:], true - } else if s[pos1] == '&' { + case '&': pos2 := strings.IndexByte(s[pos1:], ';') if pos2 == -1 { return "", "", s, false diff --git a/services/gitdiff/highlightdiff_test.go b/services/gitdiff/highlightdiff_test.go index 2ff4472bcc..0070173b9f 100644 --- a/services/gitdiff/highlightdiff_test.go +++ b/services/gitdiff/highlightdiff_test.go @@ -43,7 +43,7 @@ func TestDiffWithHighlight(t *testing.T) { diff.Text = "C" hcd.recoverOneDiff(&diff) - assert.Equal(t, "", diff.Text) + assert.Empty(t, diff.Text) } func TestDiffWithHighlightPlaceholder(t *testing.T) { @@ -53,8 +53,8 @@ func TestDiffWithHighlightPlaceholder(t *testing.T) { "a='\U00100000'", "a='\U0010FFFD''", ) - assert.Equal(t, "", hcd.PlaceholderTokenMap[0x00100000]) - assert.Equal(t, "", hcd.PlaceholderTokenMap[0x0010FFFD]) + assert.Empty(t, hcd.PlaceholderTokenMap[0x00100000]) + assert.Empty(t, hcd.PlaceholderTokenMap[0x0010FFFD]) expected := fmt.Sprintf(`<span class="nx">a</span><span class="o">=</span><span class="s1">'</span><span class="removed-code">%s</span>'`, "\U00100000") output := diffToHTML(hcd.lineWrapperTags, diffs, DiffLineDel) diff --git a/services/issue/comments_test.go b/services/issue/comments_test.go index 728af15529..cf7ba5ebc8 100644 --- a/services/issue/comments_test.go +++ b/services/issue/comments_test.go @@ -48,9 +48,9 @@ func TestDeleteComment(t *testing.T) { // Reactions don't exist anymore for this comment. unittest.AssertNotExistsBean(t, &issues_model.Reaction{CommentID: comment.ID}) // Number of comments was decreased. - assert.EqualValues(t, issue.NumComments-1, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).NumComments) + assert.Equal(t, issue.NumComments-1, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).NumComments) // A notification was fired for the deletion of this comment. - assert.EqualValues(t, hookTaskCount+1, unittest.GetCount(t, &webhook_model.HookTask{})) + assert.Equal(t, hookTaskCount+1, unittest.GetCount(t, &webhook_model.HookTask{})) }) t.Run("Comment of pending review", func(t *testing.T) { @@ -59,7 +59,7 @@ func TestDeleteComment(t *testing.T) { // We have to ensure that this comment's linked review is pending. comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4}, "review_id != 0") review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: comment.ReviewID}) - assert.EqualValues(t, issues_model.ReviewTypePending, review.Type) + assert.Equal(t, issues_model.ReviewTypePending, review.Type) issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}) require.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, &webhook_model.Webhook{ @@ -74,9 +74,9 @@ func TestDeleteComment(t *testing.T) { // The comment doesn't exist anymore. unittest.AssertNotExistsBean(t, &issues_model.Comment{ID: comment.ID}) // Ensure that the number of comments wasn't decreased. - assert.EqualValues(t, issue.NumComments, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).NumComments) + assert.Equal(t, issue.NumComments, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).NumComments) // No notification was fired for the deletion of this comment. - assert.EqualValues(t, hookTaskCount, unittest.GetCount(t, &webhook_model.HookTask{})) + assert.Equal(t, hookTaskCount, unittest.GetCount(t, &webhook_model.HookTask{})) }) } @@ -105,11 +105,11 @@ func TestUpdateComment(t *testing.T) { newComment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2}) // Content was updated. - assert.EqualValues(t, comment.Content, newComment.Content) + assert.Equal(t, comment.Content, newComment.Content) // Content version was updated. - assert.EqualValues(t, 2, newComment.ContentVersion) + assert.Equal(t, 2, newComment.ContentVersion) // A notification was fired for the update of this comment. - assert.EqualValues(t, hookTaskCount+1, unittest.GetCount(t, &webhook_model.HookTask{})) + assert.Equal(t, hookTaskCount+1, unittest.GetCount(t, &webhook_model.HookTask{})) // Issue history was saved for this comment. unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{CommentID: comment.ID, IsFirstCreated: true, ContentText: oldContent}) unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{CommentID: comment.ID, ContentText: comment.Content}, "is_first_created = false") @@ -120,7 +120,7 @@ func TestUpdateComment(t *testing.T) { comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4}, "review_id != 0") review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: comment.ReviewID}) - assert.EqualValues(t, issues_model.ReviewTypePending, review.Type) + assert.Equal(t, issues_model.ReviewTypePending, review.Type) issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}) unittest.AssertNotExistsBean(t, &issues_model.ContentHistory{CommentID: comment.ID}) require.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, &webhook_model.Webhook{ @@ -136,11 +136,11 @@ func TestUpdateComment(t *testing.T) { newComment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2}) // Content was updated. - assert.EqualValues(t, comment.Content, newComment.Content) + assert.Equal(t, comment.Content, newComment.Content) // Content version was updated. - assert.EqualValues(t, 2, newComment.ContentVersion) + assert.Equal(t, 2, newComment.ContentVersion) // No notification was fired for the update of this comment. - assert.EqualValues(t, hookTaskCount, unittest.GetCount(t, &webhook_model.HookTask{})) + assert.Equal(t, hookTaskCount, unittest.GetCount(t, &webhook_model.HookTask{})) // Issue history was not saved for this comment. unittest.AssertNotExistsBean(t, &issues_model.ContentHistory{CommentID: comment.ID}) }) diff --git a/services/issue/issue.go b/services/issue/issue.go index f6a3e90b10..b0a39a3af4 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -59,7 +59,6 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *issues_mo // ChangeTitle changes the title of this issue, as the given user. func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, title string) error { oldTitle := issue.Title - issue.Title = title if oldTitle == title { return nil @@ -73,6 +72,12 @@ func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_mode return user_model.ErrBlockedByUser } + // If the issue was reported as abusive, a shadow copy should be created before first update. + if err := issues_model.IfNeededCreateShadowCopyForIssue(ctx, issue); err != nil { + return err + } + + issue.Title = title if err := issues_model.ChangeIssueTitle(ctx, issue, doer, oldTitle); err != nil { return err } @@ -252,6 +257,12 @@ func deleteIssue(ctx context.Context, issue *issues_model.Issue) error { defer committer.Close() e := db.GetEngine(ctx) + + // If the issue was reported as abusive, a shadow copy should be created before deletion. + if err := issues_model.IfNeededCreateShadowCopyForIssue(ctx, issue); err != nil { + return err + } + if _, err := e.ID(issue.ID).NoAutoCondition().Delete(issue); err != nil { return err } diff --git a/services/issue/issue_test.go b/services/issue/issue_test.go index e15a0118ad..fb2b2870bd 100644 --- a/services/issue/issue_test.go +++ b/services/issue/issue_test.go @@ -25,8 +25,8 @@ func TestGetRefEndNamesAndURLs(t *testing.T) { repoLink := "/foo/bar" endNames, urls := GetRefEndNamesAndURLs(issues, repoLink) - assert.EqualValues(t, map[int64]string{1: "branch1", 2: "tag1", 3: "c0ffee"}, endNames) - assert.EqualValues(t, map[int64]string{ + assert.Equal(t, map[int64]string{1: "branch1", 2: "tag1", 3: "c0ffee"}, endNames) + assert.Equal(t, map[int64]string{ 1: repoLink + "/src/branch/branch1", 2: repoLink + "/src/tag/tag1", 3: repoLink + "/src/commit/c0ffee", diff --git a/services/lfs/server.go b/services/lfs/server.go index 8eef62eabe..8b2d5e66e1 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -163,11 +163,12 @@ func BatchHandler(ctx *context.Context) { } var isUpload bool - if br.Operation == "upload" { + switch br.Operation { + case "upload": isUpload = true - } else if br.Operation == "download" { + case "download": isUpload = false - } else { + default: log.Trace("Attempt to BATCH with invalid operation: %s", br.Operation) writeStatus(ctx, http.StatusBadRequest) return diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go index b0329caa0b..0d8e054041 100644 --- a/services/mailer/mail_issue.go +++ b/services/mailer/mail_issue.go @@ -85,7 +85,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo // =========== Repo watchers =========== // Make repo watchers last, since it's likely the list with the most users - if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress(ctx) && ctx.ActionType != activities_model.ActionCreatePullRequest) { + if !ctx.Issue.IsPull || !ctx.Issue.PullRequest.IsWorkInProgress(ctx) || ctx.ActionType == activities_model.ActionCreatePullRequest { ids, err = repo_model.GetRepoWatchersIDs(ctx, ctx.Issue.RepoID) if err != nil { return fmt.Errorf("GetRepoWatchersIDs(%d): %w", ctx.Issue.RepoID, err) @@ -137,9 +137,8 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi } // At this point we exclude: // user that don't have all mails enabled or users only get mail on mention and this is one ... - if !(user.EmailNotificationsPreference == user_model.EmailNotificationsEnabled || - user.EmailNotificationsPreference == user_model.EmailNotificationsAndYourOwn || - fromMention && user.EmailNotificationsPreference == user_model.EmailNotificationsOnMention) { + if user.EmailNotificationsPreference != user_model.EmailNotificationsEnabled && + user.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn && (!fromMention || user.EmailNotificationsPreference != user_model.EmailNotificationsOnMention) { continue } diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index 616eea2d85..7fe565b2e1 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -518,7 +518,7 @@ func TestFromDisplayName(t *testing.T) { t.Run(tc.userDisplayName, func(t *testing.T) { user := &user_model.User{FullName: tc.userDisplayName, Name: "tmp"} got := fromDisplayName(user) - assert.EqualValues(t, tc.fromDisplayName, got) + assert.Equal(t, tc.fromDisplayName, got) }) } @@ -535,7 +535,7 @@ func TestFromDisplayName(t *testing.T) { setting.Domain = oldDomain }() - assert.EqualValues(t, "Mister X (by Code IT on [code.it])", fromDisplayName(&user_model.User{FullName: "Mister X", Name: "tmp"})) + assert.Equal(t, "Mister X (by Code IT on [code.it])", fromDisplayName(&user_model.User{FullName: "Mister X", Name: "tmp"})) }) } diff --git a/services/mailer/mailer_test.go b/services/mailer/mailer_test.go index aef242d908..34fd847c05 100644 --- a/services/mailer/mailer_test.go +++ b/services/mailer/mailer_test.go @@ -72,7 +72,7 @@ func TestToMessage(t *testing.T) { _, err := m1.ToMessage().WriteTo(buf) require.NoError(t, err) header, _ := extractMailHeaderAndContent(t, buf.String()) - assert.EqualValues(t, map[string]string{ + assert.Equal(t, map[string]string{ "Content-Type": "multipart/alternative;", "Date": "Mon, 01 Jan 0001 00:00:00 +0000", "From": "\"Test Gitea\" <test@gitea.com>", @@ -92,7 +92,7 @@ func TestToMessage(t *testing.T) { _, err = m1.ToMessage().WriteTo(buf) require.NoError(t, err) header, _ = extractMailHeaderAndContent(t, buf.String()) - assert.EqualValues(t, map[string]string{ + assert.Equal(t, map[string]string{ "Content-Type": "multipart/alternative;", "Date": "Mon, 01 Jan 0001 00:00:00 +0000", "From": "\"Test Gitea\" <test@gitea.com>", diff --git a/services/mailer/notify.go b/services/mailer/notify.go index e61ecd0511..8acfa86fb6 100644 --- a/services/mailer/notify.go +++ b/services/mailer/notify.go @@ -30,15 +30,16 @@ func (m *mailNotifier) CreateIssueComment(ctx context.Context, doer *user_model. issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User, ) { var act activities_model.ActionType - if comment.Type == issues_model.CommentTypeClose { + switch comment.Type { + case issues_model.CommentTypeClose: act = activities_model.ActionCloseIssue - } else if comment.Type == issues_model.CommentTypeReopen { + case issues_model.CommentTypeReopen: act = activities_model.ActionReopenIssue - } else if comment.Type == issues_model.CommentTypeComment { + case issues_model.CommentTypeComment: act = activities_model.ActionCommentIssue - } else if comment.Type == issues_model.CommentTypeCode { + case issues_model.CommentTypeCode: act = activities_model.ActionCommentIssue - } else if comment.Type == issues_model.CommentTypePullRequestPush { + case issues_model.CommentTypePullRequestPush: act = 0 } @@ -94,11 +95,12 @@ func (m *mailNotifier) NewPullRequest(ctx context.Context, pr *issues_model.Pull func (m *mailNotifier) PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, r *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) { var act activities_model.ActionType - if comment.Type == issues_model.CommentTypeClose { + switch comment.Type { + case issues_model.CommentTypeClose: act = activities_model.ActionCloseIssue - } else if comment.Type == issues_model.CommentTypeReopen { + case issues_model.CommentTypeReopen: act = activities_model.ActionReopenIssue - } else if comment.Type == issues_model.CommentTypeComment { + case issues_model.CommentTypeComment: act = activities_model.ActionCommentPull } if err := MailParticipantsComment(ctx, comment, act, pr.Issue, mentions); err != nil { diff --git a/services/migrations/gitea_downloader.go b/services/migrations/gitea_downloader.go index 272a7d379a..133cc5c928 100644 --- a/services/migrations/gitea_downloader.go +++ b/services/migrations/gitea_downloader.go @@ -504,6 +504,28 @@ func (g *GiteaDownloader) GetComments(commentable base.Commentable) ([]*base.Com return allComments, true, nil } +type ForgejoPullRequest struct { + gitea_sdk.PullRequest + Flow int64 `json:"flow"` +} + +// Extracted from https://gitea.com/gitea/go-sdk/src/commit/164e3358bc02213954fb4380b821bed80a14824d/gitea/pull.go#L347-L364 +func (g *GiteaDownloader) fixPullHeadSha(pr *ForgejoPullRequest) error { + if pr.Base != nil && pr.Base.Repository != nil && pr.Base.Repository.Owner != nil && pr.Head != nil && pr.Head.Ref != "" && pr.Head.Sha == "" { + owner := pr.Base.Repository.Owner.UserName + repo := pr.Base.Repository.Name + refs, _, err := g.client.GetRepoRefs(owner, repo, pr.Head.Ref) + if err != nil { + return err + } + if len(refs) == 0 { + return fmt.Errorf("unable to resolve PR ref %q", pr.Head.Ref) + } + pr.Head.Sha = refs[0].Object.SHA + } + return nil +} + // GetPullRequests returns pull requests according page and perPage func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) { if perPage > g.maxPerPage { @@ -511,16 +533,30 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques } allPRs := make([]*base.PullRequest, 0, perPage) - prs, _, err := g.client.ListRepoPullRequests(g.repoOwner, g.repoName, gitea_sdk.ListPullRequestsOptions{ + prs := make([]*ForgejoPullRequest, 0, perPage) + opt := gitea_sdk.ListPullRequestsOptions{ ListOptions: gitea_sdk.ListOptions{ Page: page, PageSize: perPage, }, State: gitea_sdk.StateAll, - }) + } + + link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls", url.PathEscape(g.repoOwner), url.PathEscape(g.repoName))) + link.RawQuery = opt.QueryEncode() + _, err := getParsedResponse(g.client, "GET", link.String(), http.Header{"content-type": []string{"application/json"}}, nil, &prs) if err != nil { return nil, false, fmt.Errorf("error while listing pull requests (page: %d, pagesize: %d). Error: %w", page, perPage, err) } + + if g.client.CheckServerVersionConstraint(">= 1.14.0") != nil { + for i := range prs { + if err := g.fixPullHeadSha(prs[i]); err != nil { + return nil, false, fmt.Errorf("error while listing pull requests (page: %d, pagesize: %d). Error: %w", page, perPage, err) + } + } + } + for _, pr := range prs { var milestone string if pr.Milestone != nil { @@ -598,6 +634,7 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques MergeCommitSHA: mergeCommitSHA, IsLocked: pr.IsLocked, PatchURL: pr.PatchURL, + Flow: pr.Flow, Head: base.PullRequestBranch{ Ref: headRef, SHA: headSHA, diff --git a/services/migrations/gitea_downloader_test.go b/services/migrations/gitea_downloader_test.go index b3d49752f3..5acc3b86a9 100644 --- a/services/migrations/gitea_downloader_test.go +++ b/services/migrations/gitea_downloader_test.go @@ -45,7 +45,7 @@ func TestGiteaDownloadRepo(t *testing.T) { topics, err := downloader.GetTopics() require.NoError(t, err) sort.Strings(topics) - assert.EqualValues(t, []string{"ci", "gitea", "migration", "test"}, topics) + assert.Equal(t, []string{"ci", "gitea", "migration", "test"}, topics) labels, err := downloader.GetLabels() require.NoError(t, err) @@ -132,7 +132,7 @@ func TestGiteaDownloadRepo(t *testing.T) { require.NoError(t, err) assert.True(t, isEnd) assert.Len(t, issues, 7) - assert.EqualValues(t, "open", issues[0].State) + assert.Equal(t, "open", issues[0].State) issues, isEnd, err = downloader.GetIssues(3, 2) require.NoError(t, err) @@ -307,3 +307,46 @@ func TestGiteaDownloadRepo(t *testing.T) { }, }, reviews) } + +func TestForgejoDownloadRepo(t *testing.T) { + token := os.Getenv("CODE_FORGEJO_TOKEN") + + fixturePath := "./testdata/code-forgejo-org/full_download" + server := unittest.NewMockWebServer(t, "https://code.forgejo.org", fixturePath, token != "") + defer server.Close() + + downloader, err := NewGiteaDownloader(t.Context(), server.URL, "Gusted/agit-test", "", "", token) + require.NoError(t, err) + require.NotNil(t, downloader) + + prs, _, err := downloader.GetPullRequests(1, 50) + require.NoError(t, err) + assert.Len(t, prs, 1) + + assertPullRequestEqual(t, &base.PullRequest{ + Number: 1, + PosterID: 63, + PosterName: "Gusted", + PosterEmail: "postmaster@gusted.xyz", + Title: "Add extra information", + State: "open", + Created: time.Date(2025, time.April, 1, 20, 28, 45, 0, time.UTC), + Updated: time.Date(2025, time.April, 1, 20, 28, 45, 0, time.UTC), + Base: base.PullRequestBranch{ + CloneURL: "", + Ref: "main", + SHA: "79ebb873a6497c8847141ba9706b3f757196a1e6", + RepoName: "agit-test", + OwnerName: "Gusted", + }, + Head: base.PullRequestBranch{ + CloneURL: server.URL + "/Gusted/agit-test.git", + Ref: "refs/pull/1/head", + SHA: "667e9317ec37b977e6d3d7d43e3440636970563c", + RepoName: "agit-test", + OwnerName: "Gusted", + }, + PatchURL: server.URL + "/Gusted/agit-test/pulls/1.patch", + Flow: 1, + }, prs[0]) +} diff --git a/services/migrations/gitea_sdk_hack.go b/services/migrations/gitea_sdk_hack.go new file mode 100644 index 0000000000..f3959717a8 --- /dev/null +++ b/services/migrations/gitea_sdk_hack.go @@ -0,0 +1,16 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package migrations + +import ( + "io" + "net/http" + + _ "unsafe" // Needed for go:linkname support + + gitea_sdk "code.gitea.io/sdk/gitea" +) + +//go:linkname getParsedResponse code.gitea.io/sdk/gitea.(*Client).getParsedResponse +func getParsedResponse(client *gitea_sdk.Client, method, path string, header http.Header, body io.Reader, obj any) (*gitea_sdk.Response, error) diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 7edaef5557..55adad9685 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -802,6 +802,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model MergeBase: pr.Base.SHA, Index: pr.Number, HasMerged: pr.Merged, + Flow: issues_model.PullRequestFlow(pr.Flow), Issue: &issue, } diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go index e07c621acc..85e733cc51 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -64,7 +64,7 @@ func TestGiteaUploadRepo(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, Name: repoName}) assert.True(t, repo.HasWiki()) - assert.EqualValues(t, repo_model.RepositoryReady, repo.Status) + assert.Equal(t, repo_model.RepositoryReady, repo.Status) milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{ RepoID: repo.ID, @@ -173,7 +173,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) { uploader.userMap = make(map[int64]int64) err = uploader.remapUser(&source, &target) require.NoError(t, err) - assert.EqualValues(t, user.ID, target.GetUserID()) + assert.Equal(t, user.ID, target.GetUserID()) } func TestGiteaUploadRemapExternalUser(t *testing.T) { @@ -224,7 +224,7 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) { target = repo_model.Release{} err = uploader.remapUser(&source, &target) require.NoError(t, err) - assert.EqualValues(t, linkedUser.ID, target.GetUserID()) + assert.Equal(t, linkedUser.ID, target.GetUserID()) } func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { @@ -504,14 +504,14 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { head, err := uploader.updateGitForPullRequest(&testCase.pr) require.NoError(t, err) - assert.EqualValues(t, testCase.head, head) + assert.Equal(t, testCase.head, head) log.Info(stopMark) logFiltered, logStopped := logChecker.Check(5 * time.Second) assert.True(t, logStopped) if len(testCase.logFilter) > 0 { - assert.EqualValues(t, testCase.logFiltered, logFiltered, "for log message filters: %v", testCase.logFilter) + assert.Equal(t, testCase.logFiltered, logFiltered, "for log message filters: %v", testCase.logFilter) } }) } diff --git a/services/migrations/gitlab_test.go b/services/migrations/gitlab_test.go index 924dab5144..30b24f09e8 100644 --- a/services/migrations/gitlab_test.go +++ b/services/migrations/gitlab_test.go @@ -49,7 +49,7 @@ func TestGitlabDownloadRepo(t *testing.T) { topics, err := downloader.GetTopics() require.NoError(t, err) assert.Len(t, topics, 2) - assert.EqualValues(t, []string{"migration", "test"}, topics) + assert.Equal(t, []string{"migration", "test"}, topics) milestones, err := downloader.GetMilestones() require.NoError(t, err) @@ -352,7 +352,7 @@ func TestGitlabSkippedIssueNumber(t *testing.T) { // the only issue in this repository has number 2 assert.Len(t, issues, 1) assert.EqualValues(t, 2, issues[0].Number) - assert.EqualValues(t, "vpn unlimited errors", issues[0].Title) + assert.Equal(t, "vpn unlimited errors", issues[0].Title) prs, _, err := downloader.GetPullRequests(1, 10) require.NoError(t, err) @@ -361,7 +361,7 @@ func TestGitlabSkippedIssueNumber(t *testing.T) { // pull request 3 in Forgejo assert.Len(t, prs, 1) assert.EqualValues(t, 3, prs[0].Number) - assert.EqualValues(t, "Review", prs[0].Title) + assert.Equal(t, "Review", prs[0].Title) } func gitlabClientMockSetup(t *testing.T) (*http.ServeMux, *httptest.Server, *gitlab.Client) { @@ -531,7 +531,7 @@ func TestAwardsToReactions(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(testResponse), &awards)) reactions := downloader.awardsToReactions(awards) - assert.EqualValues(t, []*base.Reaction{ + assert.Equal(t, []*base.Reaction{ { UserName: "lafriks", UserID: 1241334, @@ -623,7 +623,7 @@ func TestNoteToComment(t *testing.T) { for i, note := range notes { actualComment := *downloader.convertNoteToComment(17, ¬e) - assert.EqualValues(t, actualComment, comments[i]) + assert.Equal(t, actualComment, comments[i]) } } diff --git a/services/migrations/gogs_test.go b/services/migrations/gogs_test.go index 7d7f10c2b9..6780ad2923 100644 --- a/services/migrations/gogs_test.go +++ b/services/migrations/gogs_test.go @@ -215,9 +215,9 @@ func TestGogsDownloaderFactory_New(t *testing.T) { } assert.IsType(t, &GogsDownloader{}, got) - assert.EqualValues(t, tt.baseURL, got.(*GogsDownloader).baseURL) - assert.EqualValues(t, tt.repoOwner, got.(*GogsDownloader).repoOwner) - assert.EqualValues(t, tt.repoName, got.(*GogsDownloader).repoName) + assert.Equal(t, tt.baseURL, got.(*GogsDownloader).baseURL) + assert.Equal(t, tt.repoOwner, got.(*GogsDownloader).repoOwner) + assert.Equal(t, tt.repoName, got.(*GogsDownloader).repoName) }) } } diff --git a/services/migrations/main_test.go b/services/migrations/main_test.go index 17e6505df2..d543bd6d9c 100644 --- a/services/migrations/main_test.go +++ b/services/migrations/main_test.go @@ -136,6 +136,7 @@ func assertPullRequestEqual(t *testing.T, expected, actual *base.PullRequest) { assert.ElementsMatch(t, expected.Assignees, actual.Assignees) assert.Equal(t, expected.IsLocked, actual.IsLocked) assertReactionsEqual(t, expected.Reactions, actual.Reactions) + assert.Equal(t, expected.Flow, actual.Flow) } func assertPullRequestsEqual(t *testing.T, expected, actual []*base.PullRequest) { diff --git a/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Frepos%2FGusted%2Fagit-test%2Fpulls%3Flimit=50&page=1&state=all b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Frepos%2FGusted%2Fagit-test%2Fpulls%3Flimit=50&page=1&state=all new file mode 100644 index 0000000000..87095d9e24 --- /dev/null +++ b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Frepos%2FGusted%2Fagit-test%2Fpulls%3Flimit=50&page=1&state=all @@ -0,0 +1,8 @@ +Access-Control-Expose-Headers: X-Total-Count +Cache-Control: max-age=0, private, must-revalidate, no-transform +Content-Type: application/json;charset=utf-8 +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Total-Count: 1 + +[{"id":4980,"url":"https://code.forgejo.org/Gusted/agit-test/pulls/1","number":1,"user":{"id":63,"login":"Gusted","login_name":"26734","source_id":1,"full_name":"","email":"postmaster@gusted.xyz","avatar_url":"https://code.forgejo.org/avatars/4ca5ad8bc488630869fdbd2051da61cbed7241c9c066d4e5e1dd36300f887340","html_url":"https://code.forgejo.org/Gusted","language":"en-US","is_admin":false,"last_login":"2025-04-01T16:35:18Z","created":"2023-07-08T13:33:38Z","restricted":false,"active":true,"prohibit_login":false,"location":"","pronouns":"","website":"","description":"","visibility":"public","followers_count":2,"following_count":0,"starred_repos_count":0,"username":"Gusted"},"title":"Add extra information","body":"","labels":[],"milestone":null,"assignee":null,"assignees":null,"requested_reviewers":[],"requested_reviewers_teams":[],"state":"open","draft":false,"is_locked":false,"comments":0,"review_comments":0,"additions":0,"deletions":0,"changed_files":0,"html_url":"https://code.forgejo.org/Gusted/agit-test/pulls/1","diff_url":"https://code.forgejo.org/Gusted/agit-test/pulls/1.diff","patch_url":"https://code.forgejo.org/Gusted/agit-test/pulls/1.patch","mergeable":true,"merged":false,"merged_at":null,"merge_commit_sha":null,"merged_by":null,"allow_maintainer_edit":false,"base":{"label":"main","ref":"main","sha":"79ebb873a6497c8847141ba9706b3f757196a1e6","repo_id":1414,"repo":{"id":1414,"owner":{"id":63,"login":"Gusted","login_name":"","source_id":0,"full_name":"","email":"gusted@noreply.code.forgejo.org","avatar_url":"https://code.forgejo.org/avatars/4ca5ad8bc488630869fdbd2051da61cbed7241c9c066d4e5e1dd36300f887340","html_url":"https://code.forgejo.org/Gusted","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2023-07-08T13:33:38Z","restricted":false,"active":false,"prohibit_login":false,"location":"","pronouns":"","website":"","description":"","visibility":"public","followers_count":2,"following_count":0,"starred_repos_count":0,"username":"Gusted"},"name":"agit-test","full_name":"Gusted/agit-test","description":"USED FOR FORGEJO UNIT TESTING","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":36,"language":"","languages_url":"https://code.forgejo.org/api/v1/repos/Gusted/agit-test/languages","html_url":"https://code.forgejo.org/Gusted/agit-test","url":"https://code.forgejo.org/api/v1/repos/Gusted/agit-test","link":"","ssh_url":"ssh://git@code.forgejo.org/Gusted/agit-test.git","clone_url":"https://code.forgejo.org/Gusted/agit-test.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":1,"release_counter":0,"default_branch":"main","archived":false,"created_at":"2025-04-01T20:25:03Z","updated_at":"2025-04-01T20:25:03Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":true,"push":true,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"wiki_branch":"main","globally_editable_wiki":false,"has_pull_requests":true,"has_projects":true,"has_releases":true,"has_packages":true,"has_actions":true,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":true,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"default_update_style":"merge","avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":null}},"head":{"label":"","ref":"refs/pull/1/head","sha":"667e9317ec37b977e6d3d7d43e3440636970563c","repo_id":1414,"repo":{"id":1414,"owner":{"id":63,"login":"Gusted","login_name":"","source_id":0,"full_name":"","email":"gusted@noreply.code.forgejo.org","avatar_url":"https://code.forgejo.org/avatars/4ca5ad8bc488630869fdbd2051da61cbed7241c9c066d4e5e1dd36300f887340","html_url":"https://code.forgejo.org/Gusted","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2023-07-08T13:33:38Z","restricted":false,"active":false,"prohibit_login":false,"location":"","pronouns":"","website":"","description":"","visibility":"public","followers_count":2,"following_count":0,"starred_repos_count":0,"username":"Gusted"},"name":"agit-test","full_name":"Gusted/agit-test","description":"USED FOR FORGEJO UNIT TESTING","empty":false,"private":false,"fork":false,"template":false,"parent":null,"mirror":false,"size":36,"language":"","languages_url":"https://code.forgejo.org/api/v1/repos/Gusted/agit-test/languages","html_url":"https://code.forgejo.org/Gusted/agit-test","url":"https://code.forgejo.org/api/v1/repos/Gusted/agit-test","link":"","ssh_url":"ssh://git@code.forgejo.org/Gusted/agit-test.git","clone_url":"https://code.forgejo.org/Gusted/agit-test.git","original_url":"","website":"","stars_count":0,"forks_count":0,"watchers_count":1,"open_issues_count":0,"open_pr_counter":1,"release_counter":0,"default_branch":"main","archived":false,"created_at":"2025-04-01T20:25:03Z","updated_at":"2025-04-01T20:25:03Z","archived_at":"1970-01-01T00:00:00Z","permissions":{"admin":true,"push":true,"pull":true},"has_issues":true,"internal_tracker":{"enable_time_tracker":true,"allow_only_contributors_to_track_time":true,"enable_issue_dependencies":true},"has_wiki":true,"wiki_branch":"main","globally_editable_wiki":false,"has_pull_requests":true,"has_projects":true,"has_releases":true,"has_packages":true,"has_actions":true,"ignore_whitespace_conflicts":false,"allow_merge_commits":true,"allow_rebase":true,"allow_rebase_explicit":true,"allow_squash_merge":true,"allow_fast_forward_only_merge":true,"allow_rebase_update":true,"default_delete_branch_after_merge":false,"default_merge_style":"merge","default_allow_maintainer_edit":false,"default_update_style":"merge","avatar_url":"","internal":false,"mirror_interval":"","object_format_name":"sha1","mirror_updated":"0001-01-01T00:00:00Z","repo_transfer":null,"topics":null}},"merge_base":"79ebb873a6497c8847141ba9706b3f757196a1e6","due_date":null,"created_at":"2025-04-01T20:28:45Z","updated_at":"2025-04-01T20:28:45Z","closed_at":null,"pin_order":0,"flow":1}] diff --git a/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fsettings%2Fapi b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fsettings%2Fapi new file mode 100644 index 0000000000..11c4e7b8ba --- /dev/null +++ b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fsettings%2Fapi @@ -0,0 +1,7 @@ +Content-Length: 117 +Cache-Control: max-age=0, private, must-revalidate, no-transform +Content-Type: application/json;charset=utf-8 +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN + +{"max_response_items":50,"default_paging_num":30,"default_git_trees_per_page":1000,"default_max_blob_size":10485760} diff --git a/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fversion b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fversion new file mode 100644 index 0000000000..411ed84e24 --- /dev/null +++ b/services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fversion @@ -0,0 +1,7 @@ +Cache-Control: max-age=0, private, must-revalidate, no-transform +Content-Type: application/json;charset=utf-8 +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +Content-Length: 53 + +{"version":"11.0.0-dev-617-1d1e0ced3e+gitea-1.22.0"} diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index a63cbcf40c..c46323f283 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -244,6 +244,24 @@ func pruneBrokenReferences(ctx context.Context, return pruneErr } +// checkRecoverableSyncError takes an error message from a git fetch command and returns false if it should be a fatal/blocking error +func checkRecoverableSyncError(stderrMessage string) bool { + switch { + case strings.Contains(stderrMessage, "unable to resolve reference") && strings.Contains(stderrMessage, "reference broken"): + return true + case strings.Contains(stderrMessage, "remote error") && strings.Contains(stderrMessage, "not our ref"): + return true + case strings.Contains(stderrMessage, "cannot lock ref") && strings.Contains(stderrMessage, "but expected"): + return true + case strings.Contains(stderrMessage, "cannot lock ref") && strings.Contains(stderrMessage, "unable to resolve reference"): + return true + case strings.Contains(stderrMessage, "Unable to create") && strings.Contains(stderrMessage, ".lock"): + return true + default: + return false + } +} + // runSync returns true if sync finished without error. func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bool) { repoPath := m.Repo.RepoPath() @@ -286,7 +304,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stdoutMessage := util.SanitizeCredentialURLs(stdout) // Now check if the error is a resolve reference due to broken reference - if strings.Contains(stderr, "unable to resolve reference") && strings.Contains(stderr, "reference broken") { + if checkRecoverableSyncError(stderr) { log.Warn("SyncMirrors [repo: %-v]: failed to update mirror repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err) err = nil @@ -337,6 +355,15 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo return nil, false } + if m.LFS && setting.LFS.StartServer { + log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo) + endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint) + lfsClient := lfs.NewClient(endpoint, nil) + if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil { + log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err) + } + } + log.Trace("SyncMirrors [repo: %-v]: syncing branches...", m.Repo) if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, m.Repo, gitRepo, 0); err != nil { log.Error("SyncMirrors [repo: %-v]: failed to synchronize branches: %v", m.Repo, err) @@ -346,15 +373,6 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo if err = repo_module.SyncReleasesWithTags(ctx, m.Repo, gitRepo); err != nil { log.Error("SyncMirrors [repo: %-v]: failed to synchronize tags to releases: %v", m.Repo, err) } - - if m.LFS && setting.LFS.StartServer { - log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo) - endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint) - lfsClient := lfs.NewClient(endpoint, nil) - if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil { - log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err) - } - } gitRepo.Close() log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo) @@ -382,7 +400,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stdoutMessage := util.SanitizeCredentialURLs(stdout) // Now check if the error is a resolve reference due to broken reference - if strings.Contains(stderrMessage, "unable to resolve reference") && strings.Contains(stderrMessage, "reference broken") { + if checkRecoverableSyncError(stderrMessage) { log.Warn("SyncMirrors [repo: %-v Wiki]: failed to update mirror wiki repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err) err = nil diff --git a/services/mirror/mirror_pull_test.go b/services/mirror/mirror_pull_test.go new file mode 100644 index 0000000000..97859be5b0 --- /dev/null +++ b/services/mirror/mirror_pull_test.go @@ -0,0 +1,94 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package mirror + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_parseRemoteUpdateOutput(t *testing.T) { + output := ` + * [new tag] v0.1.8 -> v0.1.8 + * [new branch] master -> origin/master + - [deleted] (none) -> origin/test1 + - [deleted] (none) -> tag1 + + f895a1e...957a993 test2 -> origin/test2 (forced update) + 957a993..a87ba5f test3 -> origin/test3 + * [new ref] refs/pull/26595/head -> refs/pull/26595/head + * [new ref] refs/pull/26595/merge -> refs/pull/26595/merge + e0639e38fb..6db2410489 refs/pull/25873/head -> refs/pull/25873/head + + 1c97ebc746...976d27d52f refs/pull/25873/merge -> refs/pull/25873/merge (forced update) +` + results := parseRemoteUpdateOutput(output, "origin") + assert.Len(t, results, 10) + assert.Equal(t, "refs/tags/v0.1.8", results[0].refName.String()) + assert.Equal(t, gitShortEmptySha, results[0].oldCommitID) + assert.Empty(t, results[0].newCommitID) + + assert.Equal(t, "refs/heads/master", results[1].refName.String()) + assert.Equal(t, gitShortEmptySha, results[1].oldCommitID) + assert.Empty(t, results[1].newCommitID) + + assert.Equal(t, "refs/heads/test1", results[2].refName.String()) + assert.Empty(t, results[2].oldCommitID) + assert.Equal(t, gitShortEmptySha, results[2].newCommitID) + + assert.Equal(t, "refs/tags/tag1", results[3].refName.String()) + assert.Empty(t, results[3].oldCommitID) + assert.Equal(t, gitShortEmptySha, results[3].newCommitID) + + assert.Equal(t, "refs/heads/test2", results[4].refName.String()) + assert.Equal(t, "f895a1e", results[4].oldCommitID) + assert.Equal(t, "957a993", results[4].newCommitID) + + assert.Equal(t, "refs/heads/test3", results[5].refName.String()) + assert.Equal(t, "957a993", results[5].oldCommitID) + assert.Equal(t, "a87ba5f", results[5].newCommitID) + + assert.Equal(t, "refs/pull/26595/head", results[6].refName.String()) + assert.Equal(t, gitShortEmptySha, results[6].oldCommitID) + assert.Empty(t, results[6].newCommitID) + + assert.Equal(t, "refs/pull/26595/merge", results[7].refName.String()) + assert.Equal(t, gitShortEmptySha, results[7].oldCommitID) + assert.Empty(t, results[7].newCommitID) + + assert.Equal(t, "refs/pull/25873/head", results[8].refName.String()) + assert.Equal(t, "e0639e38fb", results[8].oldCommitID) + assert.Equal(t, "6db2410489", results[8].newCommitID) + + assert.Equal(t, "refs/pull/25873/merge", results[9].refName.String()) + assert.Equal(t, "1c97ebc746", results[9].oldCommitID) + assert.Equal(t, "976d27d52f", results[9].newCommitID) +} + +func Test_checkRecoverableSyncError(t *testing.T) { + cases := []struct { + recoverable bool + message string + }{ + // A race condition in http git-fetch where certain refs were listed on the remote and are no longer there, would exit status 128 + {true, "fatal: remote error: upload-pack: not our ref 988881adc9fc3655077dc2d4d757d480b5ea0e11"}, + // A race condition where a local gc/prune removes a named ref during a git-fetch would exit status 1 + {true, "cannot lock ref 'refs/pull/123456/merge': unable to resolve reference 'refs/pull/134153/merge'"}, + // A race condition in http git-fetch where named refs were listed on the remote and are no longer there + {true, "error: cannot lock ref 'refs/remotes/origin/foo': unable to resolve reference 'refs/remotes/origin/foo': reference broken"}, + // A race condition in http git-fetch where named refs were force-pushed during the update, would exit status 128 + {true, "error: cannot lock ref 'refs/pull/123456/merge': is at 988881adc9fc3655077dc2d4d757d480b5ea0e11 but expected 7f894307ffc9553edbd0b671cab829786866f7b2"}, + // A race condition with other local git operations, such as git-maintenance, would exit status 128 (well, "Unable" the "U" is uppercase) + {true, "fatal: Unable to create '/data/gitea-repositories/foo-org/bar-repo.git/./objects/info/commit-graphs/commit-graph-chain.lock': File exists."}, + // Missing or unauthorized credentials, would exit status 128 + {false, "fatal: Authentication failed for 'https://example.com/foo-does-not-exist/bar.git/'"}, + // A non-existent remote repository, would exit status 128 + {false, "fatal: Could not read from remote repository."}, + // A non-functioning proxy, would exit status 128 + {false, "fatal: unable to access 'https://example.com/foo-does-not-exist/bar.git/': Failed to connect to configured-https-proxy port 1080 after 0 ms: Couldn't connect to server"}, + } + + for _, c := range cases { + assert.Equal(t, c.recoverable, checkRecoverableSyncError(c.message), "test case: %s", c.message) + } +} diff --git a/services/mirror/mirror_test.go b/services/mirror/mirror_test.go deleted file mode 100644 index 76632b6872..0000000000 --- a/services/mirror/mirror_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package mirror - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_parseRemoteUpdateOutput(t *testing.T) { - output := ` - * [new tag] v0.1.8 -> v0.1.8 - * [new branch] master -> origin/master - - [deleted] (none) -> origin/test1 - - [deleted] (none) -> tag1 - + f895a1e...957a993 test2 -> origin/test2 (forced update) - 957a993..a87ba5f test3 -> origin/test3 - * [new ref] refs/pull/26595/head -> refs/pull/26595/head - * [new ref] refs/pull/26595/merge -> refs/pull/26595/merge - e0639e38fb..6db2410489 refs/pull/25873/head -> refs/pull/25873/head - + 1c97ebc746...976d27d52f refs/pull/25873/merge -> refs/pull/25873/merge (forced update) -` - results := parseRemoteUpdateOutput(output, "origin") - assert.Len(t, results, 10) - assert.EqualValues(t, "refs/tags/v0.1.8", results[0].refName.String()) - assert.EqualValues(t, gitShortEmptySha, results[0].oldCommitID) - assert.EqualValues(t, "", results[0].newCommitID) - - assert.EqualValues(t, "refs/heads/master", results[1].refName.String()) - assert.EqualValues(t, gitShortEmptySha, results[1].oldCommitID) - assert.EqualValues(t, "", results[1].newCommitID) - - assert.EqualValues(t, "refs/heads/test1", results[2].refName.String()) - assert.EqualValues(t, "", results[2].oldCommitID) - assert.EqualValues(t, gitShortEmptySha, results[2].newCommitID) - - assert.EqualValues(t, "refs/tags/tag1", results[3].refName.String()) - assert.EqualValues(t, "", results[3].oldCommitID) - assert.EqualValues(t, gitShortEmptySha, results[3].newCommitID) - - assert.EqualValues(t, "refs/heads/test2", results[4].refName.String()) - assert.EqualValues(t, "f895a1e", results[4].oldCommitID) - assert.EqualValues(t, "957a993", results[4].newCommitID) - - assert.EqualValues(t, "refs/heads/test3", results[5].refName.String()) - assert.EqualValues(t, "957a993", results[5].oldCommitID) - assert.EqualValues(t, "a87ba5f", results[5].newCommitID) - - assert.EqualValues(t, "refs/pull/26595/head", results[6].refName.String()) - assert.EqualValues(t, gitShortEmptySha, results[6].oldCommitID) - assert.EqualValues(t, "", results[6].newCommitID) - - assert.EqualValues(t, "refs/pull/26595/merge", results[7].refName.String()) - assert.EqualValues(t, gitShortEmptySha, results[7].oldCommitID) - assert.EqualValues(t, "", results[7].newCommitID) - - assert.EqualValues(t, "refs/pull/25873/head", results[8].refName.String()) - assert.EqualValues(t, "e0639e38fb", results[8].oldCommitID) - assert.EqualValues(t, "6db2410489", results[8].newCommitID) - - assert.EqualValues(t, "refs/pull/25873/merge", results[9].refName.String()) - assert.EqualValues(t, "1c97ebc746", results[9].oldCommitID) - assert.EqualValues(t, "976d27d52f", results[9].newCommitID) -} diff --git a/services/moderation/reporting.go b/services/moderation/reporting.go index 34758988c2..e01156dc11 100644 --- a/services/moderation/reporting.go +++ b/services/moderation/reporting.go @@ -30,12 +30,12 @@ var ( // When reporting users or organizations doer should be able to view the reported entity. func CanReport(ctx context.Context, doer *user.User, contentType moderation.ReportedContentType, contentID int64) (bool, error) { hasAccess := false - var issueID int64 = 0 - var repoID int64 = 0 + var issueID int64 + var repoID int64 unitType := unit.TypeInvalid // used when checking access for issues, pull requests or comments if contentType == moderation.ReportedContentTypeUser { - reported_user, err := user.GetUserByID(ctx, contentID) + reportedUser, err := user.GetUserByID(ctx, contentID) if err != nil { if user.IsErrUserNotExist(err) { log.Warn("User #%d wanted to report user #%d but it does not exist.", doer.ID, contentID) @@ -44,10 +44,15 @@ func CanReport(ctx context.Context, doer *user.User, contentType moderation.Repo return false, err } - hasAccess = user.IsUserVisibleToViewer(ctx, reported_user, ctx.Doer) + hasAccess = user.IsUserVisibleToViewer(ctx, reportedUser, ctx.Doer) + if !hasAccess { + log.Warn("User #%d wanted to report user/org #%d but they are not able to see that profile.", doer.ID, contentID) + return false, ErrDoerNotAllowed + } } else { // for comments and issues/pulls we need to get the parent repository - if contentType == moderation.ReportedContentTypeComment { + switch contentType { + case moderation.ReportedContentTypeComment: comment, err := issues.GetCommentByID(ctx, contentID) if err != nil { if issues.IsErrCommentNotExist(err) { @@ -62,9 +67,9 @@ func CanReport(ctx context.Context, doer *user.User, contentType moderation.Repo return false, nil } issueID = comment.IssueID - } else if contentType == moderation.ReportedContentTypeIssue { + case moderation.ReportedContentTypeIssue: issueID = contentID - } else if contentType == moderation.ReportedContentTypeRepository { + case moderation.ReportedContentTypeRepository: repoID = contentID } diff --git a/services/packages/cleanup/cleanup.go b/services/packages/cleanup/cleanup.go index 7e821e2630..f8b08a0b59 100644 --- a/services/packages/cleanup/cleanup.go +++ b/services/packages/cleanup/cleanup.go @@ -122,23 +122,24 @@ func ExecuteCleanupRules(outerCtx context.Context) error { } if anyVersionDeleted { - if pcr.Type == packages_model.TypeDebian { + switch pcr.Type { + case packages_model.TypeDebian: if err := debian_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil { return fmt.Errorf("CleanupRule [%d]: debian.BuildAllRepositoryFiles failed: %w", pcr.ID, err) } - } else if pcr.Type == packages_model.TypeAlpine { + case packages_model.TypeAlpine: if err := alpine_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil { return fmt.Errorf("CleanupRule [%d]: alpine.BuildAllRepositoryFiles failed: %w", pcr.ID, err) } - } else if pcr.Type == packages_model.TypeRpm { + case packages_model.TypeRpm: if err := rpm_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil { return fmt.Errorf("CleanupRule [%d]: rpm.BuildAllRepositoryFiles failed: %w", pcr.ID, err) } - } else if pcr.Type == packages_model.TypeArch { + case packages_model.TypeArch: if err := arch_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil { return fmt.Errorf("CleanupRule [%d]: arch.BuildAllRepositoryFiles failed: %w", pcr.ID, err) } - } else if pcr.Type == packages_model.TypeAlt { + case packages_model.TypeAlt: if err := alt_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil { return fmt.Errorf("CleanupRule [%d]: alt.BuildAllRepositoryFiles failed: %w", pcr.ID, err) } diff --git a/services/packages/cleanup/cleanup_sha256_test.go b/services/packages/cleanup/cleanup_sha256_test.go index f26b98b4b0..efa254fc68 100644 --- a/services/packages/cleanup/cleanup_sha256_test.go +++ b/services/packages/cleanup/cleanup_sha256_test.go @@ -78,7 +78,7 @@ func TestCleanupSHA256(t *testing.T) { for range expected { filtered = append(filtered, true) } - assert.EqualValues(t, filtered, logFiltered, expected) + assert.Equal(t, filtered, logFiltered, expected) } ancient := 1 * time.Hour diff --git a/services/packages/container/blob_uploader.go b/services/packages/container/blob_uploader.go index cc009d1f5c..ffc47f3853 100644 --- a/services/packages/container/blob_uploader.go +++ b/services/packages/container/blob_uploader.go @@ -92,7 +92,7 @@ func (u *BlobUploader) Append(ctx context.Context, r io.Reader) error { u.BytesReceived += n - u.HashStateBytes, err = u.MultiHasher.MarshalBinary() + u.HashStateBytes, err = u.MarshalBinary() if err != nil { return err } diff --git a/services/pull/check_test.go b/services/pull/check_test.go index b965d90236..9b7e1660bc 100644 --- a/services/pull/check_test.go +++ b/services/pull/check_test.go @@ -52,7 +52,7 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) { select { case id := <-idChan: - assert.EqualValues(t, pr.ID, id) + assert.Equal(t, pr.ID, id) case <-time.After(time.Second): assert.FailNow(t, "Timeout: nothing was added to pullRequestQueue") } diff --git a/services/pull/review.go b/services/pull/review.go index d61d9623b6..18c27de5a7 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -232,7 +232,7 @@ func CreateCodeCommentKnownReviewID(ctx context.Context, doer *user_model.User, commit, err := gitRepo.LineBlame(head, gitRepo.Path, treePath, uint(line)) if err == nil { commitID = commit.ID.String() - } else if !(strings.Contains(err.Error(), "exit status 128 - fatal: no such path") || notEnoughLines.MatchString(err.Error())) { + } else if !strings.Contains(err.Error(), "exit status 128 - fatal: no such path") && !notEnoughLines.MatchString(err.Error()) { return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %w", pr.GetGitRefName(), gitRepo.Path, treePath, line, err) } } diff --git a/services/release/release_test.go b/services/release/release_test.go index 66106eb606..bcc713019b 100644 --- a/services/release/release_test.go +++ b/services/release/release_test.go @@ -138,9 +138,9 @@ func TestRelease_Create(t *testing.T) { })) assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, &release)) assert.Len(t, release.Attachments, 1) - assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID) - assert.EqualValues(t, attach.Name, release.Attachments[0].Name) - assert.EqualValues(t, attach.ExternalURL, release.Attachments[0].ExternalURL) + assert.Equal(t, attach.UUID, release.Attachments[0].UUID) + assert.Equal(t, attach.Name, release.Attachments[0].Name) + assert.Equal(t, attach.ExternalURL, release.Attachments[0].ExternalURL) release = repo_model.Release{ RepoID: repo.ID, @@ -165,8 +165,8 @@ func TestRelease_Create(t *testing.T) { })) assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, &release)) assert.Len(t, release.Attachments, 1) - assert.EqualValues(t, "test", release.Attachments[0].Name) - assert.EqualValues(t, "https://forgejo.org/", release.Attachments[0].ExternalURL) + assert.Equal(t, "test", release.Attachments[0].Name) + assert.Equal(t, "https://forgejo.org/", release.Attachments[0].ExternalURL) release = repo_model.Release{ RepoID: repo.ID, @@ -318,10 +318,10 @@ func TestRelease_Update(t *testing.T) { })) require.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release)) assert.Len(t, release.Attachments, 1) - assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID) - assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID) - assert.EqualValues(t, attach.Name, release.Attachments[0].Name) - assert.EqualValues(t, attach.ExternalURL, release.Attachments[0].ExternalURL) + assert.Equal(t, attach.UUID, release.Attachments[0].UUID) + assert.Equal(t, release.ID, release.Attachments[0].ReleaseID) + assert.Equal(t, attach.Name, release.Attachments[0].Name) + assert.Equal(t, attach.ExternalURL, release.Attachments[0].ExternalURL) // update the attachment name require.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, false, []*AttachmentChange{ @@ -334,10 +334,10 @@ func TestRelease_Update(t *testing.T) { release.Attachments = nil require.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release)) assert.Len(t, release.Attachments, 1) - assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID) - assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID) - assert.EqualValues(t, "test2.txt", release.Attachments[0].Name) - assert.EqualValues(t, attach.ExternalURL, release.Attachments[0].ExternalURL) + assert.Equal(t, attach.UUID, release.Attachments[0].UUID) + assert.Equal(t, release.ID, release.Attachments[0].ReleaseID) + assert.Equal(t, "test2.txt", release.Attachments[0].Name) + assert.Equal(t, attach.ExternalURL, release.Attachments[0].ExternalURL) // delete the attachment require.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, false, []*AttachmentChange{ @@ -361,9 +361,9 @@ func TestRelease_Update(t *testing.T) { })) assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release)) assert.Len(t, release.Attachments, 1) - assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID) - assert.EqualValues(t, "test", release.Attachments[0].Name) - assert.EqualValues(t, "https://forgejo.org/", release.Attachments[0].ExternalURL) + assert.Equal(t, release.ID, release.Attachments[0].ReleaseID) + assert.Equal(t, "test", release.Attachments[0].Name) + assert.Equal(t, "https://forgejo.org/", release.Attachments[0].ExternalURL) externalAttachmentUUID := release.Attachments[0].UUID // update the attachment name @@ -378,10 +378,10 @@ func TestRelease_Update(t *testing.T) { release.Attachments = nil assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release)) assert.Len(t, release.Attachments, 1) - assert.EqualValues(t, externalAttachmentUUID, release.Attachments[0].UUID) - assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID) - assert.EqualValues(t, "test2", release.Attachments[0].Name) - assert.EqualValues(t, "https://about.gitea.com/", release.Attachments[0].ExternalURL) + assert.Equal(t, externalAttachmentUUID, release.Attachments[0].UUID) + assert.Equal(t, release.ID, release.Attachments[0].ReleaseID) + assert.Equal(t, "test2", release.Attachments[0].Name) + assert.Equal(t, "https://about.gitea.com/", release.Attachments[0].ExternalURL) } func TestRelease_createTag(t *testing.T) { diff --git a/services/repository/archiver/archiver_test.go b/services/repository/archiver/archiver_test.go index ec4da2404f..00d82267c9 100644 --- a/services/repository/archiver/archiver_test.go +++ b/services/repository/archiver/archiver_test.go @@ -36,7 +36,7 @@ func TestArchive_Basic(t *testing.T) { bogusReq, err := NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP) require.NoError(t, err) assert.NotNil(t, bogusReq) - assert.EqualValues(t, firstCommit+".zip", bogusReq.GetArchiveName()) + assert.Equal(t, firstCommit+".zip", bogusReq.GetArchiveName()) // Check a series of bogus requests. // Step 1, valid commit with a bad extension. @@ -57,12 +57,12 @@ func TestArchive_Basic(t *testing.T) { bogusReq, err = NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master", git.ZIP) require.NoError(t, err) assert.NotNil(t, bogusReq) - assert.EqualValues(t, "master.zip", bogusReq.GetArchiveName()) + assert.Equal(t, "master.zip", bogusReq.GetArchiveName()) bogusReq, err = NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive", git.ZIP) require.NoError(t, err) assert.NotNil(t, bogusReq) - assert.EqualValues(t, "test-archive.zip", bogusReq.GetArchiveName()) + assert.Equal(t, "test-archive.zip", bogusReq.GetArchiveName()) // Now two valid requests, firstCommit with valid extensions. zipReq, err := NewRequest(ctx, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP) diff --git a/services/repository/avatar_test.go b/services/repository/avatar_test.go index e5fcf7f239..6f28113286 100644 --- a/services/repository/avatar_test.go +++ b/services/repository/avatar_test.go @@ -60,7 +60,7 @@ func TestDeleteAvatar(t *testing.T) { err = DeleteAvatar(db.DefaultContext, repo) require.NoError(t, err) - assert.Equal(t, "", repo.Avatar) + assert.Empty(t, repo.Avatar) } func TestTemplateGenerateAvatar(t *testing.T) { diff --git a/services/repository/contributors_graph_test.go b/services/repository/contributors_graph_test.go index 927c950bec..45af85272d 100644 --- a/services/repository/contributors_graph_test.go +++ b/services/repository/contributors_graph_test.go @@ -53,14 +53,14 @@ func TestRepository_ContributorsGraph(t *testing.T) { keys = append(keys, k) } slices.Sort(keys) - assert.EqualValues(t, []string{ + assert.Equal(t, []string{ "ethantkoenig@gmail.com", "jimmy.praet@telenet.be", "jon@allspice.io", "total", // generated summary }, keys) - assert.EqualValues(t, &ContributorData{ + assert.Equal(t, &ContributorData{ Name: "Ethan Koenig", AvatarLink: "/assets/img/avatar_default.png", TotalCommits: 1, @@ -73,7 +73,7 @@ func TestRepository_ContributorsGraph(t *testing.T) { }, }, }, data["ethantkoenig@gmail.com"]) - assert.EqualValues(t, &ContributorData{ + assert.Equal(t, &ContributorData{ Name: "Total", AvatarLink: "", TotalCommits: 3, diff --git a/services/repository/delete.go b/services/repository/delete.go index 7c83ba12cd..685568d693 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -89,6 +89,11 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID } } + // If the repository was reported as abusive, a shadow copy should be created before deletion. + if err := repo_model.IfNeededCreateShadowCopyForRepository(ctx, repo, false); err != nil { + return err + } + if cnt, err := sess.ID(repoID).Delete(&repo_model.Repository{}); err != nil { return err } else if cnt != 1 { diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index ca2f861c0b..78ad52c61c 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -64,13 +64,13 @@ func TestGetContents(t *testing.T) { t.Run("Get README.md contents with GetContents(ctx, )", func(t *testing.T) { fileContentResponse, err := GetContents(db.DefaultContext, repo, treePath, ref, false) - assert.EqualValues(t, expectedContentsResponse, fileContentResponse) + assert.Equal(t, expectedContentsResponse, fileContentResponse) require.NoError(t, err) }) t.Run("Get README.md contents with ref as empty string (should then use the repo's default branch) with GetContents(ctx, )", func(t *testing.T) { fileContentResponse, err := GetContents(db.DefaultContext, repo, treePath, "", false) - assert.EqualValues(t, expectedContentsResponse, fileContentResponse) + assert.Equal(t, expectedContentsResponse, fileContentResponse) require.NoError(t, err) }) } diff --git a/services/repository/files/diff_test.go b/services/repository/files/diff_test.go index d6265273c7..67c63803d3 100644 --- a/services/repository/files/diff_test.go +++ b/services/repository/files/diff_test.go @@ -124,7 +124,7 @@ func TestGetDiffPreview(t *testing.T) { require.NoError(t, err) bs, err := json.Marshal(diff) require.NoError(t, err) - assert.EqualValues(t, string(expectedBs), string(bs)) + assert.Equal(t, string(expectedBs), string(bs)) }) t.Run("empty branch, same results", func(t *testing.T) { @@ -134,7 +134,7 @@ func TestGetDiffPreview(t *testing.T) { require.NoError(t, err) bs, err := json.Marshal(diff) require.NoError(t, err) - assert.EqualValues(t, expectedBs, bs) + assert.Equal(t, expectedBs, bs) }) } diff --git a/services/repository/files/file_test.go b/services/repository/files/file_test.go index db2f4403f4..169cafba0d 100644 --- a/services/repository/files/file_test.go +++ b/services/repository/files/file_test.go @@ -14,13 +14,13 @@ func TestCleanUploadFileName(t *testing.T) { name := "this/is/test" cleanName := CleanUploadFileName(name) expectedCleanName := name - assert.EqualValues(t, expectedCleanName, cleanName) + assert.Equal(t, expectedCleanName, cleanName) }) t.Run("Clean a .git path", func(t *testing.T) { name := "this/is/test/.git" cleanName := CleanUploadFileName(name) expectedCleanName := "" - assert.EqualValues(t, expectedCleanName, cleanName) + assert.Equal(t, expectedCleanName, cleanName) }) } diff --git a/services/repository/files/tree_test.go b/services/repository/files/tree_test.go index 7865fcf2e2..5cd628722b 100644 --- a/services/repository/files/tree_test.go +++ b/services/repository/files/tree_test.go @@ -48,5 +48,5 @@ func TestGetTreeBySHA(t *testing.T) { TotalCount: 1, } - assert.EqualValues(t, expectedTree, tree) + assert.Equal(t, expectedTree, tree) } diff --git a/services/repository/lfs.go b/services/repository/lfs.go index 43acb8ee6c..2e090290a7 100644 --- a/services/repository/lfs.go +++ b/services/repository/lfs.go @@ -76,7 +76,7 @@ func GarbageCollectLFSMetaObjectsForRepo(ctx context.Context, repo *repo_model.R err = git_model.IterateLFSMetaObjectsForRepo(ctx, repo.ID, func(ctx context.Context, metaObject *git_model.LFSMetaObject) error { total++ - pointerSha := git.ComputeBlobHash(objectFormat, []byte(metaObject.Pointer.StringContent())) + pointerSha := git.ComputeBlobHash(objectFormat, []byte(metaObject.StringContent())) if gitRepo.IsObjectExist(pointerSha.String()) { return git_model.MarkLFSMetaObject(ctx, metaObject.ID) diff --git a/services/user/avatar_test.go b/services/user/avatar_test.go index b208efeb6f..17132a74ab 100644 --- a/services/user/avatar_test.go +++ b/services/user/avatar_test.go @@ -42,7 +42,7 @@ func TestUserDeleteAvatar(t *testing.T) { err := UploadAvatar(db.DefaultContext, user, buff.Bytes()) require.NoError(t, err) verification := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - assert.NotEqual(t, "", verification.Avatar) + assert.NotEmpty(t, verification.Avatar) // fail to delete ... storage.Avatars = storage.UninitializedStorage @@ -60,7 +60,7 @@ func TestUserDeleteAvatar(t *testing.T) { // ... the avatar is removed from the database verification = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - assert.Equal(t, "", verification.Avatar) + assert.Empty(t, verification.Avatar) }) t.Run("Success", func(t *testing.T) { @@ -70,12 +70,12 @@ func TestUserDeleteAvatar(t *testing.T) { err := UploadAvatar(db.DefaultContext, user, buff.Bytes()) require.NoError(t, err) verification := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - assert.NotEqual(t, "", verification.Avatar) + assert.NotEmpty(t, verification.Avatar) err = DeleteAvatar(db.DefaultContext, user) require.NoError(t, err) verification = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - assert.Equal(t, "", verification.Avatar) + assert.Empty(t, verification.Avatar) }) } diff --git a/services/user/email.go b/services/user/email.go index f49efde1be..7dfa5db9c9 100644 --- a/services/user/email.go +++ b/services/user/email.go @@ -203,6 +203,11 @@ func MakeEmailAddressPrimary(ctx context.Context, u *user_model.User, newPrimary oldPrimaryEmail := u.Email + // If the user was reported as abusive, a shadow copy should be created before first update (of certain columns). + if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u, "email"); err != nil { + return err + } + // 1. Update user table u.Email = newPrimaryEmail.Email if _, err = sess.ID(u.ID).Cols("email").Update(u); err != nil { diff --git a/services/user/user_test.go b/services/user/user_test.go index ebcdb17710..c70c02673a 100644 --- a/services/user/user_test.go +++ b/services/user/user_test.go @@ -195,7 +195,7 @@ func TestRenameUser(t *testing.T) { redirectUID, err := user_model.LookupUserRedirect(db.DefaultContext, oldUsername) require.NoError(t, err) - assert.EqualValues(t, user.ID, redirectUID) + assert.Equal(t, user.ID, redirectUID) unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, OwnerName: user.Name}) }) diff --git a/services/webhook/default_test.go b/services/webhook/default_test.go index f946870d57..fcef4612e1 100644 --- a/services/webhook/default_test.go +++ b/services/webhook/default_test.go @@ -237,7 +237,7 @@ func TestOpenProjectPayload(t *testing.T) { assert.Equal(t, 12, j.Get("number").MustBeValid().ToInt()) assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", j.Get("html_url").MustBeValid().ToString()) assert.Equal(t, jsoniter.NilValue, j.Get("updated_at").ValueType()) - assert.Equal(t, "", j.Get("state").MustBeValid().ToString()) + assert.Empty(t, j.Get("state").MustBeValid().ToString()) assert.Equal(t, "Fix bug", j.Get("title").MustBeValid().ToString()) assert.Equal(t, "fixes bug #2", j.Get("body").MustBeValid().ToString()) diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go index 4dd898e60a..1a9ce05de4 100644 --- a/services/webhook/deliver_test.go +++ b/services/webhook/deliver_test.go @@ -137,7 +137,7 @@ func TestWebhookDeliverHookTask(t *testing.T) { case "/webhook/66d222a5d6349e1311f551e50722d837e30fce98": // Version 1 assert.Equal(t, "push", r.Header.Get("X-GitHub-Event")) - assert.Equal(t, "", r.Header.Get("Content-Type")) + assert.Empty(t, r.Header.Get("Content-Type")) body, err := io.ReadAll(r.Body) require.NoError(t, err) assert.Equal(t, `{"data": 42}`, string(body)) diff --git a/services/webhook/general.go b/services/webhook/general.go index 40a2467177..176a9a27f9 100644 --- a/services/webhook/general.go +++ b/services/webhook/general.go @@ -37,11 +37,12 @@ func getPullRequestInfo(p *api.PullRequestPayload) (title, link, by, operator, o for i, user := range assignList { assignStringList[i] = user.UserName } - if p.Action == api.HookIssueAssigned { + switch p.Action { + case api.HookIssueAssigned: operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName) - } else if p.Action == api.HookIssueUnassigned { + case api.HookIssueUnassigned: operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName) - } else if p.Action == api.HookIssueMilestoned { + case api.HookIssueMilestoned: operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID) } link = p.PullRequest.HTMLURL @@ -62,11 +63,12 @@ func getIssuesInfo(p *api.IssuePayload) (issueTitle, link, by, operator, operate for i, user := range assignList { assignStringList[i] = user.UserName } - if p.Action == api.HookIssueAssigned { + switch p.Action { + case api.HookIssueAssigned: operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName) - } else if p.Action == api.HookIssueUnassigned { + case api.HookIssueUnassigned: operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName) - } else if p.Action == api.HookIssueMilestoned { + case api.HookIssueMilestoned: operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID) } link = p.Issue.HTMLURL diff --git a/services/webhook/msteams_test.go b/services/webhook/msteams_test.go index b210f299bc..da6439f198 100644 --- a/services/webhook/msteams_test.go +++ b/services/webhook/msteams_test.go @@ -335,7 +335,7 @@ func TestMSTeamsPayload(t *testing.T) { assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.Summary) assert.Len(t, pl.Sections, 1) assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) - assert.Equal(t, "", pl.Sections[0].Text) + assert.Empty(t, pl.Sections[0].Text) assert.Len(t, pl.Sections[0].Facts, 2) for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { @@ -356,7 +356,7 @@ func TestMSTeamsPayload(t *testing.T) { assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.Summary) assert.Len(t, pl.Sections, 1) assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) - assert.Equal(t, "", pl.Sections[0].Text) + assert.Empty(t, pl.Sections[0].Text) assert.Len(t, pl.Sections[0].Facts, 2) for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { diff --git a/services/webhook/notifier_test.go b/services/webhook/notifier_test.go index 6b1f170287..ac5c22ebc3 100644 --- a/services/webhook/notifier_test.go +++ b/services/webhook/notifier_test.go @@ -90,7 +90,7 @@ func TestSyncPushCommits(t *testing.T) { var payloadContent structs.PushPayload require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent)) assert.Len(t, payloadContent.Commits, 1) - assert.EqualValues(t, "2c54faec6c45d31c1abfaecdab471eac6633738a", payloadContent.Commits[0].ID) + assert.Equal(t, "2c54faec6c45d31c1abfaecdab471eac6633738a", payloadContent.Commits[0].ID) }) } @@ -129,6 +129,6 @@ func TestPushCommits(t *testing.T) { var payloadContent structs.PushPayload require.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payloadContent)) assert.Len(t, payloadContent.Commits, 1) - assert.EqualValues(t, "2c54faec6c45d31c1abfaecdab471eac6633738a", payloadContent.Commits[0].ID) + assert.Equal(t, "2c54faec6c45d31c1abfaecdab471eac6633738a", payloadContent.Commits[0].ID) }) } diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go index d76104dfc7..ff5141a1fd 100644 --- a/services/wiki/wiki_test.go +++ b/services/wiki/wiki_test.go @@ -26,7 +26,7 @@ func TestMain(m *testing.M) { func TestWebPathSegments(t *testing.T) { a := WebPathSegments("a%2Fa/b+c/d-e/f-g.-") - assert.EqualValues(t, []string{"a/a", "b c", "d e", "f-g"}, a) + assert.Equal(t, []string{"a/a", "b c", "d e", "f-g"}, a) } func TestUserTitleToWebPath(t *testing.T) { @@ -63,7 +63,7 @@ func TestWebPathToDisplayName(t *testing.T) { {"a b", "a%20b.md"}, } { _, displayName := WebPathToUserTitle(test.WebPath) - assert.EqualValues(t, test.Expected, displayName) + assert.Equal(t, test.Expected, displayName) } } @@ -80,7 +80,7 @@ func TestWebPathToGitPath(t *testing.T) { {"2000-01-02-meeting.md", "2000-01-02+meeting"}, {"2000-01-02 meeting.-.md", "2000-01-02%20meeting.-"}, } { - assert.EqualValues(t, test.Expected, WebPathToGitPath(test.WikiName)) + assert.Equal(t, test.Expected, WebPathToGitPath(test.WikiName)) } } @@ -134,9 +134,9 @@ func TestUserWebGitPathConsistency(t *testing.T) { _, userTitle1 := WebPathToUserTitle(webPath1) gitPath1 := WebPathToGitPath(webPath1) - assert.EqualValues(t, userTitle, userTitle1, "UserTitle for userTitle: %q", userTitle) - assert.EqualValues(t, webPath, webPath1, "WebPath for userTitle: %q", userTitle) - assert.EqualValues(t, gitPath, gitPath1, "GitPath for userTitle: %q", userTitle) + assert.Equal(t, userTitle, userTitle1, "UserTitle for userTitle: %q", userTitle) + assert.Equal(t, webPath, webPath1, "WebPath for userTitle: %q", userTitle) + assert.Equal(t, gitPath, gitPath1, "GitPath for userTitle: %q", userTitle) } } @@ -175,7 +175,7 @@ func TestRepository_AddWikiPage(t *testing.T) { gitPath := WebPathToGitPath(webPath) entry, err := masterTree.GetTreeEntryByPath(gitPath) require.NoError(t, err) - assert.EqualValues(t, gitPath, entry.Name(), "%s not added correctly", userTitle) + assert.Equal(t, gitPath, entry.Name(), "%s not added correctly", userTitle) }) } @@ -220,7 +220,7 @@ func TestRepository_EditWikiPage(t *testing.T) { gitPath := WebPathToGitPath(webPath) entry, err := masterTree.GetTreeEntryByPath(gitPath) require.NoError(t, err) - assert.EqualValues(t, gitPath, entry.Name(), "%s not edited correctly", newWikiName) + assert.Equal(t, gitPath, entry.Name(), "%s not edited correctly", newWikiName) if newWikiName != "Home" { _, err := masterTree.GetTreeEntryByPath("Home.md") @@ -289,7 +289,7 @@ func TestPrepareWikiFileName(t *testing.T) { t.Errorf("expect to find an escaped file but we could not detect one") } } - assert.EqualValues(t, tt.wikiPath, newWikiPath) + assert.Equal(t, tt.wikiPath, newWikiPath) }) } } @@ -311,13 +311,13 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) { existence, newWikiPath, err := prepareGitPath(gitRepo, "master", "Home") assert.False(t, existence) require.NoError(t, err) - assert.EqualValues(t, "Home.md", newWikiPath) + assert.Equal(t, "Home.md", newWikiPath) } func TestWebPathConversion(t *testing.T) { assert.Equal(t, "path/wiki", WebPathToURLPath(WebPath("path/wiki"))) assert.Equal(t, "wiki", WebPathToURLPath(WebPath("wiki"))) - assert.Equal(t, "", WebPathToURLPath(WebPath(""))) + assert.Empty(t, WebPathToURLPath(WebPath(""))) } func TestWebPathFromRequest(t *testing.T) { diff --git a/templates/admin/auth/list.tmpl b/templates/admin/auth/list.tmpl index 9c283fe3d8..0c7138bd68 100644 --- a/templates/admin/auth/list.tmpl +++ b/templates/admin/auth/list.tmpl @@ -31,7 +31,7 @@ <td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td> </tr> {{else}} - <tr><td class="tw-text-center" colspan="7">{{ctx.Locale.Tr "no_results_found"}}</td></tr> + <tr><td class="tw-text-center" colspan="7">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr> {{end}} </tbody> </table> diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl index 8796794aee..5c30df87af 100644 --- a/templates/admin/emails/list.tmpl +++ b/templates/admin/emails/list.tmpl @@ -67,7 +67,7 @@ </td> </tr> {{else}} - <tr><td class="tw-text-center" colspan="6">{{ctx.Locale.Tr "no_results_found"}}</td></tr> + <tr><td class="tw-text-center" colspan="6">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr> {{end}} </tbody> </table> diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl index 4f8783dd42..08f0a4f204 100644 --- a/templates/admin/notice.tmpl +++ b/templates/admin/notice.tmpl @@ -25,7 +25,7 @@ <td class="view-detail"><a href="#">{{svg "octicon-note" 16}}</a></td> </tr> {{else}} - <tr><td class="tw-text-center" colspan="6">{{ctx.Locale.Tr "no_results_found"}}</td></tr> + <tr><td class="tw-text-center" colspan="6">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr> {{end}} </tbody> {{if .Notices}} diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl index b719d259e0..8c9c198897 100644 --- a/templates/admin/org/list.tmpl +++ b/templates/admin/org/list.tmpl @@ -67,7 +67,7 @@ <td><a href="{{.OrganisationLink}}/settings" data-tooltip-content="{{ctx.Locale.Tr "edit"}}">{{svg "octicon-pencil"}}</a></td> </tr> {{else}} - <tr><td class="tw-text-center" colspan="7">{{ctx.Locale.Tr "no_results_found"}}</td></tr> + <tr><td class="tw-text-center" colspan="7">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr> {{end}} </tbody> </table> diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl index f22600a449..5f9965e34c 100644 --- a/templates/admin/packages/list.tmpl +++ b/templates/admin/packages/list.tmpl @@ -75,7 +75,7 @@ <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.Version.ID}}" data-name="{{.Package.Name}}" data-data-version="{{.Version.Version}}">{{svg "octicon-trash"}}</a></td> </tr> {{else}} - <tr><td class="tw-text-center" colspan="10">{{ctx.Locale.Tr "no_results_found"}}</td></tr> + <tr><td class="tw-text-center" colspan="10">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr> {{end}} </tbody> </table> diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index 0422705ea9..7a75ceded7 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -87,7 +87,7 @@ <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td> </tr> {{else}} - <tr><td class="tw-text-center" colspan="12">{{ctx.Locale.Tr "no_results_found"}}</td></tr> + <tr><td class="tw-text-center" colspan="12">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr> {{end}} </tbody> </table> diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl index f4609edbbf..368e113d24 100644 --- a/templates/admin/user/list.tmpl +++ b/templates/admin/user/list.tmpl @@ -110,7 +110,7 @@ </td> </tr> {{else}} - <tr class="no-results-row"><td class="tw-text-center" colspan="9">{{ctx.Locale.Tr "no_results_found"}}</td></tr> + <tr class="no-results-row"><td class="tw-text-center" colspan="9">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr> {{end}} </tbody> </table> diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index fed426a469..3c2def228a 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -13,7 +13,7 @@ {{template "base/footer_content" .}} - <script src="{{AssetUrlPrefix}}/js/index.js?v={{AssetVersion}}" onerror="alert('Failed to load asset files from ' + this.src + '. Please make sure the asset files can be accessed.')"></script> + <script src="{{AssetUrlPrefix}}/js/index.js?v={{AssetVersion}}" onerror="alert('{{ctx.Locale.Tr "alert.asset_load_failed"}}'.replace('{path}', this.src))"></script> {{template "custom/footer" .}} </body> diff --git a/templates/devtest/list.tmpl b/templates/devtest/list.tmpl index 90b1fcc9d0..e169311222 100644 --- a/templates/devtest/list.tmpl +++ b/templates/devtest/list.tmpl @@ -1,10 +1,21 @@ {{template "base/head" .}} -<ul> - {{range .SubNames}} - <li><a href="{{AppSubUrl}}/devtest/{{.}}">{{.}}</a></li> - {{end}} -</ul> +<div role="main" class="page-content ui container"> + <ul> + {{range .SubNames}} + <li><a href="{{AppSubUrl}}/devtest/{{.}}">{{.}}</a></li> + {{end}} + </ul> + + <article> + <h2>Error pages</h2> + <ul> + <li><a href="./error/404">Not found</a></li> + <li><a href="./error/413">Quota exhaustion</a></li> + <li><a href="./error/500">Server error</a></li> + </ul> + </article> +</div> <style> ul { diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl index d3a81bc51d..3a8995bfea 100644 --- a/templates/repo/empty.tmpl +++ b/templates/repo/empty.tmpl @@ -50,8 +50,8 @@ <h3>{{ctx.Locale.Tr "repo.create_new_repo_command"}}</h3> <div class="markup"> <pre><code>touch README.md -git init -{{if ne .Repository.DefaultBranch "master"}}git checkout -b {{.Repository.DefaultBranch}}{{end}} +git init{{if eq .Repository.ObjectFormatName "sha256"}} --object-format=sha256{{end}} +{{if ne .Repository.DefaultBranch "master"}}git switch -c {{.Repository.DefaultBranch}}{{end}} git add README.md git commit -m "first commit" git remote add origin <span class="js-clone-url">{{$.CloneButtonOriginLink.HTTPS}}</span> diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl index 8ede0f36e5..268c77d880 100644 --- a/templates/repo/issue/view_content.tmpl +++ b/templates/repo/issue/view_content.tmpl @@ -116,29 +116,17 @@ </div> {{else if .Repository.IsArchived}} <div class="ui warning message tw-text-center"> - {{if .Issue.IsPull}} - {{ctx.Locale.Tr "repo.archive.pull.nocomment"}} - {{else}} - {{ctx.Locale.Tr "repo.archive.issue.nocomment"}} - {{end}} + {{ctx.Locale.Tr "repo.archive.nocomment"}} </div> {{end}} {{else if .IsBlocked}} <div class="ui warning message"> - {{if .Issue.IsPull}} - {{ctx.Locale.Tr "repo.pulls.comment.blocked_by_user"}} - {{else}} - {{ctx.Locale.Tr "repo.issues.comment.blocked_by_user"}} - {{end}} + {{ctx.Locale.Tr "repo.comment.blocked_by_user"}} </div> {{else}} {{/* not .IsSigned */}} {{if .Repository.IsArchived}} <div class="ui warning message tw-text-center"> - {{if .Issue.IsPull}} - {{ctx.Locale.Tr "repo.archive.pull.nocomment"}} - {{else}} - {{ctx.Locale.Tr "repo.archive.issue.nocomment"}} - {{end}} + {{ctx.Locale.Tr "repo.archive.nocomment"}} </div> {{else}} <div class="ui warning message"> diff --git a/templates/repo/issue/view_content/pull_merge_instruction.tmpl b/templates/repo/issue/view_content/pull_merge_instruction.tmpl index b7aae53424..dd30a3a8d6 100644 --- a/templates/repo/issue/view_content/pull_merge_instruction.tmpl +++ b/templates/repo/issue/view_content/pull_merge_instruction.tmpl @@ -12,7 +12,7 @@ {{else}} <div>git fetch -u origin +refs/pull/{{.PullRequest.Index}}/head:{{$localBranch}}</div> {{end}} - <div>git checkout {{$localBranch}}</div> + <div>git switch {{$localBranch}}</div> </div> {{if .ShowMergeInstructions}} <div id="merge-instructions"> @@ -24,31 +24,31 @@ </div> <div class="ui secondary segment"> <div data-pull-merge-style="merge"> - <div>git checkout {{.PullRequest.BaseBranch}}</div> + <div>git switch {{.PullRequest.BaseBranch}}</div> <div>git merge --no-ff {{$localBranch}}</div> </div> <div class="tw-hidden" data-pull-merge-style="rebase"> - <div>git checkout {{$localBranch}}</div> + <div>git switch {{$localBranch}}</div> <div>git rebase {{.PullRequest.BaseBranch}}</div> - <div>git checkout {{.PullRequest.BaseBranch}}</div> + <div>git switch {{.PullRequest.BaseBranch}}</div> <div>git merge --ff-only {{$localBranch}}</div> </div> <div class="tw-hidden" data-pull-merge-style="rebase-merge"> - <div>git checkout {{$localBranch}}</div> + <div>git switch {{$localBranch}}</div> <div>git rebase {{.PullRequest.BaseBranch}}</div> - <div>git checkout {{.PullRequest.BaseBranch}}</div> + <div>git switch {{.PullRequest.BaseBranch}}</div> <div>git merge --no-ff {{$localBranch}}</div> </div> <div class="tw-hidden" data-pull-merge-style="squash"> - <div>git checkout {{.PullRequest.BaseBranch}}</div> + <div>git switch {{.PullRequest.BaseBranch}}</div> <div>git merge --squash {{$localBranch}}</div> </div> <div class="tw-hidden" data-pull-merge-style="fast-forward-only"> - <div>git checkout {{.PullRequest.BaseBranch}}</div> + <div>git switch {{.PullRequest.BaseBranch}}</div> <div>git merge --ff-only {{$localBranch}}</div> </div> <div class="tw-hidden" data-pull-merge-style="manually-merged"> - <div>git checkout {{.PullRequest.BaseBranch}}</div> + <div>git switch {{.PullRequest.BaseBranch}}</div> <div>git merge {{$localBranch}}</div> </div> <div>git push origin {{.PullRequest.BaseBranch}}</div> diff --git a/templates/repo/migrate/codebase.tmpl b/templates/repo/migrate/codebase.tmpl index cf46c8daf3..1343e52cdc 100644 --- a/templates/repo/migrate/codebase.tmpl +++ b/templates/repo/migrate/codebase.tmpl @@ -13,7 +13,7 @@ {{template "base/alert" .}} <div class="inline required field {{if .Err_CloneAddr}}error{{end}}"> <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label> - <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required> + <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required autocomplete="url"> <span class="help"> {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}} </span> @@ -109,7 +109,7 @@ </div> <div class="inline field {{if .Err_Description}}error{{end}}"> <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label> - <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea> + <textarea id="description" name="description" maxlength="2048" placeholder="{{ctx.Locale.Tr "repo.migrate.repo_desc_helper"}}">{{.description}}</textarea> </div> <div class="inline field"> diff --git a/templates/repo/migrate/git.tmpl b/templates/repo/migrate/git.tmpl index 5ebf4cfd6a..981260de5a 100644 --- a/templates/repo/migrate/git.tmpl +++ b/templates/repo/migrate/git.tmpl @@ -13,7 +13,7 @@ {{template "base/alert" .}} <div class="inline required field {{if .Err_CloneAddr}}error{{end}}"> <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label> - <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required> + <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required autocomplete="url"> <span class="help"> {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}} </span> diff --git a/templates/repo/migrate/gitbucket.tmpl b/templates/repo/migrate/gitbucket.tmpl index e8d6b60dea..b8d82eb7e2 100644 --- a/templates/repo/migrate/gitbucket.tmpl +++ b/templates/repo/migrate/gitbucket.tmpl @@ -13,7 +13,7 @@ {{template "base/alert" .}} <div class="inline required field {{if .Err_CloneAddr}}error{{end}}"> <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label> - <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required> + <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required autocomplete="url"> <span class="help"> {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}} </span> diff --git a/templates/repo/migrate/gitea.tmpl b/templates/repo/migrate/gitea.tmpl index 2f40d58bec..baaff53d8d 100644 --- a/templates/repo/migrate/gitea.tmpl +++ b/templates/repo/migrate/gitea.tmpl @@ -12,7 +12,7 @@ {{template "base/alert" .}} <div class="inline required field {{if .Err_CloneAddr}}error{{end}}"> <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label> - <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required> + <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required autocomplete="url"> <span class="help"> {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}} </span> @@ -121,7 +121,7 @@ </div> <div class="inline field {{if .Err_Description}}error{{end}}"> <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label> - <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea> + <textarea id="description" name="description" maxlength="2048" placeholder="{{ctx.Locale.Tr "repo.migrate.repo_desc_helper"}}">{{.description}}</textarea> </div> <div class="inline field"> diff --git a/templates/repo/migrate/github.tmpl b/templates/repo/migrate/github.tmpl index 91c1a272c7..a23baf9780 100644 --- a/templates/repo/migrate/github.tmpl +++ b/templates/repo/migrate/github.tmpl @@ -12,7 +12,7 @@ {{template "base/alert" .}} <div class="inline required field {{if .Err_CloneAddr}}error{{end}}"> <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label> - <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required> + <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required autocomplete="url"> <span class="help"> {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}} </span> @@ -123,7 +123,7 @@ </div> <div class="inline field {{if .Err_Description}}error{{end}}"> <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label> - <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea> + <textarea id="description" name="description" maxlength="2048" placeholder="{{ctx.Locale.Tr "repo.migrate.repo_desc_helper"}}">{{.description}}</textarea> </div> <div class="inline field"> diff --git a/templates/repo/migrate/gitlab.tmpl b/templates/repo/migrate/gitlab.tmpl index d93a00104a..03edf5c787 100644 --- a/templates/repo/migrate/gitlab.tmpl +++ b/templates/repo/migrate/gitlab.tmpl @@ -12,7 +12,7 @@ {{template "base/alert" .}} <div class="inline required field {{if .Err_CloneAddr}}error{{end}}"> <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label> - <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required> + <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required autocomplete="url"> <span class="help"> {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}} </span> @@ -120,7 +120,7 @@ </div> <div class="inline field {{if .Err_Description}}error{{end}}"> <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label> - <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea> + <textarea id="description" name="description" maxlength="2048" placeholder="{{ctx.Locale.Tr "repo.migrate.repo_desc_helper"}}">{{.description}}</textarea> </div> <div class="inline field"> diff --git a/templates/repo/migrate/gogs.tmpl b/templates/repo/migrate/gogs.tmpl index 6c647be0ed..52da3d243b 100644 --- a/templates/repo/migrate/gogs.tmpl +++ b/templates/repo/migrate/gogs.tmpl @@ -12,7 +12,7 @@ {{template "base/alert" .}} <div class="inline required field {{if .Err_CloneAddr}}error{{end}}"> <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label> - <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required> + <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required autocomplete="url"> <span class="help"> {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}} </span> @@ -120,7 +120,7 @@ </div> <div class="inline field {{if .Err_Description}}error{{end}}"> <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label> - <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea> + <textarea id="description" name="description" maxlength="2048" placeholder="{{ctx.Locale.Tr "repo.migrate.repo_desc_helper"}}">{{.description}}</textarea> </div> <div class="inline field"> diff --git a/templates/repo/migrate/onedev.tmpl b/templates/repo/migrate/onedev.tmpl index ec155555d0..0434bcbe68 100644 --- a/templates/repo/migrate/onedev.tmpl +++ b/templates/repo/migrate/onedev.tmpl @@ -13,7 +13,7 @@ {{template "base/alert" .}} <div class="inline required field {{if .Err_CloneAddr}}error{{end}}"> <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label> - <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required> + <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required autocomplete="url"> <span class="help"> {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}} </span> @@ -109,7 +109,7 @@ </div> <div class="inline field {{if .Err_Description}}error{{end}}"> <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label> - <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea> + <textarea id="description" name="description" maxlength="2048" placeholder="{{ctx.Locale.Tr "repo.migrate.repo_desc_helper"}}">{{.description}}</textarea> </div> <div class="inline field"> diff --git a/templates/repo/navbar.tmpl b/templates/repo/navbar.tmpl index b2471dc17e..7536b96c63 100644 --- a/templates/repo/navbar.tmpl +++ b/templates/repo/navbar.tmpl @@ -1,14 +1,18 @@ +{{$canReadCode := $.Permission.CanRead $.UnitTypeCode}} + <div class="ui fluid vertical menu"> <a class="{{if .PageIsPulse}}active {{end}}item" href="{{.RepoLink}}/activity"> {{ctx.Locale.Tr "repo.activity.navbar.pulse"}} </a> - <a class="{{if .PageIsContributors}}active {{end}}item" href="{{.RepoLink}}/activity/contributors"> - {{ctx.Locale.Tr "repo.activity.navbar.contributors"}} - </a> - <a class="{{if .PageIsCodeFrequency}}active{{end}} item" href="{{.RepoLink}}/activity/code-frequency"> - {{ctx.Locale.Tr "repo.activity.navbar.code_frequency"}} - </a> - <a class="{{if .PageIsRecentCommits}}active{{end}} item" href="{{.RepoLink}}/activity/recent-commits"> - {{ctx.Locale.Tr "repo.activity.navbar.recent_commits"}} - </a> + {{if $canReadCode}} + <a class="{{if .PageIsContributors}}active {{end}}item" href="{{.RepoLink}}/activity/contributors"> + {{ctx.Locale.Tr "repo.activity.navbar.contributors"}} + </a> + <a class="{{if .PageIsCodeFrequency}}active{{end}} item" href="{{.RepoLink}}/activity/code-frequency"> + {{ctx.Locale.Tr "repo.activity.navbar.code_frequency"}} + </a> + <a class="{{if .PageIsRecentCommits}}active{{end}} item" href="{{.RepoLink}}/activity/recent-commits"> + {{ctx.Locale.Tr "repo.activity.navbar.recent_commits"}} + </a> + {{end}} </div> diff --git a/templates/repo/settings/lfs_file.tmpl b/templates/repo/settings/lfs_file.tmpl index 941d09f3e6..3b6b763536 100644 --- a/templates/repo/settings/lfs_file.tmpl +++ b/templates/repo/settings/lfs_file.tmpl @@ -31,7 +31,7 @@ <strong>{{ctx.Locale.Tr "repo.audio_not_supported_in_browser"}}</strong> </audio> {{else if .IsPDFFile}} - <div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "diff.view_file"}}"></div> + <div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "repo.diff.view_file"}}"></div> {{else}} <a href="{{$.RawFileLink}}" rel="nofollow">{{ctx.Locale.Tr "repo.file_view_raw"}}</a> {{end}} diff --git a/templates/shared/actions/runner_edit.tmpl b/templates/shared/actions/runner_edit.tmpl index 54250f830b..d452d69f7a 100644 --- a/templates/shared/actions/runner_edit.tmpl +++ b/templates/shared/actions/runner_edit.tmpl @@ -40,7 +40,7 @@ <div class="field"> <button class="ui primary button" data-url="{{.Link}}">{{ctx.Locale.Tr "actions.runners.update_runner"}}</button> - <button class="ui red button delete-button show-modal" data-url="{{.Link}}/delete" data-modal="#runner-delete-modal"> + <button class="ui red button delete-button" data-url="{{.Link}}/delete" data-modal="#runner-delete-modal"> {{ctx.Locale.Tr "actions.runners.delete_runner"}}</button> </div> </form> diff --git a/templates/status/500.tmpl b/templates/status/500.tmpl index 59f7c25688..4e7e64d9c7 100644 --- a/templates/status/500.tmpl +++ b/templates/status/500.tmpl @@ -16,19 +16,14 @@ </head> <body> <div class="full height"> - <nav class="ui secondary menu"> - <div class="ui container tw-flex"> - <div class="item tw-flex-1"> - <a href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home"}}"> - <img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true"> - </a> - </div> - <div class="item"> - <button class="ui icon button disabled">{{svg "octicon-three-bars"}}</button>{{/* a fake button to make the UI looks better*/}} - </div> + <nav id="navbar" aria-label="{{ctx.Locale.Tr "aria.navbar"}}"> + <div class="navbar-left ui secondary menu"> + <a class="item" id="navbar-logo" href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home"}}"> + <img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true"> + </a> </div> </nav> - <div class="divider tw-my-0"></div> + <div role="main" class="page-content status-page-500"> <div class="ui container" > <style> .ui.message.flash-message { text-align: left; } </style> diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 231386e3aa..a16deb61a8 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -23762,7 +23762,7 @@ "x-go-name": "DefaultDeleteBranchAfterMerge" }, "default_merge_style": { - "description": "set to a merge style to be used by this repository: \"merge\", \"rebase\", \"rebase-merge\", \"squash\", or \"fast-forward-only\".", + "description": "set to a merge style to be used by this repository: \"merge\", \"rebase\", \"rebase-merge\", \"squash\", \"fast-forward-only\", \"manually-merged\", or \"rebase-update-only\".", "type": "string", "x-go-name": "DefaultMergeStyle" }, @@ -26133,6 +26133,11 @@ "format": "date-time", "x-go-name": "Deadline" }, + "flow": { + "type": "integer", + "format": "int64", + "x-go-name": "Flow" + }, "head": { "$ref": "#/definitions/PRBranchInfo" }, diff --git a/templates/user/auth/captcha.tmpl b/templates/user/auth/captcha.tmpl index 9d0d6cfb6f..b2deb5a2c9 100644 --- a/templates/user/auth/captcha.tmpl +++ b/templates/user/auth/captcha.tmpl @@ -1,7 +1,7 @@ {{if .EnableCaptcha}}{{if eq .CaptchaType "image"}} <div class="inline field tw-text-center"> <input type="hidden" name="img-captcha-id" value="{{.Captcha}}"> - <img style="transform: scaleX(-1)" onclick="this.src=`{{AppSubUrl}}/captcha/{{.Captcha}}.png?reload=${Date.now()}`" class="captcha-img" src="{{AppSubUrl}}/captcha/{{.Captcha}}.png" alt="{{ctx.Locale.Tr "captcha"}}"> + <img style="transform: scaleX(-1);background-color: white" onclick="this.src=`{{AppSubUrl}}/captcha/{{.Captcha}}.png?reload=${Date.now()}`" class="captcha-img" src="{{AppSubUrl}}/captcha/{{.Captcha}}.png" alt="{{ctx.Locale.Tr "captcha"}}"> </div> <div class="required field {{if .Err_Captcha}}error{{end}}"> <label for="captcha">{{ctx.Locale.Tr "captcha"}}</label> @@ -11,12 +11,12 @@ <div class="inline field tw-text-center required"> <div id="captcha" data-captcha-type="g-recaptcha" class="g-recaptcha-style" data-sitekey="{{.RecaptchaSitekey}}"></div> </div> - <script src='{{URLJoin .RecaptchaURL "api.js"}}'></script> + <script defer src='{{URLJoin .RecaptchaURL "api.js"}}'></script> {{else if eq .CaptchaType "hcaptcha"}} <div class="inline field tw-text-center required"> <div id="captcha" data-captcha-type="h-captcha" class="h-captcha-style" data-sitekey="{{.HcaptchaSitekey}}"></div> </div> - <script src='https://hcaptcha.com/1/api.js'></script> + <script defer src='https://hcaptcha.com/1/api.js'></script> {{else if eq .CaptchaType "mcaptcha"}} <div class="inline field tw-text-center"> <div class="m-captcha-style" id="mcaptcha__widget-container"></div> @@ -26,5 +26,5 @@ <div class="inline field tw-text-center"> <div id="captcha" data-captcha-type="cf-turnstile" data-sitekey="{{.CfTurnstileSitekey}}"></div> </div> - <script src='https://challenges.cloudflare.com/turnstile/v0/api.js'></script> + <script defer src='https://challenges.cloudflare.com/turnstile/v0/api.js'></script> {{end}}{{end}} diff --git a/templates/user/auth/finalize_openid.tmpl b/templates/user/auth/finalize_openid.tmpl index f84f860b02..372471368f 100644 --- a/templates/user/auth/finalize_openid.tmpl +++ b/templates/user/auth/finalize_openid.tmpl @@ -30,7 +30,7 @@ <div class="inline field"> <label></label> <button class="ui primary button">{{ctx.Locale.Tr "sign_in"}}</button> - <a href="{{AppSubUrl}}/user/forget_password">{{ctx.Locale.Tr "auth.forget_password"}}</a> + <a href="{{AppSubUrl}}/user/forget_password">{{ctx.Locale.Tr "auth.forgot_password"}}</a> </div> {{if .ShowRegistrationButton}} <div class="inline field"> diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index 1f39576ac2..1d62f3eea9 100644 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -12,12 +12,12 @@ {{if eq .SignedUserID .ContextUser.ID}} <p id="visibility-hint"> {{if .ContextUser.KeepActivityPrivate}} - {{ctx.Locale.Tr "user.public_activity.visibility_hint.self_private" "/user/settings#keep-activity-private"}} + {{ctx.Locale.Tr "user.public_activity.visibility_hint.self_private" (print AppSubUrl "/user/settings#keep-activity-private")}} {{else}} {{if eq .ContextUser.Visibility 2}} - {{ctx.Locale.Tr "user.public_activity.visibility_hint.self_private_profile" "/user/settings#visibility-setting"}} + {{ctx.Locale.Tr "user.public_activity.visibility_hint.self_private_profile" (print AppSubUrl "/user/settings#visibility-setting")}} {{else}} - {{ctx.Locale.Tr "user.public_activity.visibility_hint.self_public" "/user/settings#keep-activity-private"}} + {{ctx.Locale.Tr "user.public_activity.visibility_hint.self_public" (print AppSubUrl "/user/settings#keep-activity-private")}} {{end}} {{end}} </p> diff --git a/tests/e2e/dimmer.test.e2e.ts b/tests/e2e/dimmer.test.e2e.ts new file mode 100644 index 0000000000..04c6433a5a --- /dev/null +++ b/tests/e2e/dimmer.test.e2e.ts @@ -0,0 +1,39 @@ +// @watch start +// templates/shared/user/** +// web_src/css/modules/dimmer.ts +// web_src/css/modules/dimmer.css +// @watch end + +import {expect} from '@playwright/test'; +import {save_visual, test} from './utils_e2e.ts'; + +test.use({user: 'user2'}); + +test('Dimmed modal', async ({page}) => { + await page.goto('/user1'); + + await expect(page.locator('.block')).toContainText('Block'); + + // Ensure the modal is hidden + await expect(page.locator('#block-user')).toBeHidden(); + + await page.locator('.block').click(); + + // Modal and dimmer should be visible. + await expect(page.locator('#block-user')).toBeVisible(); + await expect(page.locator('.ui.dimmer')).toBeVisible(); + await save_visual(page); + + // After canceling, modal and dimmer should be hidden. + await page.locator('#block-user .cancel').click(); + await expect(page.locator('.ui.dimmer')).toBeHidden(); + await expect(page.locator('#block-user')).toBeHidden(); + await save_visual(page); + + // Open the block modal and make the dimmer visible again. + await page.locator('.block').click(); + await expect(page.locator('#block-user')).toBeVisible(); + await expect(page.locator('.ui.dimmer')).toBeVisible(); + await expect(page.locator('.ui.dimmer')).toHaveCount(1); + await save_visual(page); +}); diff --git a/tests/e2e/repo-migrate.test.e2e.ts b/tests/e2e/repo-migrate.test.e2e.ts index 5e67f89ed1..b6541179f0 100644 --- a/tests/e2e/repo-migrate.test.e2e.ts +++ b/tests/e2e/repo-migrate.test.e2e.ts @@ -3,28 +3,57 @@ // @watch end import {expect} from '@playwright/test'; -import {test, save_visual, test_context} from './utils_e2e.ts'; +import {test, save_visual, test_context, dynamic_id} from './utils_e2e.ts'; test.use({user: 'user2'}); +test('Migration Repo Name detection', async ({page}, workerInfo) => { + test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky actionability checks on Mobile Safari'); + + await page.goto('/repo/migrate?service_type=2'); + + const form = page.locator('form'); + + // Test trailing slashes are stripped + await form.getByRole('textbox', {name: 'Migrate / Clone from URL'}).fill('https://github.com/example/test/'); + await form.getByRole('textbox', {name: 'Migrate / Clone from URL'}).blur(); + await expect(form.getByRole('textbox', {name: 'Repository Name'})).toHaveValue('test'); + + // Test trailing .git is stripped + await page.reload(); + await form.getByRole('textbox', {name: 'Migrate / Clone from URL'}).fill('https://github.com/example/test.git'); + await form.getByRole('textbox', {name: 'Migrate / Clone from URL'}).blur(); + await expect(form.getByRole('textbox', {name: 'Repository Name'})).toHaveValue('test'); + + // Test trailing .git and trailing / together is stripped + await page.reload(); + await form.getByRole('textbox', {name: 'Migrate / Clone from URL'}).fill('https://github.com/example/test.git/'); + await form.getByRole('textbox', {name: 'Migrate / Clone from URL'}).blur(); + await expect(form.getByRole('textbox', {name: 'Repository Name'})).toHaveValue('test'); + + // Save screenshot only once + await save_visual(page); +}); + test('Migration Progress Page', async ({page, browser}, workerInfo) => { test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky actionability checks on Mobile Safari'); - expect((await page.goto('/user2/invalidrepo'))?.status(), 'repo should not exist yet').toBe(404); + const repoName = dynamic_id(); + expect((await page.goto(`/user2/${repoName}`))?.status(), 'repo should not exist yet').toBe(404); await page.goto('/repo/migrate?service_type=1'); const form = page.locator('form'); - await form.getByRole('textbox', {name: 'Repository Name'}).fill('invalidrepo'); - await form.getByRole('textbox', {name: 'Migrate / Clone from URL'}).fill('https://codeberg.org/forgejo/invalidrepo'); + await form.getByRole('textbox', {name: 'Repository Name'}).fill(repoName); + await form.getByRole('textbox', {name: 'Migrate / Clone from URL'}).fill(`https://codeberg.org/forgejo/${repoName}`); await save_visual(page); await form.locator('button.primary').click({timeout: 5000}); - await expect(page).toHaveURL('user2/invalidrepo'); + await expect(page).toHaveURL(`user2/${repoName}`); await save_visual(page); - const ctx = await test_context(browser); + const ctx = await test_context(browser, {storageState: {cookies: [], origins: []}}); const unauthenticatedPage = await ctx.newPage(); - expect((await unauthenticatedPage.goto('/user2/invalidrepo'))?.status(), 'public migration page should be accessible').toBe(200); + expect((await unauthenticatedPage.goto(`/user2/${repoName}`))?.status(), 'public migration page should be accessible').toBe(200); await expect(unauthenticatedPage.locator('#repo_migrating_progress')).toBeVisible(); await page.reload(); @@ -32,7 +61,7 @@ test('Migration Progress Page', async ({page, browser}, workerInfo) => { await save_visual(page); await page.getByRole('button', {name: 'Delete this repository'}).click(); const deleteModal = page.locator('#delete-repo-modal'); - await deleteModal.getByRole('textbox', {name: 'Confirmation string'}).fill('user2/invalidrepo'); + await deleteModal.getByRole('textbox', {name: 'Confirmation string'}).fill(`user2/${repoName}`); await save_visual(page); await deleteModal.getByRole('button', {name: 'Delete repository'}).click(); await expect(page).toHaveURL('/'); diff --git a/tests/e2e/utils_e2e_test.go b/tests/e2e/utils_e2e_test.go index d57c7bc2d3..f892d6c518 100644 --- a/tests/e2e/utils_e2e_test.go +++ b/tests/e2e/utils_e2e_test.go @@ -5,7 +5,6 @@ package e2e import ( "context" - "crypto/rand" "encoding/hex" "fmt" "net" @@ -23,6 +22,7 @@ import ( "forgejo.org/modules/json" modules_session "forgejo.org/modules/session" "forgejo.org/modules/setting" + "forgejo.org/modules/util" "forgejo.org/tests" "code.forgejo.org/go-chi/session" @@ -153,11 +153,7 @@ func stateHelper(t testing.TB) func(stateFile string, user *user_model.User) { require.NoError(t, err) return func(stateFile string, user *user_model.User) { - buf := make([]byte, opt.IDLength/2) - _, err = rand.Read(buf) - require.NoError(t, err) - - sessionID := hex.EncodeToString(buf) + sessionID := hex.EncodeToString(util.CryptoRandomBytes(int64(opt.IDLength) / 2)) s, err := vsp.Read(sessionID) require.NoError(t, err) diff --git a/tests/integration/actions_variables_test.go b/tests/integration/actions_variables_test.go index 0b59284f27..795a24e3da 100644 --- a/tests/integration/actions_variables_test.go +++ b/tests/integration/actions_variables_test.go @@ -64,12 +64,12 @@ func TestActionVariablesModification(t *testing.T) { resp := sess.MakeRequest(t, req, http.StatusBadRequest) var error errorJSON DecodeJSON(t, resp, &error) - assert.EqualValues(t, "Failed to find the variable.", error.Error) + assert.Equal(t, "Failed to find the variable.", error.Error) } else { sess.MakeRequest(t, req, http.StatusOK) flashCookie := sess.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DThe%2Bvariable%2Bhas%2Bbeen%2Bedited.", flashCookie.Value) + assert.Equal(t, "success%3DThe%2Bvariable%2Bhas%2Bbeen%2Bedited.", flashCookie.Value) } req = NewRequestWithValues(t, "POST", baseURL+fmt.Sprintf("/%d/delete", id), map[string]string{ @@ -79,12 +79,12 @@ func TestActionVariablesModification(t *testing.T) { resp := sess.MakeRequest(t, req, http.StatusBadRequest) var error errorJSON DecodeJSON(t, resp, &error) - assert.EqualValues(t, "Failed to find the variable.", error.Error) + assert.Equal(t, "Failed to find the variable.", error.Error) } else { sess.MakeRequest(t, req, http.StatusOK) flashCookie := sess.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DThe%2Bvariable%2Bhas%2Bbeen%2Bremoved.", flashCookie.Value) + assert.Equal(t, "success%3DThe%2Bvariable%2Bhas%2Bbeen%2Bremoved.", flashCookie.Value) } } diff --git a/tests/integration/activitypub_client_test.go b/tests/integration/activitypub_client_test.go new file mode 100644 index 0000000000..afafca52ae --- /dev/null +++ b/tests/integration/activitypub_client_test.go @@ -0,0 +1,54 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/url" + "testing" + + "forgejo.org/models/db" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestActivityPubClientBodySize(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + + clientFactory, err := activitypub.GetClientFactory(db.DefaultContext) + require.NoError(t, err) + + apClient, err := clientFactory.WithKeys(db.DefaultContext, user1, user1.APActorKeyID()) + require.NoError(t, err) + + url := u.JoinPath("/api/v1/nodeinfo").String() + + // Request with normal MaxSize + t.Run("NormalMaxSize", func(t *testing.T) { + resp, err := apClient.GetBody(url) + require.NoError(t, err) + assert.Contains(t, string(resp), "forgejo") + }) + + // Set MaxSize to something very low to always fail + // Request with low MaxSize + t.Run("LowMaxSize", func(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.MaxSize, 100)() + + _, err = apClient.GetBody(url) + require.Error(t, err) + assert.ErrorContains(t, err, "Request returned") + }) + }) +} diff --git a/tests/integration/admin_user_test.go b/tests/integration/admin_user_test.go index f98650c567..c6ce416a22 100644 --- a/tests/integration/admin_user_test.go +++ b/tests/integration/admin_user_test.go @@ -38,7 +38,7 @@ func TestAdminViewUsers(t *testing.T) { // 6th column is the 2FA column. // One user that has TOTP and another user that has WebAuthn. - assert.EqualValues(t, 2, htmlDoc.Find(".admin-setting-content table tbody tr td:nth-child(6) .octicon-check").Length()) + assert.Equal(t, 2, htmlDoc.Find(".admin-setting-content table tbody tr td:nth-child(6) .octicon-check").Length()) }) t.Run("Normal user", func(t *testing.T) { diff --git a/tests/integration/api_activitypub_person_test.go b/tests/integration/api_activitypub_person_test.go index d8492942e5..fdbf3fac46 100644 --- a/tests/integration/api_activitypub_person_test.go +++ b/tests/integration/api_activitypub_person_test.go @@ -26,33 +26,47 @@ import ( func TestActivityPubPerson(t *testing.T) { defer test.MockVariableValue(&setting.Federation.Enabled, true)() defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - defer tests.PrepareTestEnv(t)() + onGiteaRun(t, func(t *testing.T, u *url.URL) { + userID := 2 + username := "user2" + userURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", u, userID) - userID := 2 - username := "user2" - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/user-id/%v", userID)) - resp := MakeRequest(t, req, http.StatusOK) - assert.Contains(t, resp.Body.String(), "@context") + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - var person ap.Person - err := person.UnmarshalJSON(resp.Body.Bytes()) - require.NoError(t, err) + clientFactory, err := activitypub.GetClientFactory(db.DefaultContext) + require.NoError(t, err) - assert.Equal(t, ap.PersonType, person.Type) - assert.Equal(t, username, person.PreferredUsername.String()) - keyID := person.GetID().String() - assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%v$", userID), keyID) - assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%v/outbox$", userID), person.Outbox.GetID().String()) - assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%v/inbox$", userID), person.Inbox.GetID().String()) + apClient, err := clientFactory.WithKeys(db.DefaultContext, user1, user1.APActorKeyID()) + require.NoError(t, err) - pubKey := person.PublicKey - assert.NotNil(t, pubKey) - publicKeyID := keyID + "#main-key" - assert.Equal(t, pubKey.ID.String(), publicKeyID) + // Unsigned request + t.Run("UnsignedRequest", func(t *testing.T) { + req := NewRequest(t, "GET", userURL) + MakeRequest(t, req, http.StatusBadRequest) + }) - pubKeyPem := pubKey.PublicKeyPem - assert.NotNil(t, pubKeyPem) - assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", pubKeyPem) + t.Run("SignedRequestValidation", func(t *testing.T) { + // Signed requset + resp, err := apClient.GetBody(userURL) + require.NoError(t, err) + + var person ap.Person + err = person.UnmarshalJSON(resp) + require.NoError(t, err) + + assert.Equal(t, ap.PersonType, person.Type) + assert.Equal(t, username, person.PreferredUsername.String()) + assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d$", userID), person.GetID()) + assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d/outbox$", userID), person.Outbox.GetID().String()) + assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d/inbox$", userID), person.Inbox.GetID().String()) + + assert.NotNil(t, person.PublicKey) + assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d#main-key$", userID), person.PublicKey.ID) + + assert.NotNil(t, person.PublicKey.PublicKeyPem) + assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", person.PublicKey.PublicKeyPem) + }) + }) } func TestActivityPubMissingPerson(t *testing.T) { diff --git a/tests/integration/api_activitypub_repository_test.go b/tests/integration/api_activitypub_repository_test.go index 29fbe6d781..fd19b4ce33 100644 --- a/tests/integration/api_activitypub_repository_test.go +++ b/tests/integration/api_activitypub_repository_test.go @@ -28,18 +28,28 @@ import ( func TestActivityPubRepository(t *testing.T) { defer test.MockVariableValue(&setting.Federation.Enabled, true)() defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - defer tests.PrepareTestEnv(t)() - repositoryID := 2 - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/repository-id/%v", repositoryID)) - resp := MakeRequest(t, req, http.StatusOK) - assert.Contains(t, resp.Body.String(), "@context") + onGiteaRun(t, func(t *testing.T, u *url.URL) { + repositoryID := 2 - var repository forgefed_modules.Repository - err := repository.UnmarshalJSON(resp.Body.Bytes()) - require.NoError(t, err) + apServerActor := user.NewAPServerActor() - assert.Regexp(t, fmt.Sprintf("activitypub/repository-id/%v$", repositoryID), repository.GetID().String()) + cf, err := activitypub.GetClientFactory(db.DefaultContext) + require.NoError(t, err) + + c, err := cf.WithKeys(db.DefaultContext, apServerActor, apServerActor.APActorKeyID()) + require.NoError(t, err) + + resp, err := c.GetBody(fmt.Sprintf("%sapi/v1/activitypub/repository-id/%d", u, repositoryID)) + require.NoError(t, err) + assert.Contains(t, string(resp), "@context") + + var repository forgefed_modules.Repository + err = repository.UnmarshalJSON(resp) + require.NoError(t, err) + + assert.Regexp(t, fmt.Sprintf("activitypub/repository-id/%d$", repositoryID), repository.GetID().String()) + }) } func TestActivityPubMissingRepository(t *testing.T) { @@ -48,7 +58,7 @@ func TestActivityPubMissingRepository(t *testing.T) { defer tests.PrepareTestEnv(t)() repositoryID := 9999999 - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/repository-id/%v", repositoryID)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)) resp := MakeRequest(t, req, http.StatusNotFound) assert.Contains(t, resp.Body.String(), "repository does not exist") } @@ -62,14 +72,16 @@ func TestActivityPubRepositoryInboxValid(t *testing.T) { defer federatedSrv.Close() onGiteaRun(t, func(t *testing.T, u *url.URL) { - actionsUser := user.NewActionsUser() + apServerActor := user.NewAPServerActor() repositoryID := 2 timeNow := time.Now().UTC() cf, err := activitypub.GetClientFactory(db.DefaultContext) require.NoError(t, err) - c, err := cf.WithKeys(db.DefaultContext, actionsUser, "not used") + + c, err := cf.WithKeys(db.DefaultContext, apServerActor, apServerActor.APActorKeyID()) require.NoError(t, err) + repoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String() activity1 := []byte(fmt.Sprintf( @@ -139,14 +151,16 @@ func TestActivityPubRepositoryInboxInvalid(t *testing.T) { defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() onGiteaRun(t, func(t *testing.T, u *url.URL) { - actionsUser := user.NewActionsUser() + apServerActor := user.NewAPServerActor() repositoryID := 2 + cf, err := activitypub.GetClientFactory(db.DefaultContext) require.NoError(t, err) - c, err := cf.WithKeys(db.DefaultContext, actionsUser, "not used") + + c, err := cf.WithKeys(db.DefaultContext, apServerActor, apServerActor.APActorKeyID()) require.NoError(t, err) - repoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox", repositoryID)).String() + repoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String() activity := []byte(`{"type":"Wrong"}`) resp, err := c.Post(activity, repoInboxURL) require.NoError(t, err) diff --git a/tests/integration/api_admin_actions_test.go b/tests/integration/api_admin_actions_test.go index 763c35544a..8e87d98ef0 100644 --- a/tests/integration/api_admin_actions_test.go +++ b/tests/integration/api_admin_actions_test.go @@ -35,5 +35,5 @@ func TestAPISearchActionJobs_GlobalRunner(t *testing.T) { DecodeJSON(t, res, &jobs) assert.Len(t, jobs, 1) - assert.EqualValues(t, job.ID, jobs[0].ID) + assert.Equal(t, job.ID, jobs[0].ID) } diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go index c5f8906bfc..104fdf4f67 100644 --- a/tests/integration/api_admin_test.go +++ b/tests/integration/api_admin_test.go @@ -211,7 +211,7 @@ func TestAPIEditUser(t *testing.T) { errMap := make(map[string]any) json.Unmarshal(resp.Body.Bytes(), &errMap) - assert.EqualValues(t, "e-mail invalid [email: ]", errMap["message"].(string)) + assert.Equal(t, "e-mail invalid [email: ]", errMap["message"].(string)) user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"}) assert.False(t, user2.IsRestricted) diff --git a/tests/integration/api_branch_test.go b/tests/integration/api_branch_test.go index df6b7022cd..8e88501596 100644 --- a/tests/integration/api_branch_test.go +++ b/tests/integration/api_branch_test.go @@ -25,13 +25,13 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) { AddTokenAuth(token) resp := MakeRequest(t, req, NoExpectedStatus) if !exists { - assert.EqualValues(t, http.StatusNotFound, resp.Code) + assert.Equal(t, http.StatusNotFound, resp.Code) return } - assert.EqualValues(t, http.StatusOK, resp.Code) + assert.Equal(t, http.StatusOK, resp.Code) var branch api.Branch DecodeJSON(t, resp, &branch) - assert.EqualValues(t, branchName, branch.Name) + assert.Equal(t, branchName, branch.Name) assert.True(t, branch.UserCanPush) assert.True(t, branch.UserCanMerge) } @@ -45,7 +45,7 @@ func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPSta if resp.Code == http.StatusOK { var branchProtection api.BranchProtection DecodeJSON(t, resp, &branchProtection) - assert.EqualValues(t, branchName, branchProtection.RuleName) + assert.Equal(t, branchName, branchProtection.RuleName) return &branchProtection } return nil @@ -61,7 +61,7 @@ func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTP if resp.Code == http.StatusCreated { var branchProtection api.BranchProtection DecodeJSON(t, resp, &branchProtection) - assert.EqualValues(t, branchName, branchProtection.RuleName) + assert.Equal(t, branchName, branchProtection.RuleName) } } @@ -74,7 +74,7 @@ func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.Bran if resp.Code == http.StatusOK { var branchProtection api.BranchProtection DecodeJSON(t, resp, &branchProtection) - assert.EqualValues(t, branchName, branchProtection.RuleName) + assert.Equal(t, branchName, branchProtection.RuleName) } } @@ -182,7 +182,7 @@ func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBran DecodeJSON(t, resp, &branch) if resp.Result().StatusCode == http.StatusCreated { - assert.EqualValues(t, newBranch, branch.Name) + assert.Equal(t, newBranch, branch.Name) } return resp.Result().StatusCode == status diff --git a/tests/integration/api_comment_test.go b/tests/integration/api_comment_test.go index fd0cadbb1a..810b5b9727 100644 --- a/tests/integration/api_comment_test.go +++ b/tests/integration/api_comment_test.go @@ -115,7 +115,7 @@ func TestAPICreateComment(t *testing.T) { var updatedComment api.Comment DecodeJSON(t, resp, &updatedComment) - assert.EqualValues(t, commentBody, updatedComment.Body) + assert.Equal(t, commentBody, updatedComment.Body) unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody}) urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments", @@ -373,8 +373,8 @@ func TestAPIEditComment(t *testing.T) { var updatedComment api.Comment DecodeJSON(t, resp, &updatedComment) - assert.EqualValues(t, comment.ID, updatedComment.ID) - assert.EqualValues(t, newCommentBody, updatedComment.Body) + assert.Equal(t, comment.ID, updatedComment.ID) + assert.Equal(t, newCommentBody, updatedComment.Body) unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody}) } diff --git a/tests/integration/api_federation_httpsig_test.go b/tests/integration/api_federation_httpsig_test.go new file mode 100644 index 0000000000..9d66f25102 --- /dev/null +++ b/tests/integration/api_federation_httpsig_test.go @@ -0,0 +1,82 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + + "forgejo.org/models/db" + "forgejo.org/models/forgefed" + "forgejo.org/models/unittest" + "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFederationHttpSigValidation(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + userID := 2 + userURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", u, userID) + + user1 := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 1}) + + clientFactory, err := activitypub.GetClientFactory(db.DefaultContext) + require.NoError(t, err) + + apClient, err := clientFactory.WithKeys(db.DefaultContext, user1, user1.APActorKeyID()) + require.NoError(t, err) + + // Unsigned request + t.Run("UnsignedRequest", func(t *testing.T) { + req := NewRequest(t, "GET", userURL) + MakeRequest(t, req, http.StatusBadRequest) + }) + + // Signed request + t.Run("SignedRequest", func(t *testing.T) { + resp, err := apClient.Get(userURL) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + }) + + // HACK HACK HACK: the host part of the URL gets set to which IP forgejo is + // listening on, NOT localhost, which is the Domain given to forgejo which + // is then used for eg. the keyID all requests + applicationKeyID := fmt.Sprintf("%sapi/v1/activitypub/actor#main-key", setting.AppURL) + actorKeyID := fmt.Sprintf("%sapi/v1/activitypub/user-id/1#main-key", setting.AppURL) + + // Check for cached public keys + t.Run("ValidateCaches", func(t *testing.T) { + host, err := forgefed.FindFederationHostByKeyID(db.DefaultContext, applicationKeyID) + require.NoError(t, err) + assert.NotNil(t, host) + assert.True(t, host.PublicKey.Valid) + + user, err := user.GetFederatedUserByKeyID(db.DefaultContext, actorKeyID) + require.NoError(t, err) + assert.NotNil(t, user) + assert.True(t, user.PublicKey.Valid) + }) + + // Disable signature validation + defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() + + // Unsigned request + t.Run("SignatureValidationDisabled", func(t *testing.T) { + req := NewRequest(t, "GET", userURL) + MakeRequest(t, req, http.StatusOK) + }) + }) +} diff --git a/tests/integration/api_fork_test.go b/tests/integration/api_fork_test.go index 2809af2748..0d38831b10 100644 --- a/tests/integration/api_fork_test.go +++ b/tests/integration/api_fork_test.go @@ -133,7 +133,7 @@ func TestAPIForkListPrivateRepo(t *testing.T) { DecodeJSON(t, resp, &forks) assert.Empty(t, forks) - assert.EqualValues(t, "0", resp.Header().Get("X-Total-Count")) + assert.Equal(t, "0", resp.Header().Get("X-Total-Count")) }) t.Run("Logged in", func(t *testing.T) { @@ -146,6 +146,6 @@ func TestAPIForkListPrivateRepo(t *testing.T) { DecodeJSON(t, resp, &forks) assert.Len(t, forks, 1) - assert.EqualValues(t, "1", resp.Header().Get("X-Total-Count")) + assert.Equal(t, "1", resp.Header().Get("X-Total-Count")) }) } diff --git a/tests/integration/api_gpg_keys_test.go b/tests/integration/api_gpg_keys_test.go index 1acbf91de0..b042c2ce6b 100644 --- a/tests/integration/api_gpg_keys_test.go +++ b/tests/integration/api_gpg_keys_test.go @@ -86,13 +86,13 @@ func TestGPGKeys(t *testing.T) { assert.Len(t, keys, 1) primaryKey1 := keys[0] // Primary key 1 - assert.EqualValues(t, "38EA3BCED732982C", primaryKey1.KeyID) + assert.Equal(t, "38EA3BCED732982C", primaryKey1.KeyID) assert.Len(t, primaryKey1.Emails, 1) - assert.EqualValues(t, "user2@example.com", primaryKey1.Emails[0].Email) + assert.Equal(t, "user2@example.com", primaryKey1.Emails[0].Email) assert.True(t, primaryKey1.Emails[0].Verified) subKey := primaryKey1.SubsKey[0] // Subkey of 38EA3BCED732982C - assert.EqualValues(t, "70D7C694D17D03AD", subKey.KeyID) + assert.Equal(t, "70D7C694D17D03AD", subKey.KeyID) assert.Empty(t, subKey.Emails) var key api.GPGKey @@ -100,16 +100,16 @@ func TestGPGKeys(t *testing.T) { AddTokenAuth(tokenWithGPGKeyScope) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &key) - assert.EqualValues(t, "38EA3BCED732982C", key.KeyID) + assert.Equal(t, "38EA3BCED732982C", key.KeyID) assert.Len(t, key.Emails, 1) - assert.EqualValues(t, "user2@example.com", key.Emails[0].Email) + assert.Equal(t, "user2@example.com", key.Emails[0].Email) assert.True(t, key.Emails[0].Verified) req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(subKey.ID, 10)). // Subkey of 38EA3BCED732982C AddTokenAuth(tokenWithGPGKeyScope) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &key) - assert.EqualValues(t, "70D7C694D17D03AD", key.KeyID) + assert.Equal(t, "70D7C694D17D03AD", key.KeyID) assert.Empty(t, key.Emails) }) diff --git a/tests/integration/api_helper_for_declarative_test.go b/tests/integration/api_helper_for_declarative_test.go index b5941b3389..c9b6f84f4f 100644 --- a/tests/integration/api_helper_for_declarative_test.go +++ b/tests/integration/api_helper_for_declarative_test.go @@ -294,7 +294,7 @@ func doAPIMergePullRequestForm(t *testing.T, ctx APITestContext, owner, repo str expected = http.StatusOK } - if !assert.EqualValues(t, expected, resp.Code, + if !assert.Equal(t, expected, resp.Code, "Request: %s %s", req.Method, req.URL.String()) { logUnexpectedResponse(t, resp) } diff --git a/tests/integration/api_issue_label_test.go b/tests/integration/api_issue_label_test.go index 7b2fbd18be..b1b05b48d8 100644 --- a/tests/integration/api_issue_label_test.go +++ b/tests/integration/api_issue_label_test.go @@ -41,8 +41,8 @@ func TestAPIModifyLabels(t *testing.T) { apiLabel := new(api.Label) DecodeJSON(t, resp, &apiLabel) dbLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: apiLabel.ID, RepoID: repo.ID}) - assert.EqualValues(t, dbLabel.Name, apiLabel.Name) - assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) + assert.Equal(t, dbLabel.Name, apiLabel.Name) + assert.Equal(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) req = NewRequestWithJSON(t, "POST", urlStr, &api.CreateLabelOption{ Name: "TestL 2", @@ -70,7 +70,7 @@ func TestAPIModifyLabels(t *testing.T) { AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiLabel) - assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) + assert.Equal(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) // EditLabel newName := "LabelNewName" @@ -82,7 +82,7 @@ func TestAPIModifyLabels(t *testing.T) { }).AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiLabel) - assert.EqualValues(t, newColor, apiLabel.Color) + assert.Equal(t, newColor, apiLabel.Color) req = NewRequestWithJSON(t, "PATCH", singleURLStr, &api.EditLabelOption{ Color: &newColorWrong, }).AddTokenAuth(token) @@ -211,7 +211,7 @@ func TestAPIReplaceIssueLabels(t *testing.T) { var apiLabels []*api.Label DecodeJSON(t, resp, &apiLabels) if assert.Len(t, apiLabels, 1) { - assert.EqualValues(t, label.ID, apiLabels[0].ID) + assert.Equal(t, label.ID, apiLabels[0].ID) } unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issue.ID}, 1) @@ -237,7 +237,7 @@ func TestAPIReplaceIssueLabelsWithLabelNames(t *testing.T) { var apiLabels []*api.Label DecodeJSON(t, resp, &apiLabels) if assert.Len(t, apiLabels, 1) { - assert.EqualValues(t, label.Name, apiLabels[0].Name) + assert.Equal(t, label.Name, apiLabels[0].Name) } } @@ -261,8 +261,8 @@ func TestAPIModifyOrgLabels(t *testing.T) { apiLabel := new(api.Label) DecodeJSON(t, resp, &apiLabel) dbLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: apiLabel.ID, OrgID: owner.ID}) - assert.EqualValues(t, dbLabel.Name, apiLabel.Name) - assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) + assert.Equal(t, dbLabel.Name, apiLabel.Name) + assert.Equal(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) req = NewRequestWithJSON(t, "POST", urlStr, &api.CreateLabelOption{ Name: "TestL 2", @@ -290,7 +290,7 @@ func TestAPIModifyOrgLabels(t *testing.T) { AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiLabel) - assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) + assert.Equal(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) // EditLabel newName := "LabelNewName" @@ -302,7 +302,7 @@ func TestAPIModifyOrgLabels(t *testing.T) { }).AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiLabel) - assert.EqualValues(t, newColor, apiLabel.Color) + assert.Equal(t, newColor, apiLabel.Color) req = NewRequestWithJSON(t, "PATCH", singleURLStr, &api.EditLabelOption{ Color: &newColorWrong, }).AddTokenAuth(token) diff --git a/tests/integration/api_issue_milestone_test.go b/tests/integration/api_issue_milestone_test.go index 2ab7f9a346..16ebf36721 100644 --- a/tests/integration/api_issue_milestone_test.go +++ b/tests/integration/api_issue_milestone_test.go @@ -71,7 +71,7 @@ func TestAPIIssuesMilestone(t *testing.T) { AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiMilestone) - assert.EqualValues(t, apiMilestones[2], apiMilestone) + assert.Equal(t, apiMilestones[2], apiMilestone) req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/milestones?state=%s&name=%s", owner.Name, repo.Name, "all", "milestone2")). AddTokenAuth(token) diff --git a/tests/integration/api_issue_stopwatch_test.go b/tests/integration/api_issue_stopwatch_test.go index f395ad1a61..f83519a504 100644 --- a/tests/integration/api_issue_stopwatch_test.go +++ b/tests/integration/api_issue_stopwatch_test.go @@ -35,11 +35,11 @@ func TestAPIListStopWatches(t *testing.T) { stopwatch := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: owner.ID}) issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: stopwatch.IssueID}) if assert.Len(t, apiWatches, 1) { - assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix()) - assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex) - assert.EqualValues(t, issue.Title, apiWatches[0].IssueTitle) - assert.EqualValues(t, repo.Name, apiWatches[0].RepoName) - assert.EqualValues(t, repo.OwnerName, apiWatches[0].RepoOwnerName) + assert.Equal(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix()) + assert.Equal(t, issue.Index, apiWatches[0].IssueIndex) + assert.Equal(t, issue.Title, apiWatches[0].IssueTitle) + assert.Equal(t, repo.Name, apiWatches[0].RepoName) + assert.Equal(t, repo.OwnerName, apiWatches[0].RepoOwnerName) assert.Positive(t, apiWatches[0].Seconds) } } diff --git a/tests/integration/api_issue_subscription_test.go b/tests/integration/api_issue_subscription_test.go index 4d1b9b2fb1..254bc09aea 100644 --- a/tests/integration/api_issue_subscription_test.go +++ b/tests/integration/api_issue_subscription_test.go @@ -43,11 +43,11 @@ func TestAPIIssueSubscriptions(t *testing.T) { wi := new(api.WatchInfo) DecodeJSON(t, resp, wi) - assert.EqualValues(t, isWatching, wi.Subscribed) - assert.EqualValues(t, !isWatching, wi.Ignored) - assert.EqualValues(t, issue.APIURL(db.DefaultContext)+"/subscriptions", wi.URL) + assert.Equal(t, isWatching, wi.Subscribed) + assert.Equal(t, !isWatching, wi.Ignored) + assert.Equal(t, issue.APIURL(db.DefaultContext)+"/subscriptions", wi.URL) assert.EqualValues(t, issue.CreatedUnix, wi.CreatedAt.Unix()) - assert.EqualValues(t, issueRepo.APIURL(), wi.RepositoryURL) + assert.Equal(t, issueRepo.APIURL(), wi.RepositoryURL) } testSubscription(issue1, true) diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go index 99fc158fdd..764ddd38d2 100644 --- a/tests/integration/api_issue_test.go +++ b/tests/integration/api_issue_test.go @@ -506,7 +506,7 @@ func TestAPISearchIssues(t *testing.T) { req = NewRequest(t, "GET", link.String()).AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) - assert.EqualValues(t, "22", resp.Header().Get("X-Total-Count")) + assert.Equal(t, "22", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 20) query.Add("limit", "10") @@ -514,7 +514,7 @@ func TestAPISearchIssues(t *testing.T) { req = NewRequest(t, "GET", link.String()).AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) - assert.EqualValues(t, "22", resp.Header().Get("X-Total-Count")) + assert.Equal(t, "22", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 10) query = url.Values{"assigned": {"true"}, "state": {"all"}} diff --git a/tests/integration/api_issue_tracked_time_test.go b/tests/integration/api_issue_tracked_time_test.go index 41def89a55..a4f3d15f88 100644 --- a/tests/integration/api_issue_tracked_time_test.go +++ b/tests/integration/api_issue_tracked_time_test.go @@ -42,8 +42,8 @@ func TestAPIGetTrackedTimes(t *testing.T) { for i, time := range expect { assert.Equal(t, time.ID, apiTimes[i].ID) - assert.EqualValues(t, issue2.Title, apiTimes[i].Issue.Title) - assert.EqualValues(t, issue2.ID, apiTimes[i].IssueID) + assert.Equal(t, issue2.Title, apiTimes[i].Issue.Title) + assert.Equal(t, issue2.ID, apiTimes[i].IssueID) assert.Equal(t, time.Created.Unix(), apiTimes[i].Created.Unix()) assert.Equal(t, time.Time, apiTimes[i].Time) user, err := user_model.GetUserByID(db.DefaultContext, time.UserID) @@ -126,6 +126,6 @@ func TestAPIAddTrackedTimes(t *testing.T) { DecodeJSON(t, resp, &apiNewTime) assert.EqualValues(t, 33, apiNewTime.Time) - assert.EqualValues(t, user2.ID, apiNewTime.UserID) + assert.Equal(t, user2.ID, apiNewTime.UserID) assert.EqualValues(t, 947688818, apiNewTime.Created.Unix()) } diff --git a/tests/integration/api_notification_test.go b/tests/integration/api_notification_test.go index b9efaa43a3..9f5c5394e5 100644 --- a/tests/integration/api_notification_test.go +++ b/tests/integration/api_notification_test.go @@ -105,10 +105,10 @@ func TestAPINotification(t *testing.T) { assert.EqualValues(t, 5, apiN.ID) assert.False(t, apiN.Pinned) assert.True(t, apiN.Unread) - assert.EqualValues(t, "issue4", apiN.Subject.Title) + assert.Equal(t, "issue4", apiN.Subject.Title) assert.EqualValues(t, "Issue", apiN.Subject.Type) - assert.EqualValues(t, thread5.Issue.APIURL(db.DefaultContext), apiN.Subject.URL) - assert.EqualValues(t, thread5.Repository.HTMLURL(), apiN.Repository.HTMLURL) + assert.Equal(t, thread5.Issue.APIURL(db.DefaultContext), apiN.Subject.URL) + assert.Equal(t, thread5.Repository.HTMLURL(), apiN.Repository.HTMLURL) MakeRequest(t, NewRequest(t, "GET", "/api/v1/notifications/new"), http.StatusUnauthorized) diff --git a/tests/integration/api_oauth2_apps_test.go b/tests/integration/api_oauth2_apps_test.go index 2490a11888..6e238e897d 100644 --- a/tests/integration/api_oauth2_apps_test.go +++ b/tests/integration/api_oauth2_apps_test.go @@ -43,12 +43,12 @@ func testAPICreateOAuth2Application(t *testing.T) { var createdApp *api.OAuth2Application DecodeJSON(t, resp, &createdApp) - assert.EqualValues(t, appBody.Name, createdApp.Name) + assert.Equal(t, appBody.Name, createdApp.Name) assert.Len(t, createdApp.ClientSecret, 56) assert.Len(t, createdApp.ClientID, 36) assert.True(t, createdApp.ConfidentialClient) assert.NotEmpty(t, createdApp.Created) - assert.EqualValues(t, appBody.RedirectURIs[0], createdApp.RedirectURIs[0]) + assert.Equal(t, appBody.RedirectURIs[0], createdApp.RedirectURIs[0]) unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{UID: user.ID, Name: createdApp.Name}) } @@ -74,12 +74,12 @@ func testAPIListOAuth2Applications(t *testing.T) { DecodeJSON(t, resp, &appList) expectedApp := appList[0] - assert.EqualValues(t, expectedApp.Name, existApp.Name) - assert.EqualValues(t, expectedApp.ClientID, existApp.ClientID) + assert.Equal(t, expectedApp.Name, existApp.Name) + assert.Equal(t, expectedApp.ClientID, existApp.ClientID) assert.Equal(t, expectedApp.ConfidentialClient, existApp.ConfidentialClient) assert.Len(t, expectedApp.ClientID, 36) assert.Empty(t, expectedApp.ClientSecret) - assert.EqualValues(t, expectedApp.RedirectURIs[0], existApp.RedirectURIs[0]) + assert.Equal(t, expectedApp.RedirectURIs[0], existApp.RedirectURIs[0]) unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) } @@ -128,13 +128,13 @@ func testAPIGetOAuth2Application(t *testing.T) { DecodeJSON(t, resp, &app) expectedApp := app - assert.EqualValues(t, expectedApp.Name, existApp.Name) - assert.EqualValues(t, expectedApp.ClientID, existApp.ClientID) + assert.Equal(t, expectedApp.Name, existApp.Name) + assert.Equal(t, expectedApp.ClientID, existApp.ClientID) assert.Equal(t, expectedApp.ConfidentialClient, existApp.ConfidentialClient) assert.Len(t, expectedApp.ClientID, 36) assert.Empty(t, expectedApp.ClientSecret) assert.Len(t, expectedApp.RedirectURIs, 1) - assert.EqualValues(t, expectedApp.RedirectURIs[0], existApp.RedirectURIs[0]) + assert.Equal(t, expectedApp.RedirectURIs[0], existApp.RedirectURIs[0]) unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) } @@ -168,8 +168,8 @@ func testAPIUpdateOAuth2Application(t *testing.T) { expectedApp := app assert.Len(t, expectedApp.RedirectURIs, 2) - assert.EqualValues(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0]) - assert.EqualValues(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1]) + assert.Equal(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0]) + assert.Equal(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1]) assert.Equal(t, expectedApp.ConfidentialClient, appBody.ConfidentialClient) unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) } diff --git a/tests/integration/api_org_actions_test.go b/tests/integration/api_org_actions_test.go index ed8f91e5c2..a5497130f9 100644 --- a/tests/integration/api_org_actions_test.go +++ b/tests/integration/api_org_actions_test.go @@ -34,5 +34,5 @@ func TestAPISearchActionJobs_OrgRunner(t *testing.T) { DecodeJSON(t, res, &jobs) assert.Len(t, jobs, 1) - assert.EqualValues(t, job.ID, jobs[0].ID) + assert.Equal(t, job.ID, jobs[0].ID) } diff --git a/tests/integration/api_org_test.go b/tests/integration/api_org_test.go index 37e6035045..d20387747b 100644 --- a/tests/integration/api_org_test.go +++ b/tests/integration/api_org_test.go @@ -76,7 +76,7 @@ func TestAPIOrgCreate(t *testing.T) { AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiOrg) - assert.EqualValues(t, org.UserName, apiOrg.Name) + assert.Equal(t, org.UserName, apiOrg.Name) req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org.UserName). AddTokenAuth(token) @@ -96,7 +96,7 @@ func TestAPIOrgCreate(t *testing.T) { var users []*api.User DecodeJSON(t, resp, &users) assert.Len(t, users, 1) - assert.EqualValues(t, "user1", users[0].UserName) + assert.Equal(t, "user1", users[0].UserName) } func TestAPIOrgRename(t *testing.T) { @@ -238,7 +238,7 @@ func TestAPIOrgSearchEmptyTeam(t *testing.T) { DecodeJSON(t, resp, &data) assert.True(t, data.Ok) if assert.Len(t, data.Data, 1) { - assert.EqualValues(t, "Empty", data.Data[0].Name) + assert.Equal(t, "Empty", data.Data[0].Name) } } diff --git a/tests/integration/api_packages_alt_test.go b/tests/integration/api_packages_alt_test.go index e84af7dd15..c7ee2c49a5 100644 --- a/tests/integration/api_packages_alt_test.go +++ b/tests/integration/api_packages_alt_test.go @@ -579,7 +579,7 @@ enabled=1`, assert.Equal(t, []int(nil), result.DirIndexes) assert.Equal(t, []string{"hello"}, result.BaseNames) assert.Equal(t, []string{"/usr/local/bin/"}, result.DirNames) - assert.Equal(t, "", result.DistTag) + assert.Empty(t, result.DistTag) assert.Equal(t, "gitea-test-1.0.2-1.x86_64.rpm", result.AptIndexLegacyFileName) assert.Equal(t, 7116, result.AptIndexLegacyFileSize) assert.Equal(t, "9ea82dd62968719aea19c08cd2ced79a", result.MD5Sum) diff --git a/tests/integration/api_packages_cargo_test.go b/tests/integration/api_packages_cargo_test.go index 048859e312..d42dc61c62 100644 --- a/tests/integration/api_packages_cargo_test.go +++ b/tests/integration/api_packages_cargo_test.go @@ -403,7 +403,7 @@ func TestRebuildCargo(t *testing.T) { flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "error%3DCannot%2Brebuild%252C%2Bno%2Bindex%2Bis%2Binitialized.", flashCookie.Value) + assert.Equal(t, "error%3DCannot%2Brebuild%252C%2Bno%2Bindex%2Bis%2Binitialized.", flashCookie.Value) unittest.AssertExistsIf(t, false, &repo_model.Repository{OwnerID: user.ID, Name: cargo_service.IndexRepositoryName}) }) @@ -441,7 +441,7 @@ func TestRebuildCargo(t *testing.T) { flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DThe%2BCargo%2Bindex%2Bwas%2Bsuccessfully%2Brebuild.", flashCookie.Value) + assert.Equal(t, "success%3DThe%2BCargo%2Bindex%2Bwas%2Bsuccessfully%2Brebuild.", flashCookie.Value) }) }) } diff --git a/tests/integration/api_packages_conan_test.go b/tests/integration/api_packages_conan_test.go index c0185c852d..02a716b671 100644 --- a/tests/integration/api_packages_conan_test.go +++ b/tests/integration/api_packages_conan_test.go @@ -328,7 +328,7 @@ func TestPackageConan(t *testing.T) { assert.Equal(t, int64(len(contentConaninfo)), pb.Size) } else { - assert.FailNow(t, "unknown file: %s", pf.Name) + assert.FailNow(t, "unknown file", "name: %s", pf.Name) } } }) diff --git a/tests/integration/api_packages_container_cleanup_sha256_test.go b/tests/integration/api_packages_container_cleanup_sha256_test.go index b1ed435b80..5c661a6858 100644 --- a/tests/integration/api_packages_container_cleanup_sha256_test.go +++ b/tests/integration/api_packages_container_cleanup_sha256_test.go @@ -53,7 +53,7 @@ func TestPackagesContainerCleanupSHA256(t *testing.T) { for range expected { filtered = append(filtered, true) } - assert.EqualValues(t, filtered, logFiltered, expected) + assert.Equal(t, filtered, logFiltered, expected) } userToken := "" diff --git a/tests/integration/api_packages_container_test.go b/tests/integration/api_packages_container_test.go index 223c865dec..0e977e9ae7 100644 --- a/tests/integration/api_packages_container_test.go +++ b/tests/integration/api_packages_container_test.go @@ -392,7 +392,7 @@ func TestPackageContainer(t *testing.T) { assert.Equal(t, "application/vnd.docker.image.rootfs.diff.tar.gzip", pfd.Properties.GetByName(container_module.PropertyMediaType)) assert.Equal(t, blobDigest, pfd.Properties.GetByName(container_module.PropertyDigest)) default: - assert.FailNow(t, "unknown file: %s", pfd.File.Name) + assert.FailNow(t, "unknown file", "name: %s", pfd.File.Name) } } diff --git a/tests/integration/api_packages_nuget_test.go b/tests/integration/api_packages_nuget_test.go index b4da9695f0..5596925d86 100644 --- a/tests/integration/api_packages_nuget_test.go +++ b/tests/integration/api_packages_nuget_test.go @@ -393,7 +393,7 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`) assert.Equal(t, nuget_module.PropertySymbolID, pps[0].Name) assert.Equal(t, symbolID, pps[0].Value) default: - assert.FailNow(t, "unexpected file: %v", pf.Name) + assert.FailNow(t, "unexpected file", "name: %s", pf.Name) } } diff --git a/tests/integration/api_packages_rpm_test.go b/tests/integration/api_packages_rpm_test.go index 3abaec3462..3690454714 100644 --- a/tests/integration/api_packages_rpm_test.go +++ b/tests/integration/api_packages_rpm_test.go @@ -317,7 +317,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, var result Metadata decodeGzipXML(t, resp, &result) - assert.EqualValues(t, 1, result.PackageCount) + assert.Equal(t, 1, result.PackageCount) assert.Len(t, result.Packages, 1) p := result.Packages[0] assert.Equal(t, "rpm", p.Type) @@ -366,7 +366,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, var result Filelists decodeGzipXML(t, resp, &result) - assert.EqualValues(t, 1, result.PackageCount) + assert.Equal(t, 1, result.PackageCount) assert.Len(t, result.Packages, 1) p := result.Packages[0] assert.NotEmpty(t, p.Pkgid) @@ -403,7 +403,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`, var result Other decodeGzipXML(t, resp, &result) - assert.EqualValues(t, 1, result.PackageCount) + assert.Equal(t, 1, result.PackageCount) assert.Len(t, result.Packages, 1) p := result.Packages[0] assert.NotEmpty(t, p.Pkgid) diff --git a/tests/integration/api_packages_test.go b/tests/integration/api_packages_test.go index a157d18cd5..9f8115f505 100644 --- a/tests/integration/api_packages_test.go +++ b/tests/integration/api_packages_test.go @@ -115,7 +115,7 @@ func TestPackageAPI(t *testing.T) { var ap2 *api.Package DecodeJSON(t, resp, &ap2) assert.NotNil(t, ap2.Repository) - assert.EqualValues(t, repo.ID, ap2.Repository.ID) + assert.Equal(t, repo.ID, ap2.Repository.ID) // link to repository without write access, should fail req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/link/%s", user.Name, packageName, "repo3")).AddTokenAuth(tokenWritePackage) @@ -486,7 +486,7 @@ func TestPackageCleanup(t *testing.T) { defer tests.PrintCurrentTest(t)() // Upload and delete a generic package and upload a container blob - data, _ := util.CryptoRandomBytes(5) + data := util.CryptoRandomBytes(5) url := fmt.Sprintf("/api/packages/%s/generic/cleanup-test/1.1.1/file.bin", user.Name) req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(data)). AddBasicAuth(user.Name) @@ -496,7 +496,7 @@ func TestPackageCleanup(t *testing.T) { AddBasicAuth(user.Name) MakeRequest(t, req, http.StatusNoContent) - data, _ = util.CryptoRandomBytes(5) + data = util.CryptoRandomBytes(5) url = fmt.Sprintf("/v2/%s/cleanup-test/blobs/uploads?digest=sha256:%x", user.Name, sha256.Sum256(data)) req = NewRequestWithBody(t, "POST", url, bytes.NewReader(data)). AddBasicAuth(user.Name) diff --git a/tests/integration/api_pull_review_test.go b/tests/integration/api_pull_review_test.go index 930f9b816b..9f00686b57 100644 --- a/tests/integration/api_pull_review_test.go +++ b/tests/integration/api_pull_review_test.go @@ -85,7 +85,7 @@ func TestAPIPullReviewCreateDeleteComment(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &review) require.EqualValues(t, string(event), review.State) - require.EqualValues(t, 0, review.CodeCommentsCount) + require.Equal(t, 0, review.CodeCommentsCount) } { @@ -94,7 +94,7 @@ func TestAPIPullReviewCreateDeleteComment(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) var getReview api.PullReview DecodeJSON(t, resp, &getReview) - require.EqualValues(t, getReview, review) + require.Equal(t, getReview, review) } requireReviewCount(2) @@ -109,11 +109,11 @@ func TestAPIPullReviewCreateDeleteComment(t *testing.T) { }).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &reviewComment) - assert.EqualValues(t, review.ID, reviewComment.ReviewID) - assert.EqualValues(t, newCommentBody, reviewComment.Body) + assert.Equal(t, review.ID, reviewComment.ReviewID) + assert.Equal(t, newCommentBody, reviewComment.Body) assert.EqualValues(t, reviewLine, reviewComment.OldLineNum) assert.EqualValues(t, 0, reviewComment.LineNum) - assert.EqualValues(t, path, reviewComment.Path) + assert.Equal(t, path, reviewComment.Path) } { @@ -123,7 +123,7 @@ func TestAPIPullReviewCreateDeleteComment(t *testing.T) { var comment api.PullReviewComment DecodeJSON(t, resp, &comment) - assert.EqualValues(t, reviewComment, comment) + assert.Equal(t, reviewComment, comment) } { @@ -167,17 +167,17 @@ func TestAPIPullReview(t *testing.T) { return } for _, r := range reviews { - assert.EqualValues(t, pullIssue.HTMLURL(), r.HTMLPullURL) + assert.Equal(t, pullIssue.HTMLURL(), r.HTMLPullURL) } assert.EqualValues(t, 8, reviews[3].ID) assert.EqualValues(t, "APPROVED", reviews[3].State) - assert.EqualValues(t, 0, reviews[3].CodeCommentsCount) + assert.Equal(t, 0, reviews[3].CodeCommentsCount) assert.True(t, reviews[3].Stale) assert.False(t, reviews[3].Official) assert.EqualValues(t, 10, reviews[5].ID) assert.EqualValues(t, "REQUEST_CHANGES", reviews[5].State) - assert.EqualValues(t, 1, reviews[5].CodeCommentsCount) + assert.Equal(t, 1, reviews[5].CodeCommentsCount) assert.EqualValues(t, -1, reviews[5].Reviewer.ID) // ghost user assert.False(t, reviews[5].Stale) assert.True(t, reviews[5].Official) @@ -188,13 +188,13 @@ func TestAPIPullReview(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) var review api.PullReview DecodeJSON(t, resp, &review) - assert.EqualValues(t, *reviews[3], review) + assert.Equal(t, *reviews[3], review) req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls/%d/reviews/%d", repo.OwnerName, repo.Name, pullIssue.Index, reviews[5].ID). AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &review) - assert.EqualValues(t, *reviews[5], review) + assert.Equal(t, *reviews[5], review) // test GetPullReviewComments comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 7}) @@ -204,11 +204,11 @@ func TestAPIPullReview(t *testing.T) { var reviewComments []*api.PullReviewComment DecodeJSON(t, resp, &reviewComments) assert.Len(t, reviewComments, 1) - assert.EqualValues(t, "Ghost", reviewComments[0].Poster.UserName) - assert.EqualValues(t, "a review from a deleted user", reviewComments[0].Body) - assert.EqualValues(t, comment.ID, reviewComments[0].ID) + assert.Equal(t, "Ghost", reviewComments[0].Poster.UserName) + assert.Equal(t, "a review from a deleted user", reviewComments[0].Body) + assert.Equal(t, comment.ID, reviewComments[0].ID) assert.EqualValues(t, comment.UpdatedUnix, reviewComments[0].Updated.Unix()) - assert.EqualValues(t, comment.HTMLURL(db.DefaultContext), reviewComments[0].HTMLURL) + assert.Equal(t, comment.HTMLURL(db.DefaultContext), reviewComments[0].HTMLURL) // test CreatePullReview req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{ @@ -237,7 +237,7 @@ func TestAPIPullReview(t *testing.T) { DecodeJSON(t, resp, &review) assert.EqualValues(t, 6, review.ID) assert.EqualValues(t, "PENDING", review.State) - assert.EqualValues(t, 3, review.CodeCommentsCount) + assert.Equal(t, 3, review.CodeCommentsCount) // test SubmitPullReview req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d", repo.OwnerName, repo.Name, pullIssue.Index, review.ID), &api.SubmitPullReviewOptions{ @@ -248,7 +248,7 @@ func TestAPIPullReview(t *testing.T) { DecodeJSON(t, resp, &review) assert.EqualValues(t, 6, review.ID) assert.EqualValues(t, "APPROVED", review.State) - assert.EqualValues(t, 3, review.CodeCommentsCount) + assert.Equal(t, 3, review.CodeCommentsCount) // test dismiss review req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/dismissals", repo.OwnerName, repo.Name, pullIssue.Index, review.ID), &api.DismissPullReviewOptions{ @@ -275,7 +275,7 @@ func TestAPIPullReview(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &review) assert.EqualValues(t, "COMMENT", review.State) - assert.EqualValues(t, 0, review.CodeCommentsCount) + assert.Equal(t, 0, review.CodeCommentsCount) req = NewRequestf(t, http.MethodDelete, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d", repo.OwnerName, repo.Name, pullIssue.Index, review.ID). AddTokenAuth(token) MakeRequest(t, req, http.StatusNoContent) @@ -303,7 +303,7 @@ func TestAPIPullReview(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &commentReview) assert.EqualValues(t, "COMMENT", commentReview.State) - assert.EqualValues(t, 2, commentReview.CodeCommentsCount) + assert.Equal(t, 2, commentReview.CodeCommentsCount) assert.Empty(t, commentReview.Body) assert.False(t, commentReview.Dismissed) @@ -318,8 +318,8 @@ func TestAPIPullReview(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &commentReview) assert.EqualValues(t, "COMMENT", commentReview.State) - assert.EqualValues(t, 0, commentReview.CodeCommentsCount) - assert.EqualValues(t, commentBody, commentReview.Body) + assert.Equal(t, 0, commentReview.CodeCommentsCount) + assert.Equal(t, commentBody, commentReview.Body) assert.False(t, commentReview.Dismissed) // test CreatePullReview Comment without body and no comments @@ -331,7 +331,7 @@ func TestAPIPullReview(t *testing.T) { resp = MakeRequest(t, req, http.StatusUnprocessableEntity) errMap := make(map[string]any) json.Unmarshal(resp.Body.Bytes(), &errMap) - assert.EqualValues(t, "review event COMMENT requires a body or a comment", errMap["message"].(string)) + assert.Equal(t, "review event COMMENT requires a body or a comment", errMap["message"].(string)) // test get review requests // to make it simple, use same api with get review @@ -345,14 +345,14 @@ func TestAPIPullReview(t *testing.T) { DecodeJSON(t, resp, &reviews) assert.EqualValues(t, 11, reviews[0].ID) assert.EqualValues(t, "REQUEST_REVIEW", reviews[0].State) - assert.EqualValues(t, 0, reviews[0].CodeCommentsCount) + assert.Equal(t, 0, reviews[0].CodeCommentsCount) assert.False(t, reviews[0].Stale) assert.True(t, reviews[0].Official) - assert.EqualValues(t, "test_team", reviews[0].ReviewerTeam.Name) + assert.Equal(t, "test_team", reviews[0].ReviewerTeam.Name) assert.EqualValues(t, 12, reviews[1].ID) assert.EqualValues(t, "REQUEST_REVIEW", reviews[1].State) - assert.EqualValues(t, 0, reviews[0].CodeCommentsCount) + assert.Equal(t, 0, reviews[0].CodeCommentsCount) assert.False(t, reviews[1].Stale) assert.True(t, reviews[1].Official) assert.EqualValues(t, 1, reviews[1].Reviewer.ID) diff --git a/tests/integration/api_pull_test.go b/tests/integration/api_pull_test.go index e35eca38cf..301d351238 100644 --- a/tests/integration/api_pull_test.go +++ b/tests/integration/api_pull_test.go @@ -56,9 +56,9 @@ func TestAPIViewPulls(t *testing.T) { if assert.Len(t, files, 1) { assert.Equal(t, "File-WoW", files[0].Filename) assert.Empty(t, files[0].PreviousFilename) - assert.EqualValues(t, 1, files[0].Additions) - assert.EqualValues(t, 1, files[0].Changes) - assert.EqualValues(t, 0, files[0].Deletions) + assert.Equal(t, 1, files[0].Additions) + assert.Equal(t, 1, files[0].Changes) + assert.Equal(t, 0, files[0].Deletions) assert.Equal(t, "added", files[0].Status) } })) @@ -177,12 +177,12 @@ func TestAPICreatePullWithFieldsSuccess(t *testing.T) { DecodeJSON(t, res, pull) assert.NotNil(t, pull.Milestone) - assert.EqualValues(t, opts.Milestone, pull.Milestone.ID) + assert.Equal(t, opts.Milestone, pull.Milestone.ID) if assert.Len(t, pull.Assignees, 1) { - assert.EqualValues(t, opts.Assignees[0], owner10.Name) + assert.Equal(t, opts.Assignees[0], owner10.Name) } assert.NotNil(t, pull.Labels) - assert.EqualValues(t, opts.Labels[0], pull.Labels[0].ID) + assert.Equal(t, opts.Labels[0], pull.Labels[0].ID) } func TestAPICreatePullWithFieldsFailure(t *testing.T) { @@ -236,7 +236,7 @@ func TestAPIEditPull(t *testing.T) { apiPull := new(api.PullRequest) resp := MakeRequest(t, req, http.StatusCreated) DecodeJSON(t, resp, apiPull) - assert.EqualValues(t, "master", apiPull.Base.Name) + assert.Equal(t, "master", apiPull.Base.Name) newTitle := "edit a this pr" newBody := "edited body" @@ -248,7 +248,7 @@ func TestAPIEditPull(t *testing.T) { }).AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusCreated) DecodeJSON(t, resp, apiPull) - assert.EqualValues(t, "feature/1", apiPull.Base.Name) + assert.Equal(t, "feature/1", apiPull.Base.Name) // check comment history pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: apiPull.ID}) err := pull.LoadIssue(db.DefaultContext) @@ -264,7 +264,7 @@ func TestAPIEditPull(t *testing.T) { apiPullIdempotent := new(api.PullRequest) resp = MakeRequest(t, req, http.StatusCreated) DecodeJSON(t, resp, apiPullIdempotent) - assert.EqualValues(t, apiPull.State, apiPullIdempotent.State) + assert.Equal(t, apiPull.State, apiPullIdempotent.State) req = NewRequestWithJSON(t, http.MethodPatch, urlStr, &api.EditPullRequestOption{ Base: "not-exist", @@ -340,7 +340,7 @@ func TestAPIPullDeleteBranchPerms(t *testing.T) { var bodyResp userResponse DecodeJSON(t, resp, &bodyResp) - assert.EqualValues(t, "insufficient permission to delete head branch", bodyResp.Message) + assert.Equal(t, "insufficient permission to delete head branch", bodyResp.Message) // Check that the branch still exist. req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/branches/base-pr").AddTokenAuth(token) diff --git a/tests/integration/api_push_mirror_test.go b/tests/integration/api_push_mirror_test.go index 282c3aaba0..6bc5bddc28 100644 --- a/tests/integration/api_push_mirror_test.go +++ b/tests/integration/api_push_mirror_test.go @@ -123,7 +123,7 @@ func testAPIPushMirror(t *testing.T, u *url.URL) { if testCase.message != "" { err := api.APIError{} DecodeJSON(t, resp, &err) - assert.EqualValues(t, testCase.message, err.Message) + assert.Equal(t, testCase.message, err.Message) } req = NewRequest(t, "GET", urlStr).AddTokenAuth(token) @@ -132,7 +132,7 @@ func testAPIPushMirror(t *testing.T, u *url.URL) { DecodeJSON(t, resp, &pushMirrors) if assert.Len(t, pushMirrors, testCase.mirrorCount) && testCase.mirrorCount > 0 { pushMirror := pushMirrors[0] - assert.EqualValues(t, remoteAddress, pushMirror.RemoteAddress) + assert.Equal(t, remoteAddress, pushMirror.RemoteAddress) repo_model.DeletePushMirrors = deletePushMirrors req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s", urlStr, pushMirror.RemoteName)).AddTokenAuth(token) @@ -182,7 +182,7 @@ func TestAPIPushMirrorSSH(t *testing.T) { var apiError api.APIError DecodeJSON(t, resp, &apiError) - assert.EqualValues(t, "'use_ssh' is mutually exclusive with 'remote_username' and 'remote_passoword'", apiError.Message) + assert.Equal(t, "'use_ssh' is mutually exclusive with 'remote_username' and 'remote_passoword'", apiError.Message) }) t.Run("SSH not available", func(t *testing.T) { @@ -198,7 +198,7 @@ func TestAPIPushMirrorSSH(t *testing.T) { var apiError api.APIError DecodeJSON(t, resp, &apiError) - assert.EqualValues(t, "SSH authentication not available.", apiError.Message) + assert.Equal(t, "SSH authentication not available.", apiError.Message) }) t.Run("Normal", func(t *testing.T) { @@ -228,7 +228,7 @@ func TestAPIPushMirrorSSH(t *testing.T) { var pushMirrors []*api.PushMirror DecodeJSON(t, resp, &pushMirrors) assert.Len(t, pushMirrors, 1) - assert.EqualValues(t, publickey, pushMirrors[0].PublicKey) + assert.Equal(t, publickey, pushMirrors[0].PublicKey) }) t.Run("Add deploy key", func(t *testing.T) { @@ -262,7 +262,7 @@ func TestAPIPushMirrorSSH(t *testing.T) { DecodeJSON(t, resp, &commitList) assert.Len(t, commitList, 1) - assert.EqualValues(t, sha, commitList[0].SHA) + assert.Equal(t, sha, commitList[0].SHA) assert.Eventually(t, func() bool { req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/commits?limit=1", srcRepo.FullName())).AddTokenAuth(token) diff --git a/tests/integration/api_quota_management_test.go b/tests/integration/api_quota_management_test.go index c46c857197..5a30fc638e 100644 --- a/tests/integration/api_quota_management_test.go +++ b/tests/integration/api_quota_management_test.go @@ -122,20 +122,20 @@ func TestAPIQuotaCreateGroupWithRules(t *testing.T) { require.NoError(t, err) assert.NotNil(t, rule) assert.EqualValues(t, -1, rule.Limit) - assert.EqualValues(t, quota_model.LimitSubjects{quota_model.LimitSubjectSizeAll}, rule.Subjects) + assert.Equal(t, quota_model.LimitSubjects{quota_model.LimitSubjectSizeAll}, rule.Subjects) rule, err = quota_model.GetRuleByName(db.DefaultContext, "deny-git-lfs") require.NoError(t, err) assert.NotNil(t, rule) assert.EqualValues(t, 0, rule.Limit) - assert.EqualValues(t, quota_model.LimitSubjects{quota_model.LimitSubjectSizeGitLFS}, rule.Subjects) + assert.Equal(t, quota_model.LimitSubjects{quota_model.LimitSubjectSizeGitLFS}, rule.Subjects) // Verify that the new rule was also created rule, err = quota_model.GetRuleByName(db.DefaultContext, "new-rule") require.NoError(t, err) assert.NotNil(t, rule) assert.EqualValues(t, 0, rule.Limit) - assert.EqualValues(t, quota_model.LimitSubjects{quota_model.LimitSubjectSizeAssetsAll}, rule.Subjects) + assert.Equal(t, quota_model.LimitSubjects{quota_model.LimitSubjectSizeAssetsAll}, rule.Subjects) t.Run("invalid rule spec", func(t *testing.T) { defer tests.PrintCurrentTest(t)() @@ -176,7 +176,7 @@ func TestAPIQuotaEmptyState(t *testing.T) { var q api.QuotaInfo DecodeJSON(t, resp, &q) - assert.EqualValues(t, api.QuotaUsed{}, q.Used) + assert.Equal(t, api.QuotaUsed{}, q.Used) assert.Empty(t, q.Groups) }) @@ -189,7 +189,7 @@ func TestAPIQuotaEmptyState(t *testing.T) { var q api.QuotaInfo DecodeJSON(t, resp, &q) - assert.EqualValues(t, api.QuotaUsed{}, q.Used) + assert.Equal(t, api.QuotaUsed{}, q.Used) assert.Empty(t, q.Groups) t.Run("#/user/quota/artifacts", func(t *testing.T) { @@ -295,7 +295,7 @@ func TestAPIQuotaAdminRoutesRules(t *testing.T) { assert.Equal(t, "deny-all", q.Name) assert.EqualValues(t, 0, q.Limit) - assert.EqualValues(t, []string{"size:all"}, q.Subjects) + assert.Equal(t, []string{"size:all"}, q.Subjects) rule, err := quota_model.GetRuleByName(db.DefaultContext, "deny-all") require.NoError(t, err) diff --git a/tests/integration/api_quota_use_test.go b/tests/integration/api_quota_use_test.go index 2b50cca4ab..1cac2d8d76 100644 --- a/tests/integration/api_quota_use_test.go +++ b/tests/integration/api_quota_use_test.go @@ -377,7 +377,7 @@ func TestAPIQuotaError(t *testing.T) { var msg context.APIQuotaExceeded DecodeJSON(t, resp, &msg) - assert.EqualValues(t, env.Orgs.Limited.ID, msg.UserID) + assert.Equal(t, env.Orgs.Limited.ID, msg.UserID) assert.Equal(t, env.Orgs.Limited.UserName, msg.UserName) }) } diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go index 98fc293fbc..d5f7960ab1 100644 --- a/tests/integration/api_releases_test.go +++ b/tests/integration/api_releases_test.go @@ -99,7 +99,7 @@ func createNewReleaseUsingAPI(t *testing.T, token string, owner *user_model.User Title: newRelease.Title, } unittest.AssertExistsAndLoadBean(t, rel) - assert.EqualValues(t, newRelease.Note, rel.Note) + assert.Equal(t, newRelease.Note, rel.Note) return &newRelease } @@ -157,7 +157,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) { Title: newRelease.Title, } unittest.AssertExistsAndLoadBean(t, rel) - assert.EqualValues(t, rel.Note, newRelease.Note) + assert.Equal(t, rel.Note, newRelease.Note) assert.True(t, newRelease.HideArchiveLinks) } @@ -336,7 +336,7 @@ func TestAPIUploadAssetRelease(t *testing.T) { var attachment *api.Attachment DecodeJSON(t, resp, &attachment) - assert.EqualValues(t, filename, attachment.Name) + assert.Equal(t, filename, attachment.Name) assert.EqualValues(t, 104, attachment.Size) req = NewRequestWithBody(t, http.MethodPost, assetURL+"?name=test-asset", bytes.NewReader(body.Bytes())). @@ -347,7 +347,7 @@ func TestAPIUploadAssetRelease(t *testing.T) { var attachment2 *api.Attachment DecodeJSON(t, resp, &attachment2) - assert.EqualValues(t, "test-asset", attachment2.Name) + assert.Equal(t, "test-asset", attachment2.Name) assert.EqualValues(t, 104, attachment2.Size) }) @@ -365,9 +365,9 @@ func TestAPIUploadAssetRelease(t *testing.T) { var attachment *api.Attachment DecodeJSON(t, resp, &attachment) - assert.EqualValues(t, "stream.bin", attachment.Name) + assert.Equal(t, "stream.bin", attachment.Name) assert.EqualValues(t, 104, attachment.Size) - assert.EqualValues(t, "attachment", attachment.Type) + assert.Equal(t, "attachment", attachment.Type) }) } @@ -424,10 +424,10 @@ func TestAPIExternalAssetRelease(t *testing.T) { var attachment *api.Attachment DecodeJSON(t, resp, &attachment) - assert.EqualValues(t, "test-asset", attachment.Name) + assert.Equal(t, "test-asset", attachment.Name) assert.EqualValues(t, 0, attachment.Size) - assert.EqualValues(t, "https://forgejo.org/", attachment.DownloadURL) - assert.EqualValues(t, "external", attachment.Type) + assert.Equal(t, "https://forgejo.org/", attachment.DownloadURL) + assert.Equal(t, "external", attachment.Type) } func TestAPIDuplicateAssetRelease(t *testing.T) { diff --git a/tests/integration/api_repo_actions_test.go b/tests/integration/api_repo_actions_test.go index 42c1038a0e..43de376421 100644 --- a/tests/integration/api_repo_actions_test.go +++ b/tests/integration/api_repo_actions_test.go @@ -44,7 +44,7 @@ func TestAPISearchActionJobs_RepoRunner(t *testing.T) { DecodeJSON(t, res, &jobs) assert.Len(t, jobs, 1) - assert.EqualValues(t, job.ID, jobs[0].ID) + assert.Equal(t, job.ID, jobs[0].ID) } func TestAPIWorkflowDispatchReturnInfo(t *testing.T) { diff --git a/tests/integration/api_repo_archive_test.go b/tests/integration/api_repo_archive_test.go index 30e62943f0..d4ada17b86 100644 --- a/tests/integration/api_repo_archive_test.go +++ b/tests/integration/api_repo_archive_test.go @@ -34,14 +34,14 @@ func TestAPIDownloadArchive(t *testing.T) { bs, err := io.ReadAll(resp.Body) require.NoError(t, err) assert.Len(t, bs, 320) - assert.EqualValues(t, "application/zip", resp.Header().Get("Content-Type")) + assert.Equal(t, "application/zip", resp.Header().Get("Content-Type")) link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.tar.gz", user2.Name, repo.Name)) resp = MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK) bs, err = io.ReadAll(resp.Body) require.NoError(t, err) assert.Len(t, bs, 266) - assert.EqualValues(t, "application/gzip", resp.Header().Get("Content-Type")) + assert.Equal(t, "application/gzip", resp.Header().Get("Content-Type")) // Must return a link to a commit ID as the "immutable" archive link linkHeaderRe := regexp.MustCompile(`<(?P<url>https?://.*/api/v1/repos/user2/repo1/archive/[a-f0-9]+\.tar\.gz.*)>; rel="immutable"`) @@ -51,14 +51,14 @@ func TestAPIDownloadArchive(t *testing.T) { bs2, err := io.ReadAll(resp.Body) require.NoError(t, err) // The locked URL should give the same bytes as the non-locked one - assert.EqualValues(t, bs, bs2) + assert.Equal(t, bs, bs2) link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.bundle", user2.Name, repo.Name)) resp = MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK) bs, err = io.ReadAll(resp.Body) require.NoError(t, err) assert.Len(t, bs, 382) - assert.EqualValues(t, "application/octet-stream", resp.Header().Get("Content-Type")) + assert.Equal(t, "application/octet-stream", resp.Header().Get("Content-Type")) link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master", user2.Name, repo.Name)) MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusBadRequest) @@ -92,7 +92,7 @@ func TestAPIDownloadArchive2(t *testing.T) { bs2, err := io.ReadAll(resp.Body) require.NoError(t, err) // The locked URL should give the same bytes as the non-locked one - assert.EqualValues(t, bs, bs2) + assert.Equal(t, bs, bs2) link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/bundle/master", user2.Name, repo.Name)) resp = MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK) diff --git a/tests/integration/api_repo_branch_test.go b/tests/integration/api_repo_branch_test.go index caadea7e39..6ffe40ff44 100644 --- a/tests/integration/api_repo_branch_test.go +++ b/tests/integration/api_repo_branch_test.go @@ -43,8 +43,8 @@ func TestAPIRepoBranchesPlain(t *testing.T) { var branches []*api.Branch require.NoError(t, json.Unmarshal(bs, &branches)) assert.Len(t, branches, 2) - assert.EqualValues(t, "test_branch", branches[0].Name) - assert.EqualValues(t, "master", branches[1].Name) + assert.Equal(t, "test_branch", branches[0].Name) + assert.Equal(t, "master", branches[1].Name) link2, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches/test_branch", repo3.Name)) MakeRequest(t, NewRequest(t, "GET", link2.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden) @@ -54,7 +54,7 @@ func TestAPIRepoBranchesPlain(t *testing.T) { require.NoError(t, err) var branch api.Branch require.NoError(t, json.Unmarshal(bs, &branch)) - assert.EqualValues(t, "test_branch", branch.Name) + assert.Equal(t, "test_branch", branch.Name) MakeRequest(t, NewRequest(t, "POST", link.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden) @@ -66,8 +66,8 @@ func TestAPIRepoBranchesPlain(t *testing.T) { require.NoError(t, err) var branch2 api.Branch require.NoError(t, json.Unmarshal(bs, &branch2)) - assert.EqualValues(t, "test_branch2", branch2.Name) - assert.EqualValues(t, branch.Commit.ID, branch2.Commit.ID) + assert.Equal(t, "test_branch2", branch2.Name) + assert.Equal(t, branch.Commit.ID, branch2.Commit.ID) resp = MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK) bs, err = io.ReadAll(resp.Body) @@ -76,9 +76,9 @@ func TestAPIRepoBranchesPlain(t *testing.T) { branches = []*api.Branch{} require.NoError(t, json.Unmarshal(bs, &branches)) assert.Len(t, branches, 3) - assert.EqualValues(t, "test_branch", branches[0].Name) - assert.EqualValues(t, "test_branch2", branches[1].Name) - assert.EqualValues(t, "master", branches[2].Name) + assert.Equal(t, "test_branch", branches[0].Name) + assert.Equal(t, "test_branch2", branches[1].Name) + assert.Equal(t, "master", branches[2].Name) link3, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches/test_branch2", repo3.Name)) MakeRequest(t, NewRequest(t, "DELETE", link3.String()), http.StatusNotFound) @@ -105,8 +105,8 @@ func TestAPIRepoBranchesMirror(t *testing.T) { var branches []*api.Branch require.NoError(t, json.Unmarshal(bs, &branches)) assert.Len(t, branches, 2) - assert.EqualValues(t, "test_branch", branches[0].Name) - assert.EqualValues(t, "master", branches[1].Name) + assert.Equal(t, "test_branch", branches[0].Name) + assert.Equal(t, "master", branches[1].Name) link2, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches/test_branch", repo5.Name)) resp = MakeRequest(t, NewRequest(t, "GET", link2.String()).AddTokenAuth(token), http.StatusOK) @@ -114,7 +114,7 @@ func TestAPIRepoBranchesMirror(t *testing.T) { require.NoError(t, err) var branch api.Branch require.NoError(t, json.Unmarshal(bs, &branch)) - assert.EqualValues(t, "test_branch", branch.Name) + assert.Equal(t, "test_branch", branch.Name) req := NewRequest(t, "POST", link.String()).AddTokenAuth(token) req.Header.Add("Content-Type", "application/json") @@ -122,10 +122,10 @@ func TestAPIRepoBranchesMirror(t *testing.T) { resp = MakeRequest(t, req, http.StatusForbidden) bs, err = io.ReadAll(resp.Body) require.NoError(t, err) - assert.EqualValues(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs)) + assert.Equal(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs)) resp = MakeRequest(t, NewRequest(t, "DELETE", link2.String()).AddTokenAuth(token), http.StatusForbidden) bs, err = io.ReadAll(resp.Body) require.NoError(t, err) - assert.EqualValues(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs)) + assert.Equal(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs)) } diff --git a/tests/integration/api_repo_edit_test.go b/tests/integration/api_repo_edit_test.go index 288c645e37..341cb0961f 100644 --- a/tests/integration/api_repo_edit_test.go +++ b/tests/integration/api_repo_edit_test.go @@ -6,7 +6,6 @@ package integration import ( "fmt" "net/http" - "net/url" "testing" auth_model "forgejo.org/models/auth" @@ -16,6 +15,7 @@ import ( "forgejo.org/models/unittest" user_model "forgejo.org/models/user" api "forgejo.org/modules/structs" + "forgejo.org/tests" "github.com/stretchr/testify/assert" ) @@ -137,232 +137,260 @@ func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption { } func TestAPIRepoEdit(t *testing.T) { - onGiteaRun(t, func(t *testing.T, u *url.URL) { - bFalse, bTrue := false, true + defer tests.PrepareTestEnv(t)() + bFalse, bTrue := false, true - user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo1 & repo16 - org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // owner of the repo3, is an org - user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // owner of neither repos - repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo - repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo - repo15 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15}) // empty repo - repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo1 & repo16 + org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // owner of the repo3, is an org + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // owner of neither repos + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo + repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo + repo15 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15}) // empty repo + repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo - // Get user2's token - session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - // Get user4's token - session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + // Get user2's token + session := loginUser(t, user2.Name) + token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + // Get user4's token + session = loginUser(t, user4.Name) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - // Test editing a repo1 which user2 owns, changing name and many properties - origRepoEditOption := getRepoEditOptionFromRepo(repo1) - repoEditOption := getNewRepoEditOption(origRepoEditOption) - req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo1.Name), &repoEditOption). - AddTokenAuth(token2) - resp := MakeRequest(t, req, http.StatusOK) - var repo api.Repository - DecodeJSON(t, resp, &repo) - assert.NotNil(t, repo) - // check response - assert.Equal(t, *repoEditOption.Name, repo.Name) - assert.Equal(t, *repoEditOption.Description, repo.Description) - assert.Equal(t, *repoEditOption.Website, repo.Website) - assert.Equal(t, *repoEditOption.Archived, repo.Archived) - // check repo1 from database - repo1edited := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - repo1editedOption := getRepoEditOptionFromRepo(repo1edited) - assert.Equal(t, *repoEditOption.Name, *repo1editedOption.Name) - assert.Equal(t, *repoEditOption.Description, *repo1editedOption.Description) - assert.Equal(t, *repoEditOption.Website, *repo1editedOption.Website) - assert.Equal(t, *repoEditOption.Archived, *repo1editedOption.Archived) - assert.Equal(t, *repoEditOption.Private, *repo1editedOption.Private) - assert.Equal(t, *repoEditOption.HasWiki, *repo1editedOption.HasWiki) + // Test editing a repo1 which user2 owns, changing name and many properties + origRepoEditOption := getRepoEditOptionFromRepo(repo1) + repoEditOption := getNewRepoEditOption(origRepoEditOption) + req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo1.Name), &repoEditOption). + AddTokenAuth(token2) + resp := MakeRequest(t, req, http.StatusOK) + var repo api.Repository + DecodeJSON(t, resp, &repo) + assert.NotNil(t, repo) + // check response + assert.Equal(t, *repoEditOption.Name, repo.Name) + assert.Equal(t, *repoEditOption.Description, repo.Description) + assert.Equal(t, *repoEditOption.Website, repo.Website) + assert.Equal(t, *repoEditOption.Archived, repo.Archived) + // check repo1 from database + repo1edited := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + repo1editedOption := getRepoEditOptionFromRepo(repo1edited) + assert.Equal(t, *repoEditOption.Name, *repo1editedOption.Name) + assert.Equal(t, *repoEditOption.Description, *repo1editedOption.Description) + assert.Equal(t, *repoEditOption.Website, *repo1editedOption.Website) + assert.Equal(t, *repoEditOption.Archived, *repo1editedOption.Archived) + assert.Equal(t, *repoEditOption.Private, *repo1editedOption.Private) + assert.Equal(t, *repoEditOption.HasWiki, *repo1editedOption.HasWiki) - // Test editing repo1 to use internal issue and wiki (default) - *repoEditOption.HasIssues = true - repoEditOption.ExternalTracker = nil - repoEditOption.InternalTracker = &api.InternalTracker{ - EnableTimeTracker: false, - AllowOnlyContributorsToTrackTime: false, - EnableIssueDependencies: false, - } - *repoEditOption.HasWiki = true - repoEditOption.ExternalWiki = nil - url := fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name) - req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). - AddTokenAuth(token2) - resp = MakeRequest(t, req, http.StatusOK) - DecodeJSON(t, resp, &repo) - assert.NotNil(t, repo) - // check repo1 was written to database - repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - repo1editedOption = getRepoEditOptionFromRepo(repo1edited) - assert.True(t, *repo1editedOption.HasIssues) - assert.Nil(t, repo1editedOption.ExternalTracker) - assert.Equal(t, *repo1editedOption.InternalTracker, *repoEditOption.InternalTracker) - assert.True(t, *repo1editedOption.HasWiki) - assert.Nil(t, repo1editedOption.ExternalWiki) + // Test editing repo1 to use internal issue and wiki (default) + *repoEditOption.HasIssues = true + repoEditOption.ExternalTracker = nil + repoEditOption.InternalTracker = &api.InternalTracker{ + EnableTimeTracker: false, + AllowOnlyContributorsToTrackTime: false, + EnableIssueDependencies: false, + } + *repoEditOption.HasWiki = true + repoEditOption.ExternalWiki = nil + url := fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name) + req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). + AddTokenAuth(token2) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &repo) + assert.NotNil(t, repo) + // check repo1 was written to database + repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + repo1editedOption = getRepoEditOptionFromRepo(repo1edited) + assert.True(t, *repo1editedOption.HasIssues) + assert.Nil(t, repo1editedOption.ExternalTracker) + assert.Equal(t, *repo1editedOption.InternalTracker, *repoEditOption.InternalTracker) + assert.True(t, *repo1editedOption.HasWiki) + assert.Nil(t, repo1editedOption.ExternalWiki) - // Test editing repo1 to use external issue and wiki - repoEditOption.ExternalTracker = &api.ExternalTracker{ - ExternalTrackerURL: "http://www.somewebsite.com", - ExternalTrackerFormat: "http://www.somewebsite.com/{user}/{repo}?issue={index}", - ExternalTrackerStyle: "alphanumeric", - } - repoEditOption.ExternalWiki = &api.ExternalWiki{ - ExternalWikiURL: "http://www.somewebsite.com", - } - req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). - AddTokenAuth(token2) - resp = MakeRequest(t, req, http.StatusOK) - DecodeJSON(t, resp, &repo) - assert.NotNil(t, repo) - // check repo1 was written to database - repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - repo1editedOption = getRepoEditOptionFromRepo(repo1edited) - assert.True(t, *repo1editedOption.HasIssues) - assert.Equal(t, *repo1editedOption.ExternalTracker, *repoEditOption.ExternalTracker) - assert.True(t, *repo1editedOption.HasWiki) - assert.Equal(t, *repo1editedOption.ExternalWiki, *repoEditOption.ExternalWiki) + // Test editing repo1 to use external issue and wiki + repoEditOption.ExternalTracker = &api.ExternalTracker{ + ExternalTrackerURL: "http://www.somewebsite.com", + ExternalTrackerFormat: "http://www.somewebsite.com/{user}/{repo}?issue={index}", + ExternalTrackerStyle: "alphanumeric", + } + repoEditOption.ExternalWiki = &api.ExternalWiki{ + ExternalWikiURL: "http://www.somewebsite.com", + } + req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). + AddTokenAuth(token2) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &repo) + assert.NotNil(t, repo) + // check repo1 was written to database + repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + repo1editedOption = getRepoEditOptionFromRepo(repo1edited) + assert.True(t, *repo1editedOption.HasIssues) + assert.Equal(t, *repo1editedOption.ExternalTracker, *repoEditOption.ExternalTracker) + assert.True(t, *repo1editedOption.HasWiki) + assert.Equal(t, *repo1editedOption.ExternalWiki, *repoEditOption.ExternalWiki) - repoEditOption.ExternalTracker.ExternalTrackerStyle = "regexp" - repoEditOption.ExternalTracker.ExternalTrackerRegexpPattern = `(\d+)` - req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). - AddTokenAuth(token2) - resp = MakeRequest(t, req, http.StatusOK) - DecodeJSON(t, resp, &repo) - assert.NotNil(t, repo) - repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - repo1editedOption = getRepoEditOptionFromRepo(repo1edited) - assert.True(t, *repo1editedOption.HasIssues) - assert.Equal(t, *repo1editedOption.ExternalTracker, *repoEditOption.ExternalTracker) + repoEditOption.ExternalTracker.ExternalTrackerStyle = "regexp" + repoEditOption.ExternalTracker.ExternalTrackerRegexpPattern = `(\d+)` + req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). + AddTokenAuth(token2) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &repo) + assert.NotNil(t, repo) + repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + repo1editedOption = getRepoEditOptionFromRepo(repo1edited) + assert.True(t, *repo1editedOption.HasIssues) + assert.Equal(t, *repo1editedOption.ExternalTracker, *repoEditOption.ExternalTracker) - // Do some tests with invalid URL for external tracker and wiki - repoEditOption.ExternalTracker.ExternalTrackerURL = "htp://www.somewebsite.com" - req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). - AddTokenAuth(token2) - MakeRequest(t, req, http.StatusUnprocessableEntity) - repoEditOption.ExternalTracker.ExternalTrackerURL = "http://www.somewebsite.com" - repoEditOption.ExternalTracker.ExternalTrackerFormat = "http://www.somewebsite.com/{user/{repo}?issue={index}" - req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). - AddTokenAuth(token2) - MakeRequest(t, req, http.StatusUnprocessableEntity) - repoEditOption.ExternalTracker.ExternalTrackerFormat = "http://www.somewebsite.com/{user}/{repo}?issue={index}" - repoEditOption.ExternalWiki.ExternalWikiURL = "htp://www.somewebsite.com" - req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). - AddTokenAuth(token2) - MakeRequest(t, req, http.StatusUnprocessableEntity) + // Do some tests with invalid URL for external tracker and wiki + repoEditOption.ExternalTracker.ExternalTrackerURL = "htp://www.somewebsite.com" + req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). + AddTokenAuth(token2) + MakeRequest(t, req, http.StatusUnprocessableEntity) + repoEditOption.ExternalTracker.ExternalTrackerURL = "http://www.somewebsite.com" + repoEditOption.ExternalTracker.ExternalTrackerFormat = "http://www.somewebsite.com/{user/{repo}?issue={index}" + req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). + AddTokenAuth(token2) + MakeRequest(t, req, http.StatusUnprocessableEntity) + repoEditOption.ExternalTracker.ExternalTrackerFormat = "http://www.somewebsite.com/{user}/{repo}?issue={index}" + repoEditOption.ExternalWiki.ExternalWikiURL = "htp://www.somewebsite.com" + req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). + AddTokenAuth(token2) + MakeRequest(t, req, http.StatusUnprocessableEntity) - // Test small repo change through API with issue and wiki option not set; They shall not be touched. - *repoEditOption.Description = "small change" - repoEditOption.HasIssues = nil - repoEditOption.ExternalTracker = nil - repoEditOption.HasWiki = nil - repoEditOption.ExternalWiki = nil - req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). - AddTokenAuth(token2) - resp = MakeRequest(t, req, http.StatusOK) - DecodeJSON(t, resp, &repo) - assert.NotNil(t, repo) - // check repo1 was written to database - repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - repo1editedOption = getRepoEditOptionFromRepo(repo1edited) - assert.Equal(t, *repo1editedOption.Description, *repoEditOption.Description) - assert.True(t, *repo1editedOption.HasIssues) - assert.NotNil(t, *repo1editedOption.ExternalTracker) - assert.True(t, *repo1editedOption.HasWiki) - assert.NotNil(t, *repo1editedOption.ExternalWiki) + // Test small repo change through API with issue and wiki option not set; They shall not be touched. + *repoEditOption.Description = "small change" + repoEditOption.HasIssues = nil + repoEditOption.ExternalTracker = nil + repoEditOption.HasWiki = nil + repoEditOption.ExternalWiki = nil + req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). + AddTokenAuth(token2) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &repo) + assert.NotNil(t, repo) + // check repo1 was written to database + repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + repo1editedOption = getRepoEditOptionFromRepo(repo1edited) + assert.Equal(t, *repo1editedOption.Description, *repoEditOption.Description) + assert.True(t, *repo1editedOption.HasIssues) + assert.NotNil(t, *repo1editedOption.ExternalTracker) + assert.True(t, *repo1editedOption.HasWiki) + assert.NotNil(t, *repo1editedOption.ExternalWiki) - // reset repo in db - req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name), &origRepoEditOption). - AddTokenAuth(token2) - _ = MakeRequest(t, req, http.StatusOK) + // reset repo in db + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name), &origRepoEditOption). + AddTokenAuth(token2) + _ = MakeRequest(t, req, http.StatusOK) - // Test editing a non-existing repo - name := "repodoesnotexist" - req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, name), &api.EditRepoOption{Name: &name}). - AddTokenAuth(token2) - _ = MakeRequest(t, req, http.StatusNotFound) + // Test editing a non-existing repo + name := "repodoesnotexist" + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, name), &api.EditRepoOption{Name: &name}). + AddTokenAuth(token2) + _ = MakeRequest(t, req, http.StatusNotFound) - // Test editing repo16 by user4 who does not have write access - origRepoEditOption = getRepoEditOptionFromRepo(repo16) - repoEditOption = getNewRepoEditOption(origRepoEditOption) - req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption). - AddTokenAuth(token4) - MakeRequest(t, req, http.StatusNotFound) + // Test editing repo16 by user4 who does not have write access + origRepoEditOption = getRepoEditOptionFromRepo(repo16) + repoEditOption = getNewRepoEditOption(origRepoEditOption) + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption). + AddTokenAuth(token4) + MakeRequest(t, req, http.StatusNotFound) - // Tests a repo with no token given so will fail - origRepoEditOption = getRepoEditOptionFromRepo(repo16) - repoEditOption = getNewRepoEditOption(origRepoEditOption) - req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption) - _ = MakeRequest(t, req, http.StatusNotFound) + // Tests a repo with no token given so will fail + origRepoEditOption = getRepoEditOptionFromRepo(repo16) + repoEditOption = getNewRepoEditOption(origRepoEditOption) + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption) + _ = MakeRequest(t, req, http.StatusNotFound) - // Test using access token for a private repo that the user of the token owns - origRepoEditOption = getRepoEditOptionFromRepo(repo16) - repoEditOption = getNewRepoEditOption(origRepoEditOption) - req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption). - AddTokenAuth(token2) - _ = MakeRequest(t, req, http.StatusOK) - // reset repo in db - req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name), &origRepoEditOption). - AddTokenAuth(token2) - _ = MakeRequest(t, req, http.StatusOK) + // Test using access token for a private repo that the user of the token owns + origRepoEditOption = getRepoEditOptionFromRepo(repo16) + repoEditOption = getNewRepoEditOption(origRepoEditOption) + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption). + AddTokenAuth(token2) + _ = MakeRequest(t, req, http.StatusOK) + // reset repo in db + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name), &origRepoEditOption). + AddTokenAuth(token2) + _ = MakeRequest(t, req, http.StatusOK) - // Test making a repo public that is private - repo16 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) - assert.True(t, repo16.IsPrivate) - repoEditOption = &api.EditRepoOption{ - Private: &bFalse, - } - url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name) - req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). - AddTokenAuth(token2) - _ = MakeRequest(t, req, http.StatusOK) - repo16 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) - assert.False(t, repo16.IsPrivate) - // Make it private again - repoEditOption.Private = &bTrue - req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). - AddTokenAuth(token2) - _ = MakeRequest(t, req, http.StatusOK) + // Test making a repo public that is private + repo16 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) + assert.True(t, repo16.IsPrivate) + repoEditOption = &api.EditRepoOption{ + Private: &bFalse, + } + url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name) + req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). + AddTokenAuth(token2) + _ = MakeRequest(t, req, http.StatusOK) + repo16 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) + assert.False(t, repo16.IsPrivate) + // Make it private again + repoEditOption.Private = &bTrue + req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). + AddTokenAuth(token2) + _ = MakeRequest(t, req, http.StatusOK) - // Test to change empty repo - assert.False(t, repo15.IsArchived) - url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo15.Name) - req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{ - Archived: &bTrue, - }).AddTokenAuth(token2) - _ = MakeRequest(t, req, http.StatusOK) - repo15 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15}) - assert.True(t, repo15.IsArchived) - req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{ - Archived: &bFalse, - }).AddTokenAuth(token2) - _ = MakeRequest(t, req, http.StatusOK) + // Test to change empty repo + assert.False(t, repo15.IsArchived) + url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo15.Name) + req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{ + Archived: &bTrue, + }).AddTokenAuth(token2) + _ = MakeRequest(t, req, http.StatusOK) + repo15 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15}) + assert.True(t, repo15.IsArchived) + req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{ + Archived: &bFalse, + }).AddTokenAuth(token2) + _ = MakeRequest(t, req, http.StatusOK) - // Test using org repo "org3/repo3" where user2 is a collaborator - origRepoEditOption = getRepoEditOptionFromRepo(repo3) - repoEditOption = getNewRepoEditOption(origRepoEditOption) - req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, repo3.Name), &repoEditOption). - AddTokenAuth(token2) - MakeRequest(t, req, http.StatusOK) - // reset repo in db - req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, *repoEditOption.Name), &origRepoEditOption). - AddTokenAuth(token2) - _ = MakeRequest(t, req, http.StatusOK) + // Test using org repo "org3/repo3" where user2 is a collaborator + origRepoEditOption = getRepoEditOptionFromRepo(repo3) + repoEditOption = getNewRepoEditOption(origRepoEditOption) + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, repo3.Name), &repoEditOption). + AddTokenAuth(token2) + MakeRequest(t, req, http.StatusOK) + // reset repo in db + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, *repoEditOption.Name), &origRepoEditOption). + AddTokenAuth(token2) + _ = MakeRequest(t, req, http.StatusOK) - // Test using org repo "org3/repo3" with no user token - origRepoEditOption = getRepoEditOptionFromRepo(repo3) - repoEditOption = getNewRepoEditOption(origRepoEditOption) - req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, repo3.Name), &repoEditOption) - MakeRequest(t, req, http.StatusNotFound) + // Test using org repo "org3/repo3" with no user token + origRepoEditOption = getRepoEditOptionFromRepo(repo3) + repoEditOption = getNewRepoEditOption(origRepoEditOption) + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, repo3.Name), &repoEditOption) + MakeRequest(t, req, http.StatusNotFound) - // Test using repo "user2/repo1" where user4 is a NOT collaborator - origRepoEditOption = getRepoEditOptionFromRepo(repo1) - repoEditOption = getNewRepoEditOption(origRepoEditOption) - req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo1.Name), &repoEditOption). - AddTokenAuth(token4) - MakeRequest(t, req, http.StatusForbidden) + // Test using repo "user2/repo1" where user4 is a NOT collaborator + origRepoEditOption = getRepoEditOptionFromRepo(repo1) + repoEditOption = getNewRepoEditOption(origRepoEditOption) + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo1.Name), &repoEditOption). + AddTokenAuth(token4) + MakeRequest(t, req, http.StatusForbidden) + + t.Run("Default merge style", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + updateStyle := "invalid" + MakeRequest(t, NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{ + DefaultUpdateStyle: &updateStyle, + }).AddTokenAuth(token2), http.StatusUnprocessableEntity) + + MakeRequest(t, NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{ + DefaultMergeStyle: &updateStyle, + }).AddTokenAuth(token2), http.StatusUnprocessableEntity) + + var apiRepo api.Repository + DecodeJSON(t, MakeRequest(t, NewRequest(t, "GET", url).AddTokenAuth(token2), http.StatusOK), &apiRepo) + assert.Equal(t, "merge", apiRepo.DefaultMergeStyle) + assert.Equal(t, "merge", apiRepo.DefaultUpdateStyle) + + updateStyle = "rebase" + MakeRequest(t, NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{ + DefaultMergeStyle: &updateStyle, + DefaultUpdateStyle: &updateStyle, + HasPullRequests: &bTrue, + }).AddTokenAuth(token2), http.StatusOK) + + DecodeJSON(t, MakeRequest(t, NewRequest(t, "GET", url).AddTokenAuth(token2), http.StatusOK), &apiRepo) + assert.Equal(t, "rebase", apiRepo.DefaultMergeStyle) + assert.Equal(t, "rebase", apiRepo.DefaultUpdateStyle) }) } diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index cff891f6d8..2cc05c42bb 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -177,15 +177,15 @@ func TestAPICreateFile(t *testing.T) { expectedFileResponse := getExpectedFileResponseForCreate("user2/repo1", commitID, treePath, latestCommit.ID.String()) var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) - assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Date, fileResponse.Commit.Author.Date) - assert.EqualValues(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name) - assert.EqualValues(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date) + assert.Equal(t, expectedFileResponse.Content, fileResponse.Content) + assert.Equal(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) + assert.Equal(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) + assert.Equal(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) + assert.Equal(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) + assert.Equal(t, expectedFileResponse.Commit.Author.Date, fileResponse.Commit.Author.Date) + assert.Equal(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email) + assert.Equal(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name) + assert.Equal(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date) gitRepo.Close() } @@ -203,10 +203,10 @@ func TestAPICreateFile(t *testing.T) { expectedSHA := "a635aa942442ddfdba07468cf9661c08fbdf0ebf" expectedHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/new_branch/new/file%d.txt", fileID) expectedDownloadURL := fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/new_branch/new/file%d.txt", fileID) - assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) - assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) - assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL) - assert.EqualValues(t, createFileOptions.Message+"\n", fileResponse.Commit.Message) + assert.Equal(t, expectedSHA, fileResponse.Content.SHA) + assert.Equal(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) + assert.Equal(t, expectedDownloadURL, *fileResponse.Content.DownloadURL) + assert.Equal(t, createFileOptions.Message+"\n", fileResponse.Commit.Message) // Test creating a file without a message createFileOptions = getCreateFileOptions() @@ -218,7 +218,7 @@ func TestAPICreateFile(t *testing.T) { resp = MakeRequest(t, req, http.StatusCreated) DecodeJSON(t, resp, &fileResponse) expectedMessage := "Add " + treePath + "\n" - assert.EqualValues(t, expectedMessage, fileResponse.Commit.Message) + assert.Equal(t, expectedMessage, fileResponse.Commit.Message) // Test trying to create a file that already exists, should fail createFileOptions = getCreateFileOptions() @@ -296,15 +296,15 @@ func TestAPICreateFile(t *testing.T) { latestCommit, _ := gitRepo.GetCommitByPath(treePath) expectedFileResponse := getExpectedFileResponseForCreate("user2/"+reponame, commitID, treePath, latestCommit.ID.String()) DecodeJSON(t, resp, &fileResponse) - assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Date, fileResponse.Commit.Author.Date) - assert.EqualValues(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name) - assert.EqualValues(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date) + assert.Equal(t, expectedFileResponse.Content, fileResponse.Content) + assert.Equal(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) + assert.Equal(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) + assert.Equal(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) + assert.Equal(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) + assert.Equal(t, expectedFileResponse.Commit.Author.Date, fileResponse.Commit.Author.Date) + assert.Equal(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email) + assert.Equal(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name) + assert.Equal(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date) gitRepo.Close() }) }) diff --git a/tests/integration/api_repo_file_delete_test.go b/tests/integration/api_repo_file_delete_test.go index b76edf45ca..428ef37e34 100644 --- a/tests/integration/api_repo_file_delete_test.go +++ b/tests/integration/api_repo_file_delete_test.go @@ -87,7 +87,7 @@ func TestAPIDeleteFile(t *testing.T) { DecodeJSON(t, resp, &fileResponse) assert.NotNil(t, fileResponse) assert.Nil(t, fileResponse.Content) - assert.EqualValues(t, deleteFileOptions.Message+"\n", fileResponse.Commit.Message) + assert.Equal(t, deleteFileOptions.Message+"\n", fileResponse.Commit.Message) // Test deleting file without a message fileID++ @@ -100,7 +100,7 @@ func TestAPIDeleteFile(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &fileResponse) expectedMessage := "Delete " + treePath + "\n" - assert.EqualValues(t, expectedMessage, fileResponse.Commit.Message) + assert.Equal(t, expectedMessage, fileResponse.Commit.Message) // Test deleting a file with the wrong SHA fileID++ diff --git a/tests/integration/api_repo_file_update_test.go b/tests/integration/api_repo_file_update_test.go index 4ed0df2b08..b14dfbc565 100644 --- a/tests/integration/api_repo_file_update_test.go +++ b/tests/integration/api_repo_file_update_test.go @@ -140,11 +140,11 @@ func TestAPIUpdateFile(t *testing.T) { expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath, lasCommit.ID.String()) var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) - assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) + assert.Equal(t, expectedFileResponse.Content, fileResponse.Content) + assert.Equal(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) + assert.Equal(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) + assert.Equal(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) + assert.Equal(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) gitRepo.Close() } @@ -163,10 +163,10 @@ func TestAPIUpdateFile(t *testing.T) { expectedSHA := "08bd14b2e2852529157324de9c226b3364e76136" expectedHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/new_branch/update/file%d.txt", fileID) expectedDownloadURL := fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID) - assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) - assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) - assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL) - assert.EqualValues(t, updateFileOptions.Message+"\n", fileResponse.Commit.Message) + assert.Equal(t, expectedSHA, fileResponse.Content.SHA) + assert.Equal(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) + assert.Equal(t, expectedDownloadURL, *fileResponse.Content.DownloadURL) + assert.Equal(t, updateFileOptions.Message+"\n", fileResponse.Commit.Message) // Test updating a file and renaming it updateFileOptions = getUpdateFileOptions() @@ -183,9 +183,9 @@ func TestAPIUpdateFile(t *testing.T) { expectedSHA = "08bd14b2e2852529157324de9c226b3364e76136" expectedHTMLURL = fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/master/rename/update/file%d.txt", fileID) expectedDownloadURL = fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID) - assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA) - assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) - assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL) + assert.Equal(t, expectedSHA, fileResponse.Content.SHA) + assert.Equal(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) + assert.Equal(t, expectedDownloadURL, *fileResponse.Content.DownloadURL) // Test updating a file without a message updateFileOptions = getUpdateFileOptions() @@ -199,7 +199,7 @@ func TestAPIUpdateFile(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &fileResponse) expectedMessage := "Update " + treePath + "\n" - assert.EqualValues(t, expectedMessage, fileResponse.Commit.Message) + assert.Equal(t, expectedMessage, fileResponse.Commit.Message) // Test updating a file with the wrong SHA fileID++ diff --git a/tests/integration/api_repo_files_change_test.go b/tests/integration/api_repo_files_change_test.go index d188751dab..1272c3d8bf 100644 --- a/tests/integration/api_repo_files_change_test.go +++ b/tests/integration/api_repo_files_change_test.go @@ -105,18 +105,18 @@ func TestAPIChangeFiles(t *testing.T) { DecodeJSON(t, resp, &filesResponse) // check create file - assert.EqualValues(t, expectedCreateFileResponse.Content, filesResponse.Files[0]) + assert.Equal(t, expectedCreateFileResponse.Content, filesResponse.Files[0]) // check update file - assert.EqualValues(t, expectedUpdateFileResponse.Content, filesResponse.Files[1]) + assert.Equal(t, expectedUpdateFileResponse.Content, filesResponse.Files[1]) // test commit info - assert.EqualValues(t, expectedCreateFileResponse.Commit.SHA, filesResponse.Commit.SHA) - assert.EqualValues(t, expectedCreateFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL) - assert.EqualValues(t, expectedCreateFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email) - assert.EqualValues(t, expectedCreateFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name) - assert.EqualValues(t, expectedCreateFileResponse.Commit.Committer.Email, filesResponse.Commit.Committer.Email) - assert.EqualValues(t, expectedCreateFileResponse.Commit.Committer.Name, filesResponse.Commit.Committer.Name) + assert.Equal(t, expectedCreateFileResponse.Commit.SHA, filesResponse.Commit.SHA) + assert.Equal(t, expectedCreateFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL) + assert.Equal(t, expectedCreateFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email) + assert.Equal(t, expectedCreateFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name) + assert.Equal(t, expectedCreateFileResponse.Commit.Committer.Email, filesResponse.Commit.Committer.Email) + assert.Equal(t, expectedCreateFileResponse.Commit.Committer.Name, filesResponse.Commit.Committer.Name) // test delete file assert.Nil(t, filesResponse.Files[2]) @@ -149,15 +149,15 @@ func TestAPIChangeFiles(t *testing.T) { expectedUpdateSHA := "08bd14b2e2852529157324de9c226b3364e76136" expectedUpdateHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/new_branch/update/file%d.txt", fileID) expectedUpdateDownloadURL := fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID) - assert.EqualValues(t, expectedCreateSHA, filesResponse.Files[0].SHA) - assert.EqualValues(t, expectedCreateHTMLURL, *filesResponse.Files[0].HTMLURL) - assert.EqualValues(t, expectedCreateDownloadURL, *filesResponse.Files[0].DownloadURL) - assert.EqualValues(t, expectedUpdateSHA, filesResponse.Files[1].SHA) - assert.EqualValues(t, expectedUpdateHTMLURL, *filesResponse.Files[1].HTMLURL) - assert.EqualValues(t, expectedUpdateDownloadURL, *filesResponse.Files[1].DownloadURL) + assert.Equal(t, expectedCreateSHA, filesResponse.Files[0].SHA) + assert.Equal(t, expectedCreateHTMLURL, *filesResponse.Files[0].HTMLURL) + assert.Equal(t, expectedCreateDownloadURL, *filesResponse.Files[0].DownloadURL) + assert.Equal(t, expectedUpdateSHA, filesResponse.Files[1].SHA) + assert.Equal(t, expectedUpdateHTMLURL, *filesResponse.Files[1].HTMLURL) + assert.Equal(t, expectedUpdateDownloadURL, *filesResponse.Files[1].DownloadURL) assert.Nil(t, filesResponse.Files[2]) - assert.EqualValues(t, changeFilesOptions.Message+"\n", filesResponse.Commit.Message) + assert.Equal(t, changeFilesOptions.Message+"\n", filesResponse.Commit.Message) // Test updating a file and renaming it changeFilesOptions = getChangeFilesOptions() @@ -175,9 +175,9 @@ func TestAPIChangeFiles(t *testing.T) { expectedUpdateSHA = "08bd14b2e2852529157324de9c226b3364e76136" expectedUpdateHTMLURL = fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/master/rename/update/file%d.txt", fileID) expectedUpdateDownloadURL = fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID) - assert.EqualValues(t, expectedUpdateSHA, filesResponse.Files[0].SHA) - assert.EqualValues(t, expectedUpdateHTMLURL, *filesResponse.Files[0].HTMLURL) - assert.EqualValues(t, expectedUpdateDownloadURL, *filesResponse.Files[0].DownloadURL) + assert.Equal(t, expectedUpdateSHA, filesResponse.Files[0].SHA) + assert.Equal(t, expectedUpdateHTMLURL, *filesResponse.Files[0].HTMLURL) + assert.Equal(t, expectedUpdateDownloadURL, *filesResponse.Files[0].DownloadURL) // Test updating a file without a message changeFilesOptions = getChangeFilesOptions() @@ -197,7 +197,7 @@ func TestAPIChangeFiles(t *testing.T) { resp = MakeRequest(t, req, http.StatusCreated) DecodeJSON(t, resp, &filesResponse) expectedMessage := fmt.Sprintf("Add %v\nUpdate %v\nDelete %v\n", createTreePath, updateTreePath, deleteTreePath) - assert.EqualValues(t, expectedMessage, filesResponse.Commit.Message) + assert.Equal(t, expectedMessage, filesResponse.Commit.Message) // Test updating a file with the wrong SHA fileID++ diff --git a/tests/integration/api_repo_get_contents_list_test.go b/tests/integration/api_repo_get_contents_list_test.go index 543f10f6d2..2073c2cb98 100644 --- a/tests/integration/api_repo_get_contents_list_test.go +++ b/tests/integration/api_repo_get_contents_list_test.go @@ -100,7 +100,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { lastCommit, err := gitRepo.GetCommitByPath("README.md") require.NoError(t, err) expectedContentsListResponse := getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) - assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) + assert.Equal(t, expectedContentsListResponse, contentsListResponse) // No ref refType = "branch" @@ -110,7 +110,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { assert.NotNil(t, contentsListResponse) expectedContentsListResponse = getExpectedContentsListResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String()) - assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) + assert.Equal(t, expectedContentsListResponse, contentsListResponse) // ref is the branch we created above in setup ref = newBranch @@ -124,7 +124,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { lastCommit, err = branchCommit.GetCommitByPath("README.md") require.NoError(t, err) expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) - assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) + assert.Equal(t, expectedContentsListResponse, contentsListResponse) // ref is the new tag we created above in setup ref = newTag @@ -138,7 +138,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { lastCommit, err = tagCommit.GetCommitByPath("README.md") require.NoError(t, err) expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) - assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) + assert.Equal(t, expectedContentsListResponse, contentsListResponse) // ref is a commit ref = commitID @@ -148,7 +148,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, commitID) - assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) + assert.Equal(t, expectedContentsListResponse, contentsListResponse) // Test file contents a file with a bad ref ref = "badref" diff --git a/tests/integration/api_repo_get_contents_test.go b/tests/integration/api_repo_get_contents_test.go index 9cf266b3dd..4053828082 100644 --- a/tests/integration/api_repo_get_contents_test.go +++ b/tests/integration/api_repo_get_contents_test.go @@ -102,7 +102,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { assert.NotNil(t, contentsResponse) lastCommit, _ := gitRepo.GetCommitByPath("README.md") expectedContentsResponse := getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) - assert.EqualValues(t, *expectedContentsResponse, contentsResponse) + assert.Equal(t, *expectedContentsResponse, contentsResponse) // No ref refType = "branch" @@ -111,7 +111,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String()) - assert.EqualValues(t, *expectedContentsResponse, contentsResponse) + assert.Equal(t, *expectedContentsResponse, contentsResponse) // ref is the branch we created above in setup ref = newBranch @@ -123,7 +123,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { branchCommit, _ := gitRepo.GetBranchCommit(ref) lastCommit, _ = branchCommit.GetCommitByPath("README.md") expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) - assert.EqualValues(t, *expectedContentsResponse, contentsResponse) + assert.Equal(t, *expectedContentsResponse, contentsResponse) // ref is the new tag we created above in setup ref = newTag @@ -135,7 +135,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { tagCommit, _ := gitRepo.GetTagCommit(ref) lastCommit, _ = tagCommit.GetCommitByPath("README.md") expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) - assert.EqualValues(t, *expectedContentsResponse, contentsResponse) + assert.Equal(t, *expectedContentsResponse, contentsResponse) // ref is a commit ref = commitID @@ -145,7 +145,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, commitID) - assert.EqualValues(t, *expectedContentsResponse, contentsResponse) + assert.Equal(t, *expectedContentsResponse, contentsResponse) // Test file contents a file with a bad ref ref = "badref" @@ -181,16 +181,16 @@ func TestAPIGetContentsRefFormats(t *testing.T) { resp := MakeRequest(t, NewRequest(t, http.MethodGet, noRef), http.StatusOK) raw, err := io.ReadAll(resp.Body) require.NoError(t, err) - assert.EqualValues(t, content, string(raw)) + assert.Equal(t, content, string(raw)) resp = MakeRequest(t, NewRequest(t, http.MethodGet, refInPath), http.StatusOK) raw, err = io.ReadAll(resp.Body) require.NoError(t, err) - assert.EqualValues(t, content, string(raw)) + assert.Equal(t, content, string(raw)) resp = MakeRequest(t, NewRequest(t, http.MethodGet, refInQuery), http.StatusOK) raw, err = io.ReadAll(resp.Body) require.NoError(t, err) - assert.EqualValues(t, content, string(raw)) + assert.Equal(t, content, string(raw)) }) } diff --git a/tests/integration/api_repo_git_commits_test.go b/tests/integration/api_repo_git_commits_test.go index e80c0024b8..7a93029d4c 100644 --- a/tests/integration/api_repo_git_commits_test.go +++ b/tests/integration/api_repo_git_commits_test.go @@ -72,12 +72,12 @@ func TestAPIReposGitCommitList(t *testing.T) { DecodeJSON(t, resp, &apiData) assert.Len(t, apiData, 2) - assert.EqualValues(t, "cfe3b3c1fd36fba04f9183287b106497e1afe986", apiData[0].CommitMeta.SHA) + assert.Equal(t, "cfe3b3c1fd36fba04f9183287b106497e1afe986", apiData[0].CommitMeta.SHA) compareCommitFiles(t, []string{"link_hi", "test.csv"}, apiData[0].Files) - assert.EqualValues(t, "c8e31bc7688741a5287fcde4fbb8fc129ca07027", apiData[1].CommitMeta.SHA) + assert.Equal(t, "c8e31bc7688741a5287fcde4fbb8fc129ca07027", apiData[1].CommitMeta.SHA) compareCommitFiles(t, []string{"test.csv"}, apiData[1].Files) - assert.EqualValues(t, "2", resp.Header().Get("X-Total")) + assert.Equal(t, "2", resp.Header().Get("X-Total")) } func TestAPIReposGitCommitListNotMaster(t *testing.T) { @@ -96,14 +96,14 @@ func TestAPIReposGitCommitListNotMaster(t *testing.T) { DecodeJSON(t, resp, &apiData) assert.Len(t, apiData, 3) - assert.EqualValues(t, "69554a64c1e6030f051e5c3f94bfbd773cd6a324", apiData[0].CommitMeta.SHA) + assert.Equal(t, "69554a64c1e6030f051e5c3f94bfbd773cd6a324", apiData[0].CommitMeta.SHA) compareCommitFiles(t, []string{"readme.md"}, apiData[0].Files) - assert.EqualValues(t, "27566bd5738fc8b4e3fef3c5e72cce608537bd95", apiData[1].CommitMeta.SHA) + assert.Equal(t, "27566bd5738fc8b4e3fef3c5e72cce608537bd95", apiData[1].CommitMeta.SHA) compareCommitFiles(t, []string{"readme.md"}, apiData[1].Files) - assert.EqualValues(t, "5099b81332712fe655e34e8dd63574f503f61811", apiData[2].CommitMeta.SHA) + assert.Equal(t, "5099b81332712fe655e34e8dd63574f503f61811", apiData[2].CommitMeta.SHA) compareCommitFiles(t, []string{"readme.md"}, apiData[2].Files) - assert.EqualValues(t, "3", resp.Header().Get("X-Total")) + assert.Equal(t, "3", resp.Header().Get("X-Total")) } func TestAPIReposGitCommitListPage2Empty(t *testing.T) { @@ -177,7 +177,7 @@ func TestDownloadCommitDiffOrPatch(t *testing.T) { reqDiff := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/git/commits/f27c2b2b03dcab38beaf89b0ab4ff61f6de63441.diff", user.Name). AddTokenAuth(token) resp := MakeRequest(t, reqDiff, http.StatusOK) - assert.EqualValues(t, + assert.Equal(t, "commit f27c2b2b03dcab38beaf89b0ab4ff61f6de63441\nAuthor: User2 <user2@example.com>\nDate: Sun Aug 6 19:55:01 2017 +0200\n\n good signed commit\n\ndiff --git a/readme.md b/readme.md\nnew file mode 100644\nindex 0000000..458121c\n--- /dev/null\n+++ b/readme.md\n@@ -0,0 +1 @@\n+good sign\n", resp.Body.String()) @@ -185,7 +185,7 @@ func TestDownloadCommitDiffOrPatch(t *testing.T) { reqPatch := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/git/commits/f27c2b2b03dcab38beaf89b0ab4ff61f6de63441.patch", user.Name). AddTokenAuth(token) resp = MakeRequest(t, reqPatch, http.StatusOK) - assert.EqualValues(t, + assert.Equal(t, "From f27c2b2b03dcab38beaf89b0ab4ff61f6de63441 Mon Sep 17 00:00:00 2001\nFrom: User2 <user2@example.com>\nDate: Sun, 6 Aug 2017 19:55:01 +0200\nSubject: [PATCH] good signed commit\n\n---\n readme.md | 1 +\n 1 file changed, 1 insertion(+)\n create mode 100644 readme.md\n\ndiff --git a/readme.md b/readme.md\nnew file mode 100644\nindex 0000000..458121c\n--- /dev/null\n+++ b/readme.md\n@@ -0,0 +1 @@\n+good sign\n", resp.Body.String()) } @@ -208,7 +208,7 @@ func TestGetFileHistory(t *testing.T) { assert.Equal(t, "f27c2b2b03dcab38beaf89b0ab4ff61f6de63441", apiData[0].CommitMeta.SHA) compareCommitFiles(t, []string{"readme.md"}, apiData[0].Files) - assert.EqualValues(t, "1", resp.Header().Get("X-Total")) + assert.Equal(t, "1", resp.Header().Get("X-Total")) } func TestGetFileHistoryNotOnMaster(t *testing.T) { @@ -229,5 +229,5 @@ func TestGetFileHistoryNotOnMaster(t *testing.T) { assert.Equal(t, "c8e31bc7688741a5287fcde4fbb8fc129ca07027", apiData[0].CommitMeta.SHA) compareCommitFiles(t, []string{"test.csv"}, apiData[0].Files) - assert.EqualValues(t, "1", resp.Header().Get("X-Total")) + assert.Equal(t, "1", resp.Header().Get("X-Total")) } diff --git a/tests/integration/api_repo_lfs_locks_test.go b/tests/integration/api_repo_lfs_locks_test.go index b3a295c2de..e844b1f158 100644 --- a/tests/integration/api_repo_lfs_locks_test.go +++ b/tests/integration/api_repo_lfs_locks_test.go @@ -112,7 +112,7 @@ func TestAPILFSLocksLogged(t *testing.T) { var lfsLock api.LFSLockResponse DecodeJSON(t, resp, &lfsLock) assert.Equal(t, test.user.Name, lfsLock.Lock.Owner.Name) - assert.EqualValues(t, lfsLock.Lock.LockedAt.Format(time.RFC3339), lfsLock.Lock.LockedAt.Format(time.RFC3339Nano)) // locked at should be rounded to second + assert.Equal(t, lfsLock.Lock.LockedAt.Format(time.RFC3339), lfsLock.Lock.LockedAt.Format(time.RFC3339Nano)) // locked at should be rounded to second for _, id := range test.addTime { resultsTests[id].locksTimes = append(resultsTests[id].locksTimes, time.Now()) } @@ -129,9 +129,9 @@ func TestAPILFSLocksLogged(t *testing.T) { DecodeJSON(t, resp, &lfsLocks) assert.Len(t, lfsLocks.Locks, test.totalCount) for i, lock := range lfsLocks.Locks { - assert.EqualValues(t, test.locksOwners[i].Name, lock.Owner.Name) + assert.Equal(t, test.locksOwners[i].Name, lock.Owner.Name) assert.WithinDuration(t, test.locksTimes[i], lock.LockedAt, 10*time.Second) - assert.EqualValues(t, lock.LockedAt.Format(time.RFC3339), lock.LockedAt.Format(time.RFC3339Nano)) // locked at should be rounded to second + assert.Equal(t, lock.LockedAt.Format(time.RFC3339), lock.LockedAt.Format(time.RFC3339Nano)) // locked at should be rounded to second } req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/verify", test.repo.FullName()), map[string]string{}) @@ -143,7 +143,7 @@ func TestAPILFSLocksLogged(t *testing.T) { assert.Len(t, lfsLocksVerify.Ours, test.oursCount) assert.Len(t, lfsLocksVerify.Theirs, test.theirsCount) for _, lock := range lfsLocksVerify.Ours { - assert.EqualValues(t, test.user.Name, lock.Owner.Name) + assert.Equal(t, test.user.Name, lock.Owner.Name) deleteTests = append(deleteTests, struct { user *user_model.User repo *repo_model.Repository diff --git a/tests/integration/api_repo_lfs_migrate_test.go b/tests/integration/api_repo_lfs_migrate_test.go index fcfa43cac4..e95ee64639 100644 --- a/tests/integration/api_repo_lfs_migrate_test.go +++ b/tests/integration/api_repo_lfs_migrate_test.go @@ -41,7 +41,7 @@ func TestAPIRepoLFSMigrateLocal(t *testing.T) { LFS: true, }).AddTokenAuth(token) resp := MakeRequest(t, req, NoExpectedStatus) - assert.EqualValues(t, http.StatusCreated, resp.Code) + assert.Equal(t, http.StatusCreated, resp.Code) store := lfs.NewContentStore() ok, _ := store.Verify(lfs.Pointer{Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab041", Size: 6}) diff --git a/tests/integration/api_repo_pulls_test.go b/tests/integration/api_repo_pulls_test.go new file mode 100644 index 0000000000..aec492e65f --- /dev/null +++ b/tests/integration/api_repo_pulls_test.go @@ -0,0 +1,34 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "testing" + + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + api "forgejo.org/modules/structs" + "forgejo.org/tests" + + "github.com/stretchr/testify/assert" +) + +func TestAPIRepoPulls(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + // repo = user2/repo1 + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + + // issue id without assigned review member or review team + issueID := 5 + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", repo.OwnerName, repo.Name, issueID)) + res := MakeRequest(t, req, http.StatusOK) + var pr *api.PullRequest + DecodeJSON(t, res, &pr) + + assert.NotNil(t, pr.RequestedReviewers) + assert.NotNil(t, pr.RequestedReviewersTeams) +} diff --git a/tests/integration/api_repo_raw_test.go b/tests/integration/api_repo_raw_test.go index 5ae11a8e71..bc48119a46 100644 --- a/tests/integration/api_repo_raw_test.go +++ b/tests/integration/api_repo_raw_test.go @@ -30,11 +30,11 @@ func TestAPIReposRaw(t *testing.T) { req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/raw/%s/README.md", user.Name, ref). AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusOK) - assert.EqualValues(t, "file", resp.Header().Get("x-gitea-object-type")) + assert.Equal(t, "file", resp.Header().Get("x-gitea-object-type")) } // Test default branch req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/raw/README.md", user.Name). AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusOK) - assert.EqualValues(t, "file", resp.Header().Get("x-gitea-object-type")) + assert.Equal(t, "file", resp.Header().Get("x-gitea-object-type")) } diff --git a/tests/integration/api_repo_tags_test.go b/tests/integration/api_repo_tags_test.go index 81475249a1..9c69ff31bb 100644 --- a/tests/integration/api_repo_tags_test.go +++ b/tests/integration/api_repo_tags_test.go @@ -48,10 +48,10 @@ func TestAPIRepoTags(t *testing.T) { assert.Len(t, tags, 2) for _, tag := range tags { if tag.Name != "v1.1" { - assert.EqualValues(t, newTag.Name, tag.Name) - assert.EqualValues(t, newTag.Message, tag.Message) - assert.EqualValues(t, "nice!\nand some text", tag.Message) - assert.EqualValues(t, newTag.Commit.SHA, tag.Commit.SHA) + assert.Equal(t, newTag.Name, tag.Name) + assert.Equal(t, newTag.Message, tag.Message) + assert.Equal(t, "nice!\nand some text", tag.Message) + assert.Equal(t, newTag.Commit.SHA, tag.Commit.SHA) } } @@ -61,7 +61,7 @@ func TestAPIRepoTags(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) var tag *api.Tag DecodeJSON(t, resp, &tag) - assert.EqualValues(t, newTag, tag) + assert.Equal(t, newTag, tag) // delete tag delReq := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/tags/%s", user.Name, repoName, newTag.Name). diff --git a/tests/integration/api_repo_teams_test.go b/tests/integration/api_repo_teams_test.go index c75714d003..59905282a5 100644 --- a/tests/integration/api_repo_teams_test.go +++ b/tests/integration/api_repo_teams_test.go @@ -37,15 +37,15 @@ func TestAPIRepoTeams(t *testing.T) { var teams []*api.Team DecodeJSON(t, res, &teams) if assert.Len(t, teams, 2) { - assert.EqualValues(t, "Owners", teams[0].Name) + assert.Equal(t, "Owners", teams[0].Name) assert.True(t, teams[0].CanCreateOrgRepo) assert.True(t, util.SliceSortedEqual(unit.AllUnitKeyNames(), teams[0].Units)) - assert.EqualValues(t, "owner", teams[0].Permission) + assert.Equal(t, "owner", teams[0].Permission) - assert.EqualValues(t, "test_team", teams[1].Name) + assert.Equal(t, "test_team", teams[1].Name) assert.False(t, teams[1].CanCreateOrgRepo) - assert.EqualValues(t, []string{"repo.issues"}, teams[1].Units) - assert.EqualValues(t, "write", teams[1].Permission) + assert.Equal(t, []string{"repo.issues"}, teams[1].Units) + assert.Equal(t, "write", teams[1].Permission) } // IsTeam @@ -54,7 +54,7 @@ func TestAPIRepoTeams(t *testing.T) { res = MakeRequest(t, req, http.StatusOK) var team *api.Team DecodeJSON(t, res, &team) - assert.EqualValues(t, teams[1], team) + assert.Equal(t, teams[1], team) req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/teams/%s", publicOrgRepo.FullName(), "NonExistingTeam")). AddTokenAuth(token) diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index cd79c6eee6..b890f7df43 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -39,7 +39,7 @@ func TestAPIUserReposNotLogin(t *testing.T) { unittest.Cond("is_private = ?", false)) assert.Len(t, apiRepos, expectedLen) for _, repo := range apiRepos { - assert.EqualValues(t, user.ID, repo.Owner.ID) + assert.Equal(t, user.ID, repo.Owner.ID) assert.False(t, repo.Private) } } @@ -268,25 +268,25 @@ func TestAPIViewRepo(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &repo) assert.EqualValues(t, 1, repo.ID) - assert.EqualValues(t, "repo1", repo.Name) - assert.EqualValues(t, 2, repo.Releases) - assert.EqualValues(t, 1, repo.OpenIssues) - assert.EqualValues(t, 3, repo.OpenPulls) + assert.Equal(t, "repo1", repo.Name) + assert.Equal(t, 2, repo.Releases) + assert.Equal(t, 1, repo.OpenIssues) + assert.Equal(t, 3, repo.OpenPulls) req = NewRequest(t, "GET", "/api/v1/repos/user12/repo10") resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &repo) assert.EqualValues(t, 10, repo.ID) - assert.EqualValues(t, "repo10", repo.Name) - assert.EqualValues(t, 1, repo.OpenPulls) - assert.EqualValues(t, 1, repo.Forks) + assert.Equal(t, "repo10", repo.Name) + assert.Equal(t, 1, repo.OpenPulls) + assert.Equal(t, 1, repo.Forks) req = NewRequest(t, "GET", "/api/v1/repos/user5/repo4") resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &repo) assert.EqualValues(t, 4, repo.ID) - assert.EqualValues(t, "repo4", repo.Name) - assert.EqualValues(t, 1, repo.Stars) + assert.Equal(t, "repo4", repo.Name) + assert.Equal(t, 1, repo.Stars) } func TestAPIOrgRepos(t *testing.T) { @@ -339,9 +339,7 @@ func TestAPIOrgReposWithCodeUnitDisabled(t *testing.T) { var units []unit_model.Type units = append(units, unit_model.TypeCode) - if err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo21, nil, units); err != nil { - assert.Fail(t, "should have been able to delete code repository unit; failed to %v", err) - } + require.NoError(t, repo_service.UpdateRepositoryUnits(db.DefaultContext, repo21, nil, units)) assert.False(t, repo21.UnitEnabled(db.DefaultContext, unit_model.TypeCode)) session := loginUser(t, "user2") @@ -405,12 +403,12 @@ func TestAPIRepoMigrate(t *testing.T) { case "Remote visit addressed rate limitation.": t.Log("test hit github rate limitation") case "You can not import from disallowed hosts.": - assert.EqualValues(t, "private-ip", testCase.repoName) + assert.Equal(t, "private-ip", testCase.repoName) default: - assert.FailNow(t, "unexpected error '%v' on url '%s'", respJSON["message"], testCase.cloneURL) + assert.FailNow(t, "unexpected error", "'%v' on url '%s'", respJSON["message"], testCase.cloneURL) } } else { - assert.EqualValues(t, testCase.expectedStatus, resp.Code) + assert.Equal(t, testCase.expectedStatus, resp.Code) } } } @@ -748,7 +746,7 @@ func TestAPIViewRepoObjectFormat(t *testing.T) { req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1") resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &repo) - assert.EqualValues(t, "sha1", repo.ObjectFormatName) + assert.Equal(t, "sha1", repo.ObjectFormatName) } func TestAPIRepoCommitPull(t *testing.T) { @@ -798,12 +796,12 @@ func TestAPIListOwnRepoSorting(t *testing.T) { DecodeJSON(t, resp, &repos) assert.Len(t, repos, 2) - assert.EqualValues(t, "big_test_private_4", repos[0].Name) + assert.Equal(t, "big_test_private_4", repos[0].Name) // Postgres doesn't do ascii sorting. if setting.Database.Type.IsPostgreSQL() { - assert.EqualValues(t, "commitsonpr", repos[1].Name) + assert.Equal(t, "commitsonpr", repos[1].Name) } else { - assert.EqualValues(t, "commits_search_test", repos[1].Name) + assert.Equal(t, "commits_search_test", repos[1].Name) } }) @@ -815,7 +813,7 @@ func TestAPIListOwnRepoSorting(t *testing.T) { DecodeJSON(t, resp, &repos) assert.Len(t, repos, 2) - assert.EqualValues(t, "utf8", repos[0].Name) - assert.EqualValues(t, "test_workflows", repos[1].Name) + assert.Equal(t, "utf8", repos[0].Name) + assert.Equal(t, "test_workflows", repos[1].Name) }) } diff --git a/tests/integration/api_repo_topic_test.go b/tests/integration/api_repo_topic_test.go index c826fbd818..69008bbf64 100644 --- a/tests/integration/api_repo_topic_test.go +++ b/tests/integration/api_repo_topic_test.go @@ -62,7 +62,7 @@ func TestAPITopicSearch(t *testing.T) { res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) DecodeJSON(t, res, &topics) assert.Len(t, topics.TopicNames, 4) - assert.EqualValues(t, "6", res.Header().Get("x-total-count")) + assert.Equal(t, "6", res.Header().Get("x-total-count")) query.Add("q", "topic") searchURL.RawQuery = query.Encode() @@ -76,8 +76,8 @@ func TestAPITopicSearch(t *testing.T) { DecodeJSON(t, res, &topics) if assert.Len(t, topics.TopicNames, 1) { assert.EqualValues(t, 2, topics.TopicNames[0].ID) - assert.EqualValues(t, "database", topics.TopicNames[0].Name) - assert.EqualValues(t, 1, topics.TopicNames[0].RepoCount) + assert.Equal(t, "database", topics.TopicNames[0].Name) + assert.Equal(t, 1, topics.TopicNames[0].RepoCount) } } diff --git a/tests/integration/api_settings_test.go b/tests/integration/api_settings_test.go index e13f4062da..dad4b1d776 100644 --- a/tests/integration/api_settings_test.go +++ b/tests/integration/api_settings_test.go @@ -30,7 +30,7 @@ func TestAPIExposedSettings(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiSettings) - assert.EqualValues(t, &api.GeneralAPISettings{ + assert.Equal(t, &api.GeneralAPISettings{ MaxResponseItems: setting.API.MaxResponseItems, DefaultPagingNum: setting.API.DefaultPagingNum, DefaultGitTreesPerPage: setting.API.DefaultGitTreesPerPage, @@ -42,7 +42,7 @@ func TestAPIExposedSettings(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &repo) - assert.EqualValues(t, &api.GeneralRepoSettings{ + assert.Equal(t, &api.GeneralRepoSettings{ MirrorsDisabled: !setting.Mirror.Enabled, HTTPGitDisabled: setting.Repository.DisableHTTPGit, MigrationsDisabled: setting.Repository.DisableMigrations, @@ -55,7 +55,7 @@ func TestAPIExposedSettings(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &attachment) - assert.EqualValues(t, &api.GeneralAttachmentSettings{ + assert.Equal(t, &api.GeneralAttachmentSettings{ Enabled: setting.Attachment.Enabled, AllowedTypes: setting.Attachment.AllowedTypes, MaxFiles: setting.Attachment.MaxFiles, diff --git a/tests/integration/api_team_test.go b/tests/integration/api_team_test.go index 41d34dcd50..2d16ea2b95 100644 --- a/tests/integration/api_team_test.go +++ b/tests/integration/api_team_test.go @@ -41,9 +41,9 @@ func TestAPITeam(t *testing.T) { var apiTeam api.Team DecodeJSON(t, resp, &apiTeam) - assert.EqualValues(t, team.ID, apiTeam.ID) + assert.Equal(t, team.ID, apiTeam.ID) assert.Equal(t, team.Name, apiTeam.Name) - assert.EqualValues(t, convert.ToOrganization(db.DefaultContext, org), apiTeam.Organization) + assert.Equal(t, convert.ToOrganization(db.DefaultContext, org), apiTeam.Organization) // non team member user will not access the teams details teamUser2 := unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{ID: 3}) @@ -248,10 +248,10 @@ func checkTeamResponse(t *testing.T, testName string, apiTeam *api.Team, name, d if units != nil { sort.StringSlice(units).Sort() sort.StringSlice(apiTeam.Units).Sort() - assert.EqualValues(t, units, apiTeam.Units, "units") + assert.Equal(t, units, apiTeam.Units, "units") } if unitsMap != nil { - assert.EqualValues(t, unitsMap, apiTeam.UnitsMap, "unitsMap") + assert.Equal(t, unitsMap, apiTeam.UnitsMap, "unitsMap") } }) } diff --git a/tests/integration/api_team_user_test.go b/tests/integration/api_team_user_test.go index 0c76dd459d..239252786e 100644 --- a/tests/integration/api_team_user_test.go +++ b/tests/integration/api_team_user_test.go @@ -40,8 +40,8 @@ func TestAPITeamUser(t *testing.T) { expectedUser := convert.ToUser(db.DefaultContext, user, user) // test time via unix timestamp - assert.EqualValues(t, expectedUser.LastLogin.Unix(), user2.LastLogin.Unix()) - assert.EqualValues(t, expectedUser.Created.Unix(), user2.Created.Unix()) + assert.Equal(t, expectedUser.LastLogin.Unix(), user2.LastLogin.Unix()) + assert.Equal(t, expectedUser.Created.Unix(), user2.Created.Unix()) expectedUser.LastLogin = user2.LastLogin expectedUser.Created = user2.Created diff --git a/tests/integration/api_token_test.go b/tests/integration/api_token_test.go index aba6a25256..9d9a44b5d4 100644 --- a/tests/integration/api_token_test.go +++ b/tests/integration/api_token_test.go @@ -524,7 +524,7 @@ func runTestCase(t *testing.T, testCase *requiredScopeTestCase, user *user_model } else if minRequiredLevel == auth_model.Write { unauthorizedLevel = auth_model.Read } else { - assert.FailNow(t, "Invalid test case: Unknown access token scope level: %v", minRequiredLevel) + assert.FailNow(t, "Invalid test case", "Unknown access token scope level: %v", minRequiredLevel) } } diff --git a/tests/integration/api_twofa_test.go b/tests/integration/api_twofa_test.go index 6441b23199..b1b5829685 100644 --- a/tests/integration/api_twofa_test.go +++ b/tests/integration/api_twofa_test.go @@ -77,5 +77,5 @@ func TestAPIWebAuthn(t *testing.T) { DecodeJSON(t, resp, &userParsed) - assert.EqualValues(t, "Basic authorization is not allowed while having security keys enrolled", userParsed.Message) + assert.Equal(t, "Basic authorization is not allowed while having security keys enrolled", userParsed.Message) } diff --git a/tests/integration/api_user_actions_test.go b/tests/integration/api_user_actions_test.go index 1eb5089ee4..f8d5bb500e 100644 --- a/tests/integration/api_user_actions_test.go +++ b/tests/integration/api_user_actions_test.go @@ -34,5 +34,5 @@ func TestAPISearchActionJobs_UserRunner(t *testing.T) { DecodeJSON(t, res, &jobs) assert.Len(t, jobs, 1) - assert.EqualValues(t, job.ID, jobs[0].ID) + assert.Equal(t, job.ID, jobs[0].ID) } diff --git a/tests/integration/api_user_email_test.go b/tests/integration/api_user_email_test.go index b0907ad1cf..424d8c1e5e 100644 --- a/tests/integration/api_user_email_test.go +++ b/tests/integration/api_user_email_test.go @@ -28,7 +28,7 @@ func TestAPIListEmails(t *testing.T) { var emails []*api.Email DecodeJSON(t, resp, &emails) - assert.EqualValues(t, []*api.Email{ + assert.Equal(t, []*api.Email{ { Email: "user2@example.com", Verified: true, @@ -66,7 +66,7 @@ func TestAPIAddEmail(t *testing.T) { var emails []*api.Email DecodeJSON(t, resp, &emails) - assert.EqualValues(t, []*api.Email{ + assert.Equal(t, []*api.Email{ { Email: "user2@example.com", Verified: true, @@ -119,7 +119,7 @@ func TestAPIDeleteEmail(t *testing.T) { var emails []*api.Email DecodeJSON(t, resp, &emails) - assert.EqualValues(t, []*api.Email{ + assert.Equal(t, []*api.Email{ { Email: "user2@example.com", Verified: true, diff --git a/tests/integration/api_user_search_test.go b/tests/integration/api_user_search_test.go index 3df3e45778..6252c2ebde 100644 --- a/tests/integration/api_user_search_test.go +++ b/tests/integration/api_user_search_test.go @@ -68,7 +68,7 @@ func TestAPIUserSearchNotLoggedIn(t *testing.T) { for _, user := range results.Data { assert.Contains(t, user.UserName, query) modelUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: user.ID}) - assert.EqualValues(t, modelUser.GetPlaceholderEmail(), user.Email) + assert.Equal(t, modelUser.GetPlaceholderEmail(), user.Email) } } @@ -106,8 +106,8 @@ func TestAPIUserSearchSystemUsers(t *testing.T) { assert.NotEmpty(t, results.Data) if assert.Len(t, results.Data, 1) { user := results.Data[0] - assert.EqualValues(t, user.UserName, systemUser.Name) - assert.EqualValues(t, user.ID, systemUser.ID) + assert.Equal(t, user.UserName, systemUser.Name) + assert.Equal(t, user.ID, systemUser.ID) } }) } @@ -129,7 +129,7 @@ func TestAPIUserSearchAdminLoggedInUserHidden(t *testing.T) { for _, user := range results.Data { assert.Contains(t, user.UserName, query) assert.NotEmpty(t, user.Email) - assert.EqualValues(t, "private", user.Visibility) + assert.Equal(t, "private", user.Visibility) } } diff --git a/tests/integration/appearance_settings_test.go b/tests/integration/appearance_settings_test.go index 33ca9f97aa..968d30ef08 100644 --- a/tests/integration/appearance_settings_test.go +++ b/tests/integration/appearance_settings_test.go @@ -38,13 +38,13 @@ func testSelectedTheme(t *testing.T, session *TestSession, expectedTheme, expect dataTheme, dataThemeExists := page.Find("html").Attr("data-theme") assert.True(t, dataThemeExists) - assert.EqualValues(t, expectedTheme, dataTheme) + assert.Equal(t, expectedTheme, dataTheme) selectedTheme := page.Find("form[action='/user/settings/appearance/theme'] .menu .item.selected") selectorTheme, selectorThemeExists := selectedTheme.Attr("data-value") assert.True(t, selectorThemeExists) - assert.EqualValues(t, expectedTheme, selectorTheme) - assert.EqualValues(t, expectedName, strings.TrimSpace(selectedTheme.Text())) + assert.Equal(t, expectedTheme, selectorTheme) + assert.Equal(t, expectedName, strings.TrimSpace(selectedTheme.Text())) } // testSelectedTheme changes user's theme diff --git a/tests/integration/auth_ldap_test.go b/tests/integration/auth_ldap_test.go index 6b9a71a5aa..fa38b29941 100644 --- a/tests/integration/auth_ldap_test.go +++ b/tests/integration/auth_ldap_test.go @@ -220,7 +220,7 @@ func TestLDAPAuthChange(t *testing.T) { binddn, _ = doc.Find(`input[name="bind_dn"]`).Attr("value") assert.Equal(t, "uid=gitea,ou=service,dc=planetexpress,dc=com", binddn) domainname, _ := doc.Find(`input[name="default_domain_name"]`).Attr("value") - assert.Equal(t, "", domainname) + assert.Empty(t, domainname) req = NewRequestWithValues(t, "POST", href, buildAuthSourceLDAPPayload(csrf, "", "", "test.org", "", "", "off")) session.MakeRequest(t, req, http.StatusSeeOther) diff --git a/tests/integration/auth_token_test.go b/tests/integration/auth_token_test.go index b4242c2681..abb2e2550f 100644 --- a/tests/integration/auth_token_test.go +++ b/tests/integration/auth_token_test.go @@ -116,7 +116,7 @@ func TestLTAPasswordChange(t *testing.T) { assert.NotNil(t, rememberCookie) // Check if the password really changed. - assert.NotEqualValues(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).Passwd, user.Passwd) + assert.NotEqual(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).Passwd, user.Passwd) // /user/settings/account should provide with a new LTA cookie, so check for that. // If LTA cookie provides authentication /user/login shouldn't return status 200. diff --git a/tests/integration/block_test.go b/tests/integration/block_test.go index d249737555..67443f40cf 100644 --- a/tests/integration/block_test.go +++ b/tests/integration/block_test.go @@ -159,7 +159,7 @@ func TestBlockUserFromOrganization(t *testing.T) { assert.False(t, unittest.BeanExists(t, &user_model.BlockedUser{BlockID: doer.ID, UserID: org.ID})) flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "error%3DYou%2Bcannot%2Bblock%2Byourself.", flashCookie.Value) + assert.Equal(t, "error%3DYou%2Bcannot%2Bblock%2Byourself.", flashCookie.Value) }) } @@ -236,7 +236,7 @@ func TestBlockActions(t *testing.T) { // Ensures that comment creation on doer's owned repositories and doer's // posted issues are blocked. t.Run("Comment creation", func(t *testing.T) { - expectedMessage := locale.Tr("repo.issues.comment.blocked_by_user") + expectedMessage := locale.Tr("repo.comment.blocked_by_user") t.Run("Blocked by repository owner", func(t *testing.T) { defer tests.PrintCurrentTest(t)() @@ -258,7 +258,7 @@ func TestBlockActions(t *testing.T) { resp = session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) msg := htmlDoc.doc.Find("div .warning").Text() - assert.Contains(t, msg, "You cannot comment on this issue because you are blocked") + assert.Contains(t, msg, expectedMessage) }) t.Run("Blocked by issue poster", func(t *testing.T) { @@ -285,7 +285,7 @@ func TestBlockActions(t *testing.T) { resp = session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) msg := htmlDoc.doc.Find("div .warning").Text() - assert.Contains(t, msg, "You cannot comment on this issue because you are blocked") + assert.Contains(t, msg, expectedMessage) }) }) @@ -393,7 +393,7 @@ func TestBlockActions(t *testing.T) { flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "error%3DCannot%2Badd%2Bthe%2Bcollaborator%252C%2Bbecause%2Bthe%2Brepository%2Bowner%2Bhas%2Bblocked%2Bthem.", flashCookie.Value) + assert.Equal(t, "error%3DCannot%2Badd%2Bthe%2Bcollaborator%252C%2Bbecause%2Bthe%2Brepository%2Bowner%2Bhas%2Bblocked%2Bthem.", flashCookie.Value) }) t.Run("BlockedUser Add doer", func(t *testing.T) { @@ -410,7 +410,7 @@ func TestBlockActions(t *testing.T) { flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "error%3DCannot%2Badd%2Bthe%2Bcollaborator%252C%2Bbecause%2Bthey%2Bhave%2Bblocked%2Bthe%2Brepository%2Bowner.", flashCookie.Value) + assert.Equal(t, "error%3DCannot%2Badd%2Bthe%2Bcollaborator%252C%2Bbecause%2Bthey%2Bhave%2Bblocked%2Bthe%2Brepository%2Bowner.", flashCookie.Value) }) }) diff --git a/tests/integration/cmd_forgejo_actions_test.go b/tests/integration/cmd_forgejo_actions_test.go index bee50a6d98..01696d3171 100644 --- a/tests/integration/cmd_forgejo_actions_test.go +++ b/tests/integration/cmd_forgejo_actions_test.go @@ -69,7 +69,7 @@ func Test_CmdForgejo_Actions(t *testing.T) { t.Run(testCase.testName, func(t *testing.T) { defer tests.PrintCurrentTest(t)() output, err := runMainApp("forgejo-cli", "actions", "register", "--secret", testCase.secret, "--scope", testCase.scope) - assert.EqualValues(t, "", output) + assert.Empty(t, output) var exitErr *exec.ExitError require.ErrorAs(t, err, &exitErr) @@ -110,7 +110,7 @@ func Test_CmdForgejo_Actions(t *testing.T) { t.Run(testCase.testName, func(t *testing.T) { uuid, err := runMainAppWithStdin(testCase.stdin, "forgejo-cli", "actions", "register", testCase.secretOption(), "--scope=org26") require.NoError(t, err) - assert.EqualValues(t, expecteduuid, uuid) + assert.Equal(t, expecteduuid, uuid) }) } @@ -186,7 +186,7 @@ func Test_CmdForgejo_Actions(t *testing.T) { for i := 0; i < 2; i++ { uuid, err := runMainApp("forgejo-cli", cmd...) require.NoError(t, err) - if assert.EqualValues(t, testCase.uuid, uuid) { + if assert.Equal(t, testCase.uuid, uuid) { ownerName, repoName, found := strings.Cut(testCase.scope, "/") action, err := actions_model.GetRunnerByUUID(t.Context(), uuid) require.NoError(t, err) @@ -199,14 +199,14 @@ func Test_CmdForgejo_Actions(t *testing.T) { assert.Equal(t, repoName, repo.Name, action.RepoID) } if testCase.name != "" { - assert.EqualValues(t, testCase.name, action.Name) + assert.Equal(t, testCase.name, action.Name) } if testCase.labels != "" { labels := strings.Split(testCase.labels, ",") - assert.EqualValues(t, labels, action.AgentLabels) + assert.Equal(t, labels, action.AgentLabels) } if testCase.version != "" { - assert.EqualValues(t, testCase.version, action.Version) + assert.Equal(t, testCase.version, action.Version) } } } diff --git a/tests/integration/cmd_forgejo_f3_test.go b/tests/integration/cmd_forgejo_f3_test.go index 1eda413e4c..08f8289e61 100644 --- a/tests/integration/cmd_forgejo_f3_test.go +++ b/tests/integration/cmd_forgejo_f3_test.go @@ -83,7 +83,7 @@ func TestF3_CmdMirror_LocalForgejo(t *testing.T) { user := users.CreateChild(ctx) user.FromFormat(userFormat) user.Upsert(ctx) - require.EqualValues(t, user.GetID(), users.GetIDFromName(ctx, userFormat.UserName)) + require.Equal(t, user.GetID(), users.GetIDFromName(ctx, userFormat.UserName)) projectFormat := creator.GenerateProject() projectFormat.SetID(fixtureProjectID) @@ -91,7 +91,7 @@ func TestF3_CmdMirror_LocalForgejo(t *testing.T) { project := projects.CreateChild(ctx) project.FromFormat(projectFormat) project.Upsert(ctx) - require.EqualValues(t, project.GetID(), projects.GetIDFromName(ctx, projectFormat.Name)) + require.Equal(t, project.GetID(), projects.GetIDFromName(ctx, projectFormat.Name)) fromPath = fmt.Sprintf("/forge/users/%s/projects/%s", userFormat.UserName, projectFormat.Name) } @@ -106,14 +106,14 @@ func TestF3_CmdMirror_LocalForgejo(t *testing.T) { user := users.CreateChild(ctx) user.FromFormat(userFormat) user.Upsert(ctx) - require.EqualValues(t, user.GetID(), users.GetIDFromName(ctx, userFormat.UserName)) + require.Equal(t, user.GetID(), users.GetIDFromName(ctx, userFormat.UserName)) projectFormat := creator.GenerateProject() projects = user.MustFind(f3_generic.NewPathFromString("projects")) project := projects.CreateChild(ctx) project.FromFormat(projectFormat) project.Upsert(ctx) - require.EqualValues(t, project.GetID(), projects.GetIDFromName(ctx, projectFormat.Name)) + require.Equal(t, project.GetID(), projects.GetIDFromName(ctx, projectFormat.Name)) toPath = fmt.Sprintf("/forge/users/%s/projects/%s", userFormat.UserName, projectFormat.Name) } diff --git a/tests/integration/comment_roles_system_test.go b/tests/integration/comment_roles_system_test.go index 3b315f28f8..febc086916 100644 --- a/tests/integration/comment_roles_system_test.go +++ b/tests/integration/comment_roles_system_test.go @@ -49,7 +49,7 @@ func TestSystemCommentRoles(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) - assert.EqualValues(t, tc.username, htmlDoc.Find("a.author").Text()) + assert.Equal(t, tc.username, htmlDoc.Find("a.author").Text()) assert.EqualValues(t, tc.roleCount, htmlDoc.Find(".role-label").Length()) }) } diff --git a/tests/integration/comment_roles_test.go b/tests/integration/comment_roles_test.go index 00bec95722..fc9bc6f565 100644 --- a/tests/integration/comment_roles_test.go +++ b/tests/integration/comment_roles_test.go @@ -61,39 +61,39 @@ func TestCommentRoles(t *testing.T) { response = sessionUser2.MakeRequest(t, NewRequest(t, "GET", path.Join(user, repo, "pulls", testID)), http.StatusOK) page = NewHTMLParser(t, response.Body) commentHeads := page.Find(".timeline .comment .comment-header .comment-header-right") - assert.EqualValues(t, 6, commentHeads.Length()) + assert.Equal(t, 6, commentHeads.Length()) // Test the first comment and it's label "Owner" labels := commentHeads.Eq(0).Find(".role-label") - assert.EqualValues(t, 1, labels.Length()) + assert.Equal(t, 1, labels.Length()) testIssueCommentUserLabel(t, labels.Eq(0), "Owner", ownerTooltip) // Test the second (review) comment and it's labels "Author" and "Owner" labels = commentHeads.Eq(1).Find(".role-label") - assert.EqualValues(t, 2, labels.Length()) + assert.Equal(t, 2, labels.Length()) testIssueCommentUserLabel(t, labels.Eq(0), "Author", authorTooltipPR) testIssueCommentUserLabel(t, labels.Eq(1), "Owner", ownerTooltip) // Test the third (review) comment and it's labels "Author" and "Owner" labels = commentHeads.Eq(2).Find(".role-label") - assert.EqualValues(t, 2, labels.Length()) + assert.Equal(t, 2, labels.Length()) testIssueCommentUserLabel(t, labels.Eq(0), "Author", authorTooltipPR) testIssueCommentUserLabel(t, labels.Eq(1), "Owner", ownerTooltip) // Test the fourth comment and it's labels "Author" and "Owner" labels = commentHeads.Eq(3).Find(".role-label") - assert.EqualValues(t, 2, labels.Length()) + assert.Equal(t, 2, labels.Length()) testIssueCommentUserLabel(t, labels.Eq(0), "Author", authorTooltipPR) testIssueCommentUserLabel(t, labels.Eq(1), "Owner", ownerTooltip) // Test the fivth comment and it's label "Contributor" labels = commentHeads.Eq(4).Find(".role-label") - assert.EqualValues(t, 1, labels.Length()) + assert.Equal(t, 1, labels.Length()) testIssueCommentUserLabel(t, labels.Eq(0), "Contributor", contributorTooltip) // Test the sixth comment and it's label "First-time contributor" labels = commentHeads.Eq(5).Find(".role-label") - assert.EqualValues(t, 1, labels.Length()) + assert.Equal(t, 1, labels.Length()) testIssueCommentUserLabel(t, labels.Eq(0), "First-time contributor", newContributorTooltip) }) @@ -123,27 +123,27 @@ func TestCommentRoles(t *testing.T) { response := sessionUser2.MakeRequest(t, NewRequest(t, "GET", path.Join(user, repo, "issues", testID)), http.StatusOK) page := NewHTMLParser(t, response.Body) commentHeads := page.Find(".timeline .comment .comment-header .comment-header-right") - assert.EqualValues(t, 4, commentHeads.Length()) + assert.Equal(t, 4, commentHeads.Length()) // Test the first comment and it's label "Owner" labels := commentHeads.Eq(0).Find(".role-label") - assert.EqualValues(t, 1, labels.Length()) + assert.Equal(t, 1, labels.Length()) testIssueCommentUserLabel(t, labels.Eq(0), "Owner", ownerTooltip) // Test the second comment and it's labels "Author" and "Owner" labels = commentHeads.Eq(1).Find(".role-label") - assert.EqualValues(t, 2, labels.Length()) + assert.Equal(t, 2, labels.Length()) testIssueCommentUserLabel(t, labels.Eq(0), "Author", authorTooltipIssue) testIssueCommentUserLabel(t, labels.Eq(1), "Owner", ownerTooltip) // Test the third comment and it's label "Contributor" labels = commentHeads.Eq(2).Find(".role-label") - assert.EqualValues(t, 1, labels.Length()) + assert.Equal(t, 1, labels.Length()) testIssueCommentUserLabel(t, labels.Eq(0), "Contributor", contributorTooltip) // Test the fifth comment and it's lack of labels labels = commentHeads.Eq(3).Find(".role-label") - assert.EqualValues(t, 0, labels.Length()) + assert.Equal(t, 0, labels.Length()) }) } @@ -153,8 +153,8 @@ func testIssueCommentUserLabel(t *testing.T, label *goquery.Selection, expectedT title := label.Text() tooltip, exists := label.Attr("data-tooltip-content") assert.True(t, exists) - assert.EqualValues(t, expectedTitle, strings.TrimSpace(title)) - assert.EqualValues(t, expectedTooltip, strings.TrimSpace(tooltip)) + assert.Equal(t, expectedTitle, strings.TrimSpace(title)) + assert.Equal(t, expectedTooltip, strings.TrimSpace(tooltip)) } // testEasyLeaveIssueComment is used to create a comment on an issue with minimum code and parameters diff --git a/tests/integration/commit_status_test.go b/tests/integration/commit_status_test.go index 9dd1bcd283..adcedcc41b 100644 --- a/tests/integration/commit_status_test.go +++ b/tests/integration/commit_status_test.go @@ -23,7 +23,7 @@ func TestGetLatestCommitStatusForPairs(t *testing.T) { defer tests.PrintCurrentTest(t)() pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, nil) require.NoError(t, err) - assert.EqualValues(t, map[int64][]*git_model.CommitStatus{}, pairs) + assert.Equal(t, map[int64][]*git_model.CommitStatus{}, pairs) }) t.Run("Repo 1", func(t *testing.T) { @@ -31,7 +31,7 @@ func TestGetLatestCommitStatusForPairs(t *testing.T) { pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{RepoID: 1, SHA: "1234123412341234123412341234123412341234"}}) require.NoError(t, err) - assert.EqualValues(t, map[int64][]*git_model.CommitStatus{ + assert.Equal(t, map[int64][]*git_model.CommitStatus{ 1: { { ID: 7, @@ -77,7 +77,7 @@ func TestGetLatestCommitStatusForPairs(t *testing.T) { pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{RepoID: 62, SHA: "774f93df12d14931ea93259ae93418da4482fcc1"}}) require.NoError(t, err) - assert.EqualValues(t, map[int64][]*git_model.CommitStatus{ + assert.Equal(t, map[int64][]*git_model.CommitStatus{ 62: { { ID: 8, @@ -100,7 +100,7 @@ func TestGetLatestCommitStatusForPairs(t *testing.T) { pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{RepoID: 62, SHA: "774f93df12d14931ea93259ae93418da4482fcc"}}) require.NoError(t, err) - assert.EqualValues(t, map[int64][]*git_model.CommitStatus{}, pairs) + assert.Equal(t, map[int64][]*git_model.CommitStatus{}, pairs) }) t.Run("SHA with non-existent repo id", func(t *testing.T) { @@ -108,7 +108,7 @@ func TestGetLatestCommitStatusForPairs(t *testing.T) { pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{RepoID: 1, SHA: "774f93df12d14931ea93259ae93418da4482fcc1"}}) require.NoError(t, err) - assert.EqualValues(t, map[int64][]*git_model.CommitStatus{}, pairs) + assert.Equal(t, map[int64][]*git_model.CommitStatus{}, pairs) }) } @@ -119,14 +119,14 @@ func TestGetLatestCommitStatusForRepoCommitIDs(t *testing.T) { defer tests.PrintCurrentTest(t)() repoStatuses, err := git_model.GetLatestCommitStatusForRepoCommitIDs(db.DefaultContext, 62, nil) require.NoError(t, err) - assert.EqualValues(t, map[string][]*git_model.CommitStatus{}, repoStatuses) + assert.Equal(t, map[string][]*git_model.CommitStatus{}, repoStatuses) }) t.Run("Repo 1", func(t *testing.T) { defer tests.PrintCurrentTest(t)() repoStatuses, err := git_model.GetLatestCommitStatusForRepoCommitIDs(db.DefaultContext, 1, []string{"1234123412341234123412341234123412341234"}) require.NoError(t, err) - assert.EqualValues(t, map[string][]*git_model.CommitStatus{ + assert.Equal(t, map[string][]*git_model.CommitStatus{ "1234123412341234123412341234123412341234": { { ID: 3, @@ -172,7 +172,7 @@ func TestGetLatestCommitStatusForRepoCommitIDs(t *testing.T) { defer tests.PrintCurrentTest(t)() repoStatuses, err := git_model.GetLatestCommitStatusForRepoCommitIDs(db.DefaultContext, 62, []string{"774f93df12d14931ea93259ae93418da4482fcc1"}) require.NoError(t, err) - assert.EqualValues(t, map[string][]*git_model.CommitStatus{ + assert.Equal(t, map[string][]*git_model.CommitStatus{ "774f93df12d14931ea93259ae93418da4482fcc1": { { ID: 8, @@ -194,13 +194,13 @@ func TestGetLatestCommitStatusForRepoCommitIDs(t *testing.T) { defer tests.PrintCurrentTest(t)() repoStatuses, err := git_model.GetLatestCommitStatusForRepoCommitIDs(db.DefaultContext, 62, []string{"774f93df12d14931ea93259ae93418da4482fcc"}) require.NoError(t, err) - assert.EqualValues(t, map[string][]*git_model.CommitStatus{}, repoStatuses) + assert.Equal(t, map[string][]*git_model.CommitStatus{}, repoStatuses) }) t.Run("non-existent repo ID", func(t *testing.T) { defer tests.PrintCurrentTest(t)() repoStatuses, err := git_model.GetLatestCommitStatusForRepoCommitIDs(db.DefaultContext, 1, []string{"774f93df12d14931ea93259ae93418da4482fcc"}) require.NoError(t, err) - assert.EqualValues(t, map[string][]*git_model.CommitStatus{}, repoStatuses) + assert.Equal(t, map[string][]*git_model.CommitStatus{}, repoStatuses) }) } diff --git a/tests/integration/common_navigation_test.go b/tests/integration/common_navigation_test.go new file mode 100644 index 0000000000..93a2c15ece --- /dev/null +++ b/tests/integration/common_navigation_test.go @@ -0,0 +1,41 @@ +// Copyright 2024-2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package integration + +import ( + "fmt" + "net/http" + "strings" + "testing" + + "forgejo.org/models/unittest" + "forgejo.org/modules/translation" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// This test verifies common elements that are visible on all pages but most +// likely to be first seen on `/` +func TestCommonNavigationElements(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + session := loginUser(t, "user1") + locale := translation.NewLocale("en-US") + + response := session.MakeRequest(t, NewRequest(t, "GET", "/"), http.StatusOK) + page := NewHTMLParser(t, response.Body) + + // Navbar + links := page.Find("#navbar .dropdown[data-tooltip-content='Create…'] .menu") + assert.Equal(t, locale.TrString("new_repo.link"), strings.TrimSpace(links.Find("a[href='/repo/create']").Text())) + assert.Equal(t, locale.TrString("new_migrate.link"), strings.TrimSpace(links.Find("a[href='/repo/migrate']").Text())) + assert.Equal(t, locale.TrString("new_org.link"), strings.TrimSpace(links.Find("a[href='/org/create']").Text())) + + // After footer: index.js + page.AssertElement(t, "script[src^='/assets/js/index.js']", true) + onerror, _ := page.Find("script[src^='/assets/js/index.js']").Attr("onerror") + expected := fmt.Sprintf("alert('%s'.replace('{path}', this.src))", locale.TrString("alert.asset_load_failed")) + assert.Equal(t, expected, onerror) +} diff --git a/tests/integration/db_collation_test.go b/tests/integration/db_collation_test.go index 6bfe656b9b..5b84dae823 100644 --- a/tests/integration/db_collation_test.go +++ b/tests/integration/db_collation_test.go @@ -16,7 +16,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "xorm.io/xorm" ) type TestCollationTbl struct { @@ -48,11 +47,13 @@ func TestDatabaseCollationSelfCheckUI(t *testing.T) { } func TestDatabaseCollation(t *testing.T) { - x := db.GetEngine(db.DefaultContext).(*xorm.Engine) + engine, err := db.GetMasterEngine(db.GetEngine(db.DefaultContext)) + require.NoError(t, err) + x := engine // all created tables should use case-sensitive collation by default _, _ = x.Exec("DROP TABLE IF EXISTS test_collation_tbl") - err := x.Sync(&TestCollationTbl{}) + err = x.Sync(&TestCollationTbl{}) require.NoError(t, err) _, _ = x.Exec("INSERT INTO test_collation_tbl (txt) VALUES ('main')") _, _ = x.Exec("INSERT INTO test_collation_tbl (txt) VALUES ('Main')") // case-sensitive, so it inserts a new row diff --git a/tests/integration/devtest_error_test.go b/tests/integration/devtest_error_test.go new file mode 100644 index 0000000000..9aa0341fe5 --- /dev/null +++ b/tests/integration/devtest_error_test.go @@ -0,0 +1,53 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package integration + +import ( + "net/http" + "testing" + + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + + "github.com/stretchr/testify/assert" +) + +// `/devtest/error/{errcode}` provides a convenient way of testing various +// error pages sometimes which can be hard to reach otherwise. +// This file is a test of various attributes on those pages. + +func TestDevtestErrorpages(t *testing.T) { + defer test.MockVariableValue(&setting.IsProd, false)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + t.Run("Server error", func(t *testing.T) { + // `/devtest/error/x` returns 500 for any x by default. + // `/500` is simply for good look here + req := NewRequest(t, "GET", "/devtest/error/500") + resp := MakeRequest(t, req, http.StatusInternalServerError) + doc := NewHTMLParser(t, resp.Body) + assert.Equal(t, "500", doc.Find(".error-code").Text()) + assert.Contains(t, doc.Find("head title").Text(), "Internal server error") + }) + + t.Run("Page not found", + func(t *testing.T) { + req := NewRequest(t, "GET", "/devtest/error/404"). + // Without this header `notFoundInternal` returns plaintext error message + SetHeader("Accept", "text/html") + resp := MakeRequest(t, req, http.StatusNotFound) + doc := NewHTMLParser(t, resp.Body) + assert.Equal(t, "404", doc.Find(".error-code").Text()) + assert.Contains(t, doc.Find("head title").Text(), "Page not found") + }) + + t.Run("Quota exhaustion", + func(t *testing.T) { + req := NewRequest(t, "GET", "/devtest/error/413") + resp := MakeRequest(t, req, http.StatusRequestEntityTooLarge) + doc := NewHTMLParser(t, resp.Body) + assert.Equal(t, "413", doc.Find(".error-code").Text()) + }) +} diff --git a/tests/integration/dump_restore_test.go b/tests/integration/dump_restore_test.go index 239170a256..855cf2810d 100644 --- a/tests/integration/dump_restore_test.go +++ b/tests/integration/dump_restore_test.go @@ -180,7 +180,7 @@ func (c *compareDump) assertEquals(repoBefore, repoAfter *repo_model.Repository) }).([]*base.Comment) assert.True(c.t, ok) for _, comment := range comments { - assert.EqualValues(c.t, issue.Number, comment.IssueIndex) + assert.Equal(c.t, issue.Number, comment.IssueIndex) } } @@ -207,7 +207,7 @@ func (c *compareDump) assertEquals(repoBefore, repoAfter *repo_model.Repository) comments, ok := c.assertEqual(filename, []base.Comment{}, compareFields{}).([]*base.Comment) assert.True(c.t, ok) for _, comment := range comments { - assert.EqualValues(c.t, pr.Number, comment.IssueIndex) + assert.Equal(c.t, pr.Number, comment.IssueIndex) } } } @@ -215,7 +215,7 @@ func (c *compareDump) assertEquals(repoBefore, repoAfter *repo_model.Repository) func (c *compareDump) assertLoadYAMLFiles(beforeFilename, afterFilename string, before, after any) { _, beforeErr := os.Stat(beforeFilename) _, afterErr := os.Stat(afterFilename) - assert.EqualValues(c.t, errors.Is(beforeErr, os.ErrNotExist), errors.Is(afterErr, os.ErrNotExist)) + assert.Equal(c.t, errors.Is(beforeErr, os.ErrNotExist), errors.Is(afterErr, os.ErrNotExist)) if errors.Is(beforeErr, os.ErrNotExist) { return } @@ -267,7 +267,7 @@ func (c *compareDump) assertEqual(filename string, kind any, fields compareField } func (c *compareDump) assertEqualSlices(before, after reflect.Value, fields compareFields) any { - assert.EqualValues(c.t, before.Len(), after.Len()) + assert.Equal(c.t, before.Len(), after.Len()) if before.Len() == after.Len() { for i := 0; i < before.Len(); i++ { _ = c.assertEqualValues( @@ -300,15 +300,15 @@ func (c *compareDump) assertEqualValues(before, after reflect.Value, fields comp assert.True(c.t, ok) as, ok := ai.(string) assert.True(c.t, ok) - assert.EqualValues(c.t, compare.transform(bs), compare.transform(as)) + assert.Equal(c.t, compare.transform(bs), compare.transform(as)) continue } if compare.before != nil && compare.after != nil { // // The fields are expected to have different values // - assert.EqualValues(c.t, compare.before, bi) - assert.EqualValues(c.t, compare.after, ai) + assert.Equal(c.t, compare.before, bi) + assert.Equal(c.t, compare.after, ai) continue } if compare.nested != nil { @@ -319,7 +319,7 @@ func (c *compareDump) assertEqualValues(before, after reflect.Value, fields comp continue } } - assert.EqualValues(c.t, bi, ai) + assert.Equal(c.t, bi, ai) } return after.Interface() } diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go index 7321aa26c9..b6faf8a118 100644 --- a/tests/integration/editor_test.go +++ b/tests/integration/editor_test.go @@ -43,7 +43,7 @@ func TestCreateFileOnProtectedBranch(t *testing.T) { // Check if master branch has been locked successfully flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522master%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) + assert.Equal(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522master%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) // Request editor page req = NewRequest(t, "GET", "/user2/repo1/_new/master/") @@ -79,12 +79,12 @@ func TestCreateFileOnProtectedBranch(t *testing.T) { res := make(map[string]string) require.NoError(t, json.NewDecoder(resp.Body).Decode(&res)) - assert.EqualValues(t, "/user2/repo1/settings/branches", res["redirect"]) + assert.Equal(t, "/user2/repo1/settings/branches", res["redirect"]) // Check if master branch has been locked successfully flashCookie = session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "error%3DRemoving%2Bbranch%2Bprotection%2Brule%2B%25221%2522%2Bfailed.", flashCookie.Value) + assert.Equal(t, "error%3DRemoving%2Bbranch%2Bprotection%2Brule%2B%25221%2522%2Bfailed.", flashCookie.Value) }) } @@ -113,7 +113,7 @@ func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePa // Verify the change req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", branch, filePath)) resp = session.MakeRequest(t, req, http.StatusOK) - assert.EqualValues(t, newContent, resp.Body.String()) + assert.Equal(t, newContent, resp.Body.String()) return resp } @@ -144,7 +144,7 @@ func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, bra // Verify the change req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", targetBranch, filePath)) resp = session.MakeRequest(t, req, http.StatusOK) - assert.EqualValues(t, newContent, resp.Body.String()) + assert.Equal(t, newContent, resp.Body.String()) return resp } @@ -173,7 +173,7 @@ func TestEditorAddTranslation(t *testing.T) { placeholder, ok := htmlDoc.Find("input[name='commit_summary']").Attr("placeholder") assert.True(t, ok) - assert.EqualValues(t, `Add "<filename>"`, placeholder) + assert.Equal(t, `Add "<filename>"`, placeholder) } func TestCommitMail(t *testing.T) { @@ -188,7 +188,7 @@ func TestCommitMail(t *testing.T) { assert.False(t, inactivatedMail.IsActivated) otherEmail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 1, IsActivated: true}) - assert.NotEqualValues(t, otherEmail.UID, user.ID) + assert.NotEqual(t, otherEmail.UID, user.ID) primaryEmail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 3, UID: user.ID, IsActivated: true}) @@ -277,16 +277,16 @@ func TestCommitMail(t *testing.T) { session.MakeRequest(t, req, http.StatusSeeOther) if !case2.skipLastCommit { newlastCommit, _ := lastCommitAndCSRF(t, case1.link, false) - assert.NotEqualValues(t, newlastCommit, lastCommit) + assert.NotEqual(t, newlastCommit, lastCommit) } commit, err := gitRepo.GetCommitByPath(case1.fileName) require.NoError(t, err) - assert.EqualValues(t, "user2", commit.Author.Name) - assert.EqualValues(t, "user2@noreply.example.org", commit.Author.Email) - assert.EqualValues(t, "user2", commit.Committer.Name) - assert.EqualValues(t, "user2@noreply.example.org", commit.Committer.Email) + assert.Equal(t, "user2", commit.Author.Name) + assert.Equal(t, "user2@noreply.example.org", commit.Author.Email) + assert.Equal(t, "user2", commit.Committer.Name) + assert.Equal(t, "user2@noreply.example.org", commit.Committer.Email) }) t.Run("Normal", func(t *testing.T) { @@ -302,16 +302,16 @@ func TestCommitMail(t *testing.T) { session.MakeRequest(t, req, http.StatusSeeOther) if !case2.skipLastCommit { newlastCommit, _ := lastCommitAndCSRF(t, case2.link, false) - assert.NotEqualValues(t, newlastCommit, lastCommit) + assert.NotEqual(t, newlastCommit, lastCommit) } commit, err := gitRepo.GetCommitByPath(case2.fileName) require.NoError(t, err) - assert.EqualValues(t, "user2", commit.Author.Name) - assert.EqualValues(t, primaryEmail.Email, commit.Author.Email) - assert.EqualValues(t, "user2", commit.Committer.Name) - assert.EqualValues(t, primaryEmail.Email, commit.Committer.Email) + assert.Equal(t, "user2", commit.Author.Name) + assert.Equal(t, primaryEmail.Email, commit.Author.Email) + assert.Equal(t, "user2", commit.Committer.Name) + assert.Equal(t, primaryEmail.Email, commit.Committer.Email) }) } diff --git a/tests/integration/empty_repo_test.go b/tests/integration/empty_repo_test.go index baea2566ff..151f9450bd 100644 --- a/tests/integration/empty_repo_test.go +++ b/tests/integration/empty_repo_test.go @@ -123,7 +123,7 @@ func TestEmptyRepoAddFileByAPI(t *testing.T) { var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) expectedHTMLURL := setting.AppURL + "user30/empty/src/branch/new_branch/new-file.txt" - assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) + assert.Equal(t, expectedHTMLURL, *fileResponse.Content.HTMLURL) req = NewRequest(t, "GET", "/user30/empty/src/branch/new_branch/new-file.txt") resp = session.MakeRequest(t, req, http.StatusOK) diff --git a/tests/integration/explore_repos_test.go b/tests/integration/explore_repos_test.go index 604d473ac5..5109707df5 100644 --- a/tests/integration/explore_repos_test.go +++ b/tests/integration/explore_repos_test.go @@ -25,7 +25,7 @@ func TestExploreRepos(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body).Find("#repo-search-form") - assert.EqualValues(t, "Go", htmlDoc.Find("input[name='language']").AttrOr("value", "not found")) - assert.EqualValues(t, "true", htmlDoc.Find("input[name='topic']").AttrOr("value", "not found")) + assert.Equal(t, "Go", htmlDoc.Find("input[name='language']").AttrOr("value", "not found")) + assert.Equal(t, "true", htmlDoc.Find("input[name='topic']").AttrOr("value", "not found")) }) } diff --git a/tests/integration/feed_repo_test.go b/tests/integration/feed_repo_test.go index a4d5ef2610..d75d23170d 100644 --- a/tests/integration/feed_repo_test.go +++ b/tests/integration/feed_repo_test.go @@ -30,7 +30,7 @@ func TestFeedRepo(t *testing.T) { assert.Contains(t, rss.Channel.Link, "/user2/repo1") assert.NotEmpty(t, rss.Channel.PubDate) assert.Len(t, rss.Channel.Items, 1) - assert.EqualValues(t, "issue5", rss.Channel.Items[0].Description) + assert.Equal(t, "issue5", rss.Channel.Items[0].Description) assert.NotEmpty(t, rss.Channel.Items[0].PubDate) }) } diff --git a/tests/integration/forgejo_confirmation_repo_test.go b/tests/integration/forgejo_confirmation_repo_test.go index 0229046efc..28d6777aea 100644 --- a/tests/integration/forgejo_confirmation_repo_test.go +++ b/tests/integration/forgejo_confirmation_repo_test.go @@ -55,7 +55,7 @@ func TestDangerZoneConfirmation(t *testing.T) { flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DThis%2Brepository%2Bhas%2Bbeen%2Bmarked%2Bfor%2Btransfer%2Band%2Bawaits%2Bconfirmation%2Bfrom%2B%2522User%2BOne%2522", flashCookie.Value) + assert.Equal(t, "success%3DThis%2Brepository%2Bhas%2Bbeen%2Bmarked%2Bfor%2Btransfer%2Band%2Bawaits%2Bconfirmation%2Bfrom%2B%2522User%2BOne%2522", flashCookie.Value) }) }) @@ -85,7 +85,7 @@ func TestDangerZoneConfirmation(t *testing.T) { flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DThe%2Bfork%2Bhas%2Bbeen%2Bconverted%2Binto%2Ba%2Bregular%2Brepository.", flashCookie.Value) + assert.Equal(t, "success%3DThe%2Bfork%2Bhas%2Bbeen%2Bconverted%2Binto%2Ba%2Bregular%2Brepository.", flashCookie.Value) }) }) @@ -118,7 +118,7 @@ func TestDangerZoneConfirmation(t *testing.T) { flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DThe%2Brepository%2Bwiki%2527s%2Bbranch%2Bname%2Bhas%2Bbeen%2Bsuccessfully%2Bnormalized.", flashCookie.Value) + assert.Equal(t, "success%3DThe%2Brepository%2Bwiki%2527s%2Bbranch%2Bname%2Bhas%2Bbeen%2Bsuccessfully%2Bnormalized.", flashCookie.Value) }) }) @@ -148,7 +148,7 @@ func TestDangerZoneConfirmation(t *testing.T) { flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DThe%2Brepository%2Bwiki%2Bdata%2Bhas%2Bbeen%2Bdeleted.", flashCookie.Value) + assert.Equal(t, "success%3DThe%2Brepository%2Bwiki%2Bdata%2Bhas%2Bbeen%2Bdeleted.", flashCookie.Value) }) }) @@ -178,7 +178,7 @@ func TestDangerZoneConfirmation(t *testing.T) { flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DThe%2Brepository%2Bhas%2Bbeen%2Bdeleted.", flashCookie.Value) + assert.Equal(t, "success%3DThe%2Brepository%2Bhas%2Bbeen%2Bdeleted.", flashCookie.Value) }) }) } diff --git a/tests/integration/git_push_test.go b/tests/integration/git_push_test.go index 110bb6c886..bd7a464354 100644 --- a/tests/integration/git_push_test.go +++ b/tests/integration/git_push_test.go @@ -176,7 +176,7 @@ func runTestGitPush(t *testing.T, u *url.URL, objectFormat git.ObjectFormat, git dbBranches := make([]*git_model.Branch, 0) require.NoError(t, db.GetEngine(db.DefaultContext).Where("repo_id=?", repo.ID).Find(&dbBranches)) - assert.Equalf(t, len(pushedBranches), len(dbBranches), "mismatched number of branches in db") + assert.Lenf(t, dbBranches, len(pushedBranches), "mismatched number of branches in db") dbBranchesMap := make(map[string]*git_model.Branch, len(dbBranches)) for _, branch := range dbBranches { dbBranchesMap[branch.Name] = branch diff --git a/tests/integration/git_smart_http_test.go b/tests/integration/git_smart_http_test.go index 671ba07d23..84de08751d 100644 --- a/tests/integration/git_smart_http_test.go +++ b/tests/integration/git_smart_http_test.go @@ -71,7 +71,7 @@ func testGitSmartHTTP(t *testing.T, u *url.URL) { resp, err := http.DefaultClient.Do(req) require.NoError(t, err) defer resp.Body.Close() - assert.EqualValues(t, kase.code, resp.StatusCode) + assert.Equal(t, kase.code, resp.StatusCode) _, err = io.ReadAll(resp.Body) require.NoError(t, err) }) diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index d8fed1df2e..d61a28f1e2 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -462,7 +462,7 @@ func doProtectBranch(ctx APITestContext, branch string, addParameter ...paramete // Check if master branch has been locked successfully flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522"+url.QueryEscape(branch)+"%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) + assert.Equal(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522"+url.QueryEscape(branch)+"%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) } } @@ -578,7 +578,7 @@ func doEnsureCanSeePull(ctx APITestContext, pr api.PullRequest, editable bool) f doc := NewHTMLParser(t, resp.Body) editButtonCount := doc.doc.Find("div.diff-file-header-actions a[href*='/_edit/']").Length() if editable { - assert.Positive(t, editButtonCount, 0, "Expected to find a button to edit a file in the PR diff view but there were none") + assert.Positive(t, editButtonCount, "Expected to find a button to edit a file in the PR diff view but there were none") } else { assert.Equal(t, 0, editButtonCount, "Expected not to find any buttons to edit files in PR diff view but there were some") } @@ -1035,7 +1035,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string currentHeadCommitID, err := upstreamGitRepo.GetRefCommitID(pr.GetGitRefName()) require.NoError(t, err) - assert.EqualValues(t, headCommitID, currentHeadCommitID) + assert.Equal(t, headCommitID, currentHeadCommitID) }) t.Run("Succeeds", func(t *testing.T) { defer tests.PrintCurrentTest(t)() @@ -1045,7 +1045,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string currentHeadCommitID, err := upstreamGitRepo.GetRefCommitID(pr.GetGitRefName()) require.NoError(t, err) - assert.NotEqualValues(t, headCommitID, currentHeadCommitID) + assert.NotEqual(t, headCommitID, currentHeadCommitID) }) }) diff --git a/tests/integration/gpg_git_test.go b/tests/integration/gpg_git_test.go index 0fd998e815..271c225a3f 100644 --- a/tests/integration/gpg_git_test.go +++ b/tests/integration/gpg_git_test.go @@ -103,7 +103,7 @@ func TestGPGGit(t *testing.T) { t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { assert.NotNil(t, response.Verification) if response.Verification == nil { - assert.FailNow(t, "no verification provided with response! %v", response) + assert.FailNow(t, "no verification provided with response", "response: %v", response) } assert.True(t, response.Verification.Verified) if !response.Verification.Verified { @@ -115,7 +115,7 @@ func TestGPGGit(t *testing.T) { t, testCtx, user, "parentsigned", "parentsigned-always", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) { assert.NotNil(t, response.Verification) if response.Verification == nil { - assert.FailNow(t, "no verification provided with response! %v", response) + assert.FailNow(t, "no verification provided with response", "response: %v", response) } assert.True(t, response.Verification.Verified) if !response.Verification.Verified { @@ -133,7 +133,7 @@ func TestGPGGit(t *testing.T) { t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) { assert.NotNil(t, response.Verification) if response.Verification == nil { - assert.FailNow(t, "no verification provided with response! %v", response) + assert.FailNow(t, "no verification provided with response", "response: %v", response) } assert.True(t, response.Verification.Verified) if !response.Verification.Verified { @@ -151,11 +151,11 @@ func TestGPGGit(t *testing.T) { t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { assert.NotNil(t, branch.Commit) if branch.Commit == nil { - assert.FailNow(t, "no commit provided with branch! %v", branch) + assert.FailNow(t, "no commit provided with branch", "branch: %v", branch) } assert.NotNil(t, branch.Commit.Verification) if branch.Commit.Verification == nil { - assert.FailNow(t, "no verification provided with branch commit! %v", branch.Commit) + assert.FailNow(t, "no verification provided with branch commit", "commit: %v", branch.Commit) } assert.True(t, branch.Commit.Verification.Verified) if !branch.Commit.Verification.Verified { diff --git a/tests/integration/html_helper.go b/tests/integration/html_helper.go index 802dcb05a4..dc65842dd7 100644 --- a/tests/integration/html_helper.go +++ b/tests/integration/html_helper.go @@ -51,7 +51,7 @@ func (doc *HTMLDoc) AssertDropdownHasOptions(t testing.TB, dropdownName string) t.Helper() options := doc.AssertDropdown(t, dropdownName).Find(".menu [data-value]:not([data-value=''])") - assert.Positive(t, options.Length(), 0, fmt.Sprintf("%s dropdown has no options", dropdownName)) + assert.Positive(t, options.Length(), "%s dropdown has no options", dropdownName) } func (doc *HTMLDoc) AssertDropdownHasSelectedOption(t testing.TB, dropdownName, expectedValue string) { diff --git a/tests/integration/incoming_email_test.go b/tests/integration/incoming_email_test.go index a47aaed87a..40a501b0cf 100644 --- a/tests/integration/incoming_email_test.go +++ b/tests/integration/incoming_email_test.go @@ -52,12 +52,12 @@ func TestIncomingEmail(t *testing.T) { ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload) require.NoError(t, err) assert.IsType(t, ref, new(issues_model.Issue)) - assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID) + assert.Equal(t, issue.ID, ref.(*issues_model.Issue).ID) ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload) require.NoError(t, err) assert.IsType(t, ref, new(issues_model.Comment)) - assert.EqualValues(t, comment.ID, ref.(*issues_model.Comment).ID) + assert.Equal(t, comment.ID, ref.(*issues_model.Comment).ID) }) t.Run("Token", func(t *testing.T) { diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 4257a999d9..a18e3b0abb 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -561,7 +561,7 @@ func MakeRequest(t testing.TB, rw *RequestWrapper, expectedStatus int) *httptest } testWebRoutes.ServeHTTP(recorder, req) if expectedStatus != NoExpectedStatus { - if !assert.EqualValues(t, expectedStatus, recorder.Code, "Request: %s %s", req.Method, req.URL.String()) { + if !assert.Equal(t, expectedStatus, recorder.Code, "Request: %s %s", req.Method, req.URL.String()) { logUnexpectedResponse(t, recorder) } } @@ -574,7 +574,7 @@ func MakeRequestNilResponseRecorder(t testing.TB, rw *RequestWrapper, expectedSt recorder := NewNilResponseRecorder() testWebRoutes.ServeHTTP(recorder, req) if expectedStatus != NoExpectedStatus { - if !assert.EqualValues(t, expectedStatus, recorder.Code, + if !assert.Equal(t, expectedStatus, recorder.Code, "Request: %s %s", req.Method, req.URL.String()) { logUnexpectedResponse(t, &recorder.ResponseRecorder) } @@ -588,7 +588,7 @@ func MakeRequestNilResponseHashSumRecorder(t testing.TB, rw *RequestWrapper, exp recorder := NewNilResponseHashSumRecorder() testWebRoutes.ServeHTTP(recorder, req) if expectedStatus != NoExpectedStatus { - if !assert.EqualValues(t, expectedStatus, recorder.Code, + if !assert.Equal(t, expectedStatus, recorder.Code, "Request: %s %s", req.Method, req.URL.String()) { logUnexpectedResponse(t, &recorder.ResponseRecorder) } diff --git a/tests/integration/issue_subscribe_test.go b/tests/integration/issue_subscribe_test.go index 32001cd22d..533526c388 100644 --- a/tests/integration/issue_subscribe_test.go +++ b/tests/integration/issue_subscribe_test.go @@ -29,16 +29,16 @@ func testIssueSubscribe(t *testing.T, session TestSession, unavailable bool) { resp := session.MakeRequest(t, NewRequest(t, "GET", path.Join(testIssue)), http.StatusOK) area := NewHTMLParser(t, resp.Body).Find(selector) tooltip, exists := area.Attr("data-tooltip-content") - assert.EqualValues(t, unavailable, exists) + assert.Equal(t, unavailable, exists) if unavailable { - assert.EqualValues(t, "Sign in to subscribe to this issue.", tooltip) + assert.Equal(t, "Sign in to subscribe to this issue.", tooltip) } resp = session.MakeRequest(t, NewRequest(t, "GET", path.Join(testPull)), http.StatusOK) area = NewHTMLParser(t, resp.Body).Find(selector) tooltip, exists = area.Attr("data-tooltip-content") - assert.EqualValues(t, unavailable, exists) + assert.Equal(t, unavailable, exists) if unavailable { - assert.EqualValues(t, "Sign in to subscribe to this pull request.", tooltip) + assert.Equal(t, "Sign in to subscribe to this pull request.", tooltip) } } diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 5b06b15dd7..6391fd4bff 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -39,7 +39,7 @@ import ( func getIssuesSelection(t testing.TB, htmlDoc *HTMLDoc) *goquery.Selection { issueList := htmlDoc.doc.Find("#issue-list") - assert.EqualValues(t, 1, issueList.Length()) + assert.Equal(t, 1, issueList.Length()) return issueList.Find(".flex-item").Find(".issue-title") } @@ -103,11 +103,11 @@ func TestViewIssuesSortByType(t *testing.T) { if expectedNumIssues > setting.UI.IssuePagingNum { expectedNumIssues = setting.UI.IssuePagingNum } - assert.EqualValues(t, expectedNumIssues, issuesSelection.Length()) + assert.Equal(t, expectedNumIssues, issuesSelection.Length()) issuesSelection.Each(func(_ int, selection *goquery.Selection) { issue := getIssue(t, repo.ID, selection) - assert.EqualValues(t, user.ID, issue.PosterID) + assert.Equal(t, user.ID, issue.PosterID) }) } @@ -128,7 +128,7 @@ func TestViewIssuesKeyword(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) issuesSelection := getIssuesSelection(t, htmlDoc) - assert.EqualValues(t, 1, issuesSelection.Length()) + assert.Equal(t, 1, issuesSelection.Length()) issuesSelection.Each(func(_ int, selection *goquery.Selection) { issue := getIssue(t, repo.ID, selection) assert.False(t, issue.IsClosed) @@ -142,14 +142,14 @@ func TestViewIssuesKeyword(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) htmlDoc = NewHTMLParser(t, resp.Body) issuesSelection = getIssuesSelection(t, htmlDoc) - assert.EqualValues(t, 0, issuesSelection.Length()) + assert.Equal(t, 0, issuesSelection.Length()) // should match as 'first' when using a standard query req = NewRequestf(t, "GET", "%s/issues?q=%st", repo.Link(), keyword) resp = MakeRequest(t, req, http.StatusOK) htmlDoc = NewHTMLParser(t, resp.Body) issuesSelection = getIssuesSelection(t, htmlDoc) - assert.EqualValues(t, 1, issuesSelection.Length()) + assert.Equal(t, 1, issuesSelection.Length()) issuesSelection.Each(func(_ int, selection *goquery.Selection) { issue := getIssue(t, repo.ID, selection) assert.False(t, issue.IsClosed) @@ -172,7 +172,7 @@ func TestViewIssuesSearchOptions(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) issuesSelection := getIssuesSelection(t, htmlDoc) - assert.EqualValues(t, 3, issuesSelection.Length()) + assert.Equal(t, 3, issuesSelection.Length()) }) t.Run("Issues with no project", func(t *testing.T) { @@ -180,7 +180,7 @@ func TestViewIssuesSearchOptions(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) issuesSelection := getIssuesSelection(t, htmlDoc) - assert.EqualValues(t, 1, issuesSelection.Length()) + assert.Equal(t, 1, issuesSelection.Length()) issuesSelection.Each(func(_ int, selection *goquery.Selection) { issue := getIssue(t, repo.ID, selection) assert.Equal(t, issueNoProject.ID, issue.ID) @@ -194,7 +194,7 @@ func TestViewIssuesSearchOptions(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) issuesSelection := getIssuesSelection(t, htmlDoc) - assert.EqualValues(t, 2, issuesSelection.Length()) + assert.Equal(t, 2, issuesSelection.Length()) found := map[int64]bool{ 1: false, 5: false, @@ -389,7 +389,7 @@ func TestIssueDependencies(t *testing.T) { if hasDependency { assert.NotEmpty(t, issues) - assert.EqualValues(t, issues[0].Index, dependencyID) + assert.Equal(t, issues[0].Index, dependencyID) } else { assert.Empty(t, issues) } @@ -569,7 +569,7 @@ func TestIssueCommentUpdate(t *testing.T) { session.MakeRequest(t, req, http.StatusOK) comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: commentID}) - assert.Equal(t, "", comment.Content) + assert.Empty(t, comment.Content) } func TestIssueCommentUpdateSimultaneously(t *testing.T) { @@ -798,7 +798,7 @@ func TestSearchIssues(t *testing.T) { req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) - assert.EqualValues(t, "22", resp.Header().Get("X-Total-Count")) + assert.Equal(t, "22", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 20) query.Add("limit", "5") @@ -806,7 +806,7 @@ func TestSearchIssues(t *testing.T) { req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) - assert.EqualValues(t, "22", resp.Header().Get("X-Total-Count")) + assert.Equal(t, "22", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 5) query = url.Values{"assigned": {"true"}, "state": {"all"}} @@ -930,14 +930,14 @@ func TestGetIssueInfo(t *testing.T) { var apiIssue api.Issue DecodeJSON(t, resp, &apiIssue) - assert.EqualValues(t, issue.ID, apiIssue.ID) + assert.Equal(t, issue.ID, apiIssue.ID) } func TestIssuePinMove(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2") issueURL, issue := testIssueWithBean(t, "user2", 1, "Title", "Content") - assert.EqualValues(t, 0, issue.PinOrder) + assert.Equal(t, 0, issue.PinOrder) req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/pin", issueURL), map[string]string{ "_csrf": GetCSRF(t, session, issueURL), @@ -946,7 +946,7 @@ func TestIssuePinMove(t *testing.T) { issue = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID}) position := 1 - assert.EqualValues(t, position, issue.PinOrder) + assert.Equal(t, position, issue.PinOrder) newPosition := 2 @@ -961,7 +961,7 @@ func TestIssuePinMove(t *testing.T) { session5.MakeRequest(t, req, http.StatusNotFound) issue = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID}) - assert.EqualValues(t, position, issue.PinOrder) + assert.Equal(t, position, issue.PinOrder) } movePinURL := issueURL[:strings.LastIndexByte(issueURL, '/')] + "/move_pin?_csrf=" + GetCSRF(t, session, issueURL) @@ -972,7 +972,7 @@ func TestIssuePinMove(t *testing.T) { session.MakeRequest(t, req, http.StatusNoContent) issue = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID}) - assert.EqualValues(t, newPosition, issue.PinOrder) + assert.Equal(t, newPosition, issue.PinOrder) } func TestUpdateIssueDeadline(t *testing.T) { @@ -1001,7 +1001,7 @@ func TestUpdateIssueDeadline(t *testing.T) { var apiIssue api.IssueDeadline DecodeJSON(t, resp, &apiIssue) - assert.EqualValues(t, "2022-04-06", apiIssue.Deadline.Format("2006-01-02")) + assert.Equal(t, "2022-04-06", apiIssue.Deadline.Format("2006-01-02")) } func TestUpdateIssueTitle(t *testing.T) { @@ -1059,7 +1059,7 @@ func TestUpdateIssueTitle(t *testing.T) { }{} DecodeJSON(t, resp, &issueAfter) - assert.EqualValues(t, issueTitleUpdateTest.title, issueAfter.Title) + assert.Equal(t, issueTitleUpdateTest.title, issueAfter.Title) } } } @@ -1077,10 +1077,10 @@ func TestIssueReferenceURL(t *testing.T) { // the "reference" uses relative URLs, then JS code will convert them to absolute URLs for current origin, in case users are using multiple domains ref, _ := htmlDoc.Find(`.timeline-item.comment.first .reference-issue`).Attr("data-reference") - assert.EqualValues(t, "/user2/repo1/issues/1#issue-1", ref) + assert.Equal(t, "/user2/repo1/issues/1#issue-1", ref) ref, _ = htmlDoc.Find(`.timeline-item.comment:not(.first) .reference-issue`).Attr("data-reference") - assert.EqualValues(t, "/user2/repo1/issues/1#issuecomment-2", ref) + assert.Equal(t, "/user2/repo1/issues/1#issuecomment-2", ref) } func TestGetContentHistory(t *testing.T) { @@ -1109,7 +1109,7 @@ func TestGetContentHistory(t *testing.T) { var respJSON contentHistoryResp DecodeJSON(t, resp, &respJSON) - assert.EqualValues(t, canDelete, respJSON.CanSoftDelete) + assert.Equal(t, canDelete, respJSON.CanSoftDelete) assert.EqualValues(t, contentHistory.ID, respJSON.HistoryID) assert.EqualValues(t, contentHistory.ID-1, respJSON.PrevHistoryID) } @@ -1357,7 +1357,7 @@ func TestIssuePostersSearch(t *testing.T) { DecodeJSON(t, resp, &data) assert.Len(t, data.Results, 1) - assert.EqualValues(t, "user2", data.Results[0].UserName) + assert.Equal(t, "user2", data.Results[0].UserName) assert.EqualValues(t, 2, data.Results[0].UserID) }) @@ -1372,7 +1372,7 @@ func TestIssuePostersSearch(t *testing.T) { DecodeJSON(t, resp, &data) assert.Len(t, data.Results, 1) - assert.EqualValues(t, "user1", data.Results[0].UserName) + assert.Equal(t, "user1", data.Results[0].UserName) assert.EqualValues(t, 1, data.Results[0].UserID) }) } diff --git a/tests/integration/issue_tracked_time_test.go b/tests/integration/issue_tracked_time_test.go new file mode 100644 index 0000000000..f2e0df5a84 --- /dev/null +++ b/tests/integration/issue_tracked_time_test.go @@ -0,0 +1,77 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package integration + +import ( + "net/http" + "testing" + + issues_model "forgejo.org/models/issues" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + forgejo_context "forgejo.org/services/context" + "forgejo.org/tests" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIssueAddTimeManually(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, user2.Name) + issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) + require.NoError(t, issue2.LoadRepo(t.Context())) + + t.Run("No time", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session.MakeRequest(t, NewRequestWithValues(t, "POST", issue2.Link()+"/times/add", map[string]string{ + "_csrf": GetCSRF(t, session, issue2.Link()), + }), http.StatusSeeOther) + + flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.Contains(t, flashCookie.Value, "error%3DNo%2Btime%2Bwas%2Bentered.") + }) + + t.Run("Invalid hours", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session.MakeRequest(t, NewRequestWithValues(t, "POST", issue2.Link()+"/times/add", map[string]string{ + "_csrf": GetCSRF(t, session, issue2.Link()), + "hours": "-1", + }), http.StatusSeeOther) + + flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.Contains(t, flashCookie.Value, "error%3DHours%2Bmust%2Bbe%2Ba%2Bnumber%2Bbetween%2B0%2Band%2B1%252C000.") + }) + + t.Run("Invalid minutes", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session.MakeRequest(t, NewRequestWithValues(t, "POST", issue2.Link()+"/times/add", map[string]string{ + "_csrf": GetCSRF(t, session, issue2.Link()), + "minutes": "-1", + }), http.StatusSeeOther) + + flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.Contains(t, flashCookie.Value, "error%3DMinutes%2Bmust%2Bbe%2Ba%2Bnumber%2Bbetween%2B0%2Band%2B1%252C000.") + }) + + t.Run("Normal", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session.MakeRequest(t, NewRequestWithValues(t, "POST", issue2.Link()+"/times/add", map[string]string{ + "_csrf": GetCSRF(t, session, issue2.Link()), + "hours": "3", + "minutes": "14", + }), http.StatusSeeOther) + + unittest.AssertExistsIf(t, true, &issues_model.TrackedTime{IssueID: issue2.ID, Time: 11640, UserID: user2.ID}) + }) +} diff --git a/tests/integration/links_test.go b/tests/integration/links_test.go index 3d240f900a..ccfaf144cf 100644 --- a/tests/integration/links_test.go +++ b/tests/integration/links_test.go @@ -60,7 +60,7 @@ func TestRedirectsNoLogin(t *testing.T) { for link, redirectLink := range redirects { req := NewRequest(t, "GET", link) resp := MakeRequest(t, req, http.StatusSeeOther) - assert.EqualValues(t, path.Join(setting.AppSubURL, redirectLink), test.RedirectURL(resp)) + assert.Equal(t, path.Join(setting.AppSubURL, redirectLink), test.RedirectURL(resp)) } } @@ -195,7 +195,7 @@ func TestRedirectsWebhooks(t *testing.T) { for _, info := range redirects { req := NewRequest(t, info.verb, info.from) resp := MakeRequest(t, req, http.StatusSeeOther) - assert.EqualValues(t, path.Join(setting.AppSubURL, info.to), test.RedirectURL(resp), info.from) + assert.Equal(t, path.Join(setting.AppSubURL, info.to), test.RedirectURL(resp), info.from) } } diff --git a/tests/integration/markup_external_test.go b/tests/integration/markup_external_test.go index e77d9682a4..a7cbdf37cb 100644 --- a/tests/integration/markup_external_test.go +++ b/tests/integration/markup_external_test.go @@ -27,7 +27,7 @@ func TestExternalMarkupRenderer(t *testing.T) { const repoURL = "user30/renderer" req := NewRequest(t, "GET", repoURL+"/src/branch/master/README.html") resp := MakeRequest(t, req, http.StatusOK) - assert.EqualValues(t, "text/html; charset=utf-8", resp.Header()["Content-Type"][0]) + assert.Equal(t, "text/html; charset=utf-8", resp.Header()["Content-Type"][0]) bs, err := io.ReadAll(resp.Body) require.NoError(t, err) @@ -36,5 +36,5 @@ func TestExternalMarkupRenderer(t *testing.T) { div := doc.Find("div.file-view") data, err := div.Html() require.NoError(t, err) - assert.EqualValues(t, "<div>\n\ttest external renderer\n</div>", strings.TrimSpace(data)) + assert.Equal(t, "<div>\n\ttest external renderer\n</div>", strings.TrimSpace(data)) } diff --git a/tests/integration/migrate_test.go b/tests/integration/migrate_test.go index 32b3ef867d..dc11101d97 100644 --- a/tests/integration/migrate_test.go +++ b/tests/integration/migrate_test.go @@ -106,7 +106,7 @@ func TestMigrate(t *testing.T) { }) resp = session.MakeRequest(t, req, http.StatusSeeOther) // Step 5: a redirection displays the migrated repository - assert.EqualValues(t, fmt.Sprintf("/%s/%s", ownerName, migratedRepoName), test.RedirectURL(resp)) + assert.Equal(t, fmt.Sprintf("/%s/%s", ownerName, migratedRepoName), test.RedirectURL(resp)) // Step 6: check the repo was created unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: migratedRepoName}) }) @@ -163,7 +163,7 @@ func TestMigrateWithWiki(t *testing.T) { }) resp = session.MakeRequest(t, req, http.StatusSeeOther) // Step 5: a redirection displays the migrated repository - assert.EqualValues(t, fmt.Sprintf("/%s/%s", user.Name, migratedRepoName), test.RedirectURL(resp)) + assert.Equal(t, fmt.Sprintf("/%s/%s", user.Name, migratedRepoName), test.RedirectURL(resp)) // Step 6: check the repo was created and load the repo migratedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: migratedRepoName, WikiBranch: "obscure-name"}) // Step 7: check if the wiki is enabled @@ -219,7 +219,7 @@ func TestMigrateWithReleases(t *testing.T) { }) resp = session.MakeRequest(t, req, http.StatusSeeOther) // Step 5: a redirection displays the migrated repository - assert.EqualValues(t, fmt.Sprintf("/%s/%s", ownerName, migratedRepoName), test.RedirectURL(resp)) + assert.Equal(t, fmt.Sprintf("/%s/%s", ownerName, migratedRepoName), test.RedirectURL(resp)) // Step 6: check the repo was created and load the repo migratedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: migratedRepoName}) // Step 7: check if releases are enabled diff --git a/tests/integration/migration-test/migration_test.go b/tests/integration/migration-test/migration_test.go index 8076dfa452..798161a560 100644 --- a/tests/integration/migration-test/migration_test.go +++ b/tests/integration/migration-test/migration_test.go @@ -278,23 +278,36 @@ func doMigrationTest(t *testing.T, version string) { setting.InitSQLLoggersForCli(log.INFO) - err := db.InitEngineWithMigration(t.Context(), wrappedMigrate) + err := db.InitEngineWithMigration(t.Context(), func(e db.Engine) error { + engine, err := db.GetMasterEngine(e) + if err != nil { + return err + } + currentEngine = engine + return wrappedMigrate(engine) + }) require.NoError(t, err) currentEngine.Close() beans, _ := db.NamesToBean() - err = db.InitEngineWithMigration(t.Context(), func(x *xorm.Engine) error { - currentEngine = x - return migrate_base.RecreateTables(beans...)(x) + err = db.InitEngineWithMigration(t.Context(), func(e db.Engine) error { + currentEngine, err = db.GetMasterEngine(e) + if err != nil { + return err + } + return migrate_base.RecreateTables(beans...)(currentEngine) }) require.NoError(t, err) currentEngine.Close() // We do this a second time to ensure that there is not a problem with retained indices - err = db.InitEngineWithMigration(t.Context(), func(x *xorm.Engine) error { - currentEngine = x - return migrate_base.RecreateTables(beans...)(x) + err = db.InitEngineWithMigration(t.Context(), func(e db.Engine) error { + currentEngine, err = db.GetMasterEngine(e) + if err != nil { + return err + } + return migrate_base.RecreateTables(beans...)(currentEngine) }) require.NoError(t, err) diff --git a/tests/integration/mirror_pull_test.go b/tests/integration/mirror_pull_test.go index c9c8037e27..c98af1b773 100644 --- a/tests/integration/mirror_pull_test.go +++ b/tests/integration/mirror_pull_test.go @@ -88,7 +88,7 @@ func TestMirrorPull(t *testing.T) { count, err := db.Count[repo_model.Release](db.DefaultContext, findOptions) require.NoError(t, err) - assert.EqualValues(t, initCount+1, count) + assert.Equal(t, initCount+1, count) release, err := repo_model.GetRelease(db.DefaultContext, repo.ID, "v0.2") require.NoError(t, err) @@ -99,5 +99,5 @@ func TestMirrorPull(t *testing.T) { count, err = db.Count[repo_model.Release](db.DefaultContext, findOptions) require.NoError(t, err) - assert.EqualValues(t, initCount, count) + assert.Equal(t, initCount, count) } diff --git a/tests/integration/mirror_push_test.go b/tests/integration/mirror_push_test.go index ca8d536e46..556e534ea3 100644 --- a/tests/integration/mirror_push_test.go +++ b/tests/integration/mirror_push_test.go @@ -261,7 +261,7 @@ func TestSSHPushMirror(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) publickey = htmlDoc.Find(".ui.table td a[data-clipboard-text]").AttrOr("data-clipboard-text", "") - assert.EqualValues(t, publickey, pushMirror.GetPublicKey()) + assert.Equal(t, publickey, pushMirror.GetPublicKey()) }) t.Run("Add deploy key", func(t *testing.T) { diff --git a/tests/integration/new_org_test.go b/tests/integration/new_org_test.go index ab9ee4bc18..5ea386573a 100644 --- a/tests/integration/new_org_test.go +++ b/tests/integration/new_org_test.go @@ -32,6 +32,6 @@ func TestNewOrganizationForm(t *testing.T) { // Verify page header header := strings.TrimSpace(page.Find(".form[action='/org/create'] .header").Text()) - assert.EqualValues(t, locale.TrString("new_org.title"), header) + assert.Equal(t, locale.TrString("new_org.title"), header) }) } diff --git a/tests/integration/oauth_test.go b/tests/integration/oauth_test.go index a4dea51a07..298a1ee716 100644 --- a/tests/integration/oauth_test.go +++ b/tests/integration/oauth_test.go @@ -921,7 +921,7 @@ func requireCookieCSRF(t *testing.T, resp http.ResponseWriter) string { return c.Value } } - require.True(t, false, "_csrf not found in cookies") + require.Fail(t, "_csrf not found in cookies") return "" } diff --git a/tests/integration/opengraph_test.go b/tests/integration/opengraph_test.go index 38998d0c76..56fbedd351 100644 --- a/tests/integration/opengraph_test.go +++ b/tests/integration/opengraph_test.go @@ -180,7 +180,7 @@ func TestOpenGraphProperties(t *testing.T) { foundProps[prop] = content }) - assert.EqualValues(t, tc.expected, foundProps, "mismatching opengraph properties") + assert.Equal(t, tc.expected, foundProps, "mismatching opengraph properties") }) } } diff --git a/tests/integration/org_nav_test.go b/tests/integration/org_nav_test.go index 09736d12a0..d8f4368a51 100644 --- a/tests/integration/org_nav_test.go +++ b/tests/integration/org_nav_test.go @@ -41,8 +41,8 @@ func TestOrgNavigationDashboard(t *testing.T) { // Verify the "New repository" and "New migration" buttons links := doc.Find(".organization.profile .grid .column .center") - assert.EqualValues(t, locale.TrString("new_repo.link"), strings.TrimSpace(links.Find("a[href^='/repo/create?org=']").Text())) - assert.EqualValues(t, locale.TrString("new_migrate.link"), strings.TrimSpace(links.Find("a[href^='/repo/migrate?org=']").Text())) + assert.Equal(t, locale.TrString("new_repo.link"), strings.TrimSpace(links.Find("a[href^='/repo/create?org=']").Text())) + assert.Equal(t, locale.TrString("new_migrate.link"), strings.TrimSpace(links.Find("a[href^='/repo/migrate?org=']").Text())) // Check if the "View <orgname>" button is available on dashboard for the org admin (member) resp = session1.MakeRequest(t, NewRequest(t, "GET", "/org/org_navigation_test/dashboard"), http.StatusOK) diff --git a/tests/integration/org_test.go b/tests/integration/org_test.go index bc1a5bbe72..4035818e1a 100644 --- a/tests/integration/org_test.go +++ b/tests/integration/org_test.go @@ -47,7 +47,7 @@ func TestOrgRepos(t *testing.T) { sel := htmlDoc.doc.Find("a.name") assert.Len(t, repos, len(sel.Nodes)) for i := 0; i < len(repos); i++ { - assert.EqualValues(t, repos[i], strings.TrimSpace(sel.Eq(i).Text())) + assert.Equal(t, repos[i], strings.TrimSpace(sel.Eq(i).Text())) } } }) diff --git a/tests/integration/private_project_test.go b/tests/integration/private_project_test.go index b2ad8d1cd7..5259c0531c 100644 --- a/tests/integration/private_project_test.go +++ b/tests/integration/private_project_test.go @@ -48,19 +48,19 @@ func TestPrivateIssueProject(t *testing.T) { htmlDoc = NewHTMLParser(t, resp.Body) issueCardsLen := htmlDoc.Find(".project-column .issue-card").Length() if hasAccess { - assert.EqualValues(t, 2, issueCardsLen) + assert.Equal(t, 2, issueCardsLen) } else { - assert.EqualValues(t, 1, issueCardsLen) + assert.Equal(t, 1, issueCardsLen) // Ensure that the public issue is shown. - assert.EqualValues(t, publicIssueHref[0], htmlDoc.Find(".project-column .issue-card .issue-card-title").AttrOr("href", "")) + assert.Equal(t, publicIssueHref[0], htmlDoc.Find(".project-column .issue-card .issue-card-title").AttrOr("href", "")) } // And that the issue count is correct. issueCount := strings.TrimSpace(htmlDoc.Find(".project-column-issue-count").Text()) if hasAccess { - assert.EqualValues(t, "2", issueCount) + assert.Equal(t, "2", issueCount) } else { - assert.EqualValues(t, "1", issueCount) + assert.Equal(t, "1", issueCount) } } diff --git a/tests/integration/project_test.go b/tests/integration/project_test.go index 7dc8bf112d..2fbc5d00c5 100644 --- a/tests/integration/project_test.go +++ b/tests/integration/project_test.go @@ -76,9 +76,9 @@ func TestMoveRepoProjectColumns(t *testing.T) { columnsAfter, err := project1.GetColumns(db.DefaultContext) require.NoError(t, err) assert.Len(t, columns, 3) - assert.EqualValues(t, columns[1].ID, columnsAfter[0].ID) - assert.EqualValues(t, columns[2].ID, columnsAfter[1].ID) - assert.EqualValues(t, columns[0].ID, columnsAfter[2].ID) + assert.Equal(t, columns[1].ID, columnsAfter[0].ID) + assert.Equal(t, columns[2].ID, columnsAfter[1].ID) + assert.Equal(t, columns[0].ID, columnsAfter[2].ID) require.NoError(t, project_model.DeleteProjectByID(db.DefaultContext, project1.ID)) } diff --git a/tests/integration/pull_compare_test.go b/tests/integration/pull_compare_test.go index adb24c60f6..59248588fb 100644 --- a/tests/integration/pull_compare_test.go +++ b/tests/integration/pull_compare_test.go @@ -24,5 +24,5 @@ func TestPullCompare(t *testing.T) { req = NewRequest(t, "GET", link) resp = session.MakeRequest(t, req, http.StatusOK) - assert.EqualValues(t, http.StatusOK, resp.Code) + assert.Equal(t, http.StatusOK, resp.Code) } diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go index 42155076a8..43c9bef209 100644 --- a/tests/integration/pull_create_test.go +++ b/tests/integration/pull_create_test.go @@ -539,16 +539,16 @@ func TestRecentlyPushed(t *testing.T) { respChildPR := testPullCreateDirectly(t, session, "user2", "repo1", "master", "user1", "repo1", "recent-push", "Child Pull Request") elemChildPR := strings.Split(test.RedirectURL(respChildPR), "/") - assert.EqualValues(t, "user2", elemChildPR[1]) - assert.EqualValues(t, "repo1", elemChildPR[2]) - assert.EqualValues(t, "pulls", elemChildPR[3]) + assert.Equal(t, "user2", elemChildPR[1]) + assert.Equal(t, "repo1", elemChildPR[2]) + assert.Equal(t, "pulls", elemChildPR[3]) session2 := loginUser(t, "user2") // Merge the PR from the fork testPullMerge(t, session2, elemChildPR[1], elemChildPR[2], elemChildPR[4], repo_model.MergeStyleSquash, false) respBasePR := testPullCreate(t, session, "user2", "repo1", true, "master", "recent-push-base", "Base Pull Request") elemBasePR := strings.Split(test.RedirectURL(respBasePR), "/") - assert.EqualValues(t, "pulls", elemBasePR[3]) + assert.Equal(t, "pulls", elemBasePR[3]) // Leave the PR from the base repo open (it conflicts with the PR from the fork anyway) // Count recently pushed branches on the base repo diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index f60fc27bb3..774b96cb7a 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -84,7 +84,7 @@ func testPullMergeForm(t *testing.T, session *TestSession, expectedCode int, use }{} DecodeJSON(t, resp, &respJSON) - assert.EqualValues(t, fmt.Sprintf("/%s/%s/pulls/%s", user, repo, pullnum), respJSON.Redirect) + assert.Equal(t, fmt.Sprintf("/%s/%s/pulls/%s", user, repo, pullnum), respJSON.Redirect) } return resp @@ -144,7 +144,7 @@ func TestPullMerge(t *testing.T) { resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title") elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false) hookTasks = retrieveHookTasks(t, 1, false) @@ -164,7 +164,7 @@ func TestPullRebase(t *testing.T) { resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title") elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebase, false) hookTasks = retrieveHookTasks(t, 1, false) @@ -184,7 +184,7 @@ func TestPullRebaseMerge(t *testing.T) { resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title") elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebaseMerge, false) hookTasks = retrieveHookTasks(t, 1, false) @@ -205,7 +205,7 @@ func TestPullSquash(t *testing.T) { resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title") elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleSquash, false) hookTasks = retrieveHookTasks(t, 1, false) @@ -222,7 +222,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) { resp := testPullCreate(t, session, "user1", "repo1", false, "master", "feature/test", "This is a pull title") elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false) // Check PR branch deletion @@ -235,7 +235,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) { assert.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found") elem = strings.Split(respJSON.Redirect, "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) // Check branch deletion result req := NewRequest(t, "GET", respJSON.Redirect) @@ -244,7 +244,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) resultMsg := htmlDoc.doc.Find(".ui.message>p").Text() - assert.EqualValues(t, "Branch \"user1/repo1:feature/test\" has been deleted.", resultMsg) + assert.Equal(t, "Branch \"user1/repo1:feature/test\" has been deleted.", resultMsg) }) } @@ -573,11 +573,11 @@ func TestPullRetargetChildOnBranchDelete(t *testing.T) { respBasePR := testPullCreate(t, session, "user2", "repo1", true, "master", "base-pr", "Base Pull Request") elemBasePR := strings.Split(test.RedirectURL(respBasePR), "/") - assert.EqualValues(t, "pulls", elemBasePR[3]) + assert.Equal(t, "pulls", elemBasePR[3]) respChildPR := testPullCreate(t, session, "user1", "repo1", false, "base-pr", "child-pr", "Child Pull Request") elemChildPR := strings.Split(test.RedirectURL(respChildPR), "/") - assert.EqualValues(t, "pulls", elemChildPR[3]) + assert.Equal(t, "pulls", elemChildPR[3]) testPullMerge(t, session, elemBasePR[1], elemBasePR[2], elemBasePR[4], repo_model.MergeStyleMerge, true) @@ -589,8 +589,8 @@ func TestPullRetargetChildOnBranchDelete(t *testing.T) { targetBranch := htmlDoc.doc.Find("#branch_target>a").Text() prStatus := strings.TrimSpace(htmlDoc.doc.Find(".issue-title-meta>.issue-state-label").Text()) - assert.EqualValues(t, "master", targetBranch) - assert.EqualValues(t, "Open", prStatus) + assert.Equal(t, "master", targetBranch) + assert.Equal(t, "Open", prStatus) }) } @@ -603,11 +603,11 @@ func TestPullDontRetargetChildOnWrongRepo(t *testing.T) { respBasePR := testPullCreate(t, session, "user1", "repo1", false, "master", "base-pr", "Base Pull Request") elemBasePR := strings.Split(test.RedirectURL(respBasePR), "/") - assert.EqualValues(t, "pulls", elemBasePR[3]) + assert.Equal(t, "pulls", elemBasePR[3]) respChildPR := testPullCreate(t, session, "user1", "repo1", true, "base-pr", "child-pr", "Child Pull Request") elemChildPR := strings.Split(test.RedirectURL(respChildPR), "/") - assert.EqualValues(t, "pulls", elemChildPR[3]) + assert.Equal(t, "pulls", elemChildPR[3]) testPullMerge(t, session, elemBasePR[1], elemBasePR[2], elemBasePR[4], repo_model.MergeStyleMerge, true) @@ -619,8 +619,8 @@ func TestPullDontRetargetChildOnWrongRepo(t *testing.T) { targetBranch := htmlDoc.doc.Find("#branch_target>a").Text() prStatus := strings.TrimSpace(htmlDoc.doc.Find(".issue-title-meta>.issue-state-label").Text()) - assert.EqualValues(t, "base-pr", targetBranch) - assert.EqualValues(t, "Closed", prStatus) + assert.Equal(t, "base-pr", targetBranch) + assert.Equal(t, "Closed", prStatus) }) } @@ -662,7 +662,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) { // merge the pull request elem := strings.Split(test.RedirectURL(createPullResp), "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false) // check if the issue is closed @@ -696,7 +696,7 @@ func testResetRepo(t *testing.T, repoPath, branch, commitID string) { defer repo.Close() id, err := repo.GetBranchCommitID(branch) require.NoError(t, err) - assert.EqualValues(t, commitID, id) + assert.Equal(t, commitID, id) } func TestPullMergeBranchProtect(t *testing.T) { @@ -1186,7 +1186,7 @@ func TestPullDeleteBranchPerms(t *testing.T) { flashCookie := user4Session.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "error%3DYou%2Bdon%2527t%2Bhave%2Bpermission%2Bto%2Bdelete%2Bthe%2Bhead%2Bbranch.", flashCookie.Value) + assert.Equal(t, "error%3DYou%2Bdon%2527t%2Bhave%2Bpermission%2Bto%2Bdelete%2Bthe%2Bhead%2Bbranch.", flashCookie.Value) // Check that the branch still exist. req = NewRequest(t, "GET", "/user2/repo1/src/branch/base-pr") diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index 6dacdc4315..d917452f83 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -109,7 +109,7 @@ func TestPullView_SelfReviewNotification(t *testing.T) { resp := testPullCreate(t, user2Session, "user2", "test_reviewer", false, repo.DefaultBranch, "codeowner-basebranch", "Test Pull Request") prURL := test.RedirectURL(resp) elem := strings.Split(prURL, "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) req := NewRequest(t, http.MethodGet, prURL) resp = MakeRequest(t, req, http.StatusOK) @@ -390,13 +390,13 @@ func TestPullView_CodeOwner(t *testing.T) { require.NoError(t, err) prUpdated1 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}) require.NoError(t, prUpdated1.LoadIssue(db.DefaultContext)) - assert.EqualValues(t, "[WIP] Test Pull Request", prUpdated1.Issue.Title) + assert.Equal(t, "[WIP] Test Pull Request", prUpdated1.Issue.Title) err = issue_service.ChangeTitle(db.DefaultContext, prUpdated1.Issue, user2, "Test Pull Request2") require.NoError(t, err) prUpdated2 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}) require.NoError(t, prUpdated2.LoadIssue(db.DefaultContext)) - assert.EqualValues(t, "Test Pull Request2", prUpdated2.Issue.Title) + assert.Equal(t, "Test Pull Request2", prUpdated2.Issue.Title) }) // change the default branch CODEOWNERS file to change README.md's codeowner @@ -496,7 +496,7 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) { testEditFile(t, user1Session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "master", "This is a pull title") elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) testPullMerge(t, user1Session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false) // Get the commit SHA @@ -528,7 +528,7 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) { testEditFileToNewBranch(t, user1Session, "user1", "repo1", "master", "a-test-branch", "README.md", "Hello, World (Edited...again)\n") resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "a-test-branch", "This is a pull title") elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) testIssueClose(t, user1Session, elem[1], elem[2], elem[4], true) // Get the commit SHA @@ -713,7 +713,7 @@ func TestPullRequestReplyMail(t *testing.T) { defer test.MockVariableValue(&mailer.SendAsync, func(msgs ...*mailer.Message) { assert.Len(t, msgs, 2) assert.Equal(t, "user1@example.com", msgs[0].To) - assert.EqualValues(t, "Re: [user2/repo1] issue2 (PR #2)", msgs[0].Subject) + assert.Equal(t, "Re: [user2/repo1] issue2 (PR #2)", msgs[0].Subject) assert.Contains(t, msgs[0].Body, "Notification time!") called = true })() @@ -741,7 +741,7 @@ func TestPullRequestReplyMail(t *testing.T) { defer test.MockVariableValue(&mailer.SendAsync, func(msgs ...*mailer.Message) { assert.Len(t, msgs, 2) assert.Equal(t, "user1@example.com", msgs[0].To) - assert.EqualValues(t, "Re: [user2/repo1] issue2 (PR #2)", msgs[0].Subject) + assert.Equal(t, "Re: [user2/repo1] issue2 (PR #2)", msgs[0].Subject) assert.Contains(t, msgs[0].Body, "Notification time 2!") called = true })() diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go index 1fdc871bb3..9786c559ea 100644 --- a/tests/integration/pull_status_test.go +++ b/tests/integration/pull_status_test.go @@ -86,7 +86,7 @@ func TestPullCreate_CommitStatus(t *testing.T) { commitURL, exists = doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href") assert.True(t, exists) assert.NotEmpty(t, commitURL) - assert.EqualValues(t, commitID, path.Base(commitURL)) + assert.Equal(t, commitID, path.Base(commitURL)) cls, ok := doc.doc.Find("#commits-table tbody tr td.message .commit-status").Last().Attr("class") assert.True(t, ok) @@ -95,7 +95,7 @@ func TestPullCreate_CommitStatus(t *testing.T) { repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"}) css := unittest.AssertExistsAndLoadBean(t, &git_model.CommitStatusSummary{RepoID: repo1.ID, SHA: commitID}) - assert.EqualValues(t, api.CommitStatusWarning, css.State) + assert.Equal(t, api.CommitStatusWarning, css.State) }) } diff --git a/tests/integration/pull_update_test.go b/tests/integration/pull_update_test.go index 18766e8fa4..bb3f0ed744 100644 --- a/tests/integration/pull_update_test.go +++ b/tests/integration/pull_update_test.go @@ -39,8 +39,8 @@ func TestAPIPullUpdate(t *testing.T) { // Test GetDiverging diffCount, err := pull_service.GetDiverging(git.DefaultContext, pr) require.NoError(t, err) - assert.EqualValues(t, 1, diffCount.Behind) - assert.EqualValues(t, 1, diffCount.Ahead) + assert.Equal(t, 1, diffCount.Behind) + assert.Equal(t, 1, diffCount.Ahead) require.NoError(t, pr.LoadBaseRepo(db.DefaultContext)) require.NoError(t, pr.LoadIssue(db.DefaultContext)) @@ -53,8 +53,8 @@ func TestAPIPullUpdate(t *testing.T) { // Test GetDiverging after update diffCount, err = pull_service.GetDiverging(git.DefaultContext, pr) require.NoError(t, err) - assert.EqualValues(t, 0, diffCount.Behind) - assert.EqualValues(t, 2, diffCount.Ahead) + assert.Equal(t, 0, diffCount.Behind) + assert.Equal(t, 2, diffCount.Ahead) }) } @@ -68,8 +68,8 @@ func TestAPIPullUpdateByRebase(t *testing.T) { // Test GetDiverging diffCount, err := pull_service.GetDiverging(git.DefaultContext, pr) require.NoError(t, err) - assert.EqualValues(t, 1, diffCount.Behind) - assert.EqualValues(t, 1, diffCount.Ahead) + assert.Equal(t, 1, diffCount.Behind) + assert.Equal(t, 1, diffCount.Ahead) require.NoError(t, pr.LoadBaseRepo(db.DefaultContext)) require.NoError(t, pr.LoadIssue(db.DefaultContext)) @@ -82,8 +82,8 @@ func TestAPIPullUpdateByRebase(t *testing.T) { // Test GetDiverging after update diffCount, err = pull_service.GetDiverging(git.DefaultContext, pr) require.NoError(t, err) - assert.EqualValues(t, 0, diffCount.Behind) - assert.EqualValues(t, 1, diffCount.Ahead) + assert.Equal(t, 0, diffCount.Behind) + assert.Equal(t, 1, diffCount.Ahead) }) } @@ -98,8 +98,8 @@ func TestAPIViewUpdateSettings(t *testing.T) { // Test GetDiverging diffCount, err := pull_service.GetDiverging(git.DefaultContext, pr) require.NoError(t, err) - assert.EqualValues(t, 1, diffCount.Behind) - assert.EqualValues(t, 1, diffCount.Ahead) + assert.Equal(t, 1, diffCount.Behind) + assert.Equal(t, 1, diffCount.Ahead) require.NoError(t, pr.LoadBaseRepo(db.DefaultContext)) require.NoError(t, pr.LoadIssue(db.DefaultContext)) @@ -145,8 +145,8 @@ func testViewPullUpdate(t *testing.T, updateStyle string) { // Test GetDiverging diffCount, err := pull_service.GetDiverging(git.DefaultContext, pr) require.NoError(t, err) - assert.EqualValues(t, 1, diffCount.Behind) - assert.EqualValues(t, 1, diffCount.Ahead) + assert.Equal(t, 1, diffCount.Behind) + assert.Equal(t, 1, diffCount.Ahead) require.NoError(t, pr.LoadBaseRepo(db.DefaultContext)) require.NoError(t, pr.LoadIssue(db.DefaultContext)) diff --git a/tests/integration/quota_use_test.go b/tests/integration/quota_use_test.go index 314dcff6e9..105c2305d0 100644 --- a/tests/integration/quota_use_test.go +++ b/tests/integration/quota_use_test.go @@ -697,7 +697,7 @@ func (ctx *quotaWebEnvAsContext) ExpectFlashMessage(value string) { htmlDoc := NewHTMLParser(ctx.t, ctx.response.Body) flashMessage := strings.TrimSpace(htmlDoc.Find(`.flash-message`).Text()) - assert.EqualValues(ctx.t, value, flashMessage) + assert.Equal(ctx.t, value, flashMessage) } func (ctx *quotaWebEnvAsContext) ExpectFlashMessageContains(parts ...string) { diff --git a/tests/integration/release_feed_test.go b/tests/integration/release_feed_test.go index 0ab299f617..e1781e343e 100644 --- a/tests/integration/release_feed_test.go +++ b/tests/integration/release_feed_test.go @@ -28,7 +28,7 @@ func TestReleaseFeed(t *testing.T) { defer tests.PrintCurrentTest(t)() resp := MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/releases.rss"), http.StatusOK) - assert.EqualValues(t, `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"> + assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"> <channel> <title>Releases for user2/repo1</title> <link>http://localhost/user2/repo1/release</link> @@ -60,7 +60,7 @@ func TestReleaseFeed(t *testing.T) { defer tests.PrintCurrentTest(t)() resp := MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/releases.atom"), http.StatusOK) - assert.EqualValues(t, `<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom"> + assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom"> <title>Releases for user2/repo1</title> <id>http://localhost/user2/repo1/release</id> <updated></updated> diff --git a/tests/integration/release_test.go b/tests/integration/release_test.go index b1162780ab..e5cf76882c 100644 --- a/tests/integration/release_test.go +++ b/tests/integration/release_test.go @@ -64,9 +64,9 @@ func checkLatestReleaseAndCount(t *testing.T, session *TestSession, repoURL, ver htmlDoc := NewHTMLParser(t, resp.Body) labelText := htmlDoc.doc.Find("#release-list > li .detail .label").First().Text() - assert.EqualValues(t, label, labelText) + assert.Equal(t, label, labelText) titleText := htmlDoc.doc.Find("#release-list > li .detail h4 a").First().Text() - assert.EqualValues(t, version, titleText) + assert.Equal(t, version, titleText) // Check release count in the counter on the Release/Tag switch, as well as that the tab is highlighted if count < 10 { // Only check values less than 10, should be enough attempts before this test cracks @@ -76,7 +76,7 @@ func checkLatestReleaseAndCount(t *testing.T, session *TestSession, repoURL, ver } releaseList := htmlDoc.doc.Find("#release-list > li") - assert.EqualValues(t, count, releaseList.Length()) + assert.Equal(t, count, releaseList.Length()) } func TestViewReleases(t *testing.T) { @@ -212,14 +212,14 @@ func TestViewReleaseListNoLogin(t *testing.T) { commitsToMain = append(commitsToMain, s.Find(".ahead > a").Text()) }) - assert.EqualValues(t, []string{ + assert.Equal(t, []string{ "/user2/repo-release/releases/tag/empty-target-branch", "/user2/repo-release/releases/tag/non-existing-target-branch", "/user2/repo-release/releases/tag/v2.0", "/user2/repo-release/releases/tag/v1.1", "/user2/repo-release/releases/tag/v1.0", }, links) - assert.EqualValues(t, []string{ + assert.Equal(t, []string{ "1 commits", // like v1.1 "1 commits", // like v1.1 "0 commits", @@ -237,8 +237,8 @@ func TestViewSingleReleaseNoLogin(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) // check the "number of commits to main since this release" releaseList := htmlDoc.doc.Find("#release-list .ahead > a") - assert.EqualValues(t, 1, releaseList.Length()) - assert.EqualValues(t, "3 commits", releaseList.First().Text()) + assert.Equal(t, 1, releaseList.Length()) + assert.Equal(t, "3 commits", releaseList.First().Text()) } func TestViewReleaseListLogin(t *testing.T) { @@ -265,7 +265,7 @@ func TestViewReleaseListLogin(t *testing.T) { links = append(links, link) }) - assert.EqualValues(t, []string{ + assert.Equal(t, []string{ "/user2/repo1/releases/tag/draft-release", "/user2/repo1/releases/tag/v1.0", "/user2/repo1/releases/tag/v1.1", @@ -296,7 +296,7 @@ func TestViewReleaseListKeyword(t *testing.T) { links = append(links, link) }) - assert.EqualValues(t, []string{ + assert.Equal(t, []string{ "/user2/repo1/releases/tag/v1.1", }, links) } @@ -330,7 +330,7 @@ func TestViewTagsList(t *testing.T) { tagNames = append(tagNames, s.Find(".tag a.tw-flex.tw-items-center").Text()) }) - assert.EqualValues(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames) + assert.Equal(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames) } func TestDownloadReleaseAttachment(t *testing.T) { diff --git a/tests/integration/remote_test.go b/tests/integration/remote_test.go index 506ee8ecaa..b079b7289f 100644 --- a/tests/integration/remote_test.go +++ b/tests/integration/remote_test.go @@ -73,7 +73,7 @@ func TestRemote_MaybePromoteUserSuccess(t *testing.T) { assert.Equal(t, auth_model.Remote, userBeforeSignIn.LoginType) assert.Equal(t, auth_model.OAuth2, userAfterSignIn.LoginType) // the OAuth2 email was used to set the missing user email - assert.Equal(t, "", userBeforeSignIn.Email) + assert.Empty(t, userBeforeSignIn.Email) assert.Equal(t, gitlabEmail, userAfterSignIn.Email) } diff --git a/tests/integration/rename_branch_test.go b/tests/integration/rename_branch_test.go index d7440e5125..2029a3732b 100644 --- a/tests/integration/rename_branch_test.go +++ b/tests/integration/rename_branch_test.go @@ -165,7 +165,7 @@ func testRenameBranch(t *testing.T, u *url.URL) { flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "error%3DCannot%2Brename%2Bbranch%2Bmain2%2Bbecause%2Bit%2Bis%2Ba%2Bprotected%2Bbranch.", flashCookie.Value) + assert.Equal(t, "error%3DCannot%2Brename%2Bbranch%2Bmain2%2Bbecause%2Bit%2Bis%2Ba%2Bprotected%2Bbranch.", flashCookie.Value) // Verify it didn't change. repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) diff --git a/tests/integration/repo_activity_test.go b/tests/integration/repo_activity_test.go index 6199373119..967a55cad6 100644 --- a/tests/integration/repo_activity_test.go +++ b/tests/integration/repo_activity_test.go @@ -35,7 +35,7 @@ func TestRepoActivity(t *testing.T) { testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title") elem := strings.Split(test.RedirectURL(resp), "/") - assert.EqualValues(t, "pulls", elem[3]) + assert.Equal(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false) testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n") diff --git a/tests/integration/repo_collaborator_test.go b/tests/integration/repo_collaborator_test.go index e18308e7cb..4ca0dad708 100644 --- a/tests/integration/repo_collaborator_test.go +++ b/tests/integration/repo_collaborator_test.go @@ -24,14 +24,14 @@ func TestRepoCollaborators(t *testing.T) { page := NewHTMLParser(t, response.Body).Find(".repo-setting-content") // Veirfy header - assert.EqualValues(t, "Collaborators", strings.TrimSpace(page.Find("h4").Text())) + assert.Equal(t, "Collaborators", strings.TrimSpace(page.Find("h4").Text())) // Veirfy button text page = page.Find("#repo-collab-form") - assert.EqualValues(t, "Add collaborator", strings.TrimSpace(page.Find("button.primary").Text())) + assert.Equal(t, "Add collaborator", strings.TrimSpace(page.Find("button.primary").Text())) // Veirfy placeholder placeholder, exists := page.Find("#search-user-box input").Attr("placeholder") assert.True(t, exists) - assert.EqualValues(t, "Search users…", placeholder) + assert.Equal(t, "Search users…", placeholder) } diff --git a/tests/integration/repo_commits_search_test.go b/tests/integration/repo_commits_search_test.go index 92ff74ba68..5d7d215678 100644 --- a/tests/integration/repo_commits_search_test.go +++ b/tests/integration/repo_commits_search_test.go @@ -23,7 +23,7 @@ func testRepoCommitsSearch(t *testing.T, query, commit string) { doc := NewHTMLParser(t, resp.Body) sel := doc.doc.Find("#commits-table tbody tr td.sha a") - assert.EqualValues(t, commit, strings.TrimSpace(sel.Text())) + assert.Equal(t, commit, strings.TrimSpace(sel.Text())) } func TestRepoCommitsSearch(t *testing.T) { diff --git a/tests/integration/repo_commits_test.go b/tests/integration/repo_commits_test.go index f3d3a145ae..47cf7e8534 100644 --- a/tests/integration/repo_commits_test.go +++ b/tests/integration/repo_commits_test.go @@ -101,7 +101,7 @@ func testRepoCommitsWithStatus(t *testing.T, resp, respOne *httptest.ResponseRec assert.Equal(t, api.CommitStatusState(state), statuses[0].State) assert.Equal(t, setting.AppURL+"api/v1/repos/user2/repo1/statuses/65f1bf27bc3bf70f64657658635e66094edbcb4d", statuses[0].URL) assert.Equal(t, "http://test.ci/", statuses[0].TargetURL) - assert.Equal(t, "", statuses[0].Description) + assert.Empty(t, statuses[0].Description) assert.Equal(t, "testci", statuses[0].Context) assert.Len(t, status.Statuses, 1) diff --git a/tests/integration/repo_fork_test.go b/tests/integration/repo_fork_test.go index 03bbba1b2c..c986164b50 100644 --- a/tests/integration/repo_fork_test.go +++ b/tests/integration/repo_fork_test.go @@ -135,7 +135,7 @@ func TestRepoFork(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) forkButton := htmlDoc.Find("a[href*='/forks']") - assert.EqualValues(t, 1, forkButton.Length()) + assert.Equal(t, 1, forkButton.Length()) href, _ := forkButton.Attr("href") assert.Equal(t, "/user2/repo1/forks", href) diff --git a/tests/integration/repo_generate_test.go b/tests/integration/repo_generate_test.go index fd29156e4e..f039b08f06 100644 --- a/tests/integration/repo_generate_test.go +++ b/tests/integration/repo_generate_test.go @@ -36,7 +36,7 @@ func assertRepoCreateForm(t *testing.T, htmlDoc *HTMLDoc, owner *user_model.User // Verify form header header := strings.TrimSpace(htmlDoc.doc.Find(".form[action='/repo/create'] .header").Text()) - assert.EqualValues(t, locale.TrString("new_repo.title"), header) + assert.Equal(t, locale.TrString("new_repo.title"), header) htmlDoc.AssertDropdownHasSelectedOption(t, "uid", strconv.FormatInt(owner.ID, 10)) @@ -157,7 +157,7 @@ func TestRepoCreateFormTrimSpace(t *testing.T) { }) resp := session.MakeRequest(t, req, http.StatusSeeOther) - assert.EqualValues(t, "/user2/spaced-name", test.RedirectURL(resp)) + assert.Equal(t, "/user2/spaced-name", test.RedirectURL(resp)) unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 2, Name: "spaced-name"}) } diff --git a/tests/integration/repo_mergecommit_revert_test.go b/tests/integration/repo_mergecommit_revert_test.go index 600ee991f8..ef2c82bf66 100644 --- a/tests/integration/repo_mergecommit_revert_test.go +++ b/tests/integration/repo_mergecommit_revert_test.go @@ -34,5 +34,5 @@ func TestRepoMergeCommitRevert(t *testing.T) { resp = session.MakeRequest(t, req, http.StatusSeeOther) // A successful revert redirects to the main branch - assert.EqualValues(t, "/user2/test_commit_revert/src/branch/main", resp.Header().Get("Location")) + assert.Equal(t, "/user2/test_commit_revert/src/branch/main", resp.Header().Get("Location")) } diff --git a/tests/integration/repo_migration_ui_test.go b/tests/integration/repo_migration_ui_test.go index 5e2f0c0a76..279e9861fe 100644 --- a/tests/integration/repo_migration_ui_test.go +++ b/tests/integration/repo_migration_ui_test.go @@ -1,9 +1,10 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-or-later package integration import ( + "fmt" "net/http" "testing" @@ -13,104 +14,92 @@ import ( "github.com/stretchr/testify/assert" ) +// TestRepoMigrationUI is used to test various form properties of different migration types func TestRepoMigrationUI(t *testing.T) { defer tests.PrepareTestEnv(t)() - sessionUser1 := loginUser(t, "user1") - // Nothing is tested in plain Git migration form right now - testRepoMigrationFormGitHub(t, sessionUser1) - testRepoMigrationFormGitea(t, sessionUser1) - testRepoMigrationFormGitLab(t, sessionUser1) - testRepoMigrationFormGogs(t, sessionUser1) - testRepoMigrationFormOneDev(t, sessionUser1) - testRepoMigrationFormGitBucket(t, sessionUser1) - testRepoMigrationFormCodebase(t, sessionUser1) - testRepoMigrationFormForgejo(t, sessionUser1) -} + session := loginUser(t, "user1") + // Note: nothing is tested in plain Git migration form right now -func testRepoMigrationFormGitHub(t *testing.T, session *TestSession) { - response := session.MakeRequest(t, NewRequest(t, "GET", "/repo/migrate?service_type=2"), http.StatusOK) - page := NewHTMLParser(t, response.Body) + type Migration struct { + Name string + ExpectedItems []string + DescriptionHasPlaceholder bool + } - items := page.Find("#migrate_items .field .checkbox input") - expectedItems := []string{"issues", "pull_requests", "labels", "milestones", "releases"} - testRepoMigrationFormItems(t, items, expectedItems) -} + migrations := map[int]Migration{ + 2: { + "GitHub", + []string{"issues", "pull_requests", "labels", "milestones", "releases"}, + true, + }, + 3: { + "Gitea", + []string{"issues", "pull_requests", "labels", "milestones", "releases"}, + true, + }, + 4: { + "GitLab", + // Note: the checkbox "Merge requests" has name "pull_requests" + []string{"issues", "pull_requests", "labels", "milestones", "releases"}, + true, + }, + 5: { + "Gogs", + []string{"issues", "labels", "milestones"}, + true, + }, + 6: { + "OneDev", + []string{"issues", "pull_requests", "labels", "milestones"}, + true, + }, + 7: { + "GitBucket", + []string{"issues", "pull_requests", "labels", "milestones", "releases"}, + false, + }, + 8: { + "Codebase", + // Note: the checkbox "Merge requests" has name "pull_requests" + []string{"issues", "pull_requests", "labels", "milestones"}, + true, + }, + 9: { + "Forgejo", + []string{"issues", "pull_requests", "labels", "milestones", "releases"}, + true, + }, + } -func testRepoMigrationFormGitea(t *testing.T, session *TestSession) { - response := session.MakeRequest(t, NewRequest(t, "GET", "/repo/migrate?service_type=3"), http.StatusOK) - page := NewHTMLParser(t, response.Body) + for id, migration := range migrations { + t.Run(migration.Name, func(t *testing.T) { + response := session.MakeRequest(t, NewRequest(t, "GET", fmt.Sprintf("/repo/migrate?service_type=%d", id)), http.StatusOK) + page := NewHTMLParser(t, response.Body) - items := page.Find("#migrate_items .field .checkbox input") - expectedItems := []string{"issues", "pull_requests", "labels", "milestones", "releases"} - testRepoMigrationFormItems(t, items, expectedItems) -} + items := page.Find("#migrate_items .field .checkbox input") + testRepoMigrationFormItems(t, items, migration.ExpectedItems) -func testRepoMigrationFormGitLab(t *testing.T, session *TestSession) { - response := session.MakeRequest(t, NewRequest(t, "GET", "/repo/migrate?service_type=4"), http.StatusOK) - page := NewHTMLParser(t, response.Body) + page.AssertElement(t, "#clone_addr", true) + autocomplete, _ := page.Find("#clone_addr").Attr("autocomplete") + assert.Equal(t, "url", autocomplete) - items := page.Find("#migrate_items .field .checkbox input") - // Note: the checkbox "Merge requests" has name "pull_requests" - expectedItems := []string{"issues", "pull_requests", "labels", "milestones", "releases"} - testRepoMigrationFormItems(t, items, expectedItems) -} - -func testRepoMigrationFormGogs(t *testing.T, session *TestSession) { - response := session.MakeRequest(t, NewRequest(t, "GET", "/repo/migrate?service_type=5"), http.StatusOK) - page := NewHTMLParser(t, response.Body) - - items := page.Find("#migrate_items .field .checkbox input") - expectedItems := []string{"issues", "labels", "milestones"} - testRepoMigrationFormItems(t, items, expectedItems) -} - -func testRepoMigrationFormOneDev(t *testing.T, session *TestSession) { - response := session.MakeRequest(t, NewRequest(t, "GET", "/repo/migrate?service_type=6"), http.StatusOK) - page := NewHTMLParser(t, response.Body) - - items := page.Find("#migrate_items .field .checkbox input") - expectedItems := []string{"issues", "pull_requests", "labels", "milestones"} - testRepoMigrationFormItems(t, items, expectedItems) -} - -func testRepoMigrationFormGitBucket(t *testing.T, session *TestSession) { - response := session.MakeRequest(t, NewRequest(t, "GET", "/repo/migrate?service_type=7"), http.StatusOK) - page := NewHTMLParser(t, response.Body) - - items := page.Find("#migrate_items .field .checkbox input") - expectedItems := []string{"issues", "pull_requests", "labels", "milestones", "releases"} - testRepoMigrationFormItems(t, items, expectedItems) -} - -func testRepoMigrationFormCodebase(t *testing.T, session *TestSession) { - response := session.MakeRequest(t, NewRequest(t, "GET", "/repo/migrate?service_type=8"), http.StatusOK) - page := NewHTMLParser(t, response.Body) - - items := page.Find("#migrate_items .field .checkbox input") - // Note: the checkbox "Merge requests" has name "pull_requests" - expectedItems := []string{"issues", "pull_requests", "labels", "milestones"} - testRepoMigrationFormItems(t, items, expectedItems) -} - -func testRepoMigrationFormForgejo(t *testing.T, session *TestSession) { - response := session.MakeRequest(t, NewRequest(t, "GET", "/repo/migrate?service_type=9"), http.StatusOK) - page := NewHTMLParser(t, response.Body) - - items := page.Find("#migrate_items .field .checkbox input") - expectedItems := []string{"issues", "pull_requests", "labels", "milestones", "releases"} - testRepoMigrationFormItems(t, items, expectedItems) + page.AssertElement(t, "#description", true) + _, descriptionHasPlaceholder := page.Find("#description").Attr("placeholder") + assert.Equal(t, migration.DescriptionHasPlaceholder, descriptionHasPlaceholder) + }) + } } func testRepoMigrationFormItems(t *testing.T, items *goquery.Selection, expectedItems []string) { t.Helper() // Compare lengths of item lists - assert.EqualValues(t, len(expectedItems), items.Length()) + assert.Equal(t, len(expectedItems), items.Length()) // Compare contents of item lists for index, expectedName := range expectedItems { name, exists := items.Eq(index).Attr("name") assert.True(t, exists) - assert.EqualValues(t, expectedName, name) + assert.Equal(t, expectedName, name) } } diff --git a/tests/integration/repo_search_test.go b/tests/integration/repo_search_test.go index 909668d3ca..aaaa4618cf 100644 --- a/tests/integration/repo_search_test.go +++ b/tests/integration/repo_search_test.go @@ -100,7 +100,7 @@ func testSearch(t *testing.T, url string, expected []string, indexer bool) { container := doc.Find(".repository").Find(".ui.container") branchDropdown := container.Find(".js-branch-tag-selector") - assert.EqualValues(t, indexer, len(branchDropdown.Nodes) == 0) + assert.Equal(t, indexer, len(branchDropdown.Nodes) == 0) dropdownOptions := container. Find(".menu[data-test-tag=fuzzy-dropdown]"). @@ -112,9 +112,9 @@ func testSearch(t *testing.T, url string, expected []string, indexer bool) { }) if indexer { - assert.EqualValues(t, []string{"exact", "union"}, dropdownOptions) + assert.Equal(t, []string{"exact", "union"}, dropdownOptions) } else { - assert.EqualValues(t, []string{"exact", "union", "regexp"}, dropdownOptions) + assert.Equal(t, []string{"exact", "union", "regexp"}, dropdownOptions) } filenames := resultFilenames(t, doc) diff --git a/tests/integration/repo_settings_test.go b/tests/integration/repo_settings_test.go index f78df96a93..d9f4949aed 100644 --- a/tests/integration/repo_settings_test.go +++ b/tests/integration/repo_settings_test.go @@ -263,7 +263,7 @@ func TestProtectedBranch(t *testing.T) { session.MakeRequest(t, req, http.StatusSeeOther) flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "error%3DThere%2Bis%2Balready%2Ba%2Brule%2Bfor%2Bthis%2Bset%2Bof%2Bbranches", flashCookie.Value) + assert.Equal(t, "error%3DThere%2Bis%2Balready%2Ba%2Brule%2Bfor%2Bthis%2Bset%2Bof%2Bbranches", flashCookie.Value) // Verify it wasn't added. unittest.AssertCount(t, &git_model.ProtectedBranch{RuleName: "master", RepoID: repo.ID}, 1) diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go index f8961443c5..19e8553bb2 100644 --- a/tests/integration/repo_test.go +++ b/tests/integration/repo_test.go @@ -1,4 +1,5 @@ // Copyright 2017 The Gitea Authors. All rights reserved. +// Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package integration @@ -8,6 +9,7 @@ import ( "net/http" "net/url" "path" + "regexp" "strings" "testing" "time" @@ -18,6 +20,7 @@ import ( "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/git" + "forgejo.org/modules/optional" "forgejo.org/modules/setting" "forgejo.org/modules/test" "forgejo.org/modules/translation" @@ -95,7 +98,7 @@ func testViewRepo(t *testing.T) { }) commitT := time.Date(2017, time.June, 14, 13, 54, 21, 0, time.UTC).In(time.Local).Format(time.RFC1123) - assert.EqualValues(t, []file{ + assert.Equal(t, []file{ { fileName: "doc", commitID: "2a47ca4b614a9f5a43abbd5ad851a54a616ffee6", @@ -217,13 +220,13 @@ func TestRepoHTMLTitle(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo1") - assert.EqualValues(t, "user2/repo1 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "user2/repo1 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) t.Run("With description", func(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user27/repo49") - assert.EqualValues(t, "user27/repo49: A wonderful repository with more than just a README.md - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "user27/repo49: A wonderful repository with more than just a README.md - Forgejo: Beyond coding. We Forge.", htmlTitle) }) }) @@ -233,25 +236,25 @@ func TestRepoHTMLTitle(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/branch/master/deep/nesting") - assert.EqualValues(t, "repo59/deep/nesting at master - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "repo59/deep/nesting at master - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) t.Run("Non-default branch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/branch/cake-recipe/deep/nesting") - assert.EqualValues(t, "repo59/deep/nesting at cake-recipe - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "repo59/deep/nesting at cake-recipe - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) t.Run("Commit", func(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/commit/d8f53dfb33f6ccf4169c34970b5e747511c18beb/deep/nesting/") - assert.EqualValues(t, "repo59/deep/nesting at d8f53dfb33f6ccf4169c34970b5e747511c18beb - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "repo59/deep/nesting at d8f53dfb33f6ccf4169c34970b5e747511c18beb - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) t.Run("Tag", func(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/tag/v1.0/deep/nesting/") - assert.EqualValues(t, "repo59/deep/nesting at v1.0 - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "repo59/deep/nesting at v1.0 - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) }) t.Run("File", func(t *testing.T) { @@ -259,25 +262,25 @@ func TestRepoHTMLTitle(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/branch/master/deep/nesting/folder/secret_sauce_recipe.txt") - assert.EqualValues(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at master - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at master - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) t.Run("Non-default branch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/branch/cake-recipe/deep/nesting/folder/secret_sauce_recipe.txt") - assert.EqualValues(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at cake-recipe - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at cake-recipe - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) t.Run("Commit", func(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/commit/d8f53dfb33f6ccf4169c34970b5e747511c18beb/deep/nesting/folder/secret_sauce_recipe.txt") - assert.EqualValues(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at d8f53dfb33f6ccf4169c34970b5e747511c18beb - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at d8f53dfb33f6ccf4169c34970b5e747511c18beb - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) t.Run("Tag", func(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo59/src/tag/v1.0/deep/nesting/folder/secret_sauce_recipe.txt") - assert.EqualValues(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at v1.0 - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "repo59/deep/nesting/folder/secret_sauce_recipe.txt at v1.0 - user2/repo59 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) }) }) @@ -287,13 +290,13 @@ func TestRepoHTMLTitle(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo1/issues") - assert.EqualValues(t, "Issues - user2/repo1 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "Issues - user2/repo1 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) t.Run("View issue page", func(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo1/issues/1") - assert.EqualValues(t, "#1 - issue1 - user2/repo1 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "#1 - issue1 - user2/repo1 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) }) @@ -302,13 +305,13 @@ func TestRepoHTMLTitle(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo1/pulls") - assert.EqualValues(t, "Pull requests - user2/repo1 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "Pull requests - user2/repo1 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) t.Run("View pull request", func(t *testing.T) { defer tests.PrintCurrentTest(t)() htmlTitle := GetHTMLTitle(t, nil, "/user2/repo1/pulls/2") - assert.EqualValues(t, "#2 - issue2 - user2/repo1 - Forgejo: Beyond coding. We Forge.", htmlTitle) + assert.Equal(t, "#2 - issue2 - user2/repo1 - Forgejo: Beyond coding. We Forge.", htmlTitle) }) }) } @@ -327,9 +330,9 @@ func TestViewFileInRepo(t *testing.T) { repoTopics := htmlDoc.doc.Find("#repo-topics") repoSummary := htmlDoc.doc.Find(".repository-summary") - assert.EqualValues(t, 0, description.Length()) - assert.EqualValues(t, 0, repoTopics.Length()) - assert.EqualValues(t, 0, repoSummary.Length()) + assert.Equal(t, 0, description.Length()) + assert.Equal(t, 0, repoTopics.Length()) + assert.Equal(t, 0, repoSummary.Length()) } func TestViewFileInRepoRSSFeed(t *testing.T) { @@ -383,9 +386,9 @@ func TestBlameFileInRepo(t *testing.T) { repoTopics := htmlDoc.doc.Find("#repo-topics") repoSummary := htmlDoc.doc.Find(".repository-summary") - assert.EqualValues(t, 0, description.Length()) - assert.EqualValues(t, 0, repoTopics.Length()) - assert.EqualValues(t, 0, repoSummary.Length()) + assert.Equal(t, 0, description.Length()) + assert.Equal(t, 0, repoTopics.Length()) + assert.Equal(t, 0, repoSummary.Length()) }) t.Run("File size", func(t *testing.T) { @@ -691,7 +694,7 @@ func TestCommitView(t *testing.T) { // Really ensure that 404 is being sent back. doc := NewHTMLParser(t, resp.Body) - doc.AssertElement(t, `[aria-label="Page Not Found"]`, true) + doc.AssertElement(t, `[aria-label="Page not found"]`, true) }) t.Run("Too short commit ID", func(t *testing.T) { @@ -884,7 +887,7 @@ func TestRepoFilesList(t *testing.T) { return s.AttrOr("data-entryname", "") }) - assert.EqualValues(t, []string{"Charlie", "alpha", "Beta", "delta", "licensa", "LICENSE", "licensz", "README.md", "zEta"}, filesList) + assert.Equal(t, []string{"Charlie", "alpha", "Beta", "delta", "licensa", "LICENSE", "licensz", "README.md", "zEta"}, filesList) }) } @@ -1423,6 +1426,12 @@ func TestRepoSubmoduleView(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) htmlDoc.AssertElement(t, fmt.Sprintf(`tr[data-entryname="repo1"] a[href="%s"]`, u.JoinPath("/user2/repo1").String()), true) + + // Check that a link to the submodule returns a redirect and that the redirect link is correct. + req = NewRequest(t, "GET", "/"+repo.FullName()+"/src/branch/"+repo.DefaultBranch+"/repo1") + resp = MakeRequest(t, req, http.StatusSeeOther) + + assert.Equal(t, u.JoinPath("/user2/repo1").String(), resp.Header().Get("Location")) }) } @@ -1437,3 +1446,58 @@ func TestBlameDirectory(t *testing.T) { req = NewRequest(t, "GET", "/user2/repo59/blame/branch/master/deep") MakeRequest(t, req, http.StatusNotFound) } + +func TestInitInstructions(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, user.Name) + + sha256Repo, _, f := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{ + Name: optional.Some("sha256-instruction"), + AutoInit: optional.Some(false), + EnabledUnits: optional.Some([]unit_model.Type{unit_model.TypeCode}), + ObjectFormat: optional.Some("sha256"), + }) + defer f() + + sha1Repo, _, f := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{ + Name: optional.Some("sha1-instruction"), + AutoInit: optional.Some(false), + EnabledUnits: optional.Some([]unit_model.Type{unit_model.TypeCode}), + ObjectFormat: optional.Some("sha1"), + }) + defer f() + + portMatcher := regexp.MustCompile(`localhost:\d+`) + + t.Run("sha256", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + resp := session.MakeRequest(t, NewRequest(t, "GET", "/"+sha256Repo.FullName()), http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + assert.Equal(t, `touch README.md +git init --object-format=sha256 +git switch -c main +git add README.md +git commit -m "first commit" +git remote add origin http://localhost/user2/sha256-instruction.git +git push -u origin main`, portMatcher.ReplaceAllString(htmlDoc.Find(".empty-repo-guide code").First().Text(), "localhost")) + }) + + t.Run("sha1", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + resp := session.MakeRequest(t, NewRequest(t, "GET", "/"+sha1Repo.FullName()), http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + assert.Equal(t, `touch README.md +git init +git switch -c main +git add README.md +git commit -m "first commit" +git remote add origin http://localhost/user2/sha1-instruction.git +git push -u origin main`, portMatcher.ReplaceAllString(htmlDoc.Find(".empty-repo-guide code").First().Text(), "localhost")) + }) +} diff --git a/tests/integration/repo_topic_test.go b/tests/integration/repo_topic_test.go index 6dc60e2c3a..0f11d451d6 100644 --- a/tests/integration/repo_topic_test.go +++ b/tests/integration/repo_topic_test.go @@ -32,7 +32,7 @@ func TestTopicSearch(t *testing.T) { res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) DecodeJSON(t, res, &topics) assert.Len(t, topics.TopicNames, 4) - assert.EqualValues(t, "6", res.Header().Get("x-total-count")) + assert.Equal(t, "6", res.Header().Get("x-total-count")) query.Add("q", "topic") searchURL.RawQuery = query.Encode() @@ -46,8 +46,8 @@ func TestTopicSearch(t *testing.T) { DecodeJSON(t, res, &topics) if assert.Len(t, topics.TopicNames, 1) { assert.EqualValues(t, 2, topics.TopicNames[0].ID) - assert.EqualValues(t, "database", topics.TopicNames[0].Name) - assert.EqualValues(t, 1, topics.TopicNames[0].RepoCount) + assert.Equal(t, "database", topics.TopicNames[0].Name) + assert.Equal(t, 1, topics.TopicNames[0].RepoCount) } } diff --git a/tests/integration/repo_wiki_test.go b/tests/integration/repo_wiki_test.go index 5cd893702d..96d0b3dda8 100644 --- a/tests/integration/repo_wiki_test.go +++ b/tests/integration/repo_wiki_test.go @@ -110,6 +110,6 @@ func TestWikiTOC(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) - assert.EqualValues(t, "Helpdesk", htmlDoc.Find(".wiki-content-toc a").Text()) + assert.Equal(t, "Helpdesk", htmlDoc.Find(".wiki-content-toc a").Text()) }) } diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go index dce310b0fc..149b8b8d70 100644 --- a/tests/integration/repofiles_change_test.go +++ b/tests/integration/repofiles_change_test.go @@ -265,11 +265,11 @@ func TestChangeRepoFiles(t *testing.T) { lastCommit, err := gitRepo.GetCommitByPath("new/file.txt") require.NoError(t, err) expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID, lastCommit.ID.String()) - assert.EqualValues(t, expectedFileResponse.Content, filesResponse.Files[0]) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name) + assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0]) + assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA) + assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL) + assert.Equal(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email) + assert.Equal(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name) }) t.Run("Update", func(t *testing.T) { @@ -283,11 +283,11 @@ func TestChangeRepoFiles(t *testing.T) { lastCommit, err := commit.GetCommitByPath(opts.Files[0].TreePath) require.NoError(t, err) expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String()) - assert.EqualValues(t, expectedFileResponse.Content, filesResponse.Files[0]) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name) + assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0]) + assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA) + assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL) + assert.Equal(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email) + assert.Equal(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name) }) t.Run("Update and move", func(t *testing.T) { @@ -318,12 +318,12 @@ func TestChangeRepoFiles(t *testing.T) { assert.Nil(t, fromEntry) // Should no longer exist here assert.NotNil(t, toEntry) // Should exist here // assert SHA has remained the same but paths use the new file name - assert.EqualValues(t, expectedFileResponse.Content.SHA, filesResponse.Files[0].SHA) - assert.EqualValues(t, expectedFileResponse.Content.Name, filesResponse.Files[0].Name) - assert.EqualValues(t, expectedFileResponse.Content.Path, filesResponse.Files[0].Path) - assert.EqualValues(t, expectedFileResponse.Content.URL, filesResponse.Files[0].URL) - assert.EqualValues(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA) - assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL) + assert.Equal(t, expectedFileResponse.Content.SHA, filesResponse.Files[0].SHA) + assert.Equal(t, expectedFileResponse.Content.Name, filesResponse.Files[0].Name) + assert.Equal(t, expectedFileResponse.Content.Path, filesResponse.Files[0].Path) + assert.Equal(t, expectedFileResponse.Content.URL, filesResponse.Files[0].URL) + assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA) + assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL) }) t.Run("Change without branch names", func(t *testing.T) { @@ -340,7 +340,7 @@ func TestChangeRepoFiles(t *testing.T) { commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch) lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath) expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String()) - assert.EqualValues(t, expectedFileResponse.Content, filesResponse.Files[0]) + assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0]) }) t.Run("Delete files", func(t *testing.T) { @@ -352,10 +352,10 @@ func TestChangeRepoFiles(t *testing.T) { expectedFileResponse := getExpectedFileResponseForRepofilesDelete() assert.NotNil(t, filesResponse) assert.Nil(t, filesResponse.Files[0]) - assert.EqualValues(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity) - assert.EqualValues(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity) - assert.EqualValues(t, expectedFileResponse.Verification, filesResponse.Verification) + assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message) + assert.Equal(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity) + assert.Equal(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity) + assert.Equal(t, expectedFileResponse.Verification, filesResponse.Verification) filesResponse, err = files_service.ChangeRepoFiles(git.DefaultContext, repo, doer, opts) assert.Nil(t, filesResponse) @@ -376,10 +376,10 @@ func TestChangeRepoFiles(t *testing.T) { expectedFileResponse := getExpectedFileResponseForRepofilesDelete() assert.NotNil(t, filesResponse) assert.Nil(t, filesResponse.Files[0]) - assert.EqualValues(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message) - assert.EqualValues(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity) - assert.EqualValues(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity) - assert.EqualValues(t, expectedFileResponse.Verification, filesResponse.Verification) + assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message) + assert.Equal(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity) + assert.Equal(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity) + assert.Equal(t, expectedFileResponse.Verification, filesResponse.Verification) }) }) } diff --git a/tests/integration/runner_test.go b/tests/integration/runner_test.go index 2ca0a6b996..1c639eb7c3 100644 --- a/tests/integration/runner_test.go +++ b/tests/integration/runner_test.go @@ -61,7 +61,7 @@ func TestRunnerModification(t *testing.T) { sess.MakeRequest(t, req, http.StatusSeeOther) flashCookie := sess.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DRunner%2Bupdated%2Bsuccessfully", flashCookie.Value) + assert.Equal(t, "success%3DRunner%2Bupdated%2Bsuccessfully", flashCookie.Value) } req = NewRequestWithValues(t, "POST", baseURL+fmt.Sprintf("/%d/delete", id), map[string]string{ @@ -73,7 +73,7 @@ func TestRunnerModification(t *testing.T) { sess.MakeRequest(t, req, http.StatusOK) flashCookie := sess.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DRunner%2Bdeleted%2Bsuccessfully", flashCookie.Value) + assert.Equal(t, "success%3DRunner%2Bdeleted%2Bsuccessfully", flashCookie.Value) } } diff --git a/tests/integration/session_test.go b/tests/integration/session_test.go index 7af5645dec..b03714d85a 100644 --- a/tests/integration/session_test.go +++ b/tests/integration/session_test.go @@ -28,11 +28,11 @@ func Test_RegenerateSession(t *testing.T) { sess, err := auth.RegenerateSession(db.DefaultContext, "", key) require.NoError(t, err) - assert.EqualValues(t, key, sess.Key) + assert.Equal(t, key, sess.Key) assert.Empty(t, sess.Data, 0) sess, err = auth.ReadSession(db.DefaultContext, key2) require.NoError(t, err) - assert.EqualValues(t, key2, sess.Key) + assert.Equal(t, key2, sess.Key) assert.Empty(t, sess.Data, 0) } diff --git a/tests/integration/signin_test.go b/tests/integration/signin_test.go index dd8322214a..60cd5424a7 100644 --- a/tests/integration/signin_test.go +++ b/tests/integration/signin_test.go @@ -32,7 +32,7 @@ func testLoginFailed(t *testing.T, username, password, message string) { htmlDoc := NewHTMLParser(t, resp.Body) resultMsg := htmlDoc.doc.Find(".ui.message>p").Text() - assert.EqualValues(t, message, resultMsg) + assert.Equal(t, message, resultMsg) } func TestSignin(t *testing.T) { diff --git a/tests/integration/user_avatar_test.go b/tests/integration/user_avatar_test.go index b0d930d71f..c1b3a19a36 100644 --- a/tests/integration/user_avatar_test.go +++ b/tests/integration/user_avatar_test.go @@ -81,7 +81,7 @@ func TestUserAvatar(t *testing.T) { req = NewRequestf(t, "GET", "/%s.png", user2.Name) resp := MakeRequest(t, req, http.StatusSeeOther) - assert.EqualValues(t, fmt.Sprintf("/avatars/%s", user2.Avatar), resp.Header().Get("location")) + assert.Equal(t, fmt.Sprintf("/avatars/%s", user2.Avatar), resp.Header().Get("location")) // Can't test if the response matches because the image is re-generated on upload but checking that this at least doesn't give a 404 should be enough. } diff --git a/tests/integration/user_dashboard_test.go b/tests/integration/user_dashboard_test.go index b95bdab078..b18f96c090 100644 --- a/tests/integration/user_dashboard_test.go +++ b/tests/integration/user_dashboard_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. +// Copyright 2024-2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later package integration @@ -14,7 +14,6 @@ import ( unit_model "forgejo.org/models/unit" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" - "forgejo.org/modules/translation" issue_service "forgejo.org/services/issue" files_service "forgejo.org/services/repository/files" "forgejo.org/tests" @@ -24,20 +23,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestUserDashboardActionLinks(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - session := loginUser(t, "user1") - locale := translation.NewLocale("en-US") - - response := session.MakeRequest(t, NewRequest(t, "GET", "/"), http.StatusOK) - page := NewHTMLParser(t, response.Body) - links := page.Find("#navbar .dropdown[data-tooltip-content='Create…'] .menu") - assert.EqualValues(t, locale.TrString("new_repo.link"), strings.TrimSpace(links.Find("a[href='/repo/create']").Text())) - assert.EqualValues(t, locale.TrString("new_migrate.link"), strings.TrimSpace(links.Find("a[href='/repo/migrate']").Text())) - assert.EqualValues(t, locale.TrString("new_org.link"), strings.TrimSpace(links.Find("a[href='/org/create']").Text())) -} - func TestUserDashboardFeedWelcome(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) @@ -93,12 +78,12 @@ func TestDashboardTitleRendering(t *testing.T) { htmlDoc.doc.Find("#activity-feed .flex-item-main .title").Each(func(i int, s *goquery.Selection) { count++ if s.IsMatcher(goquery.Single("a")) { - assert.EqualValues(t, "❗ not rendered", s.Text()) + assert.Equal(t, "❗ not rendered", s.Text()) } else { - assert.EqualValues(t, ":exclamation: not rendered", s.Text()) + assert.Equal(t, ":exclamation: not rendered", s.Text()) } }) - assert.EqualValues(t, 6, count) + assert.Equal(t, 6, count) }) } diff --git a/tests/integration/user_federationhost_xorm_test.go b/tests/integration/user_federationhost_xorm_test.go new file mode 100644 index 0000000000..ed29a23ab7 --- /dev/null +++ b/tests/integration/user_federationhost_xorm_test.go @@ -0,0 +1,109 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "database/sql" + "testing" + + "forgejo.org/models/db" + "forgejo.org/models/forgefed" + "forgejo.org/models/user" + "forgejo.org/tests" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStoreFederationHost(t *testing.T) { + defer tests.PrepareTestEnv(t)() + t.Run("ExplicitNull", func(t *testing.T) { + federationHost := forgefed.FederationHost{ + HostFqdn: "ExplicitNull", + // Explicit null on KeyID and PublicKey + KeyID: sql.NullString{Valid: false}, + PublicKey: sql.Null[sql.RawBytes]{Valid: false}, + } + + _, err := db.GetEngine(db.DefaultContext).Insert(&federationHost) + require.NoError(t, err) + + dbFederationHost := new(forgefed.FederationHost) + has, err := db.GetEngine(db.DefaultContext).Where("host_fqdn=?", "ExplicitNull").Get(dbFederationHost) + require.NoError(t, err) + assert.True(t, has) + + assert.False(t, dbFederationHost.KeyID.Valid) + assert.False(t, dbFederationHost.PublicKey.Valid) + }) + + t.Run("NotNull", func(t *testing.T) { + federationHost := forgefed.FederationHost{ + HostFqdn: "ImplicitNull", + KeyID: sql.NullString{Valid: true, String: "meow"}, + PublicKey: sql.Null[sql.RawBytes]{Valid: true, V: sql.RawBytes{0x23, 0x42}}, + } + + _, err := db.GetEngine(db.DefaultContext).Insert(&federationHost) + require.NoError(t, err) + + dbFederationHost := new(forgefed.FederationHost) + has, err := db.GetEngine(db.DefaultContext).Where("host_fqdn=?", "ImplicitNull").Get(dbFederationHost) + require.NoError(t, err) + assert.True(t, has) + + assert.True(t, dbFederationHost.KeyID.Valid) + assert.Equal(t, "meow", dbFederationHost.KeyID.String) + + assert.True(t, dbFederationHost.PublicKey.Valid) + assert.Equal(t, sql.RawBytes{0x23, 0x42}, dbFederationHost.PublicKey.V) + }) +} + +func TestStoreFederatedUser(t *testing.T) { + defer tests.PrepareTestEnv(t)() + t.Run("ExplicitNull", func(t *testing.T) { + federatedUser := user.FederatedUser{ + UserID: 0, + ExternalID: "ExplicitNull", + FederationHostID: 0, + KeyID: sql.NullString{Valid: false}, + PublicKey: sql.Null[sql.RawBytes]{Valid: false}, + } + + _, err := db.GetEngine(db.DefaultContext).Insert(&federatedUser) + require.NoError(t, err) + + dbFederatedUser := new(user.FederatedUser) + has, err := db.GetEngine(db.DefaultContext).Where("user_id=?", 0).Get(dbFederatedUser) + require.NoError(t, err) + assert.True(t, has) + + assert.False(t, dbFederatedUser.KeyID.Valid) + assert.False(t, dbFederatedUser.PublicKey.Valid) + }) + + t.Run("NotNull", func(t *testing.T) { + federatedUser := user.FederatedUser{ + UserID: 1, + ExternalID: "ImplicitNull", + FederationHostID: 1, + KeyID: sql.NullString{Valid: true, String: "woem"}, + PublicKey: sql.Null[sql.RawBytes]{Valid: true, V: sql.RawBytes{0x42, 0x23}}, + } + + _, err := db.GetEngine(db.DefaultContext).Insert(&federatedUser) + require.NoError(t, err) + + dbFederatedUser := new(user.FederatedUser) + has, err := db.GetEngine(db.DefaultContext).Where("user_id=?", 1).Get(dbFederatedUser) + require.NoError(t, err) + assert.True(t, has) + + assert.True(t, dbFederatedUser.KeyID.Valid) + assert.Equal(t, "woem", dbFederatedUser.KeyID.String) + assert.True(t, dbFederatedUser.PublicKey.Valid) + assert.Equal(t, sql.RawBytes{0x42, 0x23}, dbFederatedUser.PublicKey.V) + }) +} diff --git a/tests/integration/user_profile_activity_test.go b/tests/integration/user_profile_activity_test.go index eb9bfa1741..47a8df94b2 100644 --- a/tests/integration/user_profile_activity_test.go +++ b/tests/integration/user_profile_activity_test.go @@ -8,7 +8,9 @@ import ( "strconv" "testing" + "forgejo.org/modules/setting" "forgejo.org/modules/structs" + "forgejo.org/modules/test" "forgejo.org/tests" "github.com/stretchr/testify/assert" @@ -23,6 +25,7 @@ import ( // - Profile visibility // - Public activity visibility func TestUserProfileActivity(t *testing.T) { + defer test.MockVariableValue(&setting.AppSubURL, "/sub")() defer tests.PrepareTestEnv(t)() // This test needs multiple users with different access statuses to check for all possible states userAdmin := loginUser(t, "user1") @@ -52,7 +55,7 @@ func TestUserProfileActivity(t *testing.T) { // When profile activity is configured as public, but the profile is private, tell the user about this and link to visibility settings. hintLink := testUser2ActivityVisibility(t, userRegular, "Your activity is only visible to you and the instance administrators because your profile is private. Configure.", true) - assert.EqualValues(t, "/user/settings#visibility-setting", hintLink) + assert.Equal(t, "/sub/user/settings#visibility-setting", hintLink) // When the profile is private, tell the admin about this. testUser2ActivityVisibility(t, userAdmin, "This activity is visible to you because you're an administrator, but the user wants it to remain private.", true) @@ -76,7 +79,7 @@ func TestUserProfileActivity(t *testing.T) { testUser2ActivityVisibility(t, userGuest, "This user has disabled the public visibility of the activity.", false) // Verify that Configure link is correct - assert.EqualValues(t, "/user/settings#keep-activity-private", hintLink) + assert.Equal(t, "/sub/user/settings#keep-activity-private", hintLink) } // testChangeUserActivityVisibility allows to easily change visibility of public activity for a user @@ -109,14 +112,14 @@ func testUser2ActivityVisibility(t *testing.T, session *TestSession, hint string hintLink, hintLinkExists := page.Find("#visibility-hint a").Attr("href") // Check that the hint aligns with the actual feed availability - assert.EqualValues(t, availability, page.Find("#activity-feed").Length() > 0) + assert.Equal(t, availability, page.Find("#activity-feed").Length() > 0) // Check availability of RSS feed button too - assert.EqualValues(t, availability, page.Find("#profile-avatar-card a[href='/user2.rss']").Length() > 0) + assert.Equal(t, availability, page.Find("#profile-avatar-card a[href='/sub/user2.rss']").Length() > 0) // Check that the current tab is displayed and is active regardless of it's actual availability // For example, on /<user> it wouldn't be available to guest, but it should be still present on /<user>?tab=activity - assert.Positive(t, page.Find("overflow-menu .active.item[href='/user2?tab=activity']").Length()) + assert.Positive(t, page.Find("overflow-menu .active.item[href='/sub/user2?tab=activity']").Length()) if hintLinkExists { return hintLink } @@ -128,5 +131,5 @@ func testUser2ActivityButtonsAvailability(t *testing.T, session *TestSession, bu t.Helper() response := session.MakeRequest(t, NewRequest(t, "GET", "/user2"), http.StatusOK) page := NewHTMLParser(t, response.Body) - assert.EqualValues(t, buttons, page.Find("overflow-menu .item[href='/user2?tab=activity']").Length() > 0) + assert.Equal(t, buttons, page.Find("overflow-menu .item[href='/sub/user2?tab=activity']").Length() > 0) } diff --git a/tests/integration/user_redirect_test.go b/tests/integration/user_redirect_test.go index 52dc5d9d3a..8e59699d66 100644 --- a/tests/integration/user_redirect_test.go +++ b/tests/integration/user_redirect_test.go @@ -38,7 +38,7 @@ func TestUserRedirect(t *testing.T) { flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DYour%2Bprofile%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) + assert.Equal(t, "success%3DYour%2Bprofile%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "user2", RedirectUserID: 2}) }) @@ -88,7 +88,7 @@ func TestUserRedirect(t *testing.T) { session.MakeRequest(t, req, http.StatusSeeOther) flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DThe%2Buser%2Baccount%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) + assert.Equal(t, "success%3DThe%2Buser%2Baccount%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) unittest.AssertExistsIf(t, true, &user_model.User{LowerName: "user2"}) unittest.AssertExistsIf(t, false, &user_model.Redirect{LowerName: "user2", RedirectUserID: 2}) @@ -105,7 +105,7 @@ func TestUserRedirect(t *testing.T) { flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DYour%2Bprofile%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) + assert.Equal(t, "success%3DYour%2Bprofile%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "user2-new", RedirectUserID: 2}) @@ -117,7 +117,7 @@ func TestUserRedirect(t *testing.T) { flashCookie = session.GetCookie(forgejo_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.EqualValues(t, "success%3DYour%2Bprofile%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) + assert.Equal(t, "success%3DYour%2Bprofile%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value) unittest.AssertExistsIf(t, false, &user_model.Redirect{LowerName: "user2-new", RedirectUserID: 2}) unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "user2-new-2", RedirectUserID: 2}) diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go index 5e02f90f9e..30103072a8 100644 --- a/tests/integration/user_test.go +++ b/tests/integration/user_test.go @@ -318,12 +318,12 @@ func TestGetUserRss(t *testing.T) { user34 := "the_34-user.with.all.allowedChars" req := NewRequestf(t, "GET", "/%s.rss", user34) resp := MakeRequest(t, req, http.StatusOK) - if assert.EqualValues(t, "application/rss+xml;charset=utf-8", resp.Header().Get("Content-Type")) { + if assert.Equal(t, "application/rss+xml;charset=utf-8", resp.Header().Get("Content-Type")) { rssDoc := NewHTMLParser(t, resp.Body).Find("channel") title, _ := rssDoc.ChildrenFiltered("title").Html() - assert.EqualValues(t, "Feed of "the_1-user.with.all.allowedChars"", title) + assert.Equal(t, "Feed of "the_1-user.with.all.allowedChars"", title) description, _ := rssDoc.ChildrenFiltered("description").Html() - assert.EqualValues(t, "<p dir="auto">some <a href="https://commonmark.org/" rel="nofollow">commonmark</a>!</p>\n", description) + assert.Equal(t, "<p dir="auto">some <a href="https://commonmark.org/" rel="nofollow">commonmark</a>!</p>\n", description) } }) t.Run("Non-existent user", func(t *testing.T) { @@ -347,11 +347,11 @@ func TestListStopWatches(t *testing.T) { stopwatch := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: owner.ID}) issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: stopwatch.IssueID}) if assert.Len(t, apiWatches, 1) { - assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix()) - assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex) - assert.EqualValues(t, issue.Title, apiWatches[0].IssueTitle) - assert.EqualValues(t, repo.Name, apiWatches[0].RepoName) - assert.EqualValues(t, repo.OwnerName, apiWatches[0].RepoOwnerName) + assert.Equal(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix()) + assert.Equal(t, issue.Index, apiWatches[0].IssueIndex) + assert.Equal(t, issue.Title, apiWatches[0].IssueTitle) + assert.Equal(t, repo.Name, apiWatches[0].RepoName) + assert.Equal(t, repo.OwnerName, apiWatches[0].RepoOwnerName) assert.Positive(t, apiWatches[0].Seconds) } } @@ -450,7 +450,7 @@ func TestUserHints(t *testing.T) { assert.Equal(t, enabled, hintChecked) link, _ := htmlDoc.Find("form[action='/user/settings/appearance/language'] a").Attr("href") - assert.EqualValues(t, "https://forgejo.org/docs/next/contributor/localization/", link) + assert.Equal(t, "https://forgejo.org/docs/next/contributor/localization/", link) } t.Run("view", func(t *testing.T) { @@ -706,7 +706,7 @@ func TestUserPronouns(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) userName := strings.TrimSpace(htmlDoc.Find(".profile-avatar-name .username").Text()) - assert.EqualValues(t, "user2", userName) + assert.Equal(t, "user2", userName) }) } @@ -934,7 +934,7 @@ func TestUserRepos(t *testing.T) { sel := htmlDoc.doc.Find("a.name") assert.Len(t, repos, len(sel.Nodes)) for i := 0; i < len(repos); i++ { - assert.EqualValues(t, repos[i], strings.TrimSpace(sel.Eq(i).Text())) + assert.Equal(t, repos[i], strings.TrimSpace(sel.Eq(i).Text())) } } } @@ -982,7 +982,7 @@ func TestUserActivate(t *testing.T) { authToken, err := auth_model.FindAuthToken(db.DefaultContext, lookupKey, auth_model.UserActivation) require.NoError(t, err) assert.False(t, authToken.IsExpired()) - assert.EqualValues(t, authToken.HashedValidator, auth_model.HashValidator(rawValidator)) + assert.Equal(t, authToken.HashedValidator, auth_model.HashValidator(rawValidator)) t.Run("No password", func(t *testing.T) { defer tests.PrintCurrentTest(t)() @@ -1052,7 +1052,7 @@ func TestUserPasswordReset(t *testing.T) { authToken, err := auth_model.FindAuthToken(db.DefaultContext, lookupKey, auth_model.PasswordReset) require.NoError(t, err) assert.False(t, authToken.IsExpired()) - assert.EqualValues(t, authToken.HashedValidator, auth_model.HashValidator(rawValidator)) + assert.Equal(t, authToken.HashedValidator, auth_model.HashValidator(rawValidator)) req = NewRequestWithValues(t, "POST", "/user/recover_account", map[string]string{ "_csrf": GetCSRF(t, session, "/user/recover_account"), @@ -1111,7 +1111,7 @@ func TestActivateEmailAddress(t *testing.T) { authToken, err := auth_model.FindAuthToken(db.DefaultContext, lookupKey, auth_model.EmailActivation("newemail@example.org")) require.NoError(t, err) assert.False(t, authToken.IsExpired()) - assert.EqualValues(t, authToken.HashedValidator, auth_model.HashValidator(rawValidator)) + assert.Equal(t, authToken.HashedValidator, auth_model.HashValidator(rawValidator)) req = NewRequestWithValues(t, "POST", "/user/activate_email", map[string]string{ "code": code, diff --git a/tests/integration/wiki_test.go b/tests/integration/wiki_test.go index 3e4761b9bd..2949038f1f 100644 --- a/tests/integration/wiki_test.go +++ b/tests/integration/wiki_test.go @@ -30,7 +30,7 @@ func assertFileExist(t *testing.T, p string) { func assertFileEqual(t *testing.T, p string, content []byte) { bs, err := os.ReadFile(p) require.NoError(t, err) - assert.EqualValues(t, content, bs) + assert.Equal(t, content, bs) } func TestRepoCloneWiki(t *testing.T) { @@ -67,6 +67,6 @@ func Test_RepoWikiPages(t *testing.T) { href, _ := firstAnchor.Attr("href") pagePath := strings.TrimPrefix(href, "/user2/repo1/wiki/") - assert.EqualValues(t, expectedPagePaths[i], pagePath) + assert.Equal(t, expectedPagePaths[i], pagePath) }) } diff --git a/tests/integration/xss_test.go b/tests/integration/xss_test.go index 2c4934c541..73523a5867 100644 --- a/tests/integration/xss_test.go +++ b/tests/integration/xss_test.go @@ -34,8 +34,8 @@ func TestXSSUserFullName(t *testing.T) { req = NewRequestf(t, "GET", "/%s", user.Name) resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) - assert.EqualValues(t, 0, htmlDoc.doc.Find("script.evil").Length()) - assert.EqualValues(t, fullName, + assert.Equal(t, 0, htmlDoc.doc.Find("script.evil").Length()) + assert.Equal(t, fullName, htmlDoc.doc.Find("div.content").Find(".header.text.center").Text(), ) } diff --git a/tests/test_utils.go b/tests/test_utils.go index e6aa9c7963..721adbd497 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -355,6 +355,7 @@ type DeclarativeRepoOptions struct { WikiBranch optional.Option[string] AutoInit optional.Option[bool] IsTemplate optional.Option[bool] + ObjectFormat optional.Option[string] } func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts DeclarativeRepoOptions) (*repo_model.Repository, string, func()) { @@ -378,14 +379,15 @@ func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts // Create the repository repo, err := repo_service.CreateRepository(db.DefaultContext, owner, owner, repo_service.CreateRepoOptions{ - Name: repoName, - Description: "Temporary Repo", - AutoInit: autoInit, - Gitignores: "", - License: "WTFPL", - Readme: "Default", - DefaultBranch: "main", - IsTemplate: opts.IsTemplate.Value(), + Name: repoName, + Description: "Temporary Repo", + AutoInit: autoInit, + Gitignores: "", + License: "WTFPL", + Readme: "Default", + DefaultBranch: "main", + IsTemplate: opts.IsTemplate.Value(), + ObjectFormatName: opts.ObjectFormat.Value(), }) require.NoError(t, err) assert.NotEmpty(t, repo) diff --git a/web_src/css/base.css b/web_src/css/base.css index 77458ec134..bfe8cd54ae 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -382,7 +382,6 @@ a.label, } .ui.menu .item > .svg { - align-self: baseline; margin-right: 0.35em; } @@ -758,10 +757,6 @@ img.ui.avatar, box-shadow: 0 6px 18px var(--color-shadow) !important; } -.ui.dimmer { - background: var(--color-overlay-backdrop); -} - /* Override semantic selector '.ui.menu:not(.vertical) .item > .button' */ /* This fixes the commit graph button on the commits page */ /* modal svg icons, copied from fomantic except width and height */ @@ -1060,6 +1055,10 @@ overflow-menu .overflow-menu-items .item { margin-bottom: 0 !important; /* reset fomantic's margin, because the active menu has special bottom border */ } +overflow-menu .overflow-menu-items .item > .svg { + align-self: baseline; +} + overflow-menu .ui.label { margin-left: 7px !important; /* save some space */ } diff --git a/web_src/css/index.css b/web_src/css/index.css index 12830299cf..eec17eab13 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -16,6 +16,7 @@ @import "./modules/card.css"; @import "./modules/checkbox.css"; @import "./modules/modal.css"; +@import "./modules/dimmer.css"; @import "./modules/switch.css"; @import "./modules/select.css"; diff --git a/web_src/css/modules/dimmer.css b/web_src/css/modules/dimmer.css new file mode 100644 index 0000000000..1d0bf83390 --- /dev/null +++ b/web_src/css/modules/dimmer.css @@ -0,0 +1,20 @@ +.ui.active.dimmer { + display: flex; + opacity: 1; +} + +.ui.dimmer { + align-items: center; + animation-fill-mode: both; + background: var(--color-overlay-backdrop); + display: none; + flex-direction: column; + height: 100%; + position: absolute; + opacity: 0; + justify-content: center; + user-select: none; + width: 100%; + will-change: opacity; + z-index: 1000; +} diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css index 49c00c4dad..bd9a5f4594 100644 --- a/web_src/fomantic/build/semantic.css +++ b/web_src/fomantic/build/semantic.css @@ -2326,363 +2326,6 @@ /******************************* Site Overrides *******************************/ -/*! - * # Fomantic-UI - Dimmer - * http://github.com/fomantic/Fomantic-UI/ - * - * - * Released under the MIT license - * http://opensource.org/licenses/MIT - * - */ - -/******************************* - Dimmer -*******************************/ - -.dimmable:not(body) { - position: relative; -} - -.ui.dimmer { - display: none; - position: absolute; - top: 0 !important; - left: 0 !important; - width: 100%; - height: 100%; - text-align: center; - vertical-align: middle; - padding: 1em; - background: rgba(0, 0, 0, 0.85); - opacity: 0; - line-height: 1; - animation-fill-mode: both; - animation-duration: 0.5s; - transition: background-color 0.5s linear; - flex-direction: column; - align-items: center; - justify-content: center; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - will-change: opacity; - z-index: 1000; -} - -/* Dimmer Content */ - -.ui.dimmer > .content { - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; - color: #FFFFFF; -} - -/* Loose Coupling */ - -.ui.segment > .ui.dimmer:not(.page) { - border-radius: inherit; -} - -/* Scrollbars */ - -/******************************* - States -*******************************/ - -/* Animating */ - -.animating.dimmable:not(body), -.dimmed.dimmable:not(body) { - overflow: hidden; -} - -/* Animating / Active / Visible */ - -.dimmed.dimmable > .ui.animating.dimmer, -.dimmed.dimmable > .ui.visible.dimmer, -.ui.active.dimmer { - display: flex; - opacity: 1; -} - -/* Disabled */ - -.ui.disabled.dimmer { - width: 0 !important; - height: 0 !important; -} - -/******************************* - Variations -*******************************/ - -/*-------------- - Legacy - ---------------*/ - -/* Animating / Active / Visible */ - -.dimmed.dimmable > .ui.animating.legacy.dimmer, -.dimmed.dimmable > .ui.visible.legacy.dimmer, -.ui.active.legacy.dimmer { - display: block; -} - -/*-------------- - Alignment - ---------------*/ - -.ui[class*="top aligned"].dimmer { - justify-content: flex-start; -} - -.ui[class*="bottom aligned"].dimmer { - justify-content: flex-end; -} - -/*-------------- - Page - ---------------*/ - -.ui.page.dimmer { - position: fixed; - transform-style: ''; - perspective: 2000px; - transform-origin: center center; -} - -.ui.page.dimmer.modals { - -moz-perspective: none; -} - -body.animating.in.dimmable, -body.dimmed.dimmable { - overflow: hidden; -} - -body.dimmable > .dimmer { - position: fixed; -} - -/*-------------- - Blurring - ---------------*/ - -.blurring.dimmable > :not(.dimmer) { - filter: initial; - transition: 800ms filter ease; -} - -.blurring.dimmed.dimmable > :not(.dimmer):not(.popup) { - filter: blur(5px) grayscale(0.7); -} - -/* Dimmer Color */ - -.blurring.dimmable > .dimmer { - background: rgba(0, 0, 0, 0.6); -} - -.blurring.dimmable > .inverted.dimmer { - background: rgba(255, 255, 255, 0.6); -} - -/*-------------- - Aligned - ---------------*/ - -.ui.dimmer > .top.aligned.content > * { - vertical-align: top; -} - -.ui.dimmer > .bottom.aligned.content > * { - vertical-align: bottom; -} - -/*-------------- - Shades - ---------------*/ - -.medium.medium.medium.medium.medium.dimmer { - background: rgba(0, 0, 0, 0.65); -} - -.light.light.light.light.light.dimmer { - background: rgba(0, 0, 0, 0.45); -} - -.very.light.light.light.light.dimmer { - background: rgba(0, 0, 0, 0.25); -} - -/*-------------- - Simple - ---------------*/ - -/* Displays without javascript */ - -.ui.simple.dimmer { - display: block; - overflow: hidden; - opacity: 0; - width: 0; - height: 0; - z-index: -100; - background: rgba(0, 0, 0, 0); -} - -.dimmed.dimmable > .ui.simple.dimmer { - overflow: visible; - opacity: 1; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.85); - z-index: 1; -} - -.ui.simple.inverted.dimmer { - background: rgba(255, 255, 255, 0); -} - -.dimmed.dimmable > .ui.simple.inverted.dimmer { - background: rgba(255, 255, 255, 0.85); -} - -/*-------------- - Partially - ----------------*/ - -.ui[class*="top dimmer"], -.ui[class*="center dimmer"], -.ui[class*="bottom dimmer"] { - height: auto; -} - -.ui[class*="bottom dimmer"] { - top: auto !important; - bottom: 0; -} - -.ui[class*="center dimmer"] { - top: 50% !important; - transform: translateY(-50%); - -webkit-transform: translateY(calc(-50% - 0.5px)); -} - -.ui.segment > .ui.ui[class*="top dimmer"] { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.ui.segment > .ui.ui[class*="center dimmer"] { - border-radius: 0; -} - -.ui.segment > .ui.ui[class*="bottom dimmer"] { - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.ui[class*="center dimmer"].transition[class*="fade up"].in { - animation-name: fadeInUpCenter; -} - -.ui[class*="center dimmer"].transition[class*="fade down"].in { - animation-name: fadeInDownCenter; -} - -.ui[class*="center dimmer"].transition[class*="fade up"].out { - animation-name: fadeOutUpCenter; -} - -.ui[class*="center dimmer"].transition[class*="fade down"].out { - animation-name: fadeOutDownCenter; -} - -.ui[class*="center dimmer"].bounce.transition { - animation-name: bounceCenter; -} - -@keyframes fadeInUpCenter { - 0% { - opacity: 0; - transform: translateY(-40%); - -webkit-transform: translateY(calc(-40% - 0.5px)); - } - - 100% { - opacity: 1; - transform: translateY(-50%); - -webkit-transform: translateY(calc(-50% - 0.5px)); - } -} - -@keyframes fadeInDownCenter { - 0% { - opacity: 0; - transform: translateY(-60%); - -webkit-transform: translateY(calc(-60% - 0.5px)); - } - - 100% { - opacity: 1; - transform: translateY(-50%); - -webkit-transform: translateY(calc(-50% - 0.5px)); - } -} - -@keyframes fadeOutUpCenter { - 0% { - opacity: 1; - transform: translateY(-50%); - -webkit-transform: translateY(calc(-50% - 0.5px)); - } - - 100% { - opacity: 0; - transform: translateY(-45%); - -webkit-transform: translateY(calc(-45% - 0.5px)); - } -} - -@keyframes fadeOutDownCenter { - 0% { - opacity: 1; - transform: translateY(-50%); - -webkit-transform: translateY(calc(-50% - 0.5px)); - } - - 100% { - opacity: 0; - transform: translateY(-55%); - -webkit-transform: translateY(calc(-55% - 0.5px)); - } -} - -@keyframes bounceCenter { - 0%, 20%, 50%, 80%, 100% { - transform: translateY(-50%); - -webkit-transform: translateY(calc(-50% - 0.5px)); - } - - 40% { - transform: translateY(calc(-50% - 30px)); - } - - 60% { - transform: translateY(calc(-50% - 15px)); - } -} - -/******************************* - Theme Overrides -*******************************/ - -/******************************* - User Overrides -*******************************/ /*! * # Fomantic-UI - Dropdown * http://github.com/fomantic/Fomantic-UI/ diff --git a/web_src/fomantic/build/semantic.js b/web_src/fomantic/build/semantic.js index fb68705c08..393d9d8142 100644 --- a/web_src/fomantic/build/semantic.js +++ b/web_src/fomantic/build/semantic.js @@ -1184,760 +1184,6 @@ $.api.settings = { -})( jQuery, window, document ); - -/*! - * # Fomantic-UI - Dimmer - * http://github.com/fomantic/Fomantic-UI/ - * - * - * Released under the MIT license - * http://opensource.org/licenses/MIT - * - */ - -;(function ($, window, document, undefined) { - -'use strict'; - -$.isFunction = $.isFunction || function(obj) { - return typeof obj === "function" && typeof obj.nodeType !== "number"; -}; - -window = (typeof window != 'undefined' && window.Math == Math) - ? window - : (typeof self != 'undefined' && self.Math == Math) - ? self - : Function('return this')() -; - -$.fn.dimmer = function(parameters) { - var - $allModules = $(this), - - time = new Date().getTime(), - performance = [], - - query = arguments[0], - methodInvoked = (typeof query == 'string'), - queryArguments = [].slice.call(arguments, 1), - - returnedValue - ; - - $allModules - .each(function() { - var - settings = ( $.isPlainObject(parameters) ) - ? $.extend(true, {}, $.fn.dimmer.settings, parameters) - : $.extend({}, $.fn.dimmer.settings), - - selector = settings.selector, - namespace = settings.namespace, - className = settings.className, - error = settings.error, - - eventNamespace = '.' + namespace, - moduleNamespace = 'module-' + namespace, - moduleSelector = $allModules.selector || '', - - clickEvent = "click", unstableClickEvent = ('ontouchstart' in document.documentElement) - ? 'touchstart' - : 'click', - - $module = $(this), - $dimmer, - $dimmable, - - element = this, - instance = $module.data(moduleNamespace), - module - ; - - module = { - - preinitialize: function() { - if( module.is.dimmer() ) { - - $dimmable = $module.parent(); - $dimmer = $module; - } - else { - $dimmable = $module; - if( module.has.dimmer() ) { - if(settings.dimmerName) { - $dimmer = $dimmable.find(selector.dimmer).filter('.' + settings.dimmerName); - } - else { - $dimmer = $dimmable.find(selector.dimmer); - } - } - else { - $dimmer = module.create(); - } - } - }, - - initialize: function() { - module.debug('Initializing dimmer', settings); - - module.bind.events(); - module.set.dimmable(); - module.instantiate(); - }, - - instantiate: function() { - module.verbose('Storing instance of module', module); - instance = module; - $module - .data(moduleNamespace, instance) - ; - }, - - destroy: function() { - module.verbose('Destroying previous module', $dimmer); - module.unbind.events(); - module.remove.variation(); - $dimmable - .off(eventNamespace) - ; - }, - - bind: { - events: function() { - if(settings.on == 'hover') { - $dimmable - .on('mouseenter' + eventNamespace, module.show) - .on('mouseleave' + eventNamespace, module.hide) - ; - } - else if(settings.on == 'click') { - $dimmable - .on(clickEvent + eventNamespace, module.toggle) - ; - } - if( module.is.page() ) { - module.debug('Setting as a page dimmer', $dimmable); - module.set.pageDimmer(); - } - - if( module.is.closable() ) { - module.verbose('Adding dimmer close event', $dimmer); - $dimmable - .on(clickEvent + eventNamespace, selector.dimmer, module.event.click) - ; - } - } - }, - - unbind: { - events: function() { - $module - .removeData(moduleNamespace) - ; - $dimmable - .off(eventNamespace) - ; - } - }, - - event: { - click: function(event) { - module.verbose('Determining if event occurred on dimmer', event); - if( $dimmer.find(event.target).length === 0 || $(event.target).is(selector.content) ) { - module.hide(); - event.stopImmediatePropagation(); - } - } - }, - - addContent: function(element) { - var - $content = $(element) - ; - module.debug('Add content to dimmer', $content); - if($content.parent()[0] !== $dimmer[0]) { - $content.detach().appendTo($dimmer); - } - }, - - create: function() { - var - $element = $( settings.template.dimmer(settings) ) - ; - if(settings.dimmerName) { - module.debug('Creating named dimmer', settings.dimmerName); - $element.addClass(settings.dimmerName); - } - $element - .appendTo($dimmable) - ; - return $element; - }, - - show: function(callback) { - callback = $.isFunction(callback) - ? callback - : function(){} - ; - module.debug('Showing dimmer', $dimmer, settings); - module.set.variation(); - if( (!module.is.dimmed() || module.is.animating()) && module.is.enabled() ) { - module.animate.show(callback); - settings.onShow.call(element); - settings.onChange.call(element); - } - else { - module.debug('Dimmer is already shown or disabled'); - } - }, - - hide: function(callback) { - callback = $.isFunction(callback) - ? callback - : function(){} - ; - if( module.is.dimmed() || module.is.animating() ) { - module.debug('Hiding dimmer', $dimmer); - module.animate.hide(callback); - settings.onHide.call(element); - settings.onChange.call(element); - } - else { - module.debug('Dimmer is not visible'); - } - }, - - toggle: function() { - module.verbose('Toggling dimmer visibility', $dimmer); - if( !module.is.dimmed() ) { - module.show(); - } - else { - if ( module.is.closable() ) { - module.hide(); - } - } - }, - - animate: { - show: function(callback) { - callback = $.isFunction(callback) - ? callback - : function(){} - ; - if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) { - if(settings.useFlex) { - module.debug('Using flex dimmer'); - module.remove.legacy(); - } - else { - module.debug('Using legacy non-flex dimmer'); - module.set.legacy(); - } - if(settings.opacity !== 'auto') { - module.set.opacity(); - } - $dimmer - .transition({ - displayType : settings.useFlex - ? 'flex' - : 'block', - animation : settings.transition + ' in', - queue : false, - duration : module.get.duration(), - useFailSafe : true, - onStart : function() { - module.set.dimmed(); - }, - onComplete : function() { - module.set.active(); - callback(); - } - }) - ; - } - else { - module.verbose('Showing dimmer animation with javascript'); - module.set.dimmed(); - if(settings.opacity == 'auto') { - settings.opacity = 0.8; - } - $dimmer - .stop() - .css({ - opacity : 0, - width : '100%', - height : '100%' - }) - .fadeTo(module.get.duration(), settings.opacity, function() { - $dimmer.removeAttr('style'); - module.set.active(); - callback(); - }) - ; - } - }, - hide: function(callback) { - callback = $.isFunction(callback) - ? callback - : function(){} - ; - if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) { - module.verbose('Hiding dimmer with css'); - $dimmer - .transition({ - displayType : settings.useFlex - ? 'flex' - : 'block', - animation : settings.transition + ' out', - queue : false, - duration : module.get.duration(), - useFailSafe : true, - onComplete : function() { - module.remove.dimmed(); - module.remove.variation(); - module.remove.active(); - callback(); - } - }) - ; - } - else { - module.verbose('Hiding dimmer with javascript'); - $dimmer - .stop() - .fadeOut(module.get.duration(), function() { - module.remove.dimmed(); - module.remove.active(); - $dimmer.removeAttr('style'); - callback(); - }) - ; - } - } - }, - - get: { - dimmer: function() { - return $dimmer; - }, - duration: function() { - if(typeof settings.duration == 'object') { - if( module.is.active() ) { - return settings.duration.hide; - } - else { - return settings.duration.show; - } - } - return settings.duration; - } - }, - - has: { - dimmer: function() { - if(settings.dimmerName) { - return ($module.find(selector.dimmer).filter('.' + settings.dimmerName).length > 0); - } - else { - return ( $module.find(selector.dimmer).length > 0 ); - } - } - }, - - is: { - active: function() { - return $dimmer.hasClass(className.active); - }, - animating: function() { - return ( $dimmer.is(':animated') || $dimmer.hasClass(className.animating) ); - }, - closable: function() { - if(settings.closable == 'auto') { - if(settings.on == 'hover') { - return false; - } - return true; - } - return settings.closable; - }, - dimmer: function() { - return $module.hasClass(className.dimmer); - }, - dimmable: function() { - return $module.hasClass(className.dimmable); - }, - dimmed: function() { - return $dimmable.hasClass(className.dimmed); - }, - disabled: function() { - return $dimmable.hasClass(className.disabled); - }, - enabled: function() { - return !module.is.disabled(); - }, - page: function () { - return $dimmable.is('body'); - }, - pageDimmer: function() { - return $dimmer.hasClass(className.pageDimmer); - } - }, - - can: { - show: function() { - return !$dimmer.hasClass(className.disabled); - } - }, - - set: { - opacity: function(opacity) { - var - color = $dimmer.css('background-color'), - colorArray = color.split(','), - isRGB = (colorArray && colorArray.length >= 3) - ; - opacity = settings.opacity === 0 ? 0 : settings.opacity || opacity; - if(isRGB) { - colorArray[2] = colorArray[2].replace(')',''); - colorArray[3] = opacity + ')'; - color = colorArray.join(','); - } - else { - color = 'rgba(0, 0, 0, ' + opacity + ')'; - } - module.debug('Setting opacity to', opacity); - $dimmer.css('background-color', color); - }, - legacy: function() { - $dimmer.addClass(className.legacy); - }, - active: function() { - $dimmer.addClass(className.active); - }, - dimmable: function() { - $dimmable.addClass(className.dimmable); - }, - dimmed: function() { - $dimmable.addClass(className.dimmed); - }, - pageDimmer: function() { - $dimmer.addClass(className.pageDimmer); - }, - disabled: function() { - $dimmer.addClass(className.disabled); - }, - variation: function(variation) { - variation = variation || settings.variation; - if(variation) { - $dimmer.addClass(variation); - } - } - }, - - remove: { - active: function() { - $dimmer - .removeClass(className.active) - ; - }, - legacy: function() { - $dimmer.removeClass(className.legacy); - }, - dimmed: function() { - $dimmable.removeClass(className.dimmed); - }, - disabled: function() { - $dimmer.removeClass(className.disabled); - }, - variation: function(variation) { - variation = variation || settings.variation; - if(variation) { - $dimmer.removeClass(variation); - } - } - }, - - setting: function(name, value) { - module.debug('Changing setting', name, value); - if( $.isPlainObject(name) ) { - $.extend(true, settings, name); - } - else if(value !== undefined) { - if($.isPlainObject(settings[name])) { - $.extend(true, settings[name], value); - } - else { - settings[name] = value; - } - } - else { - return settings[name]; - } - }, - internal: function(name, value) { - if( $.isPlainObject(name) ) { - $.extend(true, module, name); - } - else if(value !== undefined) { - module[name] = value; - } - else { - return module[name]; - } - }, - debug: function() { - if(!settings.silent && settings.debug) { - if(settings.performance) { - module.performance.log(arguments); - } - else { - module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); - module.debug.apply(console, arguments); - } - } - }, - verbose: function() { - if(!settings.silent && settings.verbose && settings.debug) { - if(settings.performance) { - module.performance.log(arguments); - } - else { - module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); - module.verbose.apply(console, arguments); - } - } - }, - error: function() { - if(!settings.silent) { - module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); - module.error.apply(console, arguments); - } - }, - performance: { - log: function(message) { - var - currentTime, - executionTime, - previousTime - ; - if(settings.performance) { - currentTime = new Date().getTime(); - previousTime = time || currentTime; - executionTime = currentTime - previousTime; - time = currentTime; - performance.push({ - 'Name' : message[0], - 'Arguments' : [].slice.call(message, 1) || '', - 'Element' : element, - 'Execution Time' : executionTime - }); - } - clearTimeout(module.performance.timer); - module.performance.timer = setTimeout(module.performance.display, 500); - }, - display: function() { - var - title = settings.name + ':', - totalTime = 0 - ; - time = false; - clearTimeout(module.performance.timer); - $.each(performance, function(index, data) { - totalTime += data['Execution Time']; - }); - title += ' ' + totalTime + 'ms'; - if(moduleSelector) { - title += ' \'' + moduleSelector + '\''; - } - if($allModules.length > 1) { - title += ' ' + '(' + $allModules.length + ')'; - } - if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) { - console.groupCollapsed(title); - if(console.table) { - console.table(performance); - } - else { - $.each(performance, function(index, data) { - console.log(data['Name'] + ': ' + data['Execution Time']+'ms'); - }); - } - console.groupEnd(); - } - performance = []; - } - }, - invoke: function(query, passedArguments, context) { - var - object = instance, - maxDepth, - found, - response - ; - passedArguments = passedArguments || queryArguments; - context = element || context; - if(typeof query == 'string' && object !== undefined) { - query = query.split(/[\. ]/); - maxDepth = query.length - 1; - $.each(query, function(depth, value) { - var camelCaseValue = (depth != maxDepth) - ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) - : query - ; - if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) { - object = object[camelCaseValue]; - } - else if( object[camelCaseValue] !== undefined ) { - found = object[camelCaseValue]; - return false; - } - else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) { - object = object[value]; - } - else if( object[value] !== undefined ) { - found = object[value]; - return false; - } - else { - module.error(error.method, query); - return false; - } - }); - } - if ( $.isFunction( found ) ) { - response = found.apply(context, passedArguments); - } - else if(found !== undefined) { - response = found; - } - if(Array.isArray(returnedValue)) { - returnedValue.push(response); - } - else if(returnedValue !== undefined) { - returnedValue = [returnedValue, response]; - } - else if(response !== undefined) { - returnedValue = response; - } - return found; - } - }; - - module.preinitialize(); - - if(methodInvoked) { - if(instance === undefined) { - module.initialize(); - } - module.invoke(query); - } - else { - if(instance !== undefined) { - instance.invoke('destroy'); - } - module.initialize(); - } - }) - ; - - return (returnedValue !== undefined) - ? returnedValue - : this - ; -}; - -$.fn.dimmer.settings = { - - name : 'Dimmer', - namespace : 'dimmer', - - silent : false, - debug : false, - verbose : false, - performance : true, - - // whether should use flex layout - useFlex : true, - - // name to distinguish between multiple dimmers in context - dimmerName : false, - - // whether to add a variation type - variation : false, - - // whether to bind close events - closable : 'auto', - - // whether to use css animations - useCSS : true, - - // css animation to use - transition : 'fade', - - // event to bind to - on : false, - - // overriding opacity value - opacity : 'auto', - - // transition durations - duration : { - show : 500, - hide : 500 - }, -// whether the dynamically created dimmer should have a loader - displayLoader: false, - loaderText : false, - loaderVariation : '', - - onChange : function(){}, - onShow : function(){}, - onHide : function(){}, - - error : { - method : 'The method you called is not defined.' - }, - - className : { - active : 'active', - animating : 'animating', - dimmable : 'dimmable', - dimmed : 'dimmed', - dimmer : 'dimmer', - disabled : 'disabled', - hide : 'hide', - legacy : 'legacy', - pageDimmer : 'page', - show : 'show', - loader : 'ui loader' - }, - - selector: { - dimmer : '> .ui.dimmer', - content : '.ui.dimmer > .content, .ui.dimmer > .content > .center' - }, - - template: { - dimmer: function(settings) { - var d = $('<div/>').addClass('ui dimmer'),l; - if(settings.displayLoader) { - l = $('<div/>') - .addClass(settings.className.loader) - .addClass(settings.loaderVariation); - if(!!settings.loaderText){ - l.text(settings.loaderText); - l.addClass('text'); - } - d.append(l); - } - return d; - } - } - -}; - })( jQuery, window, document ); /*! diff --git a/web_src/fomantic/package-lock.json b/web_src/fomantic/package-lock.json index f3fb7fc8a8..9d5ec0ca2a 100644 --- a/web_src/fomantic/package-lock.json +++ b/web_src/fomantic/package-lock.json @@ -494,9 +494,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.11.tgz", - "integrity": "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==", + "version": "22.13.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz", + "integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -2005,9 +2005,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.123", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz", - "integrity": "sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==", + "version": "1.5.128", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz", + "integrity": "sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ==", "license": "ISC" }, "node_modules/emoji-regex": { diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json index 5db57bc8d4..69474b7539 100644 --- a/web_src/fomantic/semantic.json +++ b/web_src/fomantic/semantic.json @@ -23,7 +23,6 @@ "components": [ "api", "button", - "dimmer", "dropdown", "form", "menu", diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue index cd86499ddc..12ff564aa7 100644 --- a/web_src/js/components/RepoBranchTagSelector.vue +++ b/web_src/js/components/RepoBranchTagSelector.vue @@ -98,14 +98,14 @@ const sfc = { $(`#${this.branchForm} input[name="refType"]`).val('branch'); } if (this.submitForm) { - $(`#${this.branchForm}`).trigger('submit'); + document.getElementById(this.branchForm).requestSubmit(); } this.menuVisible = false; } }, createNewBranch() { if (!this.showCreateNewBranch) return; - $(this.$refs.newBranchForm).trigger('submit'); + this.$refs.newBranchForm.requestSubmit(); }, focusSearchField() { nextTick(() => { diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js index 9934c3dd17..b46f4f8a74 100644 --- a/web_src/js/features/admin/common.js +++ b/web_src/js/features/admin/common.js @@ -175,7 +175,7 @@ export function initAdminCommon() { onUsePagedSearchChange(); } }); - $('#auth_type').trigger('change'); + document.getElementById('auth_type').dispatchEvent(new Event('change')); document.getElementById('security_protocol')?.addEventListener('change', onSecurityProtocolChange); document.getElementById('use_paged_search')?.addEventListener('change', onUsePagedSearchChange); document.getElementById('oauth2_provider')?.addEventListener('change', () => onOAuth2Change(true)); @@ -200,10 +200,12 @@ export function initAdminCommon() { } if (document.querySelector('.admin.authentication')) { - $('#auth_name').on('input', function () { + const authNameEl = document.getElementById('auth_name'); + authNameEl.addEventListener('input', (el) => { // appSubUrl is either empty or is a path that starts with `/` and doesn't have a trailing slash. - document.getElementById('oauth2-callback-url').textContent = `${window.location.origin}${appSubUrl}/user/oauth2/${encodeURIComponent(this.value)}/callback`; - }).trigger('input'); + document.getElementById('oauth2-callback-url').textContent = `${window.location.origin}${appSubUrl}/user/oauth2/${encodeURIComponent(el.target.value)}/callback`; + }); + authNameEl.dispatchEvent(new Event('input')); } // Notice diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 7848a14b66..7d553f9692 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -57,7 +57,7 @@ export function initGlobalEnterQuickSubmit() { export function initGlobalButtonClickOnEnter() { $(document).on('keypress', 'div.ui.button,span.ui.button', (e) => { if (e.code === ' ' || e.code === 'Enter') { - $(e.target).trigger('click'); + e.target.click(); e.preventDefault(); } }); @@ -314,7 +314,7 @@ export function initGlobalLinkActions() { closable: false, onApprove: async () => { if ($this.data('type') === 'form') { - $($this.data('form')).trigger('submit'); + document.querySelector($this.data('form')).requestSubmit(); return; } if ($this[0].getAttribute('hx-confirm')) { diff --git a/web_src/js/features/comp/LabelEdit.js b/web_src/js/features/comp/LabelEdit.js index 2cc75cc6b0..a97c7fe34c 100644 --- a/web_src/js/features/comp/LabelEdit.js +++ b/web_src/js/features/comp/LabelEdit.js @@ -38,7 +38,7 @@ export function initCompLabelEdit(selector) { form.reportValidity(); return false; } - $('.new-label.form').trigger('submit'); + document.querySelector('.new-label.form').requestSubmit(); }, }).modal('show'); return false; @@ -75,7 +75,7 @@ export function initCompLabelEdit(selector) { form.reportValidity(); return false; } - $('.edit-label.form').trigger('submit'); + document.querySelector('.edit-label.form').requestSubmit(); }, }).modal('show'); return false; diff --git a/web_src/js/features/comp/ReactionSelector.js b/web_src/js/features/comp/ReactionSelector.js index fd4601fb91..357a33f469 100644 --- a/web_src/js/features/comp/ReactionSelector.js +++ b/web_src/js/features/comp/ReactionSelector.js @@ -23,7 +23,7 @@ export function initCompReactionSelector($parent) { $react.remove(); } if (!data.empty) { - const $attachments = $content.find('.segment.bottom:first'); + const $attachments = $content.find('.segment.bottom').first(); $react = $(data.html); if ($attachments.length > 0) { $react.insertBefore($attachments); diff --git a/web_src/js/features/org-team.js b/web_src/js/features/org-team.js index 9b059b3a46..fe816da865 100644 --- a/web_src/js/features/org-team.js +++ b/web_src/js/features/org-team.js @@ -10,13 +10,12 @@ export function initOrgTeamSearchRepoBox() { url: `${appSubUrl}/repo/search?q={query}&uid=${$searchRepoBox.data('uid')}`, onResponse(response) { const items = []; - $.each(response.data, (_i, item) => { + for (const item of response.data) { items.push({ title: item.repository.full_name.split('/')[1], description: item.repository.full_name, }); - }); - + } return {results: items}; }, }, diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js index 794cc38010..2ff9e611ae 100644 --- a/web_src/js/features/repo-code.js +++ b/web_src/js/features/repo-code.js @@ -184,7 +184,8 @@ export function initRepoCodeView() { $('html, body').scrollTop($first.offset().top - 200); } } - }).trigger('hashchange'); + }); + window.dispatchEvent(new Event('hashchange')); } $(document).on('click', '.fold-file', ({currentTarget}) => { invertFileFolding(currentTarget.closest('.file-content'), currentTarget); diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js index 88aa93d850..038336fc0d 100644 --- a/web_src/js/features/repo-common.js +++ b/web_src/js/features/repo-common.js @@ -33,25 +33,25 @@ export function initRepoArchiveLinks() { } export function initRepoCloneLink() { - const $repoCloneSsh = $('#repo-clone-ssh'); - const $repoCloneHttps = $('#repo-clone-https'); - const $inputLink = $('#repo-clone-url'); + const repoCloneSSH = document.getElementById('repo-clone-ssh'); + const repoCloneHTTPS = document.getElementById('repo-clone-https'); + const inputLink = document.getElementById('repo-clone-url'); - if ((!$repoCloneSsh.length && !$repoCloneHttps.length) || !$inputLink.length) { + if ((!repoCloneSSH && !repoCloneHTTPS) || !inputLink) { return; } - $repoCloneSsh.on('click', () => { + repoCloneSSH?.addEventListener('click', () => { localStorage.setItem('repo-clone-protocol', 'ssh'); window.updateCloneStates(); }); - $repoCloneHttps.on('click', () => { + repoCloneHTTPS?.addEventListener('click', () => { localStorage.setItem('repo-clone-protocol', 'https'); window.updateCloneStates(); }); - $inputLink.on('focus', () => { - $inputLink.trigger('select'); + inputLink.addEventListener('focus', () => { + inputLink.select(); }); } diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js index 5b59b66f80..d3f5f2f8a9 100644 --- a/web_src/js/features/repo-editor.js +++ b/web_src/js/features/repo-editor.js @@ -185,7 +185,7 @@ export function initRepoEditor() { $('#edit-empty-content-modal') .modal({ onApprove() { - $('.edit.form').trigger('submit'); + document.querySelector('.edit.form').requestSubmit(); }, }) .modal('show'); diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index c28bf08442..889687da3e 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -37,27 +37,27 @@ export function initRepoIssueTimeTracking() { $('.issue-start-time-modal').modal({ duration: 200, onApprove() { - $('#add_time_manual_form').trigger('submit'); + document.getElementById('add_time_manual_form').requestSubmit(); }, }).modal('show'); $('.issue-start-time-modal input').on('keydown', (e) => { if ((e.keyCode || e.key) === 13) { - $('#add_time_manual_form').trigger('submit'); + document.getElementById('add_time_manual_form').requestSubmit(); } }); }); $(document).on('click', '.issue-start-time, .issue-stop-time', () => { - $('#toggle_stopwatch_form').trigger('submit'); + document.getElementById('toggle_stopwatch_form').requestSubmit(); }); $(document).on('click', '.issue-cancel-time', () => { - $('#cancel_stopwatch_form').trigger('submit'); + document.getElementById('cancel_stopwatch_form').requestSubmit(); }); $(document).on('click', 'button.issue-delete-time', function () { const sel = `.issue-delete-time-modal[data-id="${$(this).data('id')}"]`; $(sel).modal({ duration: 200, onApprove() { - $(`${sel} form`).trigger('submit'); + document.getElementById(`${sel} form`).requestSubmit(); }, }).modal('show'); }); @@ -139,7 +139,7 @@ export function initRepoIssueSidebarList() { const filteredResponse = {success: true, results: []}; const currIssueId = $('#new-dependency-drop-list').data('issue-id'); // Parse the response from the api to work with our dropdown - $.each(response, (_i, issue) => { + for (const [_, issue] of Object.entries(response)) { // Don't list current issue in the dependency list. if (issue.id === currIssueId) { return; @@ -149,7 +149,7 @@ export function initRepoIssueSidebarList() { }<div class="text small tw-break-anywhere">${htmlEscape(issue.repository.full_name)}</div>`, value: issue.id, }); - }); + } return filteredResponse; }, cache: false, @@ -247,7 +247,7 @@ export function initRepoIssueDependencyDelete() { onApprove: () => { $('#removeDependencyID').val(id); $('#dependencyType').val(type); - $('#removeDependencyForm').trigger('submit'); + document.getElementById('removeDependencyForm').requestSubmit(); }, }).modal('show'); }); @@ -345,12 +345,12 @@ export function initRepoIssueReferenceRepositorySearch() { url: `${appSubUrl}/repo/search?q={query}&limit=20`, onResponse(response) { const filteredResponse = {success: true, results: []}; - $.each(response.data, (_r, repo) => { + for (const repo of response.data) { filteredResponse.results.push({ name: htmlEscape(repo.repository.full_name), value: repo.repository.full_name, }); - }); + } return filteredResponse; }, cache: false, @@ -369,9 +369,9 @@ export function initRepoIssueWipTitle() { $('.title_wip_desc > a').on('click', (e) => { e.preventDefault(); - const $issueTitle = $('#issue_title'); - $issueTitle.trigger('focus'); - const value = $issueTitle.val().trim().toUpperCase(); + const issueTitleEl = document.getElementById('issue_title'); + issueTitleEl.focus(); + const value = issueTitleEl.value.trim().toUpperCase(); const wipPrefixes = $('.title_wip_desc').data('wip-prefixes'); for (const prefix of wipPrefixes) { @@ -380,7 +380,7 @@ export function initRepoIssueWipTitle() { } } - $issueTitle.val(`${wipPrefixes[0]} ${$issueTitle.val()}`); + issueTitleEl.value = `${wipPrefixes[0]} ${issueTitleEl.value}`; }); } diff --git a/web_src/js/features/repo-migration.js b/web_src/js/features/repo-migration.js index 59e282e4e7..eb2245b2ff 100644 --- a/web_src/js/features/repo-migration.js +++ b/web_src/js/features/repo-migration.js @@ -29,7 +29,7 @@ export function initRepoMigration() { cloneAddr?.addEventListener('change', () => { const repoName = document.getElementById('repo_name'); if (cloneAddr.value && !repoName?.value) { // Only modify if repo_name input is blank - repoName.value = cloneAddr.value.match(/^(.*\/)?((.+?)(\.git)?)$/)[3]; + repoName.value = cloneAddr.value.match(/^(.*\/)?((.+?)(\.git)?\/?)$/)[3]; } }); } diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 52c5de2bfa..47ada545de 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -55,13 +55,12 @@ export function initRepoSettingSearchTeamBox() { headers: {'X-Csrf-Token': csrfToken}, onResponse(response) { const items = []; - $.each(response.data, (_i, item) => { + for (const item of response.data) { items.push({ title: item.name, description: `${item.permission} access`, // TODO: translate this string }); - }); - + } return {results: items}; }, }, diff --git a/web_src/js/features/repo-template.js b/web_src/js/features/repo-template.js index 5f63e8b3ba..5f01be5ad3 100644 --- a/web_src/js/features/repo-template.js +++ b/web_src/js/features/repo-template.js @@ -32,12 +32,12 @@ export function initRepoTemplateSearch() { value: '', }); // Parse the response from the api to work with our dropdown - $.each(response.data, (_r, repo) => { + for (const repo of response.data) { filteredResponse.results.push({ name: htmlEscape(repo.repository.full_name), value: repo.repository.id, }); - }); + } return filteredResponse; }, cache: false, diff --git a/web_src/js/modules/dimmer.ts b/web_src/js/modules/dimmer.ts new file mode 100644 index 0000000000..7f1cbaa7ad --- /dev/null +++ b/web_src/js/modules/dimmer.ts @@ -0,0 +1,47 @@ +import $ from 'jquery'; + +class Dimmer { + dimmerEl: HTMLDivElement; + active: boolean; + + constructor() { + this.dimmerEl = document.querySelector('body > div.ui.dimmer') as HTMLDivElement; + if (!this.dimmerEl) { + this.dimmerEl = document.createElement('div'); + this.dimmerEl.classList.add('ui', 'dimmer', 'transition'); + document.body.append(this.dimmerEl); + } + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + dimmer(functionName: string, ...args: any[]) { + if (functionName === 'add content') { + this.dimmerEl.append(args[0][0]); + } else if (functionName === 'get dimmer') { + return $(this.dimmerEl); + } else if (functionName === 'show') { + this.dimmerEl.classList.add('active'); + this.dimmerEl.classList.remove('hidden'); + this.active = true; + } else if (functionName === 'hide') { + this.dimmerEl.classList.remove('active'); + this.dimmerEl.classList.add('hidden'); + this.active = false; + } else if (functionName === 'is active') { + return this.active; + } + } + + // JQuery compatibility functions. + get(_index: number): HTMLElement { + return document.body; + } + removeClass() {} + hasClass() {} +} + +export function initDimmer() { + $.fn.dimmer = (arg: string | object) => { + if (typeof arg === 'object') return new Dimmer(); + }; +} diff --git a/web_src/js/modules/fomantic.js b/web_src/js/modules/fomantic.js index c04bc6e863..c736026c6c 100644 --- a/web_src/js/modules/fomantic.js +++ b/web_src/js/modules/fomantic.js @@ -6,6 +6,7 @@ import {initAriaDropdownPatch} from './fomantic/dropdown.js'; import {initAriaModalPatch} from './fomantic/modal.js'; import {initFomanticTransition} from './fomantic/transition.js'; import {svg} from '../svg.js'; +import {initDimmer} from './dimmer.ts'; export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)'); @@ -31,4 +32,5 @@ export function initGiteaFomantic() { initAriaFormFieldPatch(); initAriaDropdownPatch(); initAriaModalPatch(); + initDimmer(); }