diff --git a/.eslintrc.yaml b/.eslintrc.yaml index f28d2ac247..91019cde84 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -4,7 +4,6 @@ reportUnusedDisableDirectives: true ignorePatterns: - /web_src/js/vendor - /web_src/fomantic - - /public/assets/js parserOptions: sourceType: module @@ -311,7 +310,7 @@ rules: jquery/no-merge: [2] jquery/no-param: [2] jquery/no-parent: [0] - jquery/no-parents: [0] + jquery/no-parents: [2] jquery/no-parse-html: [2] jquery/no-prop: [2] jquery/no-proxy: [2] @@ -320,8 +319,8 @@ rules: jquery/no-show: [2] jquery/no-size: [2] jquery/no-sizzle: [0] - jquery/no-slide: [0] - jquery/no-submit: [0] + jquery/no-slide: [2] + jquery/no-submit: [2] jquery/no-text: [0] jquery/no-toggle: [2] jquery/no-trigger: [0] @@ -459,7 +458,7 @@ rules: no-jquery/no-other-utils: [2] no-jquery/no-param: [2] no-jquery/no-parent: [0] - no-jquery/no-parents: [0] + no-jquery/no-parents: [2] no-jquery/no-parse-html-literal: [0] no-jquery/no-parse-html: [2] no-jquery/no-parse-json: [2] diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index 6212b8ef0b..6ce7141004 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -22,7 +22,7 @@ jobs: runs-on: docker container: - image: ghcr.io/visualon/renovate:37.323.3 + image: ghcr.io/visualon/renovate:37.330.1 steps: - name: Load renovate repo cache diff --git a/.golangci.yml b/.golangci.yml index 6d52f99401..6d835909fc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,13 +1,14 @@ linters: + enable-all: false + disable-all: true + fast: false enable: - bidichk - # - deadcode # deprecated - https://github.com/golangci/golangci-lint/issues/1841 - depguard - dupl - errcheck - forbidigo - gocritic - # - gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time. - gofmt - gofumpt - gosimple @@ -17,16 +18,11 @@ linters: - nolintlint - revive - staticcheck - # - structcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841 - stylecheck - typecheck - unconvert - unused - # - varcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841 - wastedassign - enable-all: false - disable-all: true - fast: false run: timeout: 10m @@ -35,6 +31,9 @@ run: - public - web_src +output: + sort-results: true + linters-settings: stylecheck: checks: ["all", "-ST1005", "-ST1003"] @@ -51,27 +50,37 @@ linters-settings: errorCode: 1 warningCode: 1 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: error-naming + - name: errorf - name: exported + - name: identical-branches - name: if-return - name: increment-decrement - - name: var-naming - - name: var-declaration + - 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: indent-error-flow - - name: errorf - - name: duplicated-imports - - name: modifies-value-receiver + - name: unreachable-code + - name: var-declaration + - name: var-naming gofumpt: extra-rules: true depguard: @@ -96,8 +105,12 @@ linters-settings: issues: max-issues-per-linter: 0 max-same-issues: 0 + exclude-dirs: [node_modules, public, web_src] + exclude-case-sensitive: true exclude-rules: - # Exclude some linters from running on tests files. + - path: models/db/sql_postgres_with_schema.go + linters: + - nolintlint - path: _test\.go linters: - gocyclo @@ -115,19 +128,19 @@ issues: - path: cmd linters: - forbidigo - - linters: + - text: "webhook" + linters: - dupl - text: "webhook" - - linters: + - text: "`ID' should not be capitalized" + linters: - gocritic - text: "`ID' should not be capitalized" - - linters: + - text: "swagger" + linters: - unused - deadcode - text: "swagger" - - linters: + - text: "argument x is overwritten before first use" + linters: - staticcheck - text: "argument x is overwritten before first use" - text: "commentFormatting: put a space between `//` and comment text" linters: - gocritic diff --git a/MAINTAINERS b/MAINTAINERS deleted file mode 100644 index 72171f80ed..0000000000 --- a/MAINTAINERS +++ /dev/null @@ -1,61 +0,0 @@ -Alexey Makhov (@makhov) -Bo-Yi Wu (@appleboy) -Ethan Koenig (@ethantkoenig) -Kees de Vries (@Bwko) -Kim Carlbäcker (@bkcsoft) -LefsFlare (@LefsFlarey) -Lunny Xiao (@lunny) -Rachid Zarouali (@xinity) -Rémy Boulanouar (@DblK) -Sandro Santilli (@strk) -Thibault Meyer (@0xbaadf00d) -Thomas Boerger (@tboerger) -Patrick G (@geek1011) -Antoine Girard (@sapk) -Lauris Bukšis-Haberkorns (@lafriks) -Jonas Östanbäck (@cez81) -David Schneiderbauer (@daviian) -Peter Žeby (@morlinest) -Matti Ranta (@techknowlogick) -Jonas Franz (@jonasfranz) -Alexey Terentyev (@axifive) -Lanre Adelowo (@adelowo) -Konrad Langenberg (@kolaente) -He-Long Zhang (@BetaCat0) -Andrew Thornton (@zeripath) -John Olheiser (@jolheiser) -Richard Mahn (@richmahn) -Mrsdizzie (@mrsdizzie) -silverwind (@silverwind) -Gary Kim (@gary-kim) -Guillermo Prandi (@guillep2k) -Mura Li (@typeless) -6543 <6543@obermui.de> (@6543) -jaqra (@jaqra) -David Svantesson (@davidsvantesson) -a1012112796 <1012112796@qq.com> (@a1012112796) -Karl Heinz Marbaise (@khmarbaise) -Norwin Roosen (@noerw) -Kyle Dumont (@kdumontnu) -Patrick Schratz (@pat-s) -Janis Estelmann (@KN4CK3R) -Steven Kriegler (@justusbunsi) -Jimmy Praet (@jpraet) -Leon Hofmeister (@delvh) -Wim (@42wim) -Jason Song (@wolfogre) -Yarden Shoham (@yardenshoham) -Yu Tian (@Zettat123) -Eddie Yang <576951401@qq.com> (@yp05327) -Dong Ge (@sillyguodong) -Xinyi Gong (@HesterG) -wxiaoguang (@wxiaoguang) -Gary Moon (@garymoon) -Philip Peterson (@philip-peterson) -Denys Konovalov (@denyskon) -Punit Inani (@puni9869) -CaiCandong <1290147055@qq.com> (@caicandong) -Rui Chen (@chenrui333) -Nanguan Lin (@lng2020) -kerwin612 (@kerwin612) -Gary Wang (@BLumia) diff --git a/Makefile b/Makefile index 6e46283ac8..6d4e3e0703 100644 --- a/Makefile +++ b/Makefile @@ -913,8 +913,9 @@ webpack: $(WEBPACK_DEST) $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json @$(MAKE) -s node-check node_modules - rm -rf $(WEBPACK_DEST_ENTRIES) - npx webpack + @rm -rf $(WEBPACK_DEST_ENTRIES) + @echo "Running webpack..." + @BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack @touch $(WEBPACK_DEST) .PHONY: svg diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 844c0621fe..6d583f0eff 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -8,6 +8,23 @@ A [patch or minor release](https://semver.org/spec/v2.0.0.html) (e.g. upgrading - [8.0.0](/release-notes/8.0.0/) +## 7.0.2 + +This is a bug fix release. See the documentation for more information on the [upgrade procedure](https://forgejo.org/docs/v7.0/admin/upgrade/). + +In addition to the following notable bug fixes, you can browse the [full list of commits](https://codeberg.org/forgejo/forgejo/compare/v7.0.1...v7.0.2) included in this release. + +* **Bug fixes:** + * [PR](https://codeberg.org/forgejo/forgejo/pulls/3562): a v7.0.0 regression where subscribing to or unsubscribing from an issue in a repository with no code produced an internal server error. + * [PR](https://codeberg.org/forgejo/forgejo/issues/3559): a v7.0.0 regression makes all the refs sent in Gitea webhooks to be full refs and might break Woodpecker CI pipelines triggered on tag (`CI_COMMIT_TAG` contained the full ref). This issue [has been fixed](https://github.com/woodpecker-ci/woodpecker/pull/3664) in the `main` branch of Woodpecker CI as well. + * [PR](https://codeberg.org/forgejo/forgejo/pulls/3555): the webhook branch filter wrongly applied the match on the full ref for branch creation and deletion (wrongly skipping events). + * [PR](https://codeberg.org/forgejo/forgejo/pulls/3537): toggling the WIP state of a pull request is possible from the sidebar, but not from the footer. + * [PR](https://codeberg.org/forgejo/forgejo/pulls/3565): when mentioning a user, the markup post-processor does not handle the case where the mentioned user does not exist: it tries to skip to the next node, which in turn, ended up skipping the rest of the line. + * [PR](https://codeberg.org/forgejo/forgejo/pulls/3570): excessive and unnecessary database queries when a user with no repositories is viewing their dashboard. + * [PR](https://codeberg.org/forgejo/forgejo/pulls/3580): duplicate status check contexts show in the branch protection settings. + * [PR](https://codeberg.org/forgejo/forgejo/pulls/3497): profile info fails to render german singular translation. + * [PR](https://codeberg.org/forgejo/forgejo/pulls/3504): inline attachments of [incoming emails](https://forgejo.org/docs/v7.0/user/incoming/) (as they occur for example with Apple Mail) are not attached to comments. + ## 7.0.1 This is a bug fix release. See the documentation for more information on the [upgrade procedure](https://forgejo.org/docs/v7.0/admin/upgrade/). @@ -34,6 +51,7 @@ $ git -C forgejo log --oneline --no-merges origin/v1.21/forgejo..origin/v7.0/for * Running the [`forgejo doctor check --fix`](https://forgejo.org/docs/v7.0/admin/command-line/#doctor-check) CLI command or setting [`[cron.gc_lfs].ENABLED=true`](https://forgejo.org/docs/v7.0/admin/config-cheat-sheet/#cron---garbage-collect-lfs-pointers-in-repositories-crongc_lfs) (the default is `false`) will corrupt the LFS storage. The workaround is to not run the doctor CLI command and disable the `cron.gc_lfs`. This regression will be [fixed in 7.0.1](https://codeberg.org/forgejo/forgejo/issues/3438). * The [`forgejo admin user create`](https://forgejo.org/docs/v7.0/admin/command-line/#admin-user-create) CLI command [requires a password](https://codeberg.org/forgejo/forgejo/commit/b122c6ef8b9254120432aed373cbe075331132ac) change by default when creating the first user and the `--admin` flag is not specified. The `--must-change-password=false` argument must be given to not require a password change. This regression will be [fixed in 7.0.1](https://codeberg.org/forgejo/forgejo/issues/3399). * **Breaking changes requiring manual intervention:** + * [Forgejo webhooks](https://codeberg.org/forgejo/forgejo/issues/3055) now always send full refs (starting with `refs/`) instead of sending short refs in some cases. This new behavior may require changes when the receiving end assumes a short ref will be received (for instance some versions of Woodpecker CI when receiving webhook payloads when a tag is set). * [MySQL 8.0 or PostgreSQL 12](https://codeberg.org/forgejo/forgejo/commit/e94f9fcafdcf284561e7fb33f60156a69c4ad6a5) are the minimum supported versions. The database must be migrated before upgrading. The requirements regarding SQLite did not change. * The `per_page` parameter is [no longer a synonym for `limit`](https://codeberg.org/forgejo/forgejo/commit/0aab2d38a7d91bc8caff332e452364468ce52d9a) in the [/repos/{owner}/{repo}/releases](https://code.forgejo.org/api/swagger/#/repository/repoListReleases) API endpoint. * The date format of the `created` and `last_update` fields of the [`/repos/{owner}/{repo}/push_mirrors`](https://code.forgejo.org/api/swagger/#/repository/repoListPushMirrors) and [/repos/{owner}/{repo}/push_mirrors](https://code.forgejo.org/api/swagger/#/repository/repoAddPushMirror) API endpoint changed [to be timestamps instead of numbers](https://codeberg.org/forgejo/forgejo/commit/0ee7cbf725f45650136be45f8e0f74d395f73b5c). diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index ec92e342d4..4777a92908 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -4,6 +4,7 @@ package cmd import ( + "errors" "fmt" "os" "text/tabwriter" @@ -91,7 +92,7 @@ func runListAuth(c *cli.Context) error { func runDeleteAuth(c *cli.Context) error { if !c.IsSet("id") { - return fmt.Errorf("--id flag is missing") + return errors.New("--id flag is missing") } ctx, cancel := installSignals() diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go index c151c0af27..8e6239ac33 100644 --- a/cmd/admin_auth_oauth.go +++ b/cmd/admin_auth_oauth.go @@ -4,6 +4,7 @@ package cmd import ( + "errors" "fmt" "net/url" @@ -193,7 +194,7 @@ func runAddOauth(c *cli.Context) error { func runUpdateOauth(c *cli.Context) error { if !c.IsSet("id") { - return fmt.Errorf("--id flag is missing") + return errors.New("--id flag is missing") } ctx, cancel := installSignals() diff --git a/cmd/admin_auth_stmp.go b/cmd/admin_auth_stmp.go index 58a6e2ac22..d724746905 100644 --- a/cmd/admin_auth_stmp.go +++ b/cmd/admin_auth_stmp.go @@ -5,7 +5,6 @@ package cmd import ( "errors" - "fmt" "strings" auth_model "code.gitea.io/gitea/models/auth" @@ -166,7 +165,7 @@ func runAddSMTP(c *cli.Context) error { func runUpdateSMTP(c *cli.Context) error { if !c.IsSet("id") { - return fmt.Errorf("--id flag is missing") + return errors.New("--id flag is missing") } ctx, cancel := installSignals() diff --git a/cmd/admin_user_delete.go b/cmd/admin_user_delete.go index 1cbc6f7527..520557554a 100644 --- a/cmd/admin_user_delete.go +++ b/cmd/admin_user_delete.go @@ -4,6 +4,7 @@ package cmd import ( + "errors" "fmt" "strings" @@ -42,7 +43,7 @@ var microcmdUserDelete = &cli.Command{ func runDeleteUser(c *cli.Context) error { if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") { - return fmt.Errorf("You must provide the id, username or email of a user to delete") + return errors.New("You must provide the id, username or email of a user to delete") } ctx, cancel := installSignals() diff --git a/cmd/admin_user_generate_access_token.go b/cmd/admin_user_generate_access_token.go index 6e78939680..6c2c10494e 100644 --- a/cmd/admin_user_generate_access_token.go +++ b/cmd/admin_user_generate_access_token.go @@ -4,6 +4,7 @@ package cmd import ( + "errors" "fmt" auth_model "code.gitea.io/gitea/models/auth" @@ -42,7 +43,7 @@ var microcmdUserGenerateAccessToken = &cli.Command{ func runGenerateAccessToken(c *cli.Context) error { if !c.IsSet("username") { - return fmt.Errorf("You must provide a username to generate a token for") + return errors.New("You must provide a username to generate a token for") } ctx, cancel := installSignals() @@ -68,7 +69,7 @@ func runGenerateAccessToken(c *cli.Context) error { return err } if exist { - return fmt.Errorf("access token name has been used already") + return errors.New("access token name has been used already") } // make sure the scopes are valid diff --git a/cmd/embedded.go b/cmd/embedded.go index 71d483d11c..9f03f7be7c 100644 --- a/cmd/embedded.go +++ b/cmd/embedded.go @@ -157,9 +157,9 @@ func runViewDo(c *cli.Context) error { } if len(matchedAssetFiles) == 0 { - return fmt.Errorf("no files matched the given pattern") + return errors.New("no files matched the given pattern") } else if len(matchedAssetFiles) > 1 { - return fmt.Errorf("too many files matched the given pattern, try to be more specific") + return errors.New("too many files matched the given pattern, try to be more specific") } data, err := matchedAssetFiles[0].fs.ReadFile(matchedAssetFiles[0].name) @@ -180,7 +180,7 @@ func runExtractDo(c *cli.Context) error { } if c.NArg() == 0 { - return fmt.Errorf("a list of pattern of files to extract is mandatory (e.g. '**' for all)") + return errors.New("a list of pattern of files to extract is mandatory (e.g. '**' for all)") } destdir := "." diff --git a/cmd/hook.go b/cmd/hook.go index 81955e753b..ff3059f9df 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -482,7 +482,7 @@ func hookPrintResults(results []private.HookPostReceiveBranchResult) { fmt.Fprintf(os.Stderr, " %s\n", res.URL) } fmt.Fprintln(os.Stderr, "") - os.Stderr.Sync() + _ = os.Stderr.Sync() } } diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go index 2c701f2672..6049b00d5e 100644 --- a/cmd/manager_logging.go +++ b/cmd/manager_logging.go @@ -4,6 +4,7 @@ package cmd import ( + "errors" "fmt" "os" @@ -249,7 +250,7 @@ func runAddFileLogger(c *cli.Context) error { if c.IsSet("filename") { vals["filename"] = c.String("filename") } else { - return fmt.Errorf("filename must be set when creating a file logger") + return errors.New("filename must be set when creating a file logger") } if c.IsSet("rotate") { vals["rotate"] = c.Bool("rotate") diff --git a/go.mod b/go.mod index f32e871921..9dc8ae3f5b 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( code.gitea.io/gitea-vet v0.2.3 code.gitea.io/sdk/gitea v0.17.1 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 - connectrpc.com/connect v1.15.0 + connectrpc.com/connect v1.16.1 gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669 gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 @@ -15,6 +15,7 @@ require ( gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 + github.com/ProtonMail/go-crypto v1.0.0 github.com/PuerkitoBio/goquery v1.8.1 github.com/alecthomas/chroma/v2 v2.13.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb @@ -92,7 +93,7 @@ require ( github.com/stretchr/testify v1.9.0 github.com/syndtr/goleveldb v1.0.0 github.com/ulikunitz/xz v0.5.11 - github.com/urfave/cli/v2 v2.27.1 + github.com/urfave/cli/v2 v2.27.2 github.com/xanzy/go-gitlab v0.96.0 github.com/yohcop/openid-go v1.0.1 github.com/yuin/goldmark v1.7.0 @@ -129,7 +130,6 @@ require ( github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/RoaringBitmap/roaring v1.7.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect @@ -161,7 +161,7 @@ require ( github.com/couchbase/go-couchbase v0.1.1 // indirect github.com/couchbase/gomemcached v0.3.0 // indirect github.com/couchbase/goutils v0.1.2 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect @@ -269,7 +269,7 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect - github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect + github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect github.com/zeebo/blake3 v0.2.3 // indirect go.etcd.io/bbolt v1.3.9 // indirect go.mongodb.org/mongo-driver v1.13.1 // indirect diff --git a/go.sum b/go.sum index 95e400e40e..a42bce3812 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8= code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM= 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.15.0 h1:lFdeCbZrVVDydAqwr4xGV2y+ULn+0Z73s5JBj2LikWo= -connectrpc.com/connect v1.15.0/go.mod h1:bQmjpDY8xItMnttnurVgOkHUBMRT9cpsNi2O4AjKhmA= +connectrpc.com/connect v1.16.1 h1:rOdrK/RTI/7TVnn3JsVxt3n028MlTRwmK5Q4heSpjis= +connectrpc.com/connect v1.16.1/go.mod h1:XpZAduBQUySsb4/KO5JffORVkDI4B6/EYPi7N8xpNZw= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -203,8 +203,8 @@ github.com/couchbase/gomemcached v0.3.0 h1:XkMDdP6w7rtvLijDE0/RhcccX+XvAk5cboyBv github.com/couchbase/gomemcached v0.3.0/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo= github.com/couchbase/goutils v0.1.2 h1:gWr8B6XNWPIhfalHNog3qQKfGiYyh4K4VhO3P2o9BCs= github.com/couchbase/goutils v0.1.2/go.mod h1:h89Ek/tiOxxqjz30nPPlwZdQbdB8BwgnuBxeoUe/ViE= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= @@ -800,8 +800,8 @@ github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= -github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= -github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= +github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= @@ -823,8 +823,8 @@ github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgk github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= -github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= -github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5 h1:3seWKGVhGoc66Ht5QlhQsr4xT2caDnFegsnh2NqvENU= github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js= diff --git a/models/actions/run.go b/models/actions/run.go index 493bd0173c..397455e41d 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -98,13 +98,10 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error { return nil } - if run.Repo == nil { - repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID) - if err != nil { - return err - } - run.Repo = repo + if err := run.LoadRepo(ctx); err != nil { + return err } + if err := run.Repo.LoadAttributes(ctx); err != nil { return err } @@ -120,6 +117,19 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error { return nil } +func (run *ActionRun) LoadRepo(ctx context.Context) error { + if run == nil || run.Repo != nil { + return nil + } + + repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID) + if err != nil { + return err + } + run.Repo = repo + return nil +} + func (run *ActionRun) Duration() time.Duration { return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration } diff --git a/models/actions/runner.go b/models/actions/runner.go index 67f003387b..9192925d5a 100644 --- a/models/actions/runner.go +++ b/models/actions/runner.go @@ -270,7 +270,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { // Only affect action runners were a owner ID is set, as actions runners // could also be created on a repository. return db.GetEngine(ctx).Table("action_runner"). - Join("LEFT", "user", "`action_runner`.owner_id = `user`.id"). + Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id"). Where("`action_runner`.owner_id != ?", 0). And(builder.IsNull{"`user`.id"}). Count(new(ActionRunner)) @@ -279,7 +279,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { subQuery := builder.Select("`action_runner`.id"). From("`action_runner`"). - Join("LEFT", "user", "`action_runner`.owner_id = `user`.id"). + Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id"). Where(builder.Neq{"`action_runner`.owner_id": 0}). And(builder.IsNull{"`user`.id"}) b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`") @@ -289,3 +289,25 @@ func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { } return res.RowsAffected() } + +func CountRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) { + return db.GetEngine(ctx).Table("action_runner"). + Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id"). + Where("`action_runner`.repo_id != ?", 0). + And(builder.IsNull{"`repository`.id"}). + Count(new(ActionRunner)) +} + +func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) { + subQuery := builder.Select("`action_runner`.id"). + From("`action_runner`"). + Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id"). + Where(builder.Neq{"`action_runner`.repo_id": 0}). + And(builder.IsNull{"`repository`.id"}) + b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`") + res, err := db.GetEngine(ctx).Exec(b) + if err != nil { + return 0, err + } + return res.RowsAffected() +} diff --git a/models/actions/variable.go b/models/actions/variable.go index b0a455e675..8aff844659 100644 --- a/models/actions/variable.go +++ b/models/actions/variable.go @@ -92,6 +92,11 @@ func DeleteVariable(ctx context.Context, id int64) error { func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) { variables := map[string]string{} + if err := run.LoadRepo(ctx); err != nil { + log.Error("LoadRepo: %v", err) + return nil, err + } + // Global globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{}) if err != nil { diff --git a/models/asymkey/gpg_key_object_verification.go b/models/asymkey/gpg_key_object_verification.go index 67764ffc58..e5c31a74a7 100644 --- a/models/asymkey/gpg_key_object_verification.go +++ b/models/asymkey/gpg_key_object_verification.go @@ -94,7 +94,6 @@ func ParseObjectWithSignature(ctx context.Context, c *GitObject) *ObjectVerifica Reason: "gpg.error.no_committer_account", } } - } } diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 5eabc7d9b4..125d64b36f 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -8,6 +8,7 @@ import ( "crypto/sha256" "encoding/base32" "encoding/base64" + "errors" "fmt" "net" "net/url" @@ -301,7 +302,7 @@ func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOp return nil, err } if app.UID != opts.UserID { - return nil, fmt.Errorf("UID mismatch") + return nil, errors.New("UID mismatch") } builtinApps := BuiltinApplications() if _, builtin := builtinApps[app.ClientID]; builtin { diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go index 5361f61db2..6602f850cf 100644 --- a/models/auth/oauth2_test.go +++ b/models/auth/oauth2_test.go @@ -16,8 +16,6 @@ import ( "github.com/stretchr/testify/assert" ) -//////////////////// Application - func TestOAuth2Application_GenerateClientSecret(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1}) diff --git a/models/db/engine.go b/models/db/engine.go index 1f0cd1a5dd..b7146e87f2 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -236,7 +236,6 @@ func NamesToBean(names ...string) ([]any, error) { // Need to 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 beanMap[strings.ToLower(x.TableName(bean))] = bean beanMap[strings.ToLower(x.TableName(bean, true))] = bean diff --git a/models/fixtures/webhook.yml b/models/fixtures/webhook.yml index f0b64cd57b..cab5c5aca0 100644 --- a/models/fixtures/webhook.yml +++ b/models/fixtures/webhook.yml @@ -29,8 +29,9 @@ - id: 4 repo_id: 2 + type: gitea url: http://www.example.com/url4 http_method: POST content_type: 1 # json - events: '{"push_only":true,"branch_filter":"{master,feature*}"}' + events: '{"send_everything":true,"branch_filter":"{master,feature*}"}' is_active: false diff --git a/models/git/branch_list.go b/models/git/branch_list.go index 980bd7b4c9..493611f217 100644 --- a/models/git/branch_list.go +++ b/models/git/branch_list.go @@ -116,6 +116,9 @@ func FindBranchNames(ctx context.Context, opts FindBranchOptions) ([]string, err } func FindBranchesByRepoAndBranchName(ctx context.Context, repoBranches map[int64]string) (map[int64]string, error) { + if len(repoBranches) == 0 { + return nil, nil + } cond := builder.NewCond() for repoID, branchName := range repoBranches { cond = cond.Or(builder.And(builder.Eq{"repo_id": repoID}, builder.Eq{"name": branchName})) diff --git a/models/git/branch_test.go b/models/git/branch_test.go index b8ea663e81..3aa578f44b 100644 --- a/models/git/branch_test.go +++ b/models/git/branch_test.go @@ -183,3 +183,12 @@ func TestOnlyGetDeletedBranchOnCorrectRepo(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, deletedBranch) } + +func TestFindBranchesByRepoAndBranchName(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + // With no repos or branches given, we find no branches. + branches, err := git_model.FindBranchesByRepoAndBranchName(db.DefaultContext, map[int64]string{}) + assert.NoError(t, err) + assert.Len(t, branches, 0) +} diff --git a/models/git/lfs_lock.go b/models/git/lfs_lock.go index 261c73032a..2f65833fe3 100644 --- a/models/git/lfs_lock.go +++ b/models/git/lfs_lock.go @@ -5,7 +5,7 @@ package git import ( "context" - "fmt" + "errors" "strings" "time" @@ -148,7 +148,7 @@ func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repositor } if !force && u.ID != lock.OwnerID { - return nil, fmt.Errorf("user doesn't own lock and force flag is not set") + return nil, errors.New("user doesn't own lock and force flag is not set") } if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil { diff --git a/models/issues/review.go b/models/issues/review.go index 92764db4d1..3c6934b060 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -345,11 +345,9 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error return nil, err } } - } else if opts.ReviewerTeam != nil { review.Type = ReviewTypeRequest review.ReviewerTeamID = opts.ReviewerTeam.ID - } else { return nil, fmt.Errorf("provide either reviewer or reviewer team") } diff --git a/models/migrations/base/db.go b/models/migrations/base/db.go index a76ce29deb..e584793385 100644 --- a/models/migrations/base/db.go +++ b/models/migrations/base/db.go @@ -214,7 +214,6 @@ func RecreateTable(sess *xorm.Session, bean any) error { return err } sequenceMap[sequence] = sequenceData - } // CASCADE causes postgres to drop all the constraints on the old table @@ -279,7 +278,6 @@ func RecreateTable(sess *xorm.Session, bean any) error { return err } } - } default: diff --git a/models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/o_auth2_application.yml b/models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml similarity index 100% rename from models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/o_auth2_application.yml rename to models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 7146c49676..5d80f9fd10 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -22,7 +22,6 @@ import ( "code.gitea.io/gitea/models/migrations/v1_20" "code.gitea.io/gitea/models/migrations/v1_21" "code.gitea.io/gitea/models/migrations/v1_22" - "code.gitea.io/gitea/models/migrations/v1_23" "code.gitea.io/gitea/models/migrations/v1_6" "code.gitea.io/gitea/models/migrations/v1_7" "code.gitea.io/gitea/models/migrations/v1_8" @@ -576,14 +575,20 @@ var migrations = []Migration{ // v293 -> v294 NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency), - // Gitea 1.22.0 ends at 294 + // Gitea 1.22.0-rc0 ends at 294 // v294 -> v295 - NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue), + NewMigration("Add unique index for project issue table", v1_22.AddUniqueIndexForProjectIssue), // v295 -> v296 - NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary), + NewMigration("Add commit status summary table", v1_22.AddCommitStatusSummary), // v296 -> v297 - NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2), + NewMigration("Add missing field of commit status summary table", v1_22.AddCommitStatusSummary2), + // v297 -> v298 + NewMigration("Add everyone_access_mode for repo_unit", noopMigration), + // v298 -> v299 + NewMigration("Drop wrongly created table o_auth2_application", v1_22.DropWronglyCreatedTable), + + // Gitea 1.22.0-rc1 ends at 299 } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go index c18d7adae1..cc3dc0d545 100644 --- a/models/migrations/v1_11/v111.go +++ b/models/migrations/v1_11/v111.go @@ -263,7 +263,6 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { for _, u := range units { var found bool for _, team := range teams { - var teamU []*TeamUnit var unitEnabled bool err = sess.Where("team_id = ?", team.ID).Find(&teamU) @@ -332,7 +331,6 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { } if !protectedBranch.EnableApprovalsWhitelist { - perm, err := getUserRepoPermission(sess, baseRepo, reviewer) if err != nil { return false, err diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go index f9d1eb5c84..db45b11aed 100644 --- a/models/migrations/v1_16/v210.go +++ b/models/migrations/v1_16/v210.go @@ -68,11 +68,6 @@ func RemigrateU2FCredentials(x *xorm.Engine) error { if err != nil { return err } - case schemas.ORACLE: - _, err := x.Exec("ALTER TABLE webauthn_credential MODIFY credential_id VARCHAR(410)") - if err != nil { - return err - } case schemas.POSTGRES: _, err := x.Exec("ALTER TABLE webauthn_credential ALTER COLUMN credential_id TYPE VARCHAR(410)") if err != nil { diff --git a/models/migrations/v1_18/v230.go b/models/migrations/v1_18/v230.go index cf94926be1..ea5b4d02e1 100644 --- a/models/migrations/v1_18/v230.go +++ b/models/migrations/v1_18/v230.go @@ -9,9 +9,9 @@ import ( // AddConfidentialColumnToOAuth2ApplicationTable: add ConfidentialClient column, setting existing rows to true func AddConfidentialClientColumnToOAuth2ApplicationTable(x *xorm.Engine) error { - type OAuth2Application struct { + type oauth2Application struct { + ID int64 ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"` } - - return x.Sync(new(OAuth2Application)) + return x.Sync(new(oauth2Application)) } diff --git a/models/migrations/v1_18/v230_test.go b/models/migrations/v1_18/v230_test.go index 308f3a5023..40db4c2ffe 100644 --- a/models/migrations/v1_18/v230_test.go +++ b/models/migrations/v1_18/v230_test.go @@ -13,12 +13,12 @@ import ( func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) { // premigration - type OAuth2Application struct { + type oauth2Application struct { ID int64 } // Prepare and load the testing database - x, deferable := base.PrepareTestEnv(t, 0, new(OAuth2Application)) + x, deferable := base.PrepareTestEnv(t, 0, new(oauth2Application)) defer deferable() if x == nil || t.Failed() { return @@ -36,7 +36,7 @@ func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) { } got := []ExpectedOAuth2Application{} - if err := x.Table("o_auth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) { + if err := x.Table("oauth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) { return } diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go index a09957b291..86388ef0b8 100644 --- a/models/migrations/v1_20/v250.go +++ b/models/migrations/v1_20/v250.go @@ -104,7 +104,7 @@ func ChangeContainerMetadataMultiArch(x *xorm.Engine) error { // Convert to new metadata format - new := &MetadataNew{ + newMetadata := &MetadataNew{ Type: old.Type, IsTagged: old.IsTagged, Platform: old.Platform, @@ -119,7 +119,7 @@ func ChangeContainerMetadataMultiArch(x *xorm.Engine) error { Manifests: manifests, } - metadataJSON, err := json.Marshal(new) + metadataJSON, err := json.Marshal(newMetadata) if err != nil { return err } diff --git a/models/migrations/v1_23/v294.go b/models/migrations/v1_22/v294.go similarity index 98% rename from models/migrations/v1_23/v294.go rename to models/migrations/v1_22/v294.go index e3e18f68f3..314b4519f1 100644 --- a/models/migrations/v1_23/v294.go +++ b/models/migrations/v1_22/v294.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_23/v294_test.go b/models/migrations/v1_22/v294_test.go similarity index 98% rename from models/migrations/v1_23/v294_test.go rename to models/migrations/v1_22/v294_test.go index d9a44ad866..82a3bcd602 100644 --- a/models/migrations/v1_23/v294_test.go +++ b/models/migrations/v1_22/v294_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_22 //nolint import ( "slices" diff --git a/models/migrations/v1_23/v295.go b/models/migrations/v1_22/v295.go similarity index 96% rename from models/migrations/v1_23/v295.go rename to models/migrations/v1_22/v295.go index 9a2003cfc1..17bdadb4ad 100644 --- a/models/migrations/v1_23/v295.go +++ b/models/migrations/v1_22/v295.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_22 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v296.go b/models/migrations/v1_22/v296.go similarity index 95% rename from models/migrations/v1_23/v296.go rename to models/migrations/v1_22/v296.go index 495ae2ab23..1ecacab95f 100644 --- a/models/migrations/v1_23/v296.go +++ b/models/migrations/v1_22/v296.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_22 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go new file mode 100644 index 0000000000..b9f3b95ade --- /dev/null +++ b/models/migrations/v1_22/v298.go @@ -0,0 +1,10 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func DropWronglyCreatedTable(x *xorm.Engine) error { + return x.DropTables("o_auth2_application") +} diff --git a/models/migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go index 4e50ca9219..586187228b 100644 --- a/models/migrations/v1_6/v71.go +++ b/models/migrations/v1_6/v71.go @@ -61,7 +61,6 @@ func AddScratchHash(x *xorm.Engine) error { if _, err := sess.ID(tfa.ID).Cols("scratch_salt, scratch_hash").Update(tfa); err != nil { return fmt.Errorf("couldn't add in scratch_hash and scratch_salt: %w", err) } - } } diff --git a/models/migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go index 9419ee1aae..a23d7c5d6e 100644 --- a/models/migrations/v1_9/v85.go +++ b/models/migrations/v1_9/v85.go @@ -81,7 +81,6 @@ func HashAppToken(x *xorm.Engine) error { if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil { return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %w", err) } - } } diff --git a/models/organization/team.go b/models/organization/team.go index 17db11c42d..1b737c2d3d 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -222,9 +222,8 @@ func GetTeamIDsByNames(ctx context.Context, orgID int64, names []string, ignoreN if err != nil { if ignoreNonExistent { continue - } else { - return nil, err } + return nil, err } ids = append(ids, u.ID) } diff --git a/models/project/board.go b/models/project/board.go index 5f142a356c..7faabc52c5 100644 --- a/models/project/board.go +++ b/models/project/board.go @@ -110,13 +110,11 @@ func createBoardsForProjectsType(ctx context.Context, project *Project) error { var items []string switch project.BoardType { - case BoardTypeBugTriage: items = setting.Project.ProjectBoardBugTriageType case BoardTypeBasicKanban: items = setting.Project.ProjectBoardBasicKanbanType - case BoardTypeNone: fallthrough default: diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index 219f4ff25d..d3fbd961bd 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -135,7 +135,6 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64) // the owner of a private repo needs to be explicitly added. cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID}) } - } else { // This is a "public" repository: // Any user that has read access, is a watcher or organization member can be requested to review diff --git a/models/repo_transfer.go b/models/repo_transfer.go index f20c5bcdc0..0c23d759f9 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -5,6 +5,7 @@ package models import ( "context" + "errors" "fmt" "code.gitea.io/gitea/models/db" @@ -120,7 +121,7 @@ func DeleteRepositoryTransfer(ctx context.Context, repoID int64) error { func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error { switch status { case repo_model.RepositoryBeingMigrated: - return fmt.Errorf("repo is not ready, currently migrating") + return errors.New("repo is not ready, currently migrating") case repo_model.RepositoryPendingTransfer: return ErrRepoTransferInProgress{} } diff --git a/models/user/user.go b/models/user/user.go index d3c3d426ca..55c6883920 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -1016,9 +1016,8 @@ func GetUserIDsByNames(ctx context.Context, names []string, ignoreNonExistent bo if err != nil { if ignoreNonExistent { continue - } else { - return nil, err } + return nil, err } ids = append(ids, u.ID) } diff --git a/models/user/user_test.go b/models/user/user_test.go index 3accd348eb..a173e8f2fd 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -5,8 +5,8 @@ package user_test import ( "context" + "crypto/rand" "fmt" - "math/rand" "strings" "testing" "time" diff --git a/modules/auth/password/password.go b/modules/auth/password/password.go index 27074358a9..85f9780709 100644 --- a/modules/auth/password/password.go +++ b/modules/auth/password/password.go @@ -63,16 +63,16 @@ func NewComplexity() { func setupComplexity(values []string) { if len(values) != 1 || values[0] != "off" { for _, val := range values { - if complex, ok := charComplexities[val]; ok { - validChars += complex.ValidChars - requiredList = append(requiredList, complex) + if complexity, ok := charComplexities[val]; ok { + validChars += complexity.ValidChars + requiredList = append(requiredList, complexity) } } if len(requiredList) == 0 { // No valid character classes found; use all classes as default - for _, complex := range charComplexities { - validChars += complex.ValidChars - requiredList = append(requiredList, complex) + for _, complexity := range charComplexities { + validChars += complexity.ValidChars + requiredList = append(requiredList, complexity) } } } diff --git a/modules/auth/password/pwn/pwn_test.go b/modules/auth/password/pwn/pwn_test.go index f9deadc8d7..a2a6b3a174 100644 --- a/modules/auth/password/pwn/pwn_test.go +++ b/modules/auth/password/pwn/pwn_test.go @@ -4,9 +4,8 @@ package pwn import ( - "math/rand" + "math/rand/v2" "net/http" - "os" "strings" "testing" "time" @@ -18,11 +17,6 @@ var client = New(WithHTTP(&http.Client{ Timeout: time.Second * 2, })) -func TestMain(m *testing.M) { - rand.Seed(time.Now().Unix()) - os.Exit(m.Run()) -} - func TestPassword(t *testing.T) { // Check input error _, err := client.CheckPassword("", false) @@ -81,24 +75,24 @@ func testPassword() string { // Set special character for i := 0; i < 5; i++ { - random := rand.Intn(len(specialCharSet)) + random := rand.IntN(len(specialCharSet)) password.WriteString(string(specialCharSet[random])) } // Set numeric for i := 0; i < 5; i++ { - random := rand.Intn(len(numberSet)) + random := rand.IntN(len(numberSet)) password.WriteString(string(numberSet[random])) } // Set uppercase for i := 0; i < 5; i++ { - random := rand.Intn(len(upperCharSet)) + random := rand.IntN(len(upperCharSet)) password.WriteString(string(upperCharSet[random])) } for i := 0; i < 5; i++ { - random := rand.Intn(len(allCharSet)) + random := rand.IntN(len(allCharSet)) password.WriteString(string(allCharSet[random])) } inRune := []rune(password.String()) diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go index 043dbb44bd..c988d6ab86 100644 --- a/modules/git/batch_reader.go +++ b/modules/git/batch_reader.go @@ -307,10 +307,10 @@ func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBu // Deal with the binary hash idx = 0 - len := objectFormat.FullLength() / 2 - for idx < len { + length := objectFormat.FullLength() / 2 + for idx < length { var read int - read, err = rd.Read(shaBuf[idx:len]) + read, err = rd.Read(shaBuf[idx:length]) n += read if err != nil { return mode, fname, sha, n, err diff --git a/modules/git/commit_reader.go b/modules/git/commit_reader.go index 49159cc418..8e2523d7fb 100644 --- a/modules/git/commit_reader.go +++ b/modules/git/commit_reader.go @@ -49,9 +49,8 @@ readLoop: if len(line) > 0 && line[0] == ' ' { _, _ = signatureSB.Write(line[1:]) continue - } else { - pgpsig = false } + pgpsig = false } if !message { diff --git a/modules/git/pipeline/lfs_nogogit.go b/modules/git/pipeline/lfs_nogogit.go index a3ee883968..349cfbd9ce 100644 --- a/modules/git/pipeline/lfs_nogogit.go +++ b/modules/git/pipeline/lfs_nogogit.go @@ -213,7 +213,6 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err errChan <- err break } - } }() diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 44273d2253..f9168bef7e 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -251,18 +251,18 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) return nil, err } - len := objectFormat.FullLength() + length := objectFormat.FullLength() commits := []*Commit{} - shaline := make([]byte, len+1) + shaline := make([]byte, length+1) for { n, err := io.ReadFull(stdoutReader, shaline) - if err != nil || n < len { + if err != nil || n < length { if err == io.EOF { err = nil } return commits, err } - objectID, err := NewIDFromString(string(shaline[0:len])) + objectID, err := NewIDFromString(string(shaline[0:length])) if err != nil { return nil, err } diff --git a/modules/git/submodule.go b/modules/git/submodule.go index 37813ea4c7..b99c81582b 100644 --- a/modules/git/submodule.go +++ b/modules/git/submodule.go @@ -64,7 +64,6 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { // ex: git@try.gitea.io:go-gitea/gitea match := scpSyntax.FindAllStringSubmatch(refURI, -1) if len(match) > 0 { - m := match[0] refHostname := m[2] pth := m[3] diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index ff0e37ca29..66724a3445 100644 --- a/modules/indexer/code/bleve/bleve.go +++ b/modules/indexer/code/bleve/bleve.go @@ -193,7 +193,6 @@ func (b *Indexer) addDelete(filename string, repo *repo_model.Repository, batch func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error { batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize) if len(changes.Updates) > 0 { - // Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first! if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil { log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err) @@ -337,7 +336,6 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int if result, err = b.inner.Indexer.Search(facetRequest); err != nil { return 0, nil, nil, err } - } languagesFacet := result.Facets["languages"] for _, term := range languagesFacet.Terms.Terms() { diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go index 4a98b4588a..8f94088742 100644 --- a/modules/indexer/issues/dboptions.go +++ b/modules/indexer/issues/dboptions.go @@ -68,7 +68,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp searchOpt.Paginator = opts.Paginator switch opts.SortType { - case "": + case "", "latest": searchOpt.SortBy = SortByCreatedDesc case "oldest": searchOpt.SortBy = SortByCreatedAsc @@ -86,7 +86,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp searchOpt.SortBy = SortByDeadlineDesc case "priority", "priorityrepo", "project-column-sorting": // Unsupported sort type for search - searchOpt.SortBy = SortByUpdatedDesc + fallthrough default: searchOpt.SortBy = SortByUpdatedDesc } diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index 53b383c8d5..c7cb59f2cf 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -145,7 +145,6 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query := elastic.NewBoolQuery() if options.Keyword != "" { - searchType := esMultiMatchTypePhrasePrefix if options.IsFuzzyKeyword { searchType = esMultiMatchTypeBestFields diff --git a/modules/log/event_format.go b/modules/log/event_format.go index 524ca3dd87..d9dbebf831 100644 --- a/modules/log/event_format.go +++ b/modules/log/event_format.go @@ -125,7 +125,6 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms if mode.Colorize { buf = append(buf, resetBytes...) } - } if flags&(Lshortfile|Llongfile) != 0 { if mode.Colorize { diff --git a/modules/markup/html.go b/modules/markup/html.go index 7a5ca14aeb..f73221a37f 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -54,7 +54,7 @@ var ( shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`) // anySHA1Pattern splits url containing SHA into parts - anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~_%.a-zA-Z0-9/]+)?(#[-+~_%.a-zA-Z0-9]+)?`) + anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~_%.a-zA-Z0-9/]+)?(\?[-+~_%\.a-zA-Z0-9=&]+)?(#[-+~_%.a-zA-Z0-9]+)?`) // comparePattern matches "http://domain/org/repo/compare/COMMIT1...COMMIT2#hash" comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`) @@ -623,10 +623,10 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) { if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) { replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention")) node = node.NextSibling.NextSibling + start = 0 } else { - node = node.NextSibling + start = loc.End } - start = 0 } } @@ -969,10 +969,10 @@ func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) { subpath = node.Data[m[4]:m[5]] } - // 4th capture group matches a optional url hash + // 5th capture group matches a optional url hash hash := "" - if m[7] > 0 { - hash = node.Data[m[6]:m[7]][1:] + if m[9] > 0 { + hash = node.Data[m[8]:m[9]][1:] } start := m[0] diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index e313be7040..917f280c73 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -403,28 +403,39 @@ func TestRegExp_anySHA1Pattern(t *testing.T) { "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js#L2703": { "a644101ed04d0beacea864ce805e0c4f86ba1cd1", "/test/unit/event.js", + "", "#L2703", }, "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js": { "a644101ed04d0beacea864ce805e0c4f86ba1cd1", "/test/unit/event.js", "", + "", }, "https://github.com/jquery/jquery/commit/0705be475092aede1eddae01319ec931fb9c65fc": { "0705be475092aede1eddae01319ec931fb9c65fc", "", "", + "", }, "https://github.com/jquery/jquery/tree/0705be475092aede1eddae01319ec931fb9c65fc/src": { "0705be475092aede1eddae01319ec931fb9c65fc", "/src", "", + "", }, "https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2": { "d8a994ef243349f321568f9e36d5c3f444b99cae", "", + "", "#diff-2", }, + "https://codeberg.org/forgejo/forgejo/src/commit/949ab9a5c4cac742f84ae5a9fa186f8d6eb2cdc0/RELEASE-NOTES.md?display=source&w=1#L7-L9": { + "949ab9a5c4cac742f84ae5a9fa186f8d6eb2cdc0", + "/RELEASE-NOTES.md", + "?display=source&w=1", + "#L7-L9", + }, } for k, v := range testCases { diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index a1a99c1a7f..cfd1a66a18 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -127,6 +127,12 @@ func TestRender_CrossReferences(t *testing.T) { test( util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345"), `

gogitea/some-repo-name#12345

`) + + sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d" + urlWithQuery := util.URLJoin(markup.TestAppURL, "forgejo", "some-repo-name", "commit", sha, "README.md") + "?display=source#L1-L5" + test( + urlWithQuery, + `

`+sha[:10]+`/README.md (L1-L5)

`) } func TestMisc_IsSameDomain(t *testing.T) { diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index 3e1d75b291..278494d95c 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -460,7 +460,6 @@ func TestColorPreview(t *testing.T) { res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase) assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase) - } negativeTests := []string{ @@ -555,7 +554,6 @@ func TestMathBlock(t *testing.T) { res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase) assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase) - } } diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index 8efe4e395d..391ee6c12b 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -147,7 +147,6 @@ func (r *Writer) resolveLink(node org.Node) string { } if len(link) > 0 && !markup.IsLinkStr(link) && link[0] != '#' && !strings.HasPrefix(link, mailto) { - var base string if r.Ctx.IsWiki { base = r.Ctx.Links.WikiLink() diff --git a/modules/packages/rubygems/marshal.go b/modules/packages/rubygems/marshal.go index 8878dcf973..4e6a5fc5f8 100644 --- a/modules/packages/rubygems/marshal.go +++ b/modules/packages/rubygems/marshal.go @@ -147,35 +147,35 @@ func (e *MarshalEncoder) marshalIntInternal(i int64) error { return e.w.WriteByte(byte(i - 5)) } - var len int + var length int if 122 < i && i <= 0xff { - len = 1 + length = 1 } else if 0xff < i && i <= 0xffff { - len = 2 + length = 2 } else if 0xffff < i && i <= 0xffffff { - len = 3 + length = 3 } else if 0xffffff < i && i <= 0x3fffffff { - len = 4 + length = 4 } else if -0x100 <= i && i < -123 { - len = -1 + length = -1 } else if -0x10000 <= i && i < -0x100 { - len = -2 + length = -2 } else if -0x1000000 <= i && i < -0x100000 { - len = -3 + length = -3 } else if -0x40000000 <= i && i < -0x1000000 { - len = -4 + length = -4 } else { return ErrInvalidIntRange } - if err := e.w.WriteByte(byte(len)); err != nil { + if err := e.w.WriteByte(byte(length)); err != nil { return err } - if len < 0 { - len = -len + if length < 0 { + length = -length } - for c := 0; c < len; c++ { + for c := 0; c < length; c++ { if err := e.w.WriteByte(byte(i >> uint(8*c) & 0xff)); err != nil { return err } @@ -244,13 +244,13 @@ func (e *MarshalEncoder) marshalArray(arr reflect.Value) error { return err } - len := arr.Len() + length := arr.Len() - if err := e.marshalIntInternal(int64(len)); err != nil { + if err := e.marshalIntInternal(int64(length)); err != nil { return err } - for i := 0; i < len; i++ { + for i := 0; i < length; i++ { if err := e.marshal(arr.Index(i).Interface()); err != nil { return err } diff --git a/modules/process/manager_stacktraces.go b/modules/process/manager_stacktraces.go index 49bd5071f6..e260893113 100644 --- a/modules/process/manager_stacktraces.go +++ b/modules/process/manager_stacktraces.go @@ -339,7 +339,6 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int } sort.Slice(processes, after(processes)) if !flat { - var sortChildren func(process *Process) sortChildren = func(process *Process) { diff --git a/modules/repository/temp.go b/modules/repository/temp.go index 53646718e0..04faa9db3d 100644 --- a/modules/repository/temp.go +++ b/modules/repository/temp.go @@ -32,7 +32,6 @@ func CreateTemporaryPath(prefix string) (string, error) { if err != nil { log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err) return "", fmt.Errorf("Failed to create dir %s-*.git: %w", prefix, err) - } return basePath, nil } diff --git a/modules/setting/time.go b/modules/setting/time.go index 6d2aa80f5b..39acba12ef 100644 --- a/modules/setting/time.go +++ b/modules/setting/time.go @@ -19,9 +19,8 @@ func loadTimeFrom(rootCfg ConfigProvider) { DefaultUILocation, err = time.LoadLocation(zone) if err != nil { log.Fatal("Load time zone failed: %v", err) - } else { - log.Info("Default UI Location is %v", zone) } + log.Info("Default UI Location is %v", zone) } if DefaultUILocation == nil { DefaultUILocation = time.Local diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index 8661653adf..55a55dd7f4 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -138,10 +138,9 @@ func wrapTmplErrMsg(msg string) { if setting.IsProd { // in prod mode, Forgejo must have correct templates to run log.Fatal("Forgejo can't run with template errors: %s", msg) - } else { - // in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded - log.Error("There are template errors but Forgejo continues to run in dev mode: %s", msg) } + // in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded + log.Error("There are template errors but Forgejo continues to run in dev mode: %s", msg) } type templateErrorPrettier struct { diff --git a/modules/templates/mailer.go b/modules/templates/mailer.go index f1832cba0e..7c97e1ea89 100644 --- a/modules/templates/mailer.go +++ b/modules/templates/mailer.go @@ -84,9 +84,8 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) { if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil { if firstRun { log.Fatal("Failed to parse mail template, err: %v", err) - } else { - log.Error("Failed to parse mail template, err: %v", err) } + log.Error("Failed to parse mail template, err: %v", err) } } } diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index c1d5e26f62..ea01612ac3 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -50,6 +50,11 @@ func TestApostrophesInMentions(t *testing.T) { assert.EqualValues(t, template.HTML("

@mention-user's comment

\n"), rendered) } +func TestNonExistantUserMention(t *testing.T) { + rendered := RenderMarkdownToHtml(context.Background(), "@ThisUserDoesNotExist @mention-user") + assert.EqualValues(t, template.HTML("

@ThisUserDoesNotExist @mention-user

\n"), rendered) +} + func TestRenderCommitBody(t *testing.T) { type args struct { ctx context.Context diff --git a/modules/util/util_test.go b/modules/util/util_test.go index 5c5b13d04b..de8f065cad 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -121,9 +121,9 @@ func Test_NormalizeEOL(t *testing.T) { } func Test_RandomInt(t *testing.T) { - int, err := CryptoRandomInt(255) - assert.True(t, int >= 0) - assert.True(t, int <= 255) + randInt, err := CryptoRandomInt(255) + assert.True(t, randInt >= 0) + assert.True(t, randInt <= 255) assert.NoError(t, err) } diff --git a/options/license/HPND-UC-export-US b/options/license/HPND-UC-export-US new file mode 100644 index 0000000000..015556c5f9 --- /dev/null +++ b/options/license/HPND-UC-export-US @@ -0,0 +1,10 @@ +Copyright (C) 1985, 1990 Regents of the University of California. + +Permission to use, copy, modify, and distribute this +software and its documentation for any purpose and without +fee is hereby granted, provided that the above copyright +notice appear in all copies. The University of California +makes no representations about the suitability of this +software for any purpose. It is provided "as is" without +express or implied warranty. Export of this software outside +of the United States of America may require an export license. diff --git a/options/license/NCL b/options/license/NCL new file mode 100644 index 0000000000..3bfb658c26 --- /dev/null +++ b/options/license/NCL @@ -0,0 +1,32 @@ +Copyright (c) 2004 the University Corporation for Atmospheric +Research ("UCAR"). All rights reserved. Developed by NCAR's +Computational and Information Systems Laboratory, UCAR, +www.cisl.ucar.edu. + +Redistribution and use of the Software in source and binary forms, +with or without modification, is permitted provided that the +following conditions are met: + +- Neither the names of NCAR's Computational and Information Systems +Laboratory, the University Corporation for Atmospheric Research, +nor the names of its sponsors or contributors may be used to +endorse or promote products derived from this Software without +specific prior written permission. + +- Redistributions of source code must retain the above copyright +notices, this list of conditions, and the disclaimer below. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions, and the disclaimer below in the +documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index b56da5de58..9f0002c9b0 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1070,11 +1070,6 @@ readme = README readme_helper = Select a README file template. readme_helper_desc = This is the place where you can write a complete description for your project. auto_init = Initialize repository (Adds .gitignore, License and README) -trust_model_helper = Select trust model for signature verification. Possible options are: -trust_model_helper_collaborator = Collaborator: Trust signatures by collaborators -trust_model_helper_committer = Committer: Trust signatures that match committers -trust_model_helper_collaborator_committer = Collaborator+Committer: Trust signatures by collaborators which match the committer -trust_model_helper_default = Default: Use the default trust model for this installation create_repo = Create repository default_branch = Default branch default_branch_label = default @@ -2005,22 +2000,22 @@ wiki = Wiki wiki.welcome = Welcome to the Wiki. wiki.welcome_desc = The wiki lets you write and share documentation with collaborators. wiki.desc = Write and share documentation with collaborators. -wiki.create_first_page = Create the First Page +wiki.create_first_page = Create the first page wiki.page = Page wiki.filter_page = Filter page wiki.new_page = Page wiki.page_title = Page title wiki.page_content = Page content wiki.default_commit_message = Write a note about this page update (optional). -wiki.save_page = Save Page +wiki.save_page = Save page wiki.cancel = Cancel wiki.last_commit_info = %s edited this page %s wiki.edit_page_button = Edit -wiki.new_page_button = New Page -wiki.file_revision = Page Revision -wiki.wiki_page_revisions = Wiki Page Revisions +wiki.new_page_button = New page +wiki.file_revision = Page revision +wiki.wiki_page_revisions = Page revisions wiki.back_to_wiki = Back to wiki page -wiki.delete_page_button = Delete Page +wiki.delete_page_button = Delete page wiki.delete_page_notice_1 = Deleting the wiki page "%s" cannot be undone. Continue? wiki.page_already_exists = A wiki page with the same name already exists. wiki.reserved_page = The wiki page name "%s" is reserved. @@ -2187,12 +2182,12 @@ settings.pulls.default_allow_edits_from_maintainers = Allow edits from maintaine settings.releases_desc = Enable repository releases settings.packages_desc = Enable repository package registry settings.projects_desc = Enable repository projects -settings.actions_desc = Enable repository actions +settings.actions_desc = Enable integrated CI/CD pipelines with Forgejo Actions settings.admin_settings = Administrator settings settings.admin_enable_health_check = Enable repository health checks (git fsck) settings.admin_code_indexer = Code indexer settings.admin_stats_indexer = Code statistics indexer -settings.admin_indexer_commit_sha = Last indexed SHA +settings.admin_indexer_commit_sha = Last indexed commit settings.admin_indexer_unindexed = Unindexed settings.reindex_button = Add to reindex queue settings.reindex_requested=Reindex requested @@ -2613,7 +2608,7 @@ diff.review.reject = Request changes diff.review.self_approve = Pull request authors can't approve their own pull request diff.committed_by = committed by diff.protected = Protected -diff.image.side_by_side = Side by Side +diff.image.side_by_side = Side by side diff.image.swipe = Swipe diff.image.overlay = Overlay diff.has_escaped = This line has hidden Unicode characters @@ -2624,16 +2619,16 @@ releases.desc = Track project versions and downloads. release.releases = Releases release.detail = Release details release.tags = Tags -release.new_release = New Release +release.new_release = New release release.draft = Draft -release.prerelease = Pre-Release +release.prerelease = Pre-release release.stable = Stable release.compare = Compare release.edit = edit release.ahead.commits = %d commits release.ahead.target = to %s since this release tag.ahead.target = to %s since this tag -release.source_code = Source Code +release.source_code = Source code release.new_subheader = Releases organize project versions. release.edit_subheader = Releases organize project versions. release.tag_name = Tag name @@ -2644,15 +2639,15 @@ release.tag_helper_existing = Existing tag. release.title = Release title release.title_empty = Title cannot be empty. release.message = Describe this release -release.prerelease_desc = Mark as Pre-Release +release.prerelease_desc = Mark as pre-release release.prerelease_helper = Mark this release unsuitable for production use. release.cancel = Cancel -release.publish = Publish Release -release.save_draft = Save Draft -release.edit_release = Update Release -release.delete_release = Delete Release -release.delete_tag = Delete Tag -release.deletion = Delete Release +release.publish = Publish release +release.save_draft = Save draft +release.edit_release = Update release +release.delete_release = Delete release +release.delete_tag = Delete tag +release.deletion = Delete release release.deletion_desc = Deleting a release only removes it from Forgejo. It will not affect the Git tag, the contents of your repository or its history. Continue? release.deletion_success = The release has been deleted. release.deletion_tag_desc = Will delete this tag from repository. Repository contents and history remain unchanged. Continue? @@ -2667,7 +2662,7 @@ release.download_count_few = %s downloads release.add_tag_msg = Use the title and content of release as tag message. release.hide_archive_links = Hide automatically generated archives release.hide_archive_links_helper = Hide automatically generated source code archives for this release. For example, if you are uploading your own. -release.add_tag = Create Tag Only +release.add_tag = Create tag release.releases_for = Releases for %s release.tags_for = Tags for %s release.system_generated = This attachment is automatically generated. @@ -2756,7 +2751,7 @@ team_name_helper = Team names should be short and memorable. team_desc_helper = Describe the purpose or role of the team. team_access_desc = Repository access team_permission_desc = Permission -team_unit_desc = Allow access to tepository sections +team_unit_desc = Allow access to repository sections team_unit_disabled = (Disabled) follow_blocked_user = You cannot follow this organisation because this organisation has blocked you. @@ -2796,9 +2791,9 @@ settings.labels_desc = Add labels which can be used on issues for all re members.membership_visibility = Membership visibility: members.public = Visible -members.public_helper = make hidden +members.public_helper = Make hidden members.private = Hidden -members.private_helper = make visible +members.private_helper = Make visible members.member_role = Member role: members.owner = Owner members.member = Member @@ -2954,8 +2949,8 @@ dashboard.last_gc_time = Time since last GC dashboard.total_gc_pause = Total GC pause dashboard.last_gc_pause = Last GC pause dashboard.gc_times = GC times -dashboard.delete_old_actions = Delete all old actions from database -dashboard.delete_old_actions.started = Delete all old actions from database started. +dashboard.delete_old_actions = Delete all old activities from database +dashboard.delete_old_actions.started = Delete all old activities from database started. dashboard.update_checker = Update checker dashboard.delete_old_system_notices = Delete all old system notices from database dashboard.gc_lfs = Garbage collect LFS meta objects @@ -2963,8 +2958,8 @@ dashboard.stop_zombie_tasks = Stop zombie tasks dashboard.stop_endless_tasks = Stop endless tasks dashboard.cancel_abandoned_jobs = Cancel abandoned jobs dashboard.start_schedule_tasks = Start schedule tasks -dashboard.sync_branch.started = Branches Sync started -dashboard.sync_tag.started = Tags Sync started +dashboard.sync_branch.started = Branch sync started +dashboard.sync_tag.started = Tag sync started dashboard.rebuild_issue_indexer = Rebuild issue indexer users.user_manage_panel = Manage user accounts @@ -2982,7 +2977,7 @@ users.repos = Repos users.created = Created users.last_login = Last sign-in users.never_login = Never signed in -users.send_register_notify = Send user registration notification +users.send_register_notify = Notify about registration via email users.new_success = The user account "%s" has been created. users.edit = Edit users.auth_source = Authentication source @@ -3666,8 +3661,7 @@ management = Manage secrets [actions] actions = Actions - -unit.desc = Manage actions +unit.desc = Manage integrated CI/CD pipelines with Forgejo Actions status.unknown = Unknown status.waiting = Waiting diff --git a/package-lock.json b/package-lock.json index be6eadaee6..c8abb233f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,13 +22,13 @@ "chartjs-plugin-zoom": "2.0.1", "clippie": "4.0.7", "css-loader": "7.0.0", - "dayjs": "1.11.10", + "dayjs": "1.11.11", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", "esbuild-loader": "4.1.0", "escape-goat": "4.0.0", "fast-glob": "3.3.2", - "htmx.org": "1.9.11", + "htmx.org": "1.9.12", "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.10", @@ -54,7 +54,7 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.4.24", + "vue": "3.4.26", "vue-bar-graph": "2.0.0", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", @@ -67,8 +67,8 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.3.0", "@playwright/test": "1.43.0", "@stoplight/spectral-cli": "6.11.1", - "@stylistic/eslint-plugin-js": "1.7.2", - "@stylistic/stylelint-plugin": "2.1.1", + "@stylistic/eslint-plugin-js": "1.8.0", + "@stylistic/stylelint-plugin": "2.1.2", "@vitejs/plugin-vue": "5.0.4", "@vue/test-utils": "2.4.5", "eslint": "8.57.0", @@ -87,7 +87,7 @@ "eslint-plugin-vue-scoped-css": "2.8.0", "eslint-plugin-wc": "2.1.0", "happy-dom": "14.7.1", - "markdownlint-cli": "0.39.0", + "markdownlint-cli": "0.40.0", "postcss-html": "1.6.0", "stylelint": "16.4.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", @@ -96,7 +96,7 @@ "svgo": "3.2.0", "updates": "16.0.1", "vite-string-plugin": "1.2.0", - "vitest": "1.5.2" + "vitest": "1.5.3" }, "engines": { "node": ">= 18.0.0" @@ -2126,12 +2126,12 @@ } }, "node_modules/@stylistic/eslint-plugin-js": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.7.2.tgz", - "integrity": "sha512-ZYX7C5p7zlHbACwFLU+lISVh6tdcRP/++PWegh2Sy0UgMT5kU0XkPa2tKWEtJYzZmPhJxu9LxbnWcnE/tTwSDQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.8.0.tgz", + "integrity": "sha512-jdvnzt+pZPg8TfclZlTZPiUbbima93ylvQ+wNgHLNmup3obY6heQvgewSu9i2CfS61BnRByv+F9fxQLPoNeHag==", "dev": true, "dependencies": { - "@types/eslint": "^8.56.8", + "@types/eslint": "^8.56.10", "acorn": "^8.11.3", "escape-string-regexp": "^4.0.0", "eslint-visitor-keys": "^3.4.3", @@ -2145,19 +2145,19 @@ } }, "node_modules/@stylistic/stylelint-plugin": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.1.tgz", - "integrity": "sha512-xqHTmQZN7EbnFDW7jw0rAsdFNO4IRqvXhrh3qhUlIwF/x09Zm7kgs/ADktHxsTJYcw346PpGihsB0t4pZhpeHw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.1.2.tgz", + "integrity": "sha512-JsSqu0Y3vsX+PBl+DwULxC0cIv9C1yIcq1MXkx7pBOGtTqU26a75I8MPYMiEYvrsXgsKLi65xVgy1iLVSZquJA==", "dev": true, "dependencies": { - "@csstools/css-parser-algorithms": "^2.5.0", - "@csstools/css-tokenizer": "^2.2.3", - "@csstools/media-query-list-parser": "^2.1.7", + "@csstools/css-parser-algorithms": "^2.6.1", + "@csstools/css-tokenizer": "^2.2.4", + "@csstools/media-query-list-parser": "^2.1.9", "is-plain-object": "^5.0.0", - "postcss-selector-parser": "^6.0.15", + "postcss-selector-parser": "^6.0.16", "postcss-value-parser": "^4.2.0", "style-search": "^0.1.0", - "stylelint": "^16.2.1" + "stylelint": "^16.4.0" }, "engines": { "node": "^18.12 || >=20.9" @@ -2224,9 +2224,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.56.9", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.9.tgz", - "integrity": "sha512-W4W3KcqzjJ0sHg2vAq9vfml6OhsJ53TcUjUqfzzZf/EChUtwspszj/S0pzMxnfRcO55/iGq47dscXw71Fxc4Zg==", + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -2545,13 +2545,13 @@ } }, "node_modules/@vitest/expect": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.2.tgz", - "integrity": "sha512-rf7MTD1WCoDlN3FfYJ9Llfp0PbdtOMZ3FIF0AVkDnKbp3oiMW1c8AmvRZBcqbAhDUAvF52e9zx4WQM1r3oraVA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.3.tgz", + "integrity": "sha512-y+waPz31pOFr3rD7vWTbwiLe5+MgsMm40jTZbQE8p8/qXyBX3CQsIXRx9XK12IbY7q/t5a5aM/ckt33b4PxK2g==", "dev": true, "dependencies": { - "@vitest/spy": "1.5.2", - "@vitest/utils": "1.5.2", + "@vitest/spy": "1.5.3", + "@vitest/utils": "1.5.3", "chai": "^4.3.10" }, "funding": { @@ -2559,12 +2559,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.2.tgz", - "integrity": "sha512-7IJ7sJhMZrqx7HIEpv3WrMYcq8ZNz9L6alo81Y6f8hV5mIE6yVZsFoivLZmr0D777klm1ReqonE9LyChdcmw6g==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.3.tgz", + "integrity": "sha512-7PlfuReN8692IKQIdCxwir1AOaP5THfNkp0Uc4BKr2na+9lALNit7ub9l3/R7MP8aV61+mHKRGiqEKRIwu6iiQ==", "dev": true, "dependencies": { - "@vitest/utils": "1.5.2", + "@vitest/utils": "1.5.3", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -2600,9 +2600,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.2.tgz", - "integrity": "sha512-CTEp/lTYos8fuCc9+Z55Ga5NVPKUgExritjF5VY7heRFUfheoAqBneUlvXSUJHUZPjnPmyZA96yLRJDP1QATFQ==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.3.tgz", + "integrity": "sha512-K3mvIsjyKYBhNIDujMD2gfQEzddLe51nNOAf45yKRt/QFJcUIeTQd2trRvv6M6oCBHNVnZwFWbQ4yj96ibiDsA==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -2626,9 +2626,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.2.tgz", - "integrity": "sha512-xCcPvI8JpCtgikT9nLpHPL1/81AYqZy1GCy4+MCHBE7xi8jgsYkULpW5hrx5PGLgOQjUpb6fd15lqcriJ40tfQ==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.3.tgz", + "integrity": "sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -2638,9 +2638,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.2.tgz", - "integrity": "sha512-sWOmyofuXLJ85VvXNsroZur7mOJGiQeM0JN3/0D1uU8U9bGFM69X1iqHaRXl6R8BwaLY6yPCogP257zxTzkUdA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.3.tgz", + "integrity": "sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -2668,36 +2668,36 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.24.tgz", - "integrity": "sha512-vbW/tgbwJYj62N/Ww99x0zhFTkZDTcGh3uwJEuadZ/nF9/xuFMC4693P9r+3sxGXISABpDKvffY5ApH9pmdd1A==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.26.tgz", + "integrity": "sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==", "dependencies": { "@babel/parser": "^7.24.4", - "@vue/shared": "3.4.24", + "@vue/shared": "3.4.26", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.24.tgz", - "integrity": "sha512-4XgABML/4cNndVsQndG6BbGN7+EoisDwi3oXNovqL/4jdNhwvP8/rfRMTb6FxkxIxUUtg6AI1/qZvwfSjxJiWA==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.26.tgz", + "integrity": "sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==", "dependencies": { - "@vue/compiler-core": "3.4.24", - "@vue/shared": "3.4.24" + "@vue/compiler-core": "3.4.26", + "@vue/shared": "3.4.26" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.24.tgz", - "integrity": "sha512-nRAlJUK02FTWfA2nuvNBAqsDZuERGFgxZ8sGH62XgFSvMxO2URblzulExsmj4gFZ8e+VAyDooU9oAoXfEDNxTA==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.26.tgz", + "integrity": "sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==", "dependencies": { "@babel/parser": "^7.24.4", - "@vue/compiler-core": "3.4.24", - "@vue/compiler-dom": "3.4.24", - "@vue/compiler-ssr": "3.4.24", - "@vue/shared": "3.4.24", + "@vue/compiler-core": "3.4.26", + "@vue/compiler-dom": "3.4.26", + "@vue/compiler-ssr": "3.4.26", + "@vue/shared": "3.4.26", "estree-walker": "^2.0.2", "magic-string": "^0.30.10", "postcss": "^8.4.38", @@ -2713,57 +2713,57 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.24.tgz", - "integrity": "sha512-ZsAtr4fhaUFnVcDqwW3bYCSDwq+9Gk69q2r/7dAHDrOMw41kylaMgOP4zRnn6GIEJkQznKgrMOGPMFnLB52RbQ==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.26.tgz", + "integrity": "sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==", "dependencies": { - "@vue/compiler-dom": "3.4.24", - "@vue/shared": "3.4.24" + "@vue/compiler-dom": "3.4.26", + "@vue/shared": "3.4.26" } }, "node_modules/@vue/reactivity": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.24.tgz", - "integrity": "sha512-nup3fSYg4i4LtNvu9slF/HF/0dkMQYfepUdORBcMSsankzRPzE7ypAFurpwyRBfU1i7Dn1kcwpYsE1wETSh91g==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.26.tgz", + "integrity": "sha512-E/ynEAu/pw0yotJeLdvZEsp5Olmxt+9/WqzvKff0gE67tw73gmbx6tRkiagE/eH0UCubzSlGRebCbidB1CpqZQ==", "dependencies": { - "@vue/shared": "3.4.24" + "@vue/shared": "3.4.26" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.24.tgz", - "integrity": "sha512-c7iMfj6cJMeAG3s5yOn9Rc5D9e2/wIuaozmGf/ICGCY3KV5H7mbTVdvEkd4ZshTq7RUZqj2k7LMJWVx+EBiY1g==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.26.tgz", + "integrity": "sha512-AFJDLpZvhT4ujUgZSIL9pdNcO23qVFh7zWCsNdGQBw8ecLNxOOnPcK9wTTIYCmBJnuPHpukOwo62a2PPivihqw==", "dependencies": { - "@vue/reactivity": "3.4.24", - "@vue/shared": "3.4.24" + "@vue/reactivity": "3.4.26", + "@vue/shared": "3.4.26" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.24.tgz", - "integrity": "sha512-uXKzuh/Emfad2Y7Qm0ABsLZZV6H3mAJ5ZVqmAOlrNQRf+T5mxpPGZBfec1hkP41t6h6FwF6RSGCs/gd8WbuySQ==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.26.tgz", + "integrity": "sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw==", "dependencies": { - "@vue/runtime-core": "3.4.24", - "@vue/shared": "3.4.24", + "@vue/runtime-core": "3.4.26", + "@vue/shared": "3.4.26", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.24.tgz", - "integrity": "sha512-H+DLK4sQF6sRgzKyofmlEVBIV/9KrQU6HIV7nt6yIwSGGKvSwlV8pqJlebUKLpbXaNHugdSfAbP6YmXF69lxow==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.26.tgz", + "integrity": "sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw==", "dependencies": { - "@vue/compiler-ssr": "3.4.24", - "@vue/shared": "3.4.24" + "@vue/compiler-ssr": "3.4.26", + "@vue/shared": "3.4.26" }, "peerDependencies": { - "vue": "3.4.24" + "vue": "3.4.26" } }, "node_modules/@vue/shared": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.24.tgz", - "integrity": "sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw==" + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.26.tgz", + "integrity": "sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==" }, "node_modules/@vue/test-utils": { "version": "2.4.5", @@ -4631,9 +4631,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" }, "node_modules/debug": { "version": "4.3.4", @@ -6924,9 +6924,9 @@ } }, "node_modules/htmx.org": { - "version": "1.9.11", - "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.11.tgz", - "integrity": "sha512-WlVuICn8dfNOOgYmdYzYG8zSnP3++AdHkMHooQAzGZObWpVXYathpz/I37ycF4zikR6YduzfCvEcxk20JkIUsw==" + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.12.tgz", + "integrity": "sha512-VZAohXyF7xPGS52IM8d1T1283y+X4D+Owf3qY1NZ9RuBypyu9l8cGsxUMAG5fEAb/DhT7rDoJ9Hpu5/HxFD3cw==" }, "node_modules/human-signals": { "version": "5.0.0", @@ -8212,9 +8212,9 @@ } }, "node_modules/markdown-it": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.0.0.tgz", - "integrity": "sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, "dependencies": { "argparse": "^2.0.1", @@ -8222,20 +8222,20 @@ "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", - "uc.micro": "^2.0.0" + "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/markdownlint": { - "version": "0.33.0", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.33.0.tgz", - "integrity": "sha512-4lbtT14A3m0LPX1WS/3d1m7Blg+ZwiLq36WvjQqFGsX3Gik99NV+VXp/PW3n+Q62xyPdbvGOCfjPqjW+/SKMig==", + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.34.0.tgz", + "integrity": "sha512-qwGyuyKwjkEMOJ10XN6OTKNOVYvOIi35RNvDLNxTof5s8UmyGHlCdpngRHoRGNvQVGuxO3BJ7uNSgdeX166WXw==", "dev": true, "dependencies": { - "markdown-it": "14.0.0", - "markdownlint-micromark": "0.1.8" + "markdown-it": "14.1.0", + "markdownlint-micromark": "0.1.9" }, "engines": { "node": ">=18" @@ -8245,20 +8245,22 @@ } }, "node_modules/markdownlint-cli": { - "version": "0.39.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.39.0.tgz", - "integrity": "sha512-ZuFN7Xpsbn1Nbp0YYkeLOfXOMOfLQBik2lKRy8pVI/llmKQ2uW7x+8k5OMgF6o7XCsTDSYC/OOmeJ+3qplvnJQ==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.40.0.tgz", + "integrity": "sha512-JXhI3dRQcaqwiFYpPz6VJ7aKYheD53GmTz9y4D/d0F1MbZDGOp9pqKlbOfUX/pHP/iAoeiE4wYRmk8/kjLakxA==", "dev": true, "dependencies": { - "commander": "~11.1.0", + "commander": "~12.0.0", "get-stdin": "~9.0.0", - "glob": "~10.3.10", - "ignore": "~5.3.0", + "glob": "~10.3.12", + "ignore": "~5.3.1", "js-yaml": "^4.1.0", "jsonc-parser": "~3.2.1", - "markdownlint": "~0.33.0", - "minimatch": "~9.0.3", - "run-con": "~1.3.2" + "jsonpointer": "5.0.1", + "markdownlint": "~0.34.0", + "minimatch": "~9.0.4", + "run-con": "~1.3.2", + "toml": "~3.0.0" }, "bin": { "markdownlint": "markdownlint.js" @@ -8268,12 +8270,12 @@ } }, "node_modules/markdownlint-cli/node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", + "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", "dev": true, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/markdownlint-cli/node_modules/glob": { @@ -8305,12 +8307,12 @@ "dev": true }, "node_modules/markdownlint-micromark": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.8.tgz", - "integrity": "sha512-1ouYkMRo9/6gou9gObuMDnvZM8jC/ly3QCFQyoSPCS2XV1ZClU0xpKbL1Ar3bWWRT1RnBZkWUEiNKrI2CwiBQA==", + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.9.tgz", + "integrity": "sha512-5hVs/DzAFa8XqYosbEAEg6ok6MF2smDj89ztn9pKkCtdKHVdPQuGMH7frFfYL9mLkvfFe4pTyAMffLbjf3/EyA==", "dev": true, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/DavidAnson" @@ -11831,6 +11833,12 @@ "resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz", "integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ==" }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "dev": true + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -12257,9 +12265,9 @@ } }, "node_modules/vite-node": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.2.tgz", - "integrity": "sha512-Y8p91kz9zU+bWtF7HGt6DVw2JbhyuB2RlZix3FPYAYmUyZ3n7iTp8eSyLyY6sxtPegvxQtmlTMhfPhUfCUF93A==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.3.tgz", + "integrity": "sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -12339,16 +12347,16 @@ } }, "node_modules/vitest": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.2.tgz", - "integrity": "sha512-l9gwIkq16ug3xY7BxHwcBQovLZG75zZL0PlsiYQbf76Rz6QGs54416UWMtC0jXeihvHvcHrf2ROEjkQRVpoZYw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.3.tgz", + "integrity": "sha512-2oM7nLXylw3mQlW6GXnRriw+7YvZFk/YNV8AxIC3Z3MfFbuziLGWP9GPxxu/7nRlXhqyxBikpamr+lEEj1sUEw==", "dev": true, "dependencies": { - "@vitest/expect": "1.5.2", - "@vitest/runner": "1.5.2", - "@vitest/snapshot": "1.5.2", - "@vitest/spy": "1.5.2", - "@vitest/utils": "1.5.2", + "@vitest/expect": "1.5.3", + "@vitest/runner": "1.5.3", + "@vitest/snapshot": "1.5.3", + "@vitest/spy": "1.5.3", + "@vitest/utils": "1.5.3", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -12362,7 +12370,7 @@ "tinybench": "^2.5.1", "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.5.2", + "vite-node": "1.5.3", "why-is-node-running": "^2.2.2" }, "bin": { @@ -12377,8 +12385,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.5.2", - "@vitest/ui": "1.5.2", + "@vitest/browser": "1.5.3", + "@vitest/ui": "1.5.3", "happy-dom": "*", "jsdom": "*" }, @@ -12413,15 +12421,15 @@ } }, "node_modules/vue": { - "version": "3.4.24", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.24.tgz", - "integrity": "sha512-NPdx7dLGyHmKHGRRU5bMRYVE+rechR+KDU5R2tSTNG36PuMwbfAJ+amEvOAw7BPfZp5sQulNELSLm5YUkau+Sg==", + "version": "3.4.26", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.26.tgz", + "integrity": "sha512-bUIq/p+VB+0xrJubaemrfhk1/FiW9iX+pDV+62I/XJ6EkspAO9/DXEjbDFoe8pIfOZBqfk45i9BMc41ptP/uRg==", "dependencies": { - "@vue/compiler-dom": "3.4.24", - "@vue/compiler-sfc": "3.4.24", - "@vue/runtime-dom": "3.4.24", - "@vue/server-renderer": "3.4.24", - "@vue/shared": "3.4.24" + "@vue/compiler-dom": "3.4.26", + "@vue/compiler-sfc": "3.4.26", + "@vue/runtime-dom": "3.4.26", + "@vue/server-renderer": "3.4.26", + "@vue/shared": "3.4.26" }, "peerDependencies": { "typescript": "*" diff --git a/package.json b/package.json index 5a95503a58..8e31e2c60b 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,13 @@ "chartjs-plugin-zoom": "2.0.1", "clippie": "4.0.7", "css-loader": "7.0.0", - "dayjs": "1.11.10", + "dayjs": "1.11.11", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", "esbuild-loader": "4.1.0", "escape-goat": "4.0.0", "fast-glob": "3.3.2", - "htmx.org": "1.9.11", + "htmx.org": "1.9.12", "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.10", @@ -53,7 +53,7 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.4.24", + "vue": "3.4.26", "vue-bar-graph": "2.0.0", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", @@ -66,8 +66,8 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.3.0", "@playwright/test": "1.43.0", "@stoplight/spectral-cli": "6.11.1", - "@stylistic/eslint-plugin-js": "1.7.2", - "@stylistic/stylelint-plugin": "2.1.1", + "@stylistic/eslint-plugin-js": "1.8.0", + "@stylistic/stylelint-plugin": "2.1.2", "@vitejs/plugin-vue": "5.0.4", "@vue/test-utils": "2.4.5", "eslint": "8.57.0", @@ -86,7 +86,7 @@ "eslint-plugin-vue-scoped-css": "2.8.0", "eslint-plugin-wc": "2.1.0", "happy-dom": "14.7.1", - "markdownlint-cli": "0.39.0", + "markdownlint-cli": "0.40.0", "postcss-html": "1.6.0", "stylelint": "16.4.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", @@ -95,7 +95,7 @@ "svgo": "3.2.0", "updates": "16.0.1", "vite-string-plugin": "1.2.0", - "vitest": "1.5.2" + "vitest": "1.5.3" }, "browserslist": ["defaults"] } diff --git a/release-notes/8.0.0/3139.md b/release-notes/8.0.0/3139.md new file mode 100644 index 0000000000..cac054bee8 --- /dev/null +++ b/release-notes/8.0.0/3139.md @@ -0,0 +1 @@ +Allow hiding auto generated release archives diff --git a/release-notes/8.0.0/3572.md b/release-notes/8.0.0/3572.md new file mode 100644 index 0000000000..c79f9e30fc --- /dev/null +++ b/release-notes/8.0.0/3572.md @@ -0,0 +1 @@ +Fix gogs migration if gogs is hosted at a subpath diff --git a/release-notes/8.0.0/3583.md b/release-notes/8.0.0/3583.md new file mode 100644 index 0000000000..0f5fdc8f60 --- /dev/null +++ b/release-notes/8.0.0/3583.md @@ -0,0 +1 @@ +Settings: OAuth2 applications: Consistently check input on client side diff --git a/release-notes/8.0.0/3608.md b/release-notes/8.0.0/3608.md new file mode 100644 index 0000000000..1c3072422a --- /dev/null +++ b/release-notes/8.0.0/3608.md @@ -0,0 +1 @@ +Fix text selection color diff --git a/release-notes/8.0.0/feat/3307.md b/release-notes/8.0.0/feat/3307.md new file mode 100644 index 0000000000..6d7dd01415 --- /dev/null +++ b/release-notes/8.0.0/feat/3307.md @@ -0,0 +1 @@ +Support [Proof Key for Code Exchange (PKCE - RFC7636)](https://www.rfc-editor.org/rfc/rfc7636) for external login sources using the OAuth2 flow. diff --git a/release-notes/8.0.0/feat/3434.md b/release-notes/8.0.0/feat/3434.md new file mode 100644 index 0000000000..a8b28eb1c3 --- /dev/null +++ b/release-notes/8.0.0/feat/3434.md @@ -0,0 +1 @@ +When PDFs are displayed in the repository, the [full height of the screen](https://codeberg.org/forgejo/forgejo/pulls/3434) is now used instead of a predefined fixed height diff --git a/renovate.json b/renovate.json index c395194167..76e47f44e3 100644 --- a/renovate.json +++ b/renovate.json @@ -60,7 +60,9 @@ { "description": "Group nodejs packages", "matchDepNames": ["node", "docker.io/node", "docker.io/library/node"], - "groupName": "nodejs packages" + "groupName": "nodejs packages", + "versionCompatibility": "^(?[^-]+)(?-.*)?$", + "versioning": "node" }, { "description": "Automerge renovate updates", @@ -71,16 +73,12 @@ }, { "description": "Split minor and patch updates", - "matchDepNames": [ - "swagger-ui-dist" - ], + "matchDepNames": ["vue", "github.com/urfave/cli/v2", "swagger-ui-dist"], "separateMinorPatch": true }, { "description": "Automerge patch updates", - "matchDepNames": [ - "swagger-ui-dist" - ], + "matchDepNames": ["vue", "github.com/urfave/cli/v2", "swagger-ui-dist"], "matchUpdateTypes": ["patch"], "automerge": true }, @@ -123,8 +121,7 @@ "customType": "regex", "fileMatch": ["^.forgejo/workflows/.+\\.yml$"], "matchStrings": ["\\s+node-version: ['\"]?(?.+?)['\"]?\\s"], - "depNameTemplate": "node", - "datasourceTemplate": "node-version" + "depNameTemplate": "node" }, { "description": "Update deps inside Makefile", diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go index 2f8b3d0210..416d7e5181 100644 --- a/routers/api/actions/artifacts.go +++ b/routers/api/actions/artifacts.go @@ -144,7 +144,6 @@ func ArtifactContexter() func(next http.Handler) http.Handler { var task *actions.ActionTask if err == nil { - task, err = actions.GetTaskByID(req.Context(), tID) if err != nil { log.Error("Error runner api getting task by ID: %v", err) @@ -467,14 +466,15 @@ func (ar artifactRoutes) downloadArtifact(ctx *ArtifactContext) { log.Error("Error getting artifact: %v", err) ctx.Error(http.StatusInternalServerError, err.Error()) return - } else if !exist { + } + if !exist { log.Error("artifact with ID %d does not exist", artifactID) ctx.Error(http.StatusNotFound, fmt.Sprintf("artifact with ID %d does not exist", artifactID)) return } if artifact.RunID != runID { - log.Error("Error dismatch runID and artifactID, task: %v, artifact: %v", runID, artifactID) - ctx.Error(http.StatusBadRequest, err.Error()) + log.Error("Error mismatch runID and artifactID, task: %v, artifact: %v", runID, artifactID) + ctx.Error(http.StatusBadRequest) return } diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go index cf0fe6c07c..481cf70d33 100644 --- a/routers/api/packages/alpine/alpine.go +++ b/routers/api/packages/alpine/alpine.go @@ -144,12 +144,12 @@ func UploadPackageFile(ctx *context.Context) { return } - upload, close, err := ctx.UploadStream() + upload, needToClose, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if close { + if needToClose { defer upload.Close() } diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go index c45e085a4d..07ea3eda34 100644 --- a/routers/api/packages/conan/conan.go +++ b/routers/api/packages/conan/conan.go @@ -310,12 +310,12 @@ func uploadFile(ctx *context.Context, fileFilter container.Set[string], fileKey return } - upload, close, err := ctx.UploadStream() + upload, needToClose, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusBadRequest, err) return } - if close { + if needToClose { defer upload.Close() } diff --git a/routers/api/packages/conda/conda.go b/routers/api/packages/conda/conda.go index 30c80fc15e..c7e4544d52 100644 --- a/routers/api/packages/conda/conda.go +++ b/routers/api/packages/conda/conda.go @@ -174,12 +174,12 @@ func EnumeratePackages(ctx *context.Context) { } func UploadPackageFile(ctx *context.Context) { - upload, close, err := ctx.UploadStream() + upload, needToClose, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if close { + if needToClose { defer upload.Close() } diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index e519766142..2cb16daebc 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -385,9 +385,9 @@ func EndUploadBlob(ctx *context.Context) { } return } - close := true + doClose := true defer func() { - if close { + if doClose { uploader.Close() } }() @@ -427,7 +427,7 @@ func EndUploadBlob(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - close = false + doClose = false if err := container_service.RemoveBlobUploadByID(ctx, uploader.ID); err != nil { apiError(ctx, http.StatusInternalServerError, err) diff --git a/routers/api/packages/cran/cran.go b/routers/api/packages/cran/cran.go index 2cec75294f..f1d616724a 100644 --- a/routers/api/packages/cran/cran.go +++ b/routers/api/packages/cran/cran.go @@ -151,12 +151,12 @@ func UploadBinaryPackageFile(ctx *context.Context) { } func uploadPackageFile(ctx *context.Context, compositeKey string, properties map[string]string) { - upload, close, err := ctx.UploadStream() + upload, needToClose, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusBadRequest, err) return } - if close { + if needToClose { defer upload.Close() } diff --git a/routers/api/packages/debian/debian.go b/routers/api/packages/debian/debian.go index 241de3ac5d..8c05476cbc 100644 --- a/routers/api/packages/debian/debian.go +++ b/routers/api/packages/debian/debian.go @@ -127,12 +127,12 @@ func UploadPackageFile(ctx *context.Context) { return } - upload, close, err := ctx.UploadStream() + upload, needToClose, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if close { + if needToClose { defer upload.Close() } diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go index 8232931134..e66f3ee676 100644 --- a/routers/api/packages/generic/generic.go +++ b/routers/api/packages/generic/generic.go @@ -90,12 +90,12 @@ func UploadPackage(ctx *context.Context) { return } - upload, close, err := ctx.UploadStream() + upload, needToClose, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if close { + if needToClose { defer upload.Close() } diff --git a/routers/api/packages/goproxy/goproxy.go b/routers/api/packages/goproxy/goproxy.go index d658066bb4..56a07dbd43 100644 --- a/routers/api/packages/goproxy/goproxy.go +++ b/routers/api/packages/goproxy/goproxy.go @@ -154,12 +154,12 @@ func resolvePackage(ctx *context.Context, ownerID int64, name, version string) ( } func UploadPackage(ctx *context.Context) { - upload, close, err := ctx.UploadStream() + upload, needToClose, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if close { + if needToClose { defer upload.Close() } diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go index 09156ece6b..26b0ae226e 100644 --- a/routers/api/packages/nuget/nuget.go +++ b/routers/api/packages/nuget/nuget.go @@ -594,13 +594,13 @@ func UploadSymbolPackage(ctx *context.Context) { func processUploadedFile(ctx *context.Context, expectedType nuget_module.PackageType) (*nuget_module.Package, *packages_module.HashedBuffer, []io.Closer) { closables := make([]io.Closer, 0, 2) - upload, close, err := ctx.UploadStream() + upload, needToClose, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusBadRequest, err) return nil, nil, closables } - if close { + if needToClose { closables = append(closables, upload) } diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go index 4de361c214..c59366992c 100644 --- a/routers/api/packages/rpm/rpm.go +++ b/routers/api/packages/rpm/rpm.go @@ -117,12 +117,12 @@ func GetRepositoryFile(ctx *context.Context) { } func UploadPackageFile(ctx *context.Context) { - upload, close, err := ctx.UploadStream() + upload, needToClose, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - if close { + if needToClose { defer upload.Close() } diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go index d2fbcd01f0..ba5f4de080 100644 --- a/routers/api/packages/rubygems/rubygems.go +++ b/routers/api/packages/rubygems/rubygems.go @@ -197,12 +197,12 @@ func DownloadPackageFile(ctx *context.Context) { // UploadPackageFile adds a file to the package. If the package does not exist, it gets created. func UploadPackageFile(ctx *context.Context) { - upload, close, err := ctx.UploadStream() + upload, needToClose, err := ctx.UploadStream() if err != nil { apiError(ctx, http.StatusBadRequest, err) return } - if close { + if needToClose { defer upload.Close() } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index f5f1acdce2..af0ee058ff 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -93,6 +93,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/settings" "code.gitea.io/gitea/routers/api/v1/user" + "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -787,6 +788,34 @@ func Routes() *web.Route { m.Use(shared.Middlewares()...) + addActionsRoutes := func( + m *web.Route, + reqChecker func(ctx *context.APIContext), + act actions.API, + ) { + m.Group("/actions", func() { + m.Group("/secrets", func() { + m.Get("", reqToken(), reqChecker, act.ListActionsSecrets) + m.Combo("/{secretname}"). + Put(reqToken(), reqChecker, bind(api.CreateOrUpdateSecretOption{}), act.CreateOrUpdateSecret). + Delete(reqToken(), reqChecker, act.DeleteSecret) + }) + + m.Group("/variables", func() { + m.Get("", reqToken(), reqChecker, act.ListVariables) + m.Combo("/{variablename}"). + Get(reqToken(), reqChecker, act.GetVariable). + Delete(reqToken(), reqChecker, act.DeleteVariable). + Post(reqToken(), reqChecker, bind(api.CreateVariableOption{}), act.CreateVariable). + Put(reqToken(), reqChecker, bind(api.UpdateVariableOption{}), act.UpdateVariable) + }) + + m.Group("/runners", func() { + m.Get("/registration-token", reqToken(), reqChecker, act.GetRegistrationToken) + }) + }) + } + m.Group("", func() { // Miscellaneous (no scope required) if setting.API.EnableSwagger { @@ -1052,26 +1081,11 @@ func Routes() *web.Route { m.Post("/accept", repo.AcceptTransfer) m.Post("/reject", repo.RejectTransfer) }, reqToken()) - m.Group("/actions", func() { - m.Group("/secrets", func() { - m.Combo("/{secretname}"). - Put(reqToken(), reqOwner(), bind(api.CreateOrUpdateSecretOption{}), repo.CreateOrUpdateSecret). - Delete(reqToken(), reqOwner(), repo.DeleteSecret) - }) - - m.Group("/variables", func() { - m.Get("", reqToken(), reqOwner(), repo.ListVariables) - m.Combo("/{variablename}"). - Get(reqToken(), reqOwner(), repo.GetVariable). - Delete(reqToken(), reqOwner(), repo.DeleteVariable). - Post(reqToken(), reqOwner(), bind(api.CreateVariableOption{}), repo.CreateVariable). - Put(reqToken(), reqOwner(), bind(api.UpdateVariableOption{}), repo.UpdateVariable) - }) - - m.Group("/runners", func() { - m.Get("/registration-token", reqToken(), reqOwner(), repo.GetRegistrationToken) - }) - }) + addActionsRoutes( + m, + reqOwner(), + repo.NewAction(), + ) m.Group("/hooks/git", func() { m.Combo("").Get(repo.ListGitHooks) m.Group("/{id}", func() { @@ -1463,27 +1477,11 @@ func Routes() *web.Route { m.Combo("/{username}").Get(reqToken(), org.IsMember). Delete(reqToken(), reqOrgOwnership(), org.DeleteMember) }) - m.Group("/actions", func() { - m.Group("/secrets", func() { - m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets) - m.Combo("/{secretname}"). - Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateSecret). - Delete(reqToken(), reqOrgOwnership(), org.DeleteSecret) - }) - - m.Group("/variables", func() { - m.Get("", reqToken(), reqOrgOwnership(), org.ListVariables) - m.Combo("/{variablename}"). - Get(reqToken(), reqOrgOwnership(), org.GetVariable). - Delete(reqToken(), reqOrgOwnership(), org.DeleteVariable). - Post(reqToken(), reqOrgOwnership(), bind(api.CreateVariableOption{}), org.CreateVariable). - Put(reqToken(), reqOrgOwnership(), bind(api.UpdateVariableOption{}), org.UpdateVariable) - }) - - m.Group("/runners", func() { - m.Get("/registration-token", reqToken(), reqOrgOwnership(), org.GetRegistrationToken) - }) - }) + addActionsRoutes( + m, + reqOrgOwnership(), + org.NewAction(), + ) m.Group("/public_members", func() { m.Get("", org.ListPublicMembers) m.Combo("/{username}").Get(org.IsPublicMember). diff --git a/routers/api/v1/org/variables.go b/routers/api/v1/org/action.go similarity index 58% rename from routers/api/v1/org/variables.go rename to routers/api/v1/org/action.go index eaf7bdc45b..03a1fa8ccc 100644 --- a/routers/api/v1/org/variables.go +++ b/routers/api/v1/org/action.go @@ -9,16 +9,188 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" + secret_model "code.gitea.io/gitea/models/secret" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/shared" "code.gitea.io/gitea/routers/api/v1/utils" actions_service "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/context" + secret_service "code.gitea.io/gitea/services/secrets" ) +// ListActionsSecrets list an organization's actions secrets +func (Action) ListActionsSecrets(ctx *context.APIContext) { + // swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets + // --- + // summary: List an organization's actions secrets + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/SecretList" + // "404": + // "$ref": "#/responses/notFound" + + opts := &secret_model.FindSecretsOptions{ + OwnerID: ctx.Org.Organization.ID, + ListOptions: utils.GetListOptions(ctx), + } + + secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts) + if err != nil { + ctx.InternalServerError(err) + return + } + + apiSecrets := make([]*api.Secret, len(secrets)) + for k, v := range secrets { + apiSecrets[k] = &api.Secret{ + Name: v.Name, + Created: v.CreatedUnix.AsTime(), + } + } + + ctx.SetTotalCountHeader(count) + ctx.JSON(http.StatusOK, apiSecrets) +} + +// create or update one secret of the organization +func (Action) CreateOrUpdateSecret(ctx *context.APIContext) { + // swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret + // --- + // summary: Create or Update a secret value in an organization + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of organization + // type: string + // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/CreateOrUpdateSecretOption" + // responses: + // "201": + // description: response when creating a secret + // "204": + // description: response when updating a secret + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/notFound" + + opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) + + _, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname"), opt.Data) + if err != nil { + if errors.Is(err, util.ErrInvalidArgument) { + ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) + } else if errors.Is(err, util.ErrNotExist) { + ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err) + } else { + ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) + } + return + } + + if created { + ctx.Status(http.StatusCreated) + } else { + ctx.Status(http.StatusNoContent) + } +} + +// DeleteSecret delete one secret of the organization +func (Action) DeleteSecret(ctx *context.APIContext) { + // swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret + // --- + // summary: Delete a secret in an organization + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of organization + // type: string + // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true + // responses: + // "204": + // description: delete one secret of the organization + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/notFound" + + err := secret_service.DeleteSecretByName(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname")) + if err != nil { + if errors.Is(err, util.ErrInvalidArgument) { + ctx.Error(http.StatusBadRequest, "DeleteSecret", err) + } else if errors.Is(err, util.ErrNotExist) { + ctx.Error(http.StatusNotFound, "DeleteSecret", err) + } else { + ctx.Error(http.StatusInternalServerError, "DeleteSecret", err) + } + return + } + + ctx.Status(http.StatusNoContent) +} + +// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization +// GetRegistrationToken returns the token to register org runners +func (Action) GetRegistrationToken(ctx *context.APIContext) { + // swagger:operation GET /orgs/{org}/actions/runners/registration-token organization orgGetRunnerRegistrationToken + // --- + // summary: Get an organization's actions runner registration token + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/RegistrationToken" + + shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0) +} + // ListVariables list org-level variables -func ListVariables(ctx *context.APIContext) { +func (Action) ListVariables(ctx *context.APIContext) { // swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList // --- // summary: Get an org-level variables list @@ -70,7 +242,7 @@ func ListVariables(ctx *context.APIContext) { } // GetVariable get an org-level variable -func GetVariable(ctx *context.APIContext) { +func (Action) GetVariable(ctx *context.APIContext) { // swagger:operation GET /orgs/{org}/actions/variables/{variablename} organization getOrgVariable // --- // summary: Get an org-level variable @@ -119,7 +291,7 @@ func GetVariable(ctx *context.APIContext) { } // DeleteVariable delete an org-level variable -func DeleteVariable(ctx *context.APIContext) { +func (Action) DeleteVariable(ctx *context.APIContext) { // swagger:operation DELETE /orgs/{org}/actions/variables/{variablename} organization deleteOrgVariable // --- // summary: Delete an org-level variable @@ -163,7 +335,7 @@ func DeleteVariable(ctx *context.APIContext) { } // CreateVariable create an org-level variable -func CreateVariable(ctx *context.APIContext) { +func (Action) CreateVariable(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/actions/variables/{variablename} organization createOrgVariable // --- // summary: Create an org-level variable @@ -227,7 +399,7 @@ func CreateVariable(ctx *context.APIContext) { } // UpdateVariable update an org-level variable -func UpdateVariable(ctx *context.APIContext) { +func (Action) UpdateVariable(ctx *context.APIContext) { // swagger:operation PUT /orgs/{org}/actions/variables/{variablename} organization updateOrgVariable // --- // summary: Update an org-level variable @@ -289,3 +461,13 @@ func UpdateVariable(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } + +var _ actions_service.API = new(Action) + +// Action implements actions_service.API +type Action struct{} + +// NewAction creates a new Action service +func NewAction() actions_service.API { + return Action{} +} diff --git a/routers/api/v1/org/runners.go b/routers/api/v1/org/runners.go deleted file mode 100644 index 2a52bd8778..0000000000 --- a/routers/api/v1/org/runners.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package org - -import ( - "code.gitea.io/gitea/routers/api/v1/shared" - "code.gitea.io/gitea/services/context" -) - -// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization - -// GetRegistrationToken returns the token to register org runners -func GetRegistrationToken(ctx *context.APIContext) { - // swagger:operation GET /orgs/{org}/actions/runners/registration-token organization orgGetRunnerRegistrationToken - // --- - // summary: Get an organization's actions runner registration token - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of the organization - // type: string - // required: true - // responses: - // "200": - // "$ref": "#/responses/RegistrationToken" - - shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0) -} diff --git a/routers/api/v1/org/secrets.go b/routers/api/v1/org/secrets.go deleted file mode 100644 index abb6bb26c4..0000000000 --- a/routers/api/v1/org/secrets.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package org - -import ( - "errors" - "net/http" - - "code.gitea.io/gitea/models/db" - secret_model "code.gitea.io/gitea/models/secret" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/web" - "code.gitea.io/gitea/routers/api/v1/utils" - "code.gitea.io/gitea/services/context" - secret_service "code.gitea.io/gitea/services/secrets" -) - -// ListActionsSecrets list an organization's actions secrets -func ListActionsSecrets(ctx *context.APIContext) { - // swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets - // --- - // summary: List an organization's actions secrets - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of the organization - // type: string - // required: true - // - name: page - // in: query - // description: page number of results to return (1-based) - // type: integer - // - name: limit - // in: query - // description: page size of results - // type: integer - // responses: - // "200": - // "$ref": "#/responses/SecretList" - // "404": - // "$ref": "#/responses/notFound" - - opts := &secret_model.FindSecretsOptions{ - OwnerID: ctx.Org.Organization.ID, - ListOptions: utils.GetListOptions(ctx), - } - - secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - apiSecrets := make([]*api.Secret, len(secrets)) - for k, v := range secrets { - apiSecrets[k] = &api.Secret{ - Name: v.Name, - Created: v.CreatedUnix.AsTime(), - } - } - - ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, apiSecrets) -} - -// create or update one secret of the organization -func CreateOrUpdateSecret(ctx *context.APIContext) { - // swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret - // --- - // summary: Create or Update a secret value in an organization - // consumes: - // - application/json - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of organization - // type: string - // required: true - // - name: secretname - // in: path - // description: name of the secret - // type: string - // required: true - // - name: body - // in: body - // schema: - // "$ref": "#/definitions/CreateOrUpdateSecretOption" - // responses: - // "201": - // description: response when creating a secret - // "204": - // description: response when updating a secret - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) - - _, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname"), opt.Data) - if err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) - } else if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err) - } else { - ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) - } - return - } - - if created { - ctx.Status(http.StatusCreated) - } else { - ctx.Status(http.StatusNoContent) - } -} - -// DeleteSecret delete one secret of the organization -func DeleteSecret(ctx *context.APIContext) { - // swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret - // --- - // summary: Delete a secret in an organization - // consumes: - // - application/json - // produces: - // - application/json - // parameters: - // - name: org - // in: path - // description: name of organization - // type: string - // required: true - // - name: secretname - // in: path - // description: name of the secret - // type: string - // required: true - // responses: - // "204": - // description: delete one secret of the organization - // "400": - // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" - - err := secret_service.DeleteSecretByName(ctx, ctx.Org.Organization.ID, 0, ctx.Params("secretname")) - if err != nil { - if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "DeleteSecret", err) - } else if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "DeleteSecret", err) - } else { - ctx.Error(http.StatusInternalServerError, "DeleteSecret", err) - } - return - } - - ctx.Status(http.StatusNoContent) -} diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 03321d956d..311cfca6e9 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -9,17 +9,76 @@ import ( actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" + secret_model "code.gitea.io/gitea/models/secret" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/shared" "code.gitea.io/gitea/routers/api/v1/utils" actions_service "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/context" secret_service "code.gitea.io/gitea/services/secrets" ) +// ListActionsSecrets list an repo's actions secrets +func (Action) ListActionsSecrets(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/actions/secrets repository repoListActionsSecrets + // --- + // summary: List an repo's actions secrets + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repository + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repository + // type: string + // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/SecretList" + // "404": + // "$ref": "#/responses/notFound" + + repo := ctx.Repo.Repository + + opts := &secret_model.FindSecretsOptions{ + RepoID: repo.ID, + ListOptions: utils.GetListOptions(ctx), + } + + secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts) + if err != nil { + ctx.InternalServerError(err) + return + } + + apiSecrets := make([]*api.Secret, len(secrets)) + for k, v := range secrets { + apiSecrets[k] = &api.Secret{ + Name: v.Name, + Created: v.CreatedUnix.AsTime(), + } + } + + ctx.SetTotalCountHeader(count) + ctx.JSON(http.StatusOK, apiSecrets) +} + // create or update one secret of the repository -func CreateOrUpdateSecret(ctx *context.APIContext) { +func (Action) CreateOrUpdateSecret(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/actions/secrets/{secretname} repository updateRepoSecret // --- // summary: Create or Update a secret value in a repository @@ -82,7 +141,7 @@ func CreateOrUpdateSecret(ctx *context.APIContext) { } // DeleteSecret delete one secret of the repository -func DeleteSecret(ctx *context.APIContext) { +func (Action) DeleteSecret(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/actions/secrets/{secretname} repository deleteRepoSecret // --- // summary: Delete a secret in a repository @@ -133,7 +192,7 @@ func DeleteSecret(ctx *context.APIContext) { } // GetVariable get a repo-level variable -func GetVariable(ctx *context.APIContext) { +func (Action) GetVariable(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/actions/variables/{variablename} repository getRepoVariable // --- // summary: Get a repo-level variable @@ -186,7 +245,7 @@ func GetVariable(ctx *context.APIContext) { } // DeleteVariable delete a repo-level variable -func DeleteVariable(ctx *context.APIContext) { +func (Action) DeleteVariable(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/actions/variables/{variablename} repository deleteRepoVariable // --- // summary: Delete a repo-level variable @@ -235,7 +294,7 @@ func DeleteVariable(ctx *context.APIContext) { } // CreateVariable create a repo-level variable -func CreateVariable(ctx *context.APIContext) { +func (Action) CreateVariable(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/actions/variables/{variablename} repository createRepoVariable // --- // summary: Create a repo-level variable @@ -302,7 +361,7 @@ func CreateVariable(ctx *context.APIContext) { } // UpdateVariable update a repo-level variable -func UpdateVariable(ctx *context.APIContext) { +func (Action) UpdateVariable(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/actions/variables/{variablename} repository updateRepoVariable // --- // summary: Update a repo-level variable @@ -369,7 +428,7 @@ func UpdateVariable(ctx *context.APIContext) { } // ListVariables list repo-level variables -func ListVariables(ctx *context.APIContext) { +func (Action) ListVariables(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/actions/variables repository getRepoVariablesList // --- // summary: Get repo-level variables list @@ -423,3 +482,38 @@ func ListVariables(ctx *context.APIContext) { ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, variables) } + +// GetRegistrationToken returns the token to register repo runners +func (Action) GetRegistrationToken(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/runners/registration-token repository repoGetRunnerRegistrationToken + // --- + // summary: Get a repository's actions runner registration token + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/RegistrationToken" + + shared.GetRegistrationToken(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID) +} + +var _ actions_service.API = new(Action) + +// Action implements actions_service.API +type Action struct{} + +// NewAction creates a new Action service +func NewAction() actions_service.API { + return Action{} +} diff --git a/routers/api/v1/repo/compare.go b/routers/api/v1/repo/compare.go index 549b9b7fa9..cfd61d768c 100644 --- a/routers/api/v1/repo/compare.go +++ b/routers/api/v1/repo/compare.go @@ -16,7 +16,7 @@ import ( // CompareDiff compare two branches or commits func CompareDiff(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/compare/{basehead} Get commit comparison information + // swagger:operation GET /repos/{owner}/{repo}/compare/{basehead} repository repoCompareDiff // --- // summary: Get commit comparison information // produces: diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 0d304dd66d..35c1fdcc0c 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -217,7 +217,6 @@ func SearchIssues(ctx *context.APIContext) { var includedAnyLabels []int64 { - labels := ctx.FormTrim("labels") var includedLabelNames []string if len(labels) > 0 { diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index 864644e1ef..2a896de4fe 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -180,7 +180,6 @@ func ListPushMirrors(ctx *context.APIContext) { if err == nil { responsePushMirrors = append(responsePushMirrors, m) } - } ctx.SetLinkHeader(len(responsePushMirrors), utils.GetListOptions(ctx).PageSize) ctx.SetTotalCountHeader(count) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index eec3c49bc4..852ec78ade 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1058,7 +1058,6 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) isSameRepo = true headUser = ctx.Repo.Owner headBranch = headInfos[0] - } else if len(headInfos) == 2 { headUser, err = user_model.GetUserByName(ctx, headInfos[0]) if err != nil { @@ -1072,18 +1071,16 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) headBranch = headInfos[1] // The head repository can also point to the same repo isSameRepo = ctx.Repo.Owner.ID == headUser.ID - } else { ctx.NotFound() return nil, nil, nil, nil, "", "" } ctx.Repo.PullRequest.SameRepo = isSameRepo - log.Info("Base branch: %s", baseBranch) - log.Info("Repo path: %s", ctx.Repo.GitRepo.Path) + log.Trace("Repo path: %q, base branch: %q, head branch: %q", ctx.Repo.GitRepo.Path, baseBranch, headBranch) // Check if base branch is valid. - if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) { - ctx.NotFound("IsBranchExist") + if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) && !ctx.Repo.GitRepo.IsTagExist(baseBranch) { + ctx.NotFound("BaseNotExist") return nil, nil, nil, nil, "", "" } @@ -1146,7 +1143,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } // Check if head branch is valid. - if !headGitRepo.IsBranchExist(headBranch) { + if !headGitRepo.IsBranchExist(headBranch) && !headGitRepo.IsTagExist(headBranch) { headGitRepo.Close() ctx.NotFound() return nil, nil, nil, nil, "", "" diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 77c0d25e2a..6799e43c73 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -4,6 +4,7 @@ package repo import ( + "errors" "fmt" "net/http" "strings" @@ -519,7 +520,11 @@ func CreatePullReview(ctx *context.APIContext) { // create review and associate all pending review comments review, _, err := pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, opts.CommitID, nil) if err != nil { - ctx.Error(http.StatusInternalServerError, "SubmitReview", err) + if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) { + ctx.Error(http.StatusUnprocessableEntity, "", err) + } else { + ctx.Error(http.StatusInternalServerError, "SubmitReview", err) + } return } @@ -607,7 +612,11 @@ func SubmitPullReview(ctx *context.APIContext) { // create review and associate all pending review comments review, _, err = pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, headCommitID, nil) if err != nil { - ctx.Error(http.StatusInternalServerError, "SubmitReview", err) + if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) { + ctx.Error(http.StatusUnprocessableEntity, "", err) + } else { + ctx.Error(http.StatusInternalServerError, "SubmitReview", err) + } return } @@ -875,7 +884,6 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions } if ctx.Repo.Repository.Owner.IsOrganization() && len(opts.TeamReviewers) > 0 { - teamReviewers := make([]*organization.Team, 0, len(opts.TeamReviewers)) for _, t := range opts.TeamReviewers { var teamReviewer *organization.Team diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 6692633fab..66eb227c19 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -1094,7 +1094,6 @@ func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error { // update MirrorInterval if opts.MirrorInterval != nil { - // MirrorInterval should be a duration interval, err := time.ParseDuration(*opts.MirrorInterval) if err != nil { diff --git a/routers/api/v1/repo/runners.go b/routers/api/v1/repo/runners.go deleted file mode 100644 index fe133b311d..0000000000 --- a/routers/api/v1/repo/runners.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package repo - -import ( - "code.gitea.io/gitea/routers/api/v1/shared" - "code.gitea.io/gitea/services/context" -) - -// GetRegistrationToken returns the token to register repo runners -func GetRegistrationToken(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/runners/registration-token repository repoGetRunnerRegistrationToken - // --- - // summary: Get a repository's actions runner registration token - // produces: - // - application/json - // parameters: - // - name: owner - // in: path - // description: owner of the repo - // type: string - // required: true - // - name: repo - // in: path - // description: name of the repo - // type: string - // required: true - // responses: - // "200": - // "$ref": "#/responses/RegistrationToken" - - shared.GetRegistrationToken(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID) -} diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index b6f51cdadc..1b92c7bceb 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -478,7 +478,6 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) func findWikiRepoCommit(ctx *context.APIContext) (*git.Repository, *git.Commit) { wikiRepo, err := gitrepo.OpenWikiRepository(ctx, ctx.Repo.Repository) if err != nil { - if git.IsErrNotExist(err) || err.Error() == "no such file or directory" { ctx.NotFound(err) } else { diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index cb356a184a..da9fa20082 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -259,7 +259,6 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r UserMsg: fmt.Sprintf("branch %s is protected from force push", branchName), }) return - } } diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index b48345684b..3ad7a4738e 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -5,6 +5,7 @@ package auth import ( go_context "context" + "crypto/sha256" "encoding/base64" "errors" "fmt" @@ -860,13 +861,19 @@ func SignInOAuth(ctx *context.Context) { return } - if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp); err != nil { + codeChallenge, err := generateCodeChallenge(ctx) + if err != nil { + ctx.ServerError("SignIn", fmt.Errorf("could not generate code_challenge: %w", err)) + return + } + + if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp, codeChallenge); err != nil { if strings.Contains(err.Error(), "no provider for ") { if err = oauth2.ResetOAuth2(ctx); err != nil { ctx.ServerError("SignIn", err) return } - if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp); err != nil { + if err = authSource.Cfg.(*oauth2.Source).Callout(ctx.Req, ctx.Resp, codeChallenge); err != nil { ctx.ServerError("SignIn", err) } return @@ -1203,6 +1210,27 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model ctx.Redirect(setting.AppSubURL + "/user/two_factor") } +// generateCodeChallenge stores a code verifier in the session and returns a S256 code challenge for PKCE +func generateCodeChallenge(ctx *context.Context) (codeChallenge string, err error) { + codeVerifier, err := util.CryptoRandomString(43) // 256/log2(62) = 256 bits of entropy (each char having log2(62) of randomness) + if err != nil { + return "", err + } + if err = ctx.Session.Set("CodeVerifier", codeVerifier); err != nil { + return "", err + } + return encodeCodeChallenge(codeVerifier) +} + +func encodeCodeChallenge(codeVerifier string) (string, error) { + hasher := sha256.New() + _, err := io.WriteString(hasher, codeVerifier) + codeChallenge := base64.RawURLEncoding.EncodeToString(hasher.Sum(nil)) + return codeChallenge, err +} + +// OAuth2UserLoginCallback attempts to handle the callback from the OAuth2 provider and if successful +// login the user func oAuth2UserLoginCallback(ctx *context.Context, authSource *auth.Source, request *http.Request, response http.ResponseWriter) (*user_model.User, goth.User, error) { gothUser, err := oAuth2FetchUser(ctx, authSource, request, response) if err != nil { @@ -1239,7 +1267,9 @@ func oAuth2FetchUser(ctx *context.Context, authSource *auth.Source, request *htt } // Proceed to authenticate through goth. - gothUser, err := oauth2Source.Callback(request, response) + codeVerifier, _ := ctx.Session.Get("CodeVerifier").(string) + _ = ctx.Session.Delete("CodeVerifier") + gothUser, err := oauth2Source.Callback(request, response, codeVerifier) if err != nil { if err.Error() == "securecookie: the value is too long" || strings.Contains(err.Error(), "Data too long") { log.Error("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength) diff --git a/routers/web/auth/oauth_test.go b/routers/web/auth/oauth_test.go index 4339d9d1eb..3726daee93 100644 --- a/routers/web/auth/oauth_test.go +++ b/routers/web/auth/oauth_test.go @@ -93,3 +93,10 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) { assert.Equal(t, user.Email, oidcToken.Email) assert.Equal(t, user.IsActive, oidcToken.EmailVerified) } + +func TestEncodeCodeChallenge(t *testing.T) { + // test vector from https://datatracker.ietf.org/doc/html/rfc7636#page-18 + codeChallenge, err := encodeCodeChallenge("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk") + assert.NoError(t, err) + assert.Equal(t, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", codeChallenge) +} diff --git a/routers/web/org/setting/blocked_users.go b/routers/web/org/setting/blocked_users.go index b23f5ba596..0c7f245c13 100644 --- a/routers/web/org/setting/blocked_users.go +++ b/routers/web/org/setting/blocked_users.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" + shared_user "code.gitea.io/gitea/routers/web/shared/user" "code.gitea.io/gitea/services/context" user_service "code.gitea.io/gitea/services/user" ) @@ -27,6 +28,12 @@ func BlockedUsers(ctx *context.Context) { return } + err = shared_user.LoadHeaderCount(ctx) + if err != nil { + ctx.ServerError("LoadHeaderCount", err) + return + } + ctx.Data["BlockedUsers"] = blockedUsers ctx.HTML(http.StatusOK, tplBlockedUsers) diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index fa687bbb93..dc21f1a4ed 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -552,7 +552,7 @@ func getRunJobs(ctx *context_module.Context, runIndex, jobIndex int64) (*actions return nil, nil } if len(jobs) == 0 { - ctx.Error(http.StatusNotFound, err.Error()) + ctx.Error(http.StatusNotFound) return nil, nil } @@ -689,7 +689,6 @@ func ArtifactsDownloadView(ctx *context_module.Context) { writer := zip.NewWriter(ctx.Resp) defer writer.Close() for _, art := range artifacts { - f, err := storage.ActionsArtifacts.Open(art.StoragePath) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 718454e063..33491ec696 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -212,8 +212,6 @@ func SearchCommits(ctx *context.Context) { // FileHistory show a file's reversions func FileHistory(ctx *context.Context) { - ctx.Data["IsRepoToolbarCommits"] = true - fileName := ctx.Repo.TreePath if len(fileName) == 0 { Commits(ctx) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index b07209c779..a61e23add3 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -800,7 +800,6 @@ func CompareDiff(ctx *context.Context) { } ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + separator + base.ShortSha(afterCommitID) - ctx.Data["IsRepoToolbarCommits"] = true ctx.Data["IsDiffCompare"] = true _, templateErrs := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b0b4b65331..97f2195116 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -927,7 +927,6 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles } } } - } if template.Ref != "" && !strings.HasPrefix(template.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/ @@ -1680,7 +1679,6 @@ func ViewIssue(ctx *context.Context) { if comment.ProjectID > 0 && comment.Project == nil { comment.Project = ghostProject } - } else if comment.Type == issues_model.CommentTypeAssignees || comment.Type == issues_model.CommentTypeReviewRequest { if err = comment.LoadAssigneeUserAndTeam(ctx); err != nil { ctx.ServerError("LoadAssigneeUserAndTeam", err) @@ -2605,7 +2603,6 @@ func SearchIssues(ctx *context.Context) { var includedAnyLabels []int64 { - labels := ctx.FormTrim("labels") var includedLabelNames []string if len(labels) > 0 { @@ -2993,7 +2990,6 @@ func NewComment(ctx *context.Context) { if (ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) || (ctx.IsSigned && issue.IsPoster(ctx.Doer.ID))) && (form.Status == "reopen" || form.Status == "close") && !(issue.IsPull && issue.PullRequest.HasMerged) { - // Duplication and conflict check should apply to reopen pull request. var pr *issues_model.PullRequest diff --git a/routers/web/repo/issue_dependency.go b/routers/web/repo/issue_dependency.go index e3b85ee638..66b38688ec 100644 --- a/routers/web/repo/issue_dependency.go +++ b/routers/web/repo/issue_dependency.go @@ -109,7 +109,7 @@ func RemoveDependency(ctx *context.Context) { } // Dependency Type - depTypeStr := ctx.Req.PostForm.Get("dependencyType") + depTypeStr := ctx.Req.PostFormValue("dependencyType") var depType issues_model.DependencyType diff --git a/routers/web/repo/issue_watch.go b/routers/web/repo/issue_watch.go index c8d7187b8e..5cff9f4ddd 100644 --- a/routers/web/repo/issue_watch.go +++ b/routers/web/repo/issue_watch.go @@ -46,7 +46,7 @@ func IssueWatch(ctx *context.Context) { return } - watch, err := strconv.ParseBool(ctx.Req.PostForm.Get("watch")) + watch, err := strconv.ParseBool(ctx.Req.PostFormValue("watch")) if err != nil { ctx.ServerError("watch is not bool", err) return diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index c6c6142534..a0dd36927f 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -670,7 +670,6 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C } if pb != nil && pb.EnableStatusCheck { - var missingRequiredChecks []string for _, requiredContext := range pb.StatusCheckContexts { contextFound := false @@ -873,7 +872,6 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi // Validate the given commit sha to show (if any passed) if willShowSpecifiedCommit || willShowSpecifiedCommitRange { - foundStartCommit := len(specifiedStartCommit) == 0 foundEndCommit := len(specifiedEndCommit) == 0 @@ -1185,7 +1183,6 @@ func UpdatePullRequest(ctx *context.Context) { ctx.Flash.Error(flashError) ctx.Redirect(issue.Link()) return - } ctx.Flash.Error(err.Error()) ctx.Redirect(issue.Link()) @@ -1439,7 +1436,6 @@ func CompareAndPullRequestPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes") ctx.Data["PageIsComparePull"] = true ctx.Data["IsDiffCompare"] = true - ctx.Data["IsRepoToolbarCommits"] = true ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index 81af2cbb51..24763668d0 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -248,6 +248,8 @@ func SubmitReview(ctx *context.Context) { if issues_model.IsContentEmptyErr(err) { ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty")) ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index)) + } else if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) { + ctx.Status(http.StatusUnprocessableEntity) } else { ctx.ServerError("SubmitReview", err) } @@ -302,7 +304,6 @@ func UpdateViewedFiles(ctx *context.Context) { updatedFiles := make(map[string]pull_model.ViewedState, len(data.Files)) for file, viewed := range data.Files { - // Only unviewed and viewed are possible, has-changed can not be set from the outside state := pull_model.Unviewed if viewed { diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index e98cd46c4e..2644b27229 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -352,7 +352,6 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool { // or of directory if not in root directory. ctx.Data["LatestCommit"] = latestCommit if latestCommit != nil { - verification := asymkey_model.ParseCommitWithSignature(ctx, latestCommit) if err := asymkey_model.CalculateTrustStatus(verification, ctx.Repo.Repository.GetTrustModel(), func(user *user_model.User) (bool, error) { diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go index 099f6236a6..1f3de70db0 100644 --- a/routers/web/webfinger.go +++ b/routers/web/webfinger.go @@ -102,23 +102,11 @@ func WebfingerQuery(ctx *context.Context) { default: ctx.Error(http.StatusNotFound) return - - } - case 4: - //nolint:gocritic - if parts[3] == "teams" { - ctx.Error(http.StatusNotFound) - return - - } else { - ctx.Error(http.StatusNotFound) - return } default: ctx.Error(http.StatusNotFound) return - } default: diff --git a/services/actions/interface.go b/services/actions/interface.go new file mode 100644 index 0000000000..d4fa782fec --- /dev/null +++ b/services/actions/interface.go @@ -0,0 +1,28 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import "code.gitea.io/gitea/services/context" + +// API for actions of a repository or organization +type API interface { + // ListActionsSecrets list secrets + ListActionsSecrets(*context.APIContext) + // CreateOrUpdateSecret create or update a secret + CreateOrUpdateSecret(*context.APIContext) + // DeleteSecret delete a secret + DeleteSecret(*context.APIContext) + // ListVariables list variables + ListVariables(*context.APIContext) + // GetVariable get a variable + GetVariable(*context.APIContext) + // DeleteVariable delete a variable + DeleteVariable(*context.APIContext) + // CreateVariable create a variable + CreateVariable(*context.APIContext) + // UpdateVariable update a variable + UpdateVariable(*context.APIContext) + // GetRegistrationToken get registration token + GetRegistrationToken(*context.APIContext) +} diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 365212d9c2..78f413c214 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -329,13 +329,15 @@ func handleWorkflows( TriggerEvent: dwf.TriggerEvent.Name, Status: actions_model.StatusWaiting, } - if need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer); err != nil { + + need, err := ifNeedApproval(ctx, run, input.Repo, input.Doer) + if err != nil { log.Error("check if need approval for repo %d with user %d: %v", input.Repo.ID, input.Doer.ID, err) continue - } else { - run.NeedApproval = need } + run.NeedApproval = need + if err := run.LoadAttributes(ctx); err != nil { log.Error("LoadAttributes: %v", err) continue diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index 33013e9a9d..18f3324fd2 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -132,13 +132,10 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) Status: actions_model.StatusWaiting, } - if err := run.LoadAttributes(ctx); err != nil { - log.Error("LoadAttributes: %v", err) - } - vars, err := actions_model.GetVariablesOfRun(ctx, run) if err != nil { - log.Error("GetVariablesOfSchedule: %v", err) + log.Error("GetVariablesOfRun: %v", err) + return err } // Parse the workflow specification from the cron schedule diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go index 7c5d3da595..1f70edaa82 100644 --- a/services/auth/source/ldap/source_sync.go +++ b/services/auth/source/ldap/source_sync.go @@ -159,7 +159,6 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { !strings.EqualFold(usr.Email, su.Mail) || usr.FullName != fullName || !usr.IsActive { - log.Trace("SyncExternalUsers[%s]: Updating user %s", source.authSource.Name, usr.Name) opts := &user_service.UpdateOptions{ diff --git a/services/auth/source/oauth2/source_callout.go b/services/auth/source/oauth2/source_callout.go index 8d70bee248..f95a80fc19 100644 --- a/services/auth/source/oauth2/source_callout.go +++ b/services/auth/source/oauth2/source_callout.go @@ -5,16 +5,25 @@ package oauth2 import ( "net/http" + "net/url" "github.com/markbates/goth" "github.com/markbates/goth/gothic" ) // Callout redirects request/response pair to authenticate against the provider -func (source *Source) Callout(request *http.Request, response http.ResponseWriter) error { +func (source *Source) Callout(request *http.Request, response http.ResponseWriter, codeChallengeS256 string) error { // not sure if goth is thread safe (?) when using multiple providers request.Header.Set(ProviderHeaderKey, source.authSource.Name) + var querySuffix string + if codeChallengeS256 != "" { + querySuffix = "&" + url.Values{ + "code_challenge_method": []string{"S256"}, + "code_challenge": []string{codeChallengeS256}, + }.Encode() + } + // don't use the default gothic begin handler to prevent issues when some error occurs // normally the gothic library will write some custom stuff to the response instead of our own nice error page // gothic.BeginAuthHandler(response, request) @@ -24,17 +33,29 @@ func (source *Source) Callout(request *http.Request, response http.ResponseWrite url, err := gothic.GetAuthURL(response, request) if err == nil { - http.Redirect(response, request, url, http.StatusTemporaryRedirect) + // hacky way to set the code_challenge, but no better way until + // https://github.com/markbates/goth/issues/516 is resolved + http.Redirect(response, request, url+querySuffix, http.StatusTemporaryRedirect) } return err } // Callback handles OAuth callback, resolve to a goth user and send back to original url // this will trigger a new authentication request, but because we save it in the session we can use that -func (source *Source) Callback(request *http.Request, response http.ResponseWriter) (goth.User, error) { +func (source *Source) Callback(request *http.Request, response http.ResponseWriter, codeVerifier string) (goth.User, error) { // not sure if goth is thread safe (?) when using multiple providers request.Header.Set(ProviderHeaderKey, source.authSource.Name) + if codeVerifier != "" { + // hacky way to set the code_verifier... + // Will be picked up inside CompleteUserAuth: params := req.URL.Query() + // https://github.com/markbates/goth/pull/474/files + request = request.Clone(request.Context()) + q := request.URL.Query() + q.Add("code_verifier", codeVerifier) + request.URL.RawQuery = q.Encode() + } + gothRWMutex.RLock() defer gothRWMutex.RUnlock() diff --git a/services/context/repo.go b/services/context/repo.go index ec0c1ae7ea..6428ddc304 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -862,7 +862,6 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { case RepoRefBranch: ref := getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsBranchExist) if len(ref) == 0 { - // check if ref is HEAD parts := strings.Split(path, "/") if parts[0] == headRefName { @@ -1005,7 +1004,6 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context return cancel } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() - } else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) { ctx.Repo.IsViewTag = true ctx.Repo.TagName = refName diff --git a/services/doctor/dbconsistency.go b/services/doctor/dbconsistency.go index d036f75b2a..6b931b7036 100644 --- a/services/doctor/dbconsistency.go +++ b/services/doctor/dbconsistency.go @@ -159,6 +159,12 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er Fixer: actions_model.FixRunnersWithoutBelongingOwner, FixedMessage: "Removed", }, + { + Name: "Action Runners without existing repository", + Counter: actions_model.CountRunnersWithoutBelongingRepo, + Fixer: actions_model.FixRunnersWithoutBelongingRepo, + FixedMessage: "Removed", + }, { Name: "Topics with empty repository count", Counter: repo_model.CountOrphanedTopics, diff --git a/services/doctor/packages_nuget.go b/services/doctor/packages_nuget.go index 8c0a2d856d..47fdb3ac12 100644 --- a/services/doctor/packages_nuget.go +++ b/services/doctor/packages_nuget.go @@ -51,7 +51,6 @@ func PackagesNugetNuspecCheck(ctx context.Context, logger log.Logger, autofix bo logger.Info("Found %d versions for package %s", len(pvs), pkg.Name) for _, pv := range pvs { - pfs, err := packages.GetFilesByVersionID(ctx, pv.ID) if err != nil { logger.Error("Failed to get files for package version %s %s: %v", pkg.Name, pv.Version, err) diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 0c89839edf..d9dbeedee5 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -1040,8 +1040,8 @@ func createDiffFile(diff *Diff, line string) *DiffFile { // diff --git a/b b/b b/b b/b b/b b/b // midpoint := (len(line) + len(cmdDiffHead) - 1) / 2 - new, old := line[len(cmdDiffHead):midpoint], line[midpoint+1:] - if len(new) > 2 && len(old) > 2 && new[2:] == old[2:] { + newl, old := line[len(cmdDiffHead):midpoint], line[midpoint+1:] + if len(newl) > 2 && len(old) > 2 && newl[2:] == old[2:] { curFile.OldName = old[2:] curFile.Name = old[2:] } diff --git a/services/issue/commit.go b/services/issue/commit.go index e493a03211..8b927d52b6 100644 --- a/services/issue/commit.go +++ b/services/issue/commit.go @@ -117,7 +117,6 @@ func UpdateIssuesCommit(ctx context.Context, doer *user_model.User, repo *repo_m var refIssue *issues_model.Issue var err error for _, ref := range references.FindAllIssueReferences(c.Message) { - // issue is from another repo if len(ref.Owner) > 0 && len(ref.Name) > 0 { refRepo, err = repo_model.GetRepositoryByOwnerAndName(ctx, ref.Owner, ref.Name) @@ -185,15 +184,15 @@ func UpdateIssuesCommit(ctx context.Context, doer *user_model.User, repo *repo_m continue } } - close := ref.Action == references.XRefActionCloses - if close && len(ref.TimeLog) > 0 { + isClosed := ref.Action == references.XRefActionCloses + if isClosed && len(ref.TimeLog) > 0 { if err := issueAddTime(ctx, refIssue, doer, c.Timestamp, ref.TimeLog); err != nil { return err } } - if close != refIssue.IsClosed { + if isClosed != refIssue.IsClosed { refIssue.Repo = refRepo - if err := ChangeStatus(ctx, refIssue, doer, c.Sha1, close); err != nil { + if err := ChangeStatus(ctx, refIssue, doer, c.Sha1, isClosed); err != nil { return err } } diff --git a/services/mailer/incoming/incoming.go b/services/mailer/incoming/incoming.go index eade0cf271..6530b7cc60 100644 --- a/services/mailer/incoming/incoming.go +++ b/services/mailer/incoming/incoming.go @@ -219,6 +219,11 @@ loop: } err := func() error { + if isAlreadyHandled(handledSet, msg) { + log.Debug("Skipping already handled message") + return nil + } + r := msg.GetBody(section) if r == nil { return fmt.Errorf("could not get body from message: %w", err) @@ -277,6 +282,11 @@ loop: return nil } +// isAlreadyHandled tests if the message was already handled +func isAlreadyHandled(handledSet *imap.SeqSet, msg *imap.Message) bool { + return handledSet.Contains(msg.SeqNum) +} + // isAutomaticReply tests if the headers indicate an automatic reply func isAutomaticReply(env *enmime.Envelope) bool { autoSubmitted := env.GetHeader("Auto-Submitted") @@ -367,6 +377,14 @@ func getContentFromMailReader(env *enmime.Envelope) *MailContent { Content: attachment.Content, }) } + for _, inline := range env.Inlines { + if inline.FileName != "" { + attachments = append(attachments, &Attachment{ + Name: inline.FileName, + Content: inline.Content, + }) + } + } return &MailContent{ Content: reply.FromText(env.Text), diff --git a/services/mailer/incoming/incoming_test.go b/services/mailer/incoming/incoming_test.go index 5d84848e3f..f2bb7fc498 100644 --- a/services/mailer/incoming/incoming_test.go +++ b/services/mailer/incoming/incoming_test.go @@ -7,10 +7,24 @@ import ( "strings" "testing" + "github.com/emersion/go-imap" "github.com/jhillyerd/enmime" "github.com/stretchr/testify/assert" ) +func TestNotHandleTwice(t *testing.T) { + handledSet := new(imap.SeqSet) + msg := imap.NewMessage(90, []imap.FetchItem{imap.FetchBody}) + + handled := isAlreadyHandled(handledSet, msg) + assert.Equal(t, false, handled) + + handledSet.AddNum(msg.SeqNum) + + handled = isAlreadyHandled(handledSet, msg) + assert.Equal(t, true, handled) +} + func TestIsAutomaticReply(t *testing.T) { cases := []struct { Headers map[string]string @@ -95,6 +109,32 @@ func TestGetContentFromMailReader(t *testing.T) { assert.Equal(t, "attachment.txt", content.Attachments[0].Name) assert.Equal(t, []byte("attachment content"), content.Attachments[0].Content) + mailString = "Content-Type: multipart/mixed; boundary=message-boundary\r\n" + + "\r\n" + + "--message-boundary\r\n" + + "Content-Type: multipart/alternative; boundary=text-boundary\r\n" + + "\r\n" + + "--text-boundary\r\n" + + "Content-Type: text/plain\r\n" + + "Content-Disposition: inline\r\n" + + "\r\n" + + "mail content\r\n" + + "--text-boundary--\r\n" + + "--message-boundary\r\n" + + "Content-Type: text/plain\r\n" + + "Content-Disposition: inline; filename=attachment.txt\r\n" + + "\r\n" + + "attachment content\r\n" + + "--message-boundary--\r\n" + + env, err = enmime.ReadEnvelope(strings.NewReader(mailString)) + assert.NoError(t, err) + content = getContentFromMailReader(env) + assert.Equal(t, "mail content", content.Content) + assert.Len(t, content.Attachments, 1) + assert.Equal(t, "attachment.txt", content.Attachments[0].Name) + assert.Equal(t, []byte("attachment content"), content.Attachments[0].Content) + mailString = "Content-Type: multipart/mixed; boundary=message-boundary\r\n" + "\r\n" + "--message-boundary\r\n" + diff --git a/services/migrations/gitea_downloader.go b/services/migrations/gitea_downloader.go index d402a238f2..272bf02e11 100644 --- a/services/migrations/gitea_downloader.go +++ b/services/migrations/gitea_downloader.go @@ -410,7 +410,6 @@ func (g *GiteaDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, err return nil, false, fmt.Errorf("error while listing issues: %w", err) } for _, issue := range issues { - labels := make([]*base.Label, 0, len(issue.Labels)) for i := range issue.Labels { labels = append(labels, g.convertGiteaLabel(issue.Labels[i])) diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go index bbc44e958a..065b687fa6 100644 --- a/services/migrations/gitlab.go +++ b/services/migrations/gitlab.go @@ -421,7 +421,6 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er return nil, false, fmt.Errorf("error while listing issues: %w", err) } for _, issue := range issues { - labels := make([]*base.Label, 0, len(issue.Labels)) for _, l := range issue.Labels { labels = append(labels, &base.Label{ diff --git a/services/migrations/gogs.go b/services/migrations/gogs.go index 72c52d180b..b31d05fa73 100644 --- a/services/migrations/gogs.go +++ b/services/migrations/gogs.go @@ -38,17 +38,24 @@ func (f *GogsDownloaderFactory) New(ctx context.Context, opts base.MigrateOption return nil, err } - baseURL := u.Scheme + "://" + u.Host repoNameSpace := strings.TrimSuffix(u.Path, ".git") repoNameSpace = strings.Trim(repoNameSpace, "/") fields := strings.Split(repoNameSpace, "/") - if len(fields) < 2 { + numFields := len(fields) + if numFields < 2 { return nil, fmt.Errorf("invalid path: %s", repoNameSpace) } - log.Trace("Create gogs downloader. BaseURL: %s RepoOwner: %s RepoName: %s", baseURL, fields[0], fields[1]) - return NewGogsDownloader(ctx, baseURL, opts.AuthUsername, opts.AuthPassword, opts.AuthToken, fields[0], fields[1]), nil + repoOwner := fields[numFields-2] + repoName := fields[numFields-1] + + u.Path = "" + u = u.JoinPath(fields[:numFields-2]...) + baseURL := u.String() + + log.Trace("Create gogs downloader. BaseURL: %s RepoOwner: %s RepoName: %s", baseURL, repoOwner, repoName) + return NewGogsDownloader(ctx, baseURL, opts.AuthUsername, opts.AuthPassword, opts.AuthToken, repoOwner, repoName), nil } // GitServiceType returns the type of git service diff --git a/services/migrations/gogs_test.go b/services/migrations/gogs_test.go index 610af183de..ca02b4317b 100644 --- a/services/migrations/gogs_test.go +++ b/services/migrations/gogs_test.go @@ -137,3 +137,87 @@ func TestGogsDownloadRepo(t *testing.T) { _, _, err = downloader.GetPullRequests(1, 3) assert.Error(t, err) } + +func TestGogsDownloaderFactory_New(t *testing.T) { + tests := []struct { + name string + args base.MigrateOptions + baseURL string + repoOwner string + repoName string + wantErr bool + }{ + { + name: "Gogs_at_root", + args: base.MigrateOptions{ + CloneAddr: "https://git.example.com/user/repo.git", + AuthUsername: "username", + AuthPassword: "password", + AuthToken: "authtoken", + }, + baseURL: "https://git.example.com/", + repoOwner: "user", + repoName: "repo", + wantErr: false, + }, + { + name: "Gogs_at_sub_path", + args: base.MigrateOptions{ + CloneAddr: "https://git.example.com/subpath/user/repo.git", + AuthUsername: "username", + AuthPassword: "password", + AuthToken: "authtoken", + }, + baseURL: "https://git.example.com/subpath", + repoOwner: "user", + repoName: "repo", + wantErr: false, + }, + { + name: "Gogs_at_2nd_sub_path", + args: base.MigrateOptions{ + CloneAddr: "https://git.example.com/sub1/sub2/user/repo.git", + AuthUsername: "username", + AuthPassword: "password", + AuthToken: "authtoken", + }, + baseURL: "https://git.example.com/sub1/sub2", + repoOwner: "user", + repoName: "repo", + wantErr: false, + }, + { + name: "Gogs_URL_too_short", + args: base.MigrateOptions{ + CloneAddr: "https://git.example.com/repo.git", + AuthUsername: "username", + AuthPassword: "password", + AuthToken: "authtoken", + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := &GogsDownloaderFactory{} + opts := base.MigrateOptions{ + CloneAddr: tt.args.CloneAddr, + AuthUsername: tt.args.AuthUsername, + AuthPassword: tt.args.AuthPassword, + AuthToken: tt.args.AuthToken, + } + got, err := f.New(context.Background(), opts) + if (err != nil) != tt.wantErr { + t.Errorf("GogsDownloaderFactory.New() error = %v, wantErr %v", err, tt.wantErr) + return + } else if err != nil { + return + } + + 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) + }) + } +} diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go index 72e545581a..0270f87039 100644 --- a/services/mirror/mirror.go +++ b/services/mirror/mirror.go @@ -40,7 +40,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { } log.Trace("Doing: Update") - handler := func(idx int, bean any) error { + handler := func(bean any) error { var repo *repo_model.Repository var mirrorType SyncType var referenceID int64 @@ -91,7 +91,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { pullMirrorsRequested := 0 if pullLimit != 0 { if err := repo_model.MirrorsIterate(ctx, pullLimit, func(idx int, bean any) error { - if err := handler(idx, bean); err != nil { + if err := handler(bean); err != nil { return err } pullMirrorsRequested++ @@ -105,7 +105,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { pushMirrorsRequested := 0 if pushLimit != 0 { if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean any) error { - if err := handler(idx, bean); err != nil { + if err := handler(bean); err != nil { return err } pushMirrorsRequested++ diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index fa23986c54..9f7ffb29c9 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -466,7 +466,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) if len(results) > 0 { - if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok { + if ok := checkAndUpdateEmptyRepository(ctx, m, results); !ok { log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err) return false } @@ -523,13 +523,13 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum] } - if newCommit, err := gitRepo.GetCommit(newCommitID); err != nil { + newCommit, err := gitRepo.GetCommit(newCommitID) + if err != nil { log.Error("SyncMirrors [repo: %-v]: unable to get commit %s: %v", m.Repo, newCommitID, err) continue - } else { - theCommits.HeadCommit = repo_module.CommitToPushCommit(newCommit) } + theCommits.HeadCommit = repo_module.CommitToPushCommit(newCommit) theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID) notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ @@ -557,7 +557,6 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err) return false } - } log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo) @@ -565,7 +564,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { return true } -func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, gitRepo *git.Repository, results []*mirrorSyncResult) bool { +func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, results []*mirrorSyncResult) bool { if !m.Repo.IsEmpty { return true } diff --git a/services/pull/merge.go b/services/pull/merge.go index 2989d77c6a..1d6431ab66 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -237,9 +237,9 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U if err = ref.Issue.LoadRepo(ctx); err != nil { return err } - close := ref.RefAction == references.XRefActionCloses - if close != ref.Issue.IsClosed { - if err = issue_service.ChangeStatus(ctx, ref.Issue, doer, pr.MergedCommitID, close); err != nil { + isClosed := ref.RefAction == references.XRefActionCloses + if isClosed != ref.Issue.IsClosed { + if err = issue_service.ChangeStatus(ctx, ref.Issue, doer, pr.MergedCommitID, isClosed); err != nil { // Allow ErrDependenciesLeft if !issues_model.IsErrDependenciesLeft(err) { return err diff --git a/services/pull/pull.go b/services/pull/pull.go index 720efdb0cb..5fa426483f 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -803,7 +803,6 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ if err != nil { log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err) return "" - } if len(commits) == 0 { break diff --git a/services/pull/review.go b/services/pull/review.go index 6ad931b679..cff6f346ae 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -6,6 +6,7 @@ package pull import ( "context" + "errors" "fmt" "io" "regexp" @@ -43,6 +44,9 @@ func (err ErrDismissRequestOnClosedPR) Unwrap() error { return util.ErrPermissionDenied } +// ErrSubmitReviewOnClosedPR represents an error when an user tries to submit an approve or reject review associated to a closed or merged PR. +var ErrSubmitReviewOnClosedPR = errors.New("can't submit review for a closed or merged PR") + // checkInvalidation checks if the line of code comment got changed by another commit. // If the line got changed the comment is going to be invalidated. func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error { @@ -293,6 +297,10 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos if reviewType != issues_model.ReviewTypeApprove && reviewType != issues_model.ReviewTypeReject { stale = false } else { + if issue.IsClosed { + return nil, nil, ErrSubmitReviewOnClosedPR + } + headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName()) if err != nil { return nil, nil, err diff --git a/services/repository/adopt.go b/services/repository/adopt.go index b337eac38a..31e3e581b3 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -357,7 +357,6 @@ func ListUnadoptedRepositories(ctx context.Context, query string, opts *db.ListO return err } repoNamesToCheck = repoNamesToCheck[:0] - } return filepath.SkipDir }); err != nil { diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go index bd40a75dc8..1944f11f03 100644 --- a/services/repository/commitstatus/commitstatus.go +++ b/services/repository/commitstatus/commitstatus.go @@ -127,6 +127,9 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato // FindReposLastestCommitStatuses loading repository default branch latest combinded commit status with cache func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Repository) ([]*git_model.CommitStatus, error) { + if len(repos) == 0 { + return nil, nil + } results := make([]*git_model.CommitStatus, len(repos)) for i, repo := range repos { if cv := getCommitStatusCache(repo.ID, repo.DefaultBranch); cv != nil { diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go index 516e7f1475..7241d3d655 100644 --- a/services/repository/contributors_graph.go +++ b/services/repository/contributors_graph.go @@ -194,7 +194,6 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int Stats: &commitStats, } extendedCommitStats = append(extendedCommitStats, res) - } _ = stdoutReader.Close() return nil diff --git a/services/repository/files/update.go b/services/repository/files/update.go index e677949e86..81a61da5ed 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -208,7 +208,6 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use return nil, fmt.Errorf("ConvertToSHA1: Invalid last commit ID: %w", err) } opts.LastCommitID = lastCommitID.String() - } for _, file := range opts.Files { @@ -360,7 +359,6 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep Path: file.Options.treePath, } } - } } diff --git a/services/user/delete.go b/services/user/delete.go index e890990994..74dbc09b82 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -106,7 +106,6 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) if purge || (setting.Service.UserDeleteWithCommentsMaxTime != 0 && u.CreatedUnix.AsTime().Add(setting.Service.UserDeleteWithCommentsMaxTime).After(time.Now())) { - // Delete Comments const batchSize = 50 for { diff --git a/services/user/update_test.go b/services/user/update_test.go index 7ed764b539..c2ff26a140 100644 --- a/services/user/update_test.go +++ b/services/user/update_test.go @@ -94,7 +94,7 @@ func TestUpdateAuth(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28}) - copy := *user + userCopy := *user assert.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{ LoginName: optional.Some("new-login"), @@ -106,8 +106,8 @@ func TestUpdateAuth(t *testing.T) { MustChangePassword: optional.Some(true), })) assert.True(t, user.MustChangePassword) - assert.NotEqual(t, copy.Passwd, user.Passwd) - assert.NotEqual(t, copy.Salt, user.Salt) + assert.NotEqual(t, userCopy.Passwd, user.Passwd) + assert.NotEqual(t, userCopy.Salt, user.Salt) assert.NoError(t, UpdateAuth(db.DefaultContext, user, &UpdateAuthOptions{ ProhibitLogin: optional.Some(true), diff --git a/services/webhook/default.go b/services/webhook/default.go index 314f539648..ea877698c8 100644 --- a/services/webhook/default.go +++ b/services/webhook/default.go @@ -12,6 +12,8 @@ import ( "strings" webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/svg" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -67,6 +69,18 @@ func (defaultHandler) UnmarshalForm(bind func(any)) forms.WebhookForm { } func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (req *http.Request, body []byte, err error) { + payloadContent := t.PayloadContent + if w.Type == webhook_module.GITEA && + (t.EventType == webhook_module.HookEventCreate || t.EventType == webhook_module.HookEventDelete) { + // Woodpecker expects the ref to be short on tag creation only + // https://github.com/woodpecker-ci/woodpecker/blob/00ccec078cdced80cf309cd4da460a5041d7991a/server/forge/gitea/helper.go#L134 + // see https://codeberg.org/codeberg/community/issues/1556 + payloadContent, err = substituteRefShortName(payloadContent) + if err != nil { + return nil, nil, fmt.Errorf("could not substiture ref: %w", err) + } + } + switch w.HTTPMethod { case "": log.Info("HTTP Method for %s webhook %s [ID: %d] is not set, defaulting to POST", w.Type, w.URL, w.ID) @@ -74,7 +88,7 @@ func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, case http.MethodPost: switch w.ContentType { case webhook_model.ContentTypeJSON: - req, err = http.NewRequest("POST", w.URL, strings.NewReader(t.PayloadContent)) + req, err = http.NewRequest("POST", w.URL, strings.NewReader(payloadContent)) if err != nil { return nil, nil, err } @@ -82,7 +96,7 @@ func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, req.Header.Set("Content-Type", "application/json") case webhook_model.ContentTypeForm: forms := url.Values{ - "payload": []string{t.PayloadContent}, + "payload": []string{payloadContent}, } req, err = http.NewRequest("POST", w.URL, strings.NewReader(forms.Encode())) @@ -100,7 +114,7 @@ func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, return nil, nil, fmt.Errorf("invalid URL: %w", err) } vals := u.Query() - vals["payload"] = []string{t.PayloadContent} + vals["payload"] = []string{payloadContent} u.RawQuery = vals.Encode() req, err = http.NewRequest("GET", u.String(), nil) if err != nil { @@ -109,12 +123,12 @@ func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, case http.MethodPut: switch w.Type { case webhook_module.MATRIX: // used when t.Version == 1 - txnID, err := getMatrixTxnID([]byte(t.PayloadContent)) + txnID, err := getMatrixTxnID([]byte(payloadContent)) if err != nil { return nil, nil, err } url := fmt.Sprintf("%s/%s", w.URL, url.PathEscape(txnID)) - req, err = http.NewRequest("PUT", url, strings.NewReader(t.PayloadContent)) + req, err = http.NewRequest("PUT", url, strings.NewReader(payloadContent)) if err != nil { return nil, nil, err } @@ -125,6 +139,22 @@ func (defaultHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, return nil, nil, fmt.Errorf("invalid http method: %v", w.HTTPMethod) } - body = []byte(t.PayloadContent) + body = []byte(payloadContent) return req, body, shared.AddDefaultHeaders(req, []byte(w.Secret), t, body) } + +func substituteRefShortName(body string) (string, error) { + var m map[string]any + if err := json.Unmarshal([]byte(body), &m); err != nil { + return body, err + } + ref, ok := m["ref"].(string) + if !ok { + return body, fmt.Errorf("expected string 'ref', got %T", m["ref"]) + } + + m["ref"] = git.RefName(ref).ShortName() + + buf, err := json.Marshal(m) + return string(buf), err +} diff --git a/services/webhook/default_test.go b/services/webhook/default_test.go new file mode 100644 index 0000000000..29f1521cca --- /dev/null +++ b/services/webhook/default_test.go @@ -0,0 +1,224 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package webhook + +import ( + "context" + "testing" + + webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/json" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGiteaPayload(t *testing.T) { + dh := defaultHandler{ + forgejo: false, + } + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.GITEA, + URL: "https://gitea.example.com/", + Meta: ``, + HTTPMethod: "POST", + ContentType: webhook_model.ContentTypeJSON, + } + + // Woodpecker expects the ref to be short on tag creation only + // https://github.com/woodpecker-ci/woodpecker/blob/00ccec078cdced80cf309cd4da460a5041d7991a/server/forge/gitea/helper.go#L134 + // see https://codeberg.org/codeberg/community/issues/1556 + t.Run("Create", func(t *testing.T) { + p := createTestPayload() + data, err := p.JSONPayload() + require.NoError(t, err) + + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventCreate, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + require.NoError(t, err) + require.NotNil(t, req) + require.NotNil(t, reqBody) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://gitea.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body struct { + Ref string `json:"ref"` + } + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "test", body.Ref) // short ref + }) + + t.Run("Push", func(t *testing.T) { + p := pushTestPayload() + data, err := p.JSONPayload() + require.NoError(t, err) + + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + require.NoError(t, err) + require.NotNil(t, req) + require.NotNil(t, reqBody) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://gitea.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body struct { + Ref string `json:"ref"` + } + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "refs/heads/test", body.Ref) // full ref + }) + + t.Run("Delete", func(t *testing.T) { + p := deleteTestPayload() + data, err := p.JSONPayload() + require.NoError(t, err) + + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventDelete, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + require.NoError(t, err) + require.NotNil(t, req) + require.NotNil(t, reqBody) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://gitea.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body struct { + Ref string `json:"ref"` + } + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "test", body.Ref) // short ref + }) +} + +func TestForgejoPayload(t *testing.T) { + dh := defaultHandler{ + forgejo: true, + } + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.FORGEJO, + URL: "https://forgejo.example.com/", + Meta: ``, + HTTPMethod: "POST", + ContentType: webhook_model.ContentTypeJSON, + } + + // always return the full ref for consistency + t.Run("Create", func(t *testing.T) { + p := createTestPayload() + data, err := p.JSONPayload() + require.NoError(t, err) + + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventCreate, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + require.NoError(t, err) + require.NotNil(t, req) + require.NotNil(t, reqBody) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://forgejo.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body struct { + Ref string `json:"ref"` + } + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "refs/heads/test", body.Ref) // full ref + }) + + t.Run("Push", func(t *testing.T) { + p := pushTestPayload() + data, err := p.JSONPayload() + require.NoError(t, err) + + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + require.NoError(t, err) + require.NotNil(t, req) + require.NotNil(t, reqBody) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://forgejo.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body struct { + Ref string `json:"ref"` + } + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "refs/heads/test", body.Ref) // full ref + }) + + t.Run("Delete", func(t *testing.T) { + p := deleteTestPayload() + data, err := p.JSONPayload() + require.NoError(t, err) + + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventDelete, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + require.NoError(t, err) + require.NotNil(t, req) + require.NotNil(t, reqBody) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://forgejo.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body struct { + Ref string `json:"ref"` + } + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "refs/heads/test", body.Ref) // full ref + }) +} diff --git a/services/webhook/discord.go b/services/webhook/discord.go index cb756688c8..80f6cfb79b 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -304,14 +304,12 @@ func (discordHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, func parseHookPullRequestEventType(event webhook_module.HookEventType) (string, error) { switch event { - case webhook_module.HookEventPullRequestReviewApproved: return "approved", nil case webhook_module.HookEventPullRequestReviewRejected: return "rejected", nil case webhook_module.HookEventPullRequestReviewComment: return "comment", nil - default: return "", errors.New("unknown event type") } diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index 24d6cc6d99..06176e8dd3 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -217,7 +217,6 @@ func (m matrixConvertor) Push(p *api.PushPayload) (MatrixPayload, error) { if i < len(p.Commits)-1 { text += "
" } - } return m.newPayload(text, p.Commits...) diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go index cf4f2fdfd2..1366ea8e8f 100644 --- a/services/webhook/webhook.go +++ b/services/webhook/webhook.go @@ -82,19 +82,17 @@ var hookQueue *queue.WorkerPoolQueue[int64] // getPayloadBranch returns branch for hook event, if applicable. func getPayloadBranch(p api.Payloader) string { + var ref string switch pp := p.(type) { case *api.CreatePayload: - if pp.RefType == "branch" { - return pp.Ref - } + ref = pp.Ref case *api.DeletePayload: - if pp.RefType == "branch" { - return pp.Ref - } + ref = pp.Ref case *api.PushPayload: - if strings.HasPrefix(pp.Ref, git.BranchPrefix) { - return pp.Ref[len(git.BranchPrefix):] - } + ref = pp.Ref + } + if strings.HasPrefix(ref, git.BranchPrefix) { + return ref[len(git.BranchPrefix):] } return "" } diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go index 2436b5a4db..f8b66d46fc 100644 --- a/services/webhook/webhook_test.go +++ b/services/webhook/webhook_test.go @@ -4,6 +4,7 @@ package webhook import ( + "fmt" "testing" "code.gitea.io/gitea/models/db" @@ -41,38 +42,58 @@ func TestPrepareWebhooks(t *testing.T) { } } +func eventType(p api.Payloader) webhook_module.HookEventType { + switch p.(type) { + case *api.CreatePayload: + return webhook_module.HookEventCreate + case *api.DeletePayload: + return webhook_module.HookEventDelete + case *api.PushPayload: + return webhook_module.HookEventPush + } + panic(fmt.Sprintf("no event type for payload %T", p)) +} + func TestPrepareWebhooksBranchFilterMatch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) - activateWebhook(t, 4) + // branch_filter: {master,feature*} + w := unittest.AssertExistsAndLoadBean(t, &webhook_model.Webhook{ID: 4}) + activateWebhook(t, w.ID) - hookTasks := []*webhook_model.HookTask{ - {HookID: 4, EventType: webhook_module.HookEventPush}, - } - for _, hookTask := range hookTasks { - unittest.AssertNotExistsBean(t, hookTask) - } - // this test also ensures that * doesn't handle / in any special way (like shell would) - assert.NoError(t, PrepareWebhooks(db.DefaultContext, EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791", Commits: []*api.PayloadCommit{{}}})) - for _, hookTask := range hookTasks { - unittest.AssertExistsAndLoadBean(t, hookTask) + for _, p := range []api.Payloader{ + &api.PushPayload{Ref: "refs/heads/feature/7791"}, + &api.CreatePayload{Ref: "refs/heads/feature/7791"}, // branch creation + &api.DeletePayload{Ref: "refs/heads/feature/7791"}, // branch deletion + } { + t.Run(fmt.Sprintf("%T", p), func(t *testing.T) { + db.DeleteBeans(db.DefaultContext, webhook_model.HookTask{HookID: w.ID}) + typ := eventType(p) + assert.NoError(t, PrepareWebhook(db.DefaultContext, w, typ, p)) + unittest.AssertExistsAndLoadBean(t, &webhook_model.HookTask{ + HookID: w.ID, + EventType: typ, + }) + }) } } func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) - hookTasks := []*webhook_model.HookTask{ - {HookID: 4, EventType: webhook_module.HookEventPush}, - } - for _, hookTask := range hookTasks { - unittest.AssertNotExistsBean(t, hookTask) - } - assert.NoError(t, PrepareWebhooks(db.DefaultContext, EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"})) + // branch_filter: {master,feature*} + w := unittest.AssertExistsAndLoadBean(t, &webhook_model.Webhook{ID: 4}) + activateWebhook(t, w.ID) - for _, hookTask := range hookTasks { - unittest.AssertNotExistsBean(t, hookTask) + for _, p := range []api.Payloader{ + &api.PushPayload{Ref: "refs/heads/fix_weird_bug"}, + &api.CreatePayload{Ref: "refs/heads/fix_weird_bug"}, // branch creation + &api.DeletePayload{Ref: "refs/heads/fix_weird_bug"}, // branch deletion + } { + t.Run(fmt.Sprintf("%T", p), func(t *testing.T) { + db.DeleteBeans(db.DefaultContext, webhook_model.HookTask{HookID: w.ID}) + assert.NoError(t, PrepareWebhook(db.DefaultContext, w, eventType(p), p)) + unittest.AssertNotExistsBean(t, &webhook_model.HookTask{HookID: w.ID}) + }) } } diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index bfd2ee6670..9b89b8335f 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -76,7 +76,8 @@ {{ctx.Locale.Tr "admin.dashboard.system_status"}} {{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}} -
+
+
{{template "admin/system_status" .}}
diff --git a/templates/admin/layout_head.tmpl b/templates/admin/layout_head.tmpl index 8ba47f2f14..7cc6624d50 100644 --- a/templates/admin/layout_head.tmpl +++ b/templates/admin/layout_head.tmpl @@ -1,6 +1,6 @@ {{template "base/head" .ctxData}} diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl index 9311a46e38..c9f80259e2 100644 --- a/templates/org/team/sidebar.tmpl +++ b/templates/org/team/sidebar.tmpl @@ -22,7 +22,7 @@ {{if .Team.Description}} {{.Team.Description}} {{else}} - {{ctx.Locale.Tr "org.teams.no_desc"}} + {{ctx.Locale.Tr "org.teams.no_desc"}} {{end}} {{if eq .Team.LowerName "owners"}} diff --git a/templates/package/shared/cleanup_rules/list.tmpl b/templates/package/shared/cleanup_rules/list.tmpl index 6505c8f9a5..ba1683b932 100644 --- a/templates/package/shared/cleanup_rules/list.tmpl +++ b/templates/package/shared/cleanup_rules/list.tmpl @@ -16,26 +16,26 @@ {{.Type.Name}}
- {{if .Enabled}}{{ctx.Locale.Tr "enabled"}}{{else}}{{ctx.Locale.Tr "disabled"}}{{end}} +

{{if .Enabled}}{{ctx.Locale.Tr "enabled"}}{{else}}{{ctx.Locale.Tr "disabled"}}{{end}}

{{if .KeepCount}}
- {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count"}}: {{if eq .KeepCount 1}}{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count.1"}}{{else}}{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count.n" .KeepCount}}{{end}} +

{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count"}}:

{{if eq .KeepCount 1}}{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count.1"}}{{else}}{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.count.n" .KeepCount}}{{end}}
{{end}} {{if .KeepPattern}}
- {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.pattern"}}: {{StringUtils.EllipsisString .KeepPattern 100}} +

{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.keep.pattern"}}:

{{StringUtils.EllipsisString .KeepPattern 100}}
{{end}} {{if .RemoveDays}}
- {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.remove.days"}}: {{ctx.Locale.Tr "tool.days" .RemoveDays}} +

{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.remove.days"}}:

{{ctx.Locale.Tr "tool.days" .RemoveDays}}
{{end}} {{if .RemovePattern}}
- {{ctx.Locale.Tr "packages.owner.settings.cleanuprules.remove.pattern"}}: {{StringUtils.EllipsisString .RemovePattern 100}} +

{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.remove.pattern"}}:

{{StringUtils.EllipsisString .RemovePattern 100}}
{{end}} diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index 0908f5648c..b892cff996 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -41,9 +41,9 @@
{{range .Projects}}
  • -

    +

    {{svg .IconName 16}} - {{.Title}} + {{.Title}}

    diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index f9b85360e0..3e000660e2 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -1,8 +1,8 @@ {{$canWriteProject := and .CanWriteProjects (or (not .Repository) (not .Repository.IsArchived))}} -
    -
    -

    {{.Project.Title}}

    +
    +
    +

    {{.Project.Title}}

    {{if $canWriteProject}}