diff --git a/CHANGELOG.md b/CHANGELOG.md index d22f02e069f1..c08834645cb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,34 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log; to see the highlights of what has been added to each release, please refer to the [blog](https://blog.gitea.io). +## [1.20.1](https://github.com/go-gitea/gitea/releases/tag/1.20.1) - 2023-07-22 + +* SECURITY + * Disallow dangerous URL schemes (#25960) (#25964) +* ENHANCEMENTS + * Show the mismatched ROOT_URL warning on the sign-in page if OAuth2 is enabled (#25947) (#25972) + * Make pending commit status yellow again (#25935) (#25968) +* BUGFIXES + * Fix version in rpm repodata/primary.xml.gz (#26009) (#26048) + * Fix env config parsing for "GITEA____APP_NAME" (#26001) (#26013) + * ParseScope with owner/repo always sets owner to zero (#25987) (#25989) + * Fix SSPI auth panic (#25955) (#25969) + * Avoid creating directories when loading config (#25944) (#25957) + * Make environment-to-ini work with INSTALL_LOCK=true (#25926) (#25937) + * Ignore `runs-on` with expressions when warning no matched runners (#25917) (#25933) + * Avoid opening/closing PRs which are already merged (#25883) (#25903) +* DOCS + * RPM Registry: Show zypper commands for SUSE based distros as well (#25981) (#26020) + * Correctly refer to dev tags as nightly in the docker docs (#26004) (#26019) + * Update path related documents (#25417) (#25982) +* MISC + * Adding remaining enum for migration repo model type. (#26021) (#26034) + * Fix the route for pull-request's authors (#26016) (#26018) + * Fix commit status color on dashboard repolist (#25993) (#25998) + * Avoid hard-coding height in language dropdown menu (#25986) (#25997) + * Add shutting down notice (#25920) (#25922) + * Fix incorrect milestone count when provide a keyword (#25880) (#25904) + ## [1.20.0](https://github.com/go-gitea/gitea/releases/tag/v1.20.0) - 2023-07-16 * BREAKING diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 384a57cf47f4..3a0da33a0a38 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -409,6 +409,11 @@ "path": "github.com/go-chi/cors/LICENSE", "licenseText": "Copyright (c) 2014 Olivier Poitrey \u003crs@dailymotion.com\u003e\nCopyright (c) 2016-Present https://github.com/go-chi authors\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" }, + { + "name": "github.com/go-co-op/gocron", + "path": "github.com/go-co-op/gocron/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2014, 辣椒面\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + }, { "name": "github.com/go-enry/go-enry/v2", "path": "github.com/go-enry/go-enry/v2/LICENSE", @@ -484,11 +489,6 @@ "path": "github.com/gogs/chardet/LICENSE", "licenseText": "Copyright (c) 2012 chardet Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\nPartial of the Software is derived from ICU project. See icu-license.html for\nlicense of the derivative portions.\n" }, - { - "name": "github.com/gogs/cron", - "path": "github.com/gogs/cron/LICENSE", - "licenseText": "Copyright (C) 2012 Rob Figueiredo\nAll Rights Reserved.\n\nMIT LICENSE\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - }, { "name": "github.com/gogs/go-gogs-client", "path": "github.com/gogs/go-gogs-client/LICENSE", @@ -909,6 +909,11 @@ "path": "github.com/robfig/cron/LICENSE", "licenseText": "Copyright (C) 2012 Rob Figueiredo\nAll Rights Reserved.\n\nMIT LICENSE\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" }, + { + "name": "github.com/robfig/cron/v3", + "path": "github.com/robfig/cron/v3/LICENSE", + "licenseText": "Copyright (C) 2012 Rob Figueiredo\nAll Rights Reserved.\n\nMIT LICENSE\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" + }, { "name": "github.com/rs/xid", "path": "github.com/rs/xid/LICENSE", diff --git a/cmd/admin.go b/cmd/admin.go index add3d67d74cc..745427ca2425 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -5,6 +5,7 @@ package cmd import ( + "context" "errors" "fmt" "net/url" @@ -297,10 +298,12 @@ var ( &cli.BoolFlag{ Name: "force-smtps", Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.", + Value: true, }, &cli.BoolFlag{ Name: "skip-verify", Usage: "Skip TLS verify.", + Value: true, }, &cli.StringFlag{ Name: "helo-hostname", @@ -310,6 +313,7 @@ var ( &cli.BoolFlag{ Name: "disable-helo", Usage: "Disable SMTP helo.", + Value: true, }, &cli.StringFlag{ Name: "allowed-domains", @@ -319,10 +323,12 @@ var ( &cli.BoolFlag{ Name: "skip-local-2fa", Usage: "Skip 2FA to log on.", + Value: true, }, &cli.BoolFlag{ Name: "active", Usage: "This Authentication Source is Activated.", + Value: true, }, } @@ -373,7 +379,7 @@ func runRepoSyncReleases(_ *cli.Context) error { continue } - oldnum, err := getReleaseCount(repo.ID) + oldnum, err := getReleaseCount(ctx, repo.ID) if err != nil { log.Warn(" GetReleaseCountByRepoID: %v", err) } @@ -385,7 +391,7 @@ func runRepoSyncReleases(_ *cli.Context) error { continue } - count, err = getReleaseCount(repo.ID) + count, err = getReleaseCount(ctx, repo.ID) if err != nil { log.Warn(" GetReleaseCountByRepoID: %v", err) gitRepo.Close() @@ -401,9 +407,9 @@ func runRepoSyncReleases(_ *cli.Context) error { return nil } -func getReleaseCount(id int64) (int64, error) { +func getReleaseCount(ctx context.Context, id int64) (int64, error) { return repo_model.GetReleaseCountByRepoID( - db.DefaultContext, + ctx, id, repo_model.FindReleasesOptions{ IncludeTags: true, diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go index 84c962561e1a..7d34fc9ac21b 100644 --- a/cmd/manager_logging.go +++ b/cmd/manager_logging.go @@ -117,6 +117,7 @@ var ( Name: "rotate", Aliases: []string{"r"}, Usage: "Rotate logs", + Value: true, }, &cli.Int64Flag{ Name: "max-size", @@ -127,6 +128,7 @@ var ( Name: "daily", Aliases: []string{"d"}, Usage: "Rotate logs daily", + Value: true, }, &cli.IntFlag{ Name: "max-days", @@ -137,6 +139,7 @@ var ( Name: "compress", Aliases: []string{"z"}, Usage: "Compress rotated logs", + Value: true, }, &cli.IntFlag{ Name: "compression-level", diff --git a/docs/content/doc/usage/actions/act-runner.en-us.md b/docs/content/doc/usage/actions/act-runner.en-us.md index ddab43530e2e..1f4475508f9c 100644 --- a/docs/content/doc/usage/actions/act-runner.en-us.md +++ b/docs/content/doc/usage/actions/act-runner.en-us.md @@ -110,9 +110,9 @@ Note that the repository may still use instance-level or organization-level runn The level of the runner determines where to obtain the registration token. -- Instance level: The admin settings page, like `/admin/runners`. -- Organization level: The organization settings page, like `//settings/runners`. -- Repository level: The repository settings page, like `///settings/runners`. +- Instance level: The admin settings page, like `/admin/actions/runners`. +- Organization level: The organization settings page, like `//settings/actions/runners`. +- Repository level: The repository settings page, like `///settings/actions/runners`. If you cannot see the settings page, please make sure that you have the right permissions and that Actions have been enabled. diff --git a/docs/content/doc/usage/actions/act-runner.zh-cn.md b/docs/content/doc/usage/actions/act-runner.zh-cn.md index fb202e307821..cc5728290073 100644 --- a/docs/content/doc/usage/actions/act-runner.zh-cn.md +++ b/docs/content/doc/usage/actions/act-runner.zh-cn.md @@ -109,9 +109,9 @@ docker run -v $(pwd)/config.yaml:/config.yaml -e CONFIG_FILE=/config.yaml ... Runner级别决定了从哪里获取注册令牌。 -- 实例级别:管理员设置页面,例如 `/admin/runners`。 -- 组织级别:组织设置页面,例如 `//settings/runners`。 -- 存储库级别:存储库设置页面,例如 `///settings/runners`。 +- 实例级别:管理员设置页面,例如 `/admin/actions/runners`。 +- 组织级别:组织设置页面,例如 `//settings/actions/runners`。 +- 存储库级别:存储库设置页面,例如 `///settings/actions/runners`。 如果您无法看到设置页面,请确保您具有正确的权限并且已启用 Actions。 diff --git a/docs/content/doc/usage/actions/quickstart.en-us.md b/docs/content/doc/usage/actions/quickstart.en-us.md index 132d11f13f58..829f1a62c0b1 100644 --- a/docs/content/doc/usage/actions/quickstart.en-us.md +++ b/docs/content/doc/usage/actions/quickstart.en-us.md @@ -66,7 +66,11 @@ If you are unsure which address to use, the LAN address is usually the right cho `token` is used for authentication and identification, such as `P2U1U0oB4XaRCi8azcngmPCLbRpUGapalhmddh23`. It is one-time use only and cannot be used to register multiple runners. -You can obtain tokens from `/admin/runners`. +You can obtain different levels of 'tokens' from the following places to create the corresponding level of' runners': + +- Instance level: The admin settings page, like `/admin/actions/runners`. +- Organization level: The organization settings page, like `//settings/actions/runners`. +- Repository level: The repository settings page, like `///settings/actions/runners`. ![register runner](/images/usage/actions/register-runner.png) diff --git a/docs/content/doc/usage/actions/quickstart.zh-cn.md b/docs/content/doc/usage/actions/quickstart.zh-cn.md index 7a06b6edbd63..1893300b618f 100644 --- a/docs/content/doc/usage/actions/quickstart.zh-cn.md +++ b/docs/content/doc/usage/actions/quickstart.zh-cn.md @@ -66,7 +66,11 @@ Runner和Job容器(由Runner启动以执行Job)将连接到此地址。 `token` 用于身份验证和标识,例如 `P2U1U0oB4XaRCi8azcngmPCLbRpUGapalhmddh23`。 它只能使用一次,并且不能用于注册多个Runner。 -您可以从 `/admin/runners` 获取令牌。 +您可以从以下位置获取不同级别的`token`,从而创建出相应级别的`runner` + +- 实例级别:管理员设置页面,例如 `/admin/actions/runners`。 +- 组织级别:组织设置页面,例如 `//settings/actions/runners`。 +- 存储库级别:存储库设置页面,例如 `///settings/actions/runners`。 ![register runner](/images/usage/actions/register-runner.png) diff --git a/docs/content/doc/usage/secrets.en-us.md b/docs/content/doc/usage/secrets.en-us.md index c193a54a6c09..c82628f50cf8 100644 --- a/docs/content/doc/usage/secrets.en-us.md +++ b/docs/content/doc/usage/secrets.en-us.md @@ -18,7 +18,7 @@ menu: # Secrets Secrets allow you to store sensitive information in your user, organization or repository. -Secrets are available on Gitea 1.19+. +Secrets are available on Gitea 1.19+ and are only visible in 1.20+ when ACTIONS are enabled. # Naming your secrets diff --git a/go.mod b/go.mod index 29a17b16abf5..9038bdd689f2 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 github.com/go-chi/chi/v5 v5.0.8 github.com/go-chi/cors v1.2.1 + github.com/go-co-op/gocron v1.30.1 github.com/go-enry/go-enry/v2 v2.8.4 github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e github.com/go-git/go-billy/v5 v5.4.1 @@ -52,7 +53,6 @@ require ( github.com/go-webauthn/webauthn v0.8.6 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f - github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 github.com/golang-jwt/jwt/v5 v5.0.0 github.com/google/go-github/v53 v53.2.0 @@ -253,6 +253,7 @@ require ( github.com/rhysd/actionlint v1.6.25 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron v1.2.0 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 9c6250c1016c..976c0ead3835 100644 --- a/go.sum +++ b/go.sum @@ -371,6 +371,8 @@ github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= +github.com/go-co-op/gocron v1.30.1 h1:tjWUvJl5KrcwpkEkSXFSQFr4F9h5SfV/m4+RX0cV2fs= +github.com/go-co-op/gocron v1.30.1/go.mod h1:39f6KNSGVOU1LO/ZOoZfcSxwlsJDQOKSu8erN0SH48Y= github.com/go-enry/go-enry/v2 v2.8.4 h1:QrY3hx/RiqCJJRbdU0MOcjfTM1a586J0WSooqdlJIhs= github.com/go-enry/go-enry/v2 v2.8.4/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8= github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo= @@ -496,8 +498,6 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= -github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQdcMdzjbqqXMEnHfq0Or6p8= -github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= @@ -787,6 +787,7 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -1038,10 +1039,14 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -1250,6 +1255,7 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= diff --git a/models/actions/run.go b/models/actions/run.go index 7b62ff884f4c..ab6e319b1cc8 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -34,7 +34,7 @@ type ActionRun struct { Index int64 `xorm:"index unique(repo_index)"` // a unique number for each run of a repository TriggerUserID int64 `xorm:"index"` TriggerUser *user_model.User `xorm:"-"` - Ref string + Ref string `xorm:"index"` // the commit/tag/… that caused the run CommitSHA string IsForkPullRequest bool // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow. NeedApproval bool // may need approval if it's a fork pull request @@ -164,6 +164,73 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err return err } +// CancelRunningJobs cancels all running and waiting jobs associated with a specific workflow. +func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string) error { + // Find all runs in the specified repository, reference, and workflow with statuses 'Running' or 'Waiting'. + runs, total, err := FindRuns(ctx, FindRunOptions{ + RepoID: repoID, + Ref: ref, + WorkflowID: workflowID, + Status: []Status{StatusRunning, StatusWaiting}, + }) + if err != nil { + return err + } + + // If there are no runs found, there's no need to proceed with cancellation, so return nil. + if total == 0 { + return nil + } + + // Iterate over each found run and cancel its associated jobs. + for _, run := range runs { + // Find all jobs associated with the current run. + jobs, _, err := FindRunJobs(ctx, FindRunJobOptions{ + RunID: run.ID, + }) + if err != nil { + return err + } + + // Iterate over each job and attempt to cancel it. + for _, job := range jobs { + // Skip jobs that are already in a terminal state (completed, cancelled, etc.). + status := job.Status + if status.IsDone() { + continue + } + + // If the job has no associated task (probably an error), set its status to 'Cancelled' and stop it. + if job.TaskID == 0 { + job.Status = StatusCancelled + job.Stopped = timeutil.TimeStampNow() + + // Update the job's status and stopped time in the database. + n, err := UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped") + if err != nil { + return err + } + + // If the update affected 0 rows, it means the job has changed in the meantime, so we need to try again. + if n == 0 { + return fmt.Errorf("job has changed, try again") + } + + // Continue with the next job. + continue + } + + // If the job has an associated task, try to stop the task, effectively cancelling the job. + if err := StopTask(ctx, job.TaskID, StatusCancelled); err != nil { + return err + } + } + } + + // Return nil to indicate successful cancellation of all running and waiting jobs. + return nil +} + // InsertRun inserts a run func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error { ctx, commiter, err := db.TxContext(ctx) @@ -195,6 +262,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork } runJobs := make([]*ActionRunJob, 0, len(jobs)) + var hasWaiting bool for _, v := range jobs { id, job := v.Job() needs := job.Needs() @@ -205,6 +273,8 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork status := StatusWaiting if len(needs) > 0 || run.NeedApproval { status = StatusBlocked + } else { + hasWaiting = true } job.Name, _ = util.SplitStringAtByteN(job.Name, 255) runJobs = append(runJobs, &ActionRunJob{ @@ -225,6 +295,13 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork return err } + // if there is a job in the waiting status, increase tasks version. + if hasWaiting { + if err := IncreaseTaskVersion(ctx, run.OwnerID, run.RepoID); err != nil { + return err + } + } + return commiter.Commit() } diff --git a/models/actions/run_job.go b/models/actions/run_job.go index 0002e507704d..c7620cd8bca2 100644 --- a/models/actions/run_job.go +++ b/models/actions/run_job.go @@ -111,6 +111,13 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col return affected, nil } + if affected != 0 && util.SliceContains(cols, "status") && job.Status.IsWaiting() { + // if the status of job changes to waiting again, increase tasks version. + if err := IncreaseTaskVersion(ctx, job.OwnerID, job.RepoID); err != nil { + return affected, err + } + } + if job.RunID == 0 { var err error if job, err = GetRunJobByID(ctx, job.ID); err != nil { diff --git a/models/actions/run_list.go b/models/actions/run_list.go index 29ab193d57f3..db36f6df981d 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -66,12 +66,13 @@ func (runs RunList) LoadRepos() error { type FindRunOptions struct { db.ListOptions - RepoID int64 - OwnerID int64 - WorkflowFileName string - TriggerUserID int64 - Approved bool // not util.OptionalBool, it works only when it's true - Status Status + RepoID int64 + OwnerID int64 + WorkflowID string + Ref string // the commit/tag/… that caused this workflow + TriggerUserID int64 + Approved bool // not util.OptionalBool, it works only when it's true + Status []Status } func (opts FindRunOptions) toConds() builder.Cond { @@ -82,8 +83,8 @@ func (opts FindRunOptions) toConds() builder.Cond { if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) } - if opts.WorkflowFileName != "" { - cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowFileName}) + if opts.WorkflowID != "" { + cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowID}) } if opts.TriggerUserID > 0 { cond = cond.And(builder.Eq{"trigger_user_id": opts.TriggerUserID}) @@ -91,8 +92,11 @@ func (opts FindRunOptions) toConds() builder.Cond { if opts.Approved { cond = cond.And(builder.Gt{"approved_by": 0}) } - if opts.Status > StatusUnknown { - cond = cond.And(builder.Eq{"status": opts.Status}) + if len(opts.Status) > 0 { + cond = cond.And(builder.In("status", opts.Status)) + } + if opts.Ref != "" { + cond = cond.And(builder.Eq{"ref": opts.Ref}) } return cond } diff --git a/models/actions/task.go b/models/actions/task.go index 55044ec82d10..9cc0fd0df83d 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -215,12 +215,11 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro } func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) { - dbCtx, commiter, err := db.TxContext(ctx) + ctx, commiter, err := db.TxContext(ctx) if err != nil { return nil, false, err } defer commiter.Close() - ctx = dbCtx.WithContext(ctx) e := db.GetEngine(ctx) diff --git a/models/actions/tasks_version.go b/models/actions/tasks_version.go new file mode 100644 index 000000000000..5c0a86538d57 --- /dev/null +++ b/models/actions/tasks_version.go @@ -0,0 +1,105 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/timeutil" +) + +// ActionTasksVersion +// If both ownerID and repoID is zero, its scope is global. +// If ownerID is not zero and repoID is zero, its scope is org (there is no user-level runner currrently). +// If ownerID is zero and repoID is not zero, its scope is repo. +type ActionTasksVersion struct { + ID int64 `xorm:"pk autoincr"` + OwnerID int64 `xorm:"UNIQUE(owner_repo)"` + RepoID int64 `xorm:"INDEX UNIQUE(owner_repo)"` + Version int64 + CreatedUnix timeutil.TimeStamp `xorm:"created"` + UpdatedUnix timeutil.TimeStamp `xorm:"updated"` +} + +func init() { + db.RegisterModel(new(ActionTasksVersion)) +} + +func GetTasksVersionByScope(ctx context.Context, ownerID, repoID int64) (int64, error) { + var tasksVersion ActionTasksVersion + has, err := db.GetEngine(ctx).Where("owner_id = ? AND repo_id = ?", ownerID, repoID).Get(&tasksVersion) + if err != nil { + return 0, err + } else if !has { + return 0, nil + } + return tasksVersion.Version, err +} + +func insertTasksVersion(ctx context.Context, ownerID, repoID int64) (*ActionTasksVersion, error) { + tasksVersion := &ActionTasksVersion{ + OwnerID: ownerID, + RepoID: repoID, + Version: 1, + } + if _, err := db.GetEngine(ctx).Insert(tasksVersion); err != nil { + return nil, err + } + return tasksVersion, nil +} + +func increaseTasksVersionByScope(ctx context.Context, ownerID, repoID int64) error { + result, err := db.GetEngine(ctx).Exec("UPDATE action_tasks_version SET version = version + 1 WHERE owner_id = ? AND repo_id = ?", ownerID, repoID) + if err != nil { + return err + } + affected, err := result.RowsAffected() + if err != nil { + return err + } + + if affected == 0 { + // if update sql does not affect any rows, the database may be broken, + // so re-insert the row of version data here. + if _, err := insertTasksVersion(ctx, ownerID, repoID); err != nil { + return err + } + } + + return nil +} + +func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error { + ctx, commiter, err := db.TxContext(ctx) + if err != nil { + return err + } + defer commiter.Close() + + // 1. increase global + if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil { + log.Error("IncreaseTasksVersionByScope(Global): %v", err) + return err + } + + // 2. increase owner + if ownerID > 0 { + if err := increaseTasksVersionByScope(ctx, ownerID, 0); err != nil { + log.Error("IncreaseTasksVersionByScope(Owner): %v", err) + return err + } + } + + // 3. increase repo + if repoID > 0 { + if err := increaseTasksVersionByScope(ctx, 0, repoID); err != nil { + log.Error("IncreaseTasksVersionByScope(Repo): %v", err) + return err + } + } + + return commiter.Commit() +} diff --git a/models/activities/action.go b/models/activities/action.go index 57f579372f80..7f22605d0d15 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -391,10 +391,10 @@ func (a *Action) GetIssueInfos() []string { } // GetIssueTitle returns the title of first issue associated -// with the action. +// with the action. This function will be invoked in template so keep db.DefaultContext here func (a *Action) GetIssueTitle() string { index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) - issue, err := issues_model.GetIssueByIndex(a.RepoID, index) + issue, err := issues_model.GetIssueByIndex(db.DefaultContext, a.RepoID, index) if err != nil { log.Error("GetIssueByIndex: %v", err) return "500 when get issue" @@ -404,9 +404,9 @@ func (a *Action) GetIssueTitle() string { // GetIssueContent returns the content of first issue associated with // this action. -func (a *Action) GetIssueContent() string { +func (a *Action) GetIssueContent(ctx context.Context) string { index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) - issue, err := issues_model.GetIssueByIndex(a.RepoID, index) + issue, err := issues_model.GetIssueByIndex(ctx, a.RepoID, index) if err != nil { log.Error("GetIssueByIndex: %v", err) return "500 when get issue" diff --git a/models/activities/repo_activity.go b/models/activities/repo_activity.go index 72b6be312260..509f9caaf3e4 100644 --- a/models/activities/repo_activity.go +++ b/models/activities/repo_activity.go @@ -47,21 +47,21 @@ type ActivityStats struct { func GetActivityStats(ctx context.Context, repo *repo_model.Repository, timeFrom time.Time, releases, issues, prs, code bool) (*ActivityStats, error) { stats := &ActivityStats{Code: &git.CodeActivityStats{}} if releases { - if err := stats.FillReleases(repo.ID, timeFrom); err != nil { + if err := stats.FillReleases(ctx, repo.ID, timeFrom); err != nil { return nil, fmt.Errorf("FillReleases: %w", err) } } if prs { - if err := stats.FillPullRequests(repo.ID, timeFrom); err != nil { + if err := stats.FillPullRequests(ctx, repo.ID, timeFrom); err != nil { return nil, fmt.Errorf("FillPullRequests: %w", err) } } if issues { - if err := stats.FillIssues(repo.ID, timeFrom); err != nil { + if err := stats.FillIssues(ctx, repo.ID, timeFrom); err != nil { return nil, fmt.Errorf("FillIssues: %w", err) } } - if err := stats.FillUnresolvedIssues(repo.ID, timeFrom, issues, prs); err != nil { + if err := stats.FillUnresolvedIssues(ctx, repo.ID, timeFrom, issues, prs); err != nil { return nil, fmt.Errorf("FillUnresolvedIssues: %w", err) } if code { @@ -205,41 +205,41 @@ func (stats *ActivityStats) PublishedReleaseCount() int { } // FillPullRequests returns pull request information for activity page -func (stats *ActivityStats) FillPullRequests(repoID int64, fromTime time.Time) error { +func (stats *ActivityStats) FillPullRequests(ctx context.Context, repoID int64, fromTime time.Time) error { var err error var count int64 // Merged pull requests - sess := pullRequestsForActivityStatement(repoID, fromTime, true) + sess := pullRequestsForActivityStatement(ctx, repoID, fromTime, true) sess.OrderBy("pull_request.merged_unix DESC") stats.MergedPRs = make(issues_model.PullRequestList, 0) if err = sess.Find(&stats.MergedPRs); err != nil { return err } - if err = stats.MergedPRs.LoadAttributes(); err != nil { + if err = stats.MergedPRs.LoadAttributes(ctx); err != nil { return err } // Merged pull request authors - sess = pullRequestsForActivityStatement(repoID, fromTime, true) + sess = pullRequestsForActivityStatement(ctx, repoID, fromTime, true) if _, err = sess.Select("count(distinct issue.poster_id) as `count`").Table("pull_request").Get(&count); err != nil { return err } stats.MergedPRAuthorCount = count // Opened pull requests - sess = pullRequestsForActivityStatement(repoID, fromTime, false) + sess = pullRequestsForActivityStatement(ctx, repoID, fromTime, false) sess.OrderBy("issue.created_unix ASC") stats.OpenedPRs = make(issues_model.PullRequestList, 0) if err = sess.Find(&stats.OpenedPRs); err != nil { return err } - if err = stats.OpenedPRs.LoadAttributes(); err != nil { + if err = stats.OpenedPRs.LoadAttributes(ctx); err != nil { return err } // Opened pull request authors - sess = pullRequestsForActivityStatement(repoID, fromTime, false) + sess = pullRequestsForActivityStatement(ctx, repoID, fromTime, false) if _, err = sess.Select("count(distinct issue.poster_id) as `count`").Table("pull_request").Get(&count); err != nil { return err } @@ -248,8 +248,8 @@ func (stats *ActivityStats) FillPullRequests(repoID int64, fromTime time.Time) e return nil } -func pullRequestsForActivityStatement(repoID int64, fromTime time.Time, merged bool) *xorm.Session { - sess := db.GetEngine(db.DefaultContext).Where("pull_request.base_repo_id=?", repoID). +func pullRequestsForActivityStatement(ctx context.Context, repoID int64, fromTime time.Time, merged bool) *xorm.Session { + sess := db.GetEngine(ctx).Where("pull_request.base_repo_id=?", repoID). Join("INNER", "issue", "pull_request.issue_id = issue.id") if merged { @@ -264,12 +264,12 @@ func pullRequestsForActivityStatement(repoID int64, fromTime time.Time, merged b } // FillIssues returns issue information for activity page -func (stats *ActivityStats) FillIssues(repoID int64, fromTime time.Time) error { +func (stats *ActivityStats) FillIssues(ctx context.Context, repoID int64, fromTime time.Time) error { var err error var count int64 // Closed issues - sess := issuesForActivityStatement(repoID, fromTime, true, false) + sess := issuesForActivityStatement(ctx, repoID, fromTime, true, false) sess.OrderBy("issue.closed_unix DESC") stats.ClosedIssues = make(issues_model.IssueList, 0) if err = sess.Find(&stats.ClosedIssues); err != nil { @@ -277,14 +277,14 @@ func (stats *ActivityStats) FillIssues(repoID int64, fromTime time.Time) error { } // Closed issue authors - sess = issuesForActivityStatement(repoID, fromTime, true, false) + sess = issuesForActivityStatement(ctx, repoID, fromTime, true, false) if _, err = sess.Select("count(distinct issue.poster_id) as `count`").Table("issue").Get(&count); err != nil { return err } stats.ClosedIssueAuthorCount = count // New issues - sess = issuesForActivityStatement(repoID, fromTime, false, false) + sess = issuesForActivityStatement(ctx, repoID, fromTime, false, false) sess.OrderBy("issue.created_unix ASC") stats.OpenedIssues = make(issues_model.IssueList, 0) if err = sess.Find(&stats.OpenedIssues); err != nil { @@ -292,7 +292,7 @@ func (stats *ActivityStats) FillIssues(repoID int64, fromTime time.Time) error { } // Opened issue authors - sess = issuesForActivityStatement(repoID, fromTime, false, false) + sess = issuesForActivityStatement(ctx, repoID, fromTime, false, false) if _, err = sess.Select("count(distinct issue.poster_id) as `count`").Table("issue").Get(&count); err != nil { return err } @@ -302,12 +302,12 @@ func (stats *ActivityStats) FillIssues(repoID int64, fromTime time.Time) error { } // FillUnresolvedIssues returns unresolved issue and pull request information for activity page -func (stats *ActivityStats) FillUnresolvedIssues(repoID int64, fromTime time.Time, issues, prs bool) error { +func (stats *ActivityStats) FillUnresolvedIssues(ctx context.Context, repoID int64, fromTime time.Time, issues, prs bool) error { // Check if we need to select anything if !issues && !prs { return nil } - sess := issuesForActivityStatement(repoID, fromTime, false, true) + sess := issuesForActivityStatement(ctx, repoID, fromTime, false, true) if !issues || !prs { sess.And("issue.is_pull = ?", prs) } @@ -316,8 +316,8 @@ func (stats *ActivityStats) FillUnresolvedIssues(repoID int64, fromTime time.Tim return sess.Find(&stats.UnresolvedIssues) } -func issuesForActivityStatement(repoID int64, fromTime time.Time, closed, unresolved bool) *xorm.Session { - sess := db.GetEngine(db.DefaultContext).Where("issue.repo_id = ?", repoID). +func issuesForActivityStatement(ctx context.Context, repoID int64, fromTime time.Time, closed, unresolved bool) *xorm.Session { + sess := db.GetEngine(ctx).Where("issue.repo_id = ?", repoID). And("issue.is_closed = ?", closed) if !unresolved { @@ -336,12 +336,12 @@ func issuesForActivityStatement(repoID int64, fromTime time.Time, closed, unreso } // FillReleases returns release information for activity page -func (stats *ActivityStats) FillReleases(repoID int64, fromTime time.Time) error { +func (stats *ActivityStats) FillReleases(ctx context.Context, repoID int64, fromTime time.Time) error { var err error var count int64 // Published releases list - sess := releasesForActivityStatement(repoID, fromTime) + sess := releasesForActivityStatement(ctx, repoID, fromTime) sess.OrderBy("release.created_unix DESC") stats.PublishedReleases = make([]*repo_model.Release, 0) if err = sess.Find(&stats.PublishedReleases); err != nil { @@ -349,7 +349,7 @@ func (stats *ActivityStats) FillReleases(repoID int64, fromTime time.Time) error } // Published releases authors - sess = releasesForActivityStatement(repoID, fromTime) + sess = releasesForActivityStatement(ctx, repoID, fromTime) if _, err = sess.Select("count(distinct release.publisher_id) as `count`").Table("release").Get(&count); err != nil { return err } @@ -358,8 +358,8 @@ func (stats *ActivityStats) FillReleases(repoID int64, fromTime time.Time) error return nil } -func releasesForActivityStatement(repoID int64, fromTime time.Time) *xorm.Session { - return db.GetEngine(db.DefaultContext).Where("release.repo_id = ?", repoID). +func releasesForActivityStatement(ctx context.Context, repoID int64, fromTime time.Time) *xorm.Session { + return db.GetEngine(ctx).Where("release.repo_id = ?", repoID). And("release.is_draft = ?", false). And("release.created_unix >= ?", fromTime.Unix()) } diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go index e9c8406c3a51..6f1d350eb42f 100644 --- a/models/issues/comment_list.go +++ b/models/issues/comment_list.go @@ -465,8 +465,9 @@ func (comments CommentList) loadReviews(ctx context.Context) error { return nil } -// loadAttributes loads all attributes -func (comments CommentList) loadAttributes(ctx context.Context) (err error) { +// LoadAttributes loads attributes of the comments, except for attachments and +// comments +func (comments CommentList) LoadAttributes(ctx context.Context) (err error) { if err = comments.LoadPosters(ctx); err != nil { return err } @@ -501,9 +502,3 @@ func (comments CommentList) loadAttributes(ctx context.Context) (err error) { return comments.loadDependentIssues(ctx) } - -// LoadAttributes loads attributes of the comments, except for attachments and -// comments -func (comments CommentList) LoadAttributes() error { - return comments.loadAttributes(db.DefaultContext) -} diff --git a/models/issues/issue.go b/models/issues/issue.go index c1a802c792a0..9d60d011ed7a 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -354,7 +354,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) { return err } - if err = issue.Comments.loadAttributes(ctx); err != nil { + if err = issue.Comments.LoadAttributes(ctx); err != nil { return err } if issue.IsTimetrackerEnabled(ctx) { @@ -502,7 +502,7 @@ func (issue *Issue) GetLastEventLabelFake() string { } // GetIssueByIndex returns raw issue without loading attributes by index in a repository. -func GetIssueByIndex(repoID, index int64) (*Issue, error) { +func GetIssueByIndex(ctx context.Context, repoID, index int64) (*Issue, error) { if index < 1 { return nil, ErrIssueNotExist{} } @@ -510,7 +510,7 @@ func GetIssueByIndex(repoID, index int64) (*Issue, error) { RepoID: repoID, Index: index, } - has, err := db.GetEngine(db.DefaultContext).Get(issue) + has, err := db.GetEngine(ctx).Get(issue) if err != nil { return nil, err } else if !has { @@ -520,12 +520,12 @@ func GetIssueByIndex(repoID, index int64) (*Issue, error) { } // GetIssueWithAttrsByIndex returns issue by index in a repository. -func GetIssueWithAttrsByIndex(repoID, index int64) (*Issue, error) { - issue, err := GetIssueByIndex(repoID, index) +func GetIssueWithAttrsByIndex(ctx context.Context, repoID, index int64) (*Issue, error) { + issue, err := GetIssueByIndex(ctx, repoID, index) if err != nil { return nil, err } - return issue, issue.LoadAttributes(db.DefaultContext) + return issue, issue.LoadAttributes(ctx) } // GetIssueByID returns an issue by given ID. @@ -846,7 +846,7 @@ func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) ([]*Issue, return nil, err } - err = IssueList(issues).LoadAttributes() + err = IssueList(issues).LoadAttributes(ctx) if err != nil { return nil, err } diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index 9cc41ec6ab37..a932ac255436 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -526,7 +526,7 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) { } // loadAttributes loads all attributes, expect for attachments and comments -func (issues IssueList) loadAttributes(ctx context.Context) error { +func (issues IssueList) LoadAttributes(ctx context.Context) error { if _, err := issues.LoadRepositories(ctx); err != nil { return fmt.Errorf("issue.loadAttributes: LoadRepositories: %w", err) } @@ -562,12 +562,6 @@ func (issues IssueList) loadAttributes(ctx context.Context) error { return nil } -// LoadAttributes loads attributes of the issues, except for attachments and -// comments -func (issues IssueList) LoadAttributes() error { - return issues.loadAttributes(db.DefaultContext) -} - // LoadComments loads comments func (issues IssueList) LoadComments(ctx context.Context) error { return issues.loadComments(ctx, builder.NewCond()) diff --git a/models/issues/issue_list_test.go b/models/issues/issue_list_test.go index 696c3b765d3d..9069e1012da5 100644 --- a/models/issues/issue_list_test.go +++ b/models/issues/issue_list_test.go @@ -39,7 +39,7 @@ func TestIssueList_LoadAttributes(t *testing.T) { unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}), } - assert.NoError(t, issueList.LoadAttributes()) + assert.NoError(t, issueList.LoadAttributes(db.DefaultContext)) for _, issue := range issueList { assert.EqualValues(t, issue.RepoID, issue.Repo.ID) for _, label := range issue.Labels { diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 9fd13f09956a..6540ce02c0c5 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -440,7 +440,7 @@ func Issues(ctx context.Context, opts *IssuesOptions) ([]*Issue, error) { return nil, fmt.Errorf("unable to query Issues: %w", err) } - if err := issues.LoadAttributes(); err != nil { + if err := issues.LoadAttributes(ctx); err != nil { return nil, fmt.Errorf("unable to LoadAttributes for Issues: %w", err) } diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index c443928344d1..3b2416900b5d 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -51,16 +51,16 @@ func listPullRequestStatement(baseRepoID int64, opts *PullRequestsOptions) (*xor } // GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged -func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) { +func GetUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, error) { prs := make([]*PullRequest, 0, 2) - sess := db.GetEngine(db.DefaultContext). + sess := db.GetEngine(ctx). Join("INNER", "issue", "issue.id = pull_request.issue_id"). Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND issue.is_closed = ? AND flow = ?", repoID, branch, false, false, PullRequestFlowGithub) return prs, sess.Find(&prs) } // CanMaintainerWriteToBranch check whether user is a maintainer and could write to the branch -func CanMaintainerWriteToBranch(p access_model.Permission, branch string, user *user_model.User) bool { +func CanMaintainerWriteToBranch(ctx context.Context, p access_model.Permission, branch string, user *user_model.User) bool { if p.CanWrite(unit.TypeCode) { return true } @@ -69,18 +69,18 @@ func CanMaintainerWriteToBranch(p access_model.Permission, branch string, user * return false } - prs, err := GetUnmergedPullRequestsByHeadInfo(p.Units[0].RepoID, branch) + prs, err := GetUnmergedPullRequestsByHeadInfo(ctx, p.Units[0].RepoID, branch) if err != nil { return false } for _, pr := range prs { if pr.AllowMaintainerEdit { - err = pr.LoadBaseRepo(db.DefaultContext) + err = pr.LoadBaseRepo(ctx) if err != nil { continue } - prPerm, err := access_model.GetUserRepoPermission(db.DefaultContext, pr.BaseRepo, user) + prPerm, err := access_model.GetUserRepoPermission(ctx, pr.BaseRepo, user) if err != nil { continue } @@ -104,9 +104,9 @@ func HasUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch // GetUnmergedPullRequestsByBaseInfo returns all pull requests that are open and has not been merged // by given base information (repo and branch). -func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequest, error) { +func GetUnmergedPullRequestsByBaseInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, error) { prs := make([]*PullRequest, 0, 2) - return prs, db.GetEngine(db.DefaultContext). + return prs, db.GetEngine(ctx). Where("base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?", repoID, branch, false, false). OrderBy("issue.updated_unix DESC"). @@ -154,7 +154,7 @@ func PullRequests(baseRepoID int64, opts *PullRequestsOptions) ([]*PullRequest, // PullRequestList defines a list of pull requests type PullRequestList []*PullRequest -func (prs PullRequestList) loadAttributes(ctx context.Context) error { +func (prs PullRequestList) LoadAttributes(ctx context.Context) error { if len(prs) == 0 { return nil } @@ -199,8 +199,3 @@ func (prs PullRequestList) GetIssueIDs() []int64 { } return issueIDs } - -// LoadAttributes load all the prs attributes -func (prs PullRequestList) LoadAttributes() error { - return prs.loadAttributes(db.DefaultContext) -} diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go index 5856b5dc5863..0990a3b87067 100644 --- a/models/issues/pull_test.go +++ b/models/issues/pull_test.go @@ -148,7 +148,7 @@ func TestHasUnmergedPullRequestsByHeadInfo(t *testing.T) { func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(1, "branch2") + prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(db.DefaultContext, 1, "branch2") assert.NoError(t, err) assert.Len(t, prs, 1) for _, pr := range prs { @@ -159,7 +159,7 @@ func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) { func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - prs, err := issues_model.GetUnmergedPullRequestsByBaseInfo(1, "master") + prs, err := issues_model.GetUnmergedPullRequestsByBaseInfo(db.DefaultContext, 1, "master") assert.NoError(t, err) assert.Len(t, prs, 1) pr := prs[0] @@ -242,13 +242,13 @@ func TestPullRequestList_LoadAttributes(t *testing.T) { unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}), unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}), } - assert.NoError(t, issues_model.PullRequestList(prs).LoadAttributes()) + assert.NoError(t, issues_model.PullRequestList(prs).LoadAttributes(db.DefaultContext)) for _, pr := range prs { assert.NotNil(t, pr.Issue) assert.Equal(t, pr.IssueID, pr.Issue.ID) } - assert.NoError(t, issues_model.PullRequestList([]*issues_model.PullRequest{}).LoadAttributes()) + assert.NoError(t, issues_model.PullRequestList([]*issues_model.PullRequest{}).LoadAttributes(db.DefaultContext)) } // TODO TestAddTestPullRequestTask diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go index d117b74bc037..58c6b775f077 100644 --- a/models/issues/tracked_time.go +++ b/models/issues/tracked_time.go @@ -43,11 +43,7 @@ func (t *TrackedTime) AfterLoad() { } // LoadAttributes load Issue, User -func (t *TrackedTime) LoadAttributes() (err error) { - return t.loadAttributes(db.DefaultContext) -} - -func (t *TrackedTime) loadAttributes(ctx context.Context) (err error) { +func (t *TrackedTime) LoadAttributes(ctx context.Context) (err error) { // Load the issue if t.Issue == nil { t.Issue, err = GetIssueByID(ctx, t.IssueID) @@ -76,9 +72,9 @@ func (t *TrackedTime) loadAttributes(ctx context.Context) (err error) { } // LoadAttributes load Issue, User -func (tl TrackedTimeList) LoadAttributes() error { +func (tl TrackedTimeList) LoadAttributes(ctx context.Context) error { for _, t := range tl { - if err := t.LoadAttributes(); err != nil { + if err := t.LoadAttributes(ctx); err != nil { return err } } @@ -143,8 +139,8 @@ func GetTrackedTimes(ctx context.Context, options *FindTrackedTimesOptions) (tra } // CountTrackedTimes returns count of tracked times that fit to the given options. -func CountTrackedTimes(opts *FindTrackedTimesOptions) (int64, error) { - sess := db.GetEngine(db.DefaultContext).Where(opts.toCond()) +func CountTrackedTimes(ctx context.Context, opts *FindTrackedTimesOptions) (int64, error) { + sess := db.GetEngine(ctx).Where(opts.toCond()) if opts.RepositoryID > 0 || opts.MilestoneID > 0 { sess = sess.Join("INNER", "issue", "issue.id = tracked_time.issue_id") } @@ -157,8 +153,8 @@ func GetTrackedSeconds(ctx context.Context, opts FindTrackedTimesOptions) (track } // AddTime will add the given time (in seconds) to the issue -func AddTime(user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { - ctx, committer, err := db.TxContext(db.DefaultContext) +func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { + ctx, committer, err := db.TxContext(ctx) if err != nil { return nil, err } @@ -276,7 +272,7 @@ func DeleteTime(t *TrackedTime) error { } defer committer.Close() - if err := t.loadAttributes(ctx); err != nil { + if err := t.LoadAttributes(ctx); err != nil { return err } diff --git a/models/issues/tracked_time_test.go b/models/issues/tracked_time_test.go index 37ba1cfdc490..1d881091839b 100644 --- a/models/issues/tracked_time_test.go +++ b/models/issues/tracked_time_test.go @@ -25,7 +25,7 @@ func TestAddTime(t *testing.T) { assert.NoError(t, err) // 3661 = 1h 1min 1s - trackedTime, err := issues_model.AddTime(user3, issue1, 3661, time.Now()) + trackedTime, err := issues_model.AddTime(db.DefaultContext, user3, issue1, 3661, time.Now()) assert.NoError(t, err) assert.Equal(t, int64(3), trackedTime.UserID) assert.Equal(t, int64(1), trackedTime.IssueID) diff --git a/models/migrate.go b/models/migrate.go index 82cacd4a75b2..9705d0ad04c4 100644 --- a/models/migrate.go +++ b/models/migrate.go @@ -128,8 +128,8 @@ func InsertIssueComments(comments []*issues_model.Comment) error { } // InsertPullRequests inserted pull requests -func InsertPullRequests(prs ...*issues_model.PullRequest) error { - ctx, committer, err := db.TxContext(db.DefaultContext) +func InsertPullRequests(ctx context.Context, prs ...*issues_model.PullRequest) error { + ctx, committer, err := db.TxContext(ctx) if err != nil { return err } diff --git a/models/migrate_test.go b/models/migrate_test.go index 42102f9a7d22..74736a28492c 100644 --- a/models/migrate_test.go +++ b/models/migrate_test.go @@ -122,7 +122,7 @@ func TestMigrate_InsertPullRequests(t *testing.T) { Issue: i, } - err := InsertPullRequests(p) + err := InsertPullRequests(db.DefaultContext, p) assert.NoError(t, err) _ = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{IssueID: i.ID}) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 6599cb9cda3e..fc11d54071fa 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -515,6 +515,10 @@ var migrations = []Migration{ NewMigration("Alter Actions Artifact table", v1_21.AlterActionArtifactTable), // v266 -> v267 NewMigration("Reduce commit status", v1_21.ReduceCommitStatus), + // v267 -> v268 + NewMigration("Add action_tasks_version table", v1_21.CreateActionTasksVersionTable), + // v268 -> v269 + NewMigration("Update Action Ref", v1_21.UpdateActionsRefIndex), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_21/v267.go b/models/migrations/v1_21/v267.go new file mode 100644 index 000000000000..bc0e954bdcc9 --- /dev/null +++ b/models/migrations/v1_21/v267.go @@ -0,0 +1,23 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_21 //nolint + +import ( + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +func CreateActionTasksVersionTable(x *xorm.Engine) error { + type ActionTasksVersion struct { + ID int64 `xorm:"pk autoincr"` + OwnerID int64 `xorm:"UNIQUE(owner_repo)"` + RepoID int64 `xorm:"INDEX UNIQUE(owner_repo)"` + Version int64 + CreatedUnix timeutil.TimeStamp `xorm:"created"` + UpdatedUnix timeutil.TimeStamp `xorm:"updated"` + } + + return x.Sync(new(ActionTasksVersion)) +} diff --git a/models/migrations/v1_21/v268.go b/models/migrations/v1_21/v268.go new file mode 100644 index 000000000000..332793ff073b --- /dev/null +++ b/models/migrations/v1_21/v268.go @@ -0,0 +1,16 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_21 //nolint + +import ( + "xorm.io/xorm" +) + +// UpdateActionsRefIndex updates the index of actions ref field +func UpdateActionsRefIndex(x *xorm.Engine) error { + type ActionRun struct { + Ref string `xorm:"index"` // the commit/tag/… causing the run + } + return x.Sync(new(ActionRun)) +} diff --git a/modules/context/permission.go b/modules/context/permission.go index 09343b8b50bc..14a9801dccba 100644 --- a/modules/context/permission.go +++ b/modules/context/permission.go @@ -35,7 +35,7 @@ func RequireRepoWriter(unitType unit.Type) func(ctx *Context) { // CanEnableEditor checks if the user is allowed to write to the branch of the repo func CanEnableEditor() func(ctx *Context) { return func(ctx *Context) { - if !ctx.Repo.CanWriteToBranch(ctx.Doer, ctx.Repo.BranchName) { + if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) { ctx.NotFound("CanWriteToBranch denies permission", nil) return } diff --git a/modules/context/repo.go b/modules/context/repo.go index 2c67735c934c..f5c56cf83323 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -66,13 +66,13 @@ type Repository struct { } // CanWriteToBranch checks if the branch is writable by the user -func (r *Repository) CanWriteToBranch(user *user_model.User, branch string) bool { - return issues_model.CanMaintainerWriteToBranch(r.Permission, branch, user) +func (r *Repository) CanWriteToBranch(ctx context.Context, user *user_model.User, branch string) bool { + return issues_model.CanMaintainerWriteToBranch(ctx, r.Permission, branch, user) } // CanEnableEditor returns true if repository is editable and user has proper access level. -func (r *Repository) CanEnableEditor(user *user_model.User) bool { - return r.IsViewBranch && r.CanWriteToBranch(user, r.BranchName) && r.Repository.CanEnableEditor() && !r.Repository.IsArchived +func (r *Repository) CanEnableEditor(ctx context.Context, user *user_model.User) bool { + return r.IsViewBranch && r.CanWriteToBranch(ctx, user, r.BranchName) && r.Repository.CanEnableEditor() && !r.Repository.IsArchived } // CanCreateBranch returns true if repository is editable and user has proper access level. @@ -118,7 +118,7 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use sign, keyID, _, err := asymkey_service.SignCRUDAction(ctx, r.Repository.RepoPath(), doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName) - canCommit := r.CanEnableEditor(doer) && userCanPush + canCommit := r.CanEnableEditor(ctx, doer) && userCanPush if requireSigned { canCommit = canCommit && sign } @@ -134,7 +134,7 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use return CanCommitToBranchResults{ CanCommitToBranch: canCommit, - EditorEnabled: r.CanEnableEditor(doer), + EditorEnabled: r.CanEnableEditor(ctx, doer), UserCanPush: userCanPush, RequireSigned: requireSigned, WillSign: sign, diff --git a/modules/graceful/net_unix.go b/modules/graceful/net_unix.go index e9c128512308..f5af1e39378a 100644 --- a/modules/graceful/net_unix.go +++ b/modules/graceful/net_unix.go @@ -150,11 +150,13 @@ func CloseProvidedListeners() error { return returnableError } -// GetListener obtains a listener for the local network address. The network must be +// DefaultGetListener obtains a listener for the local network address. The network must be // a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket". It // returns an provided net.Listener for the matching network and address, or -// creates a new one using net.Listen. -func GetListener(network, address string) (net.Listener, error) { +// creates a new one using net.Listen. This function can be replaced by changing the +// GetListener variable at the top of this file, for example to listen on an onion service using +// github.com/cretz/bine +func DefaultGetListener(network, address string) (net.Listener, error) { // Add a deferral to say that we've tried to grab a listener defer GetManager().InformCleanup() switch network { diff --git a/modules/graceful/net_windows.go b/modules/graceful/net_windows.go index a2f58e224a94..15d228d6b654 100644 --- a/modules/graceful/net_windows.go +++ b/modules/graceful/net_windows.go @@ -9,9 +9,11 @@ package graceful import "net" -// GetListener obtains a listener for the local network address. -// On windows this is basically just a shim around net.Listen. -func GetListener(network, address string) (net.Listener, error) { +// DefaultGetListener obtains a listener for the local network address. +// On windows this is basically just a shim around net.Listen. This function +// can be replaced by changing the GetListener variable at the top of this file, +// for example to listen on an onion service using github.com/cretz/bine +func DefaultGetListener(network, address string) (net.Listener, error) { // Add a deferral to say that we've tried to grab a listener defer GetManager().InformCleanup() diff --git a/modules/graceful/server.go b/modules/graceful/server.go index e42d35cd49b8..bd917828bc86 100644 --- a/modules/graceful/server.go +++ b/modules/graceful/server.go @@ -33,6 +33,14 @@ var ( PerWriteWriteTimeoutKbTime = 10 * time.Second ) +// GetListener returns a listener from a GetListener function, which must have the +// signature: `func FunctioName(network, address string) (net.Listener, error)`. +// This determines the implementation of net.Listener which the server will use.` +// It is implemented in this way so that downstreams may specify the type of listener +// they want to provide Gitea on by default, such as with a hidden service or a p2p network +// No need to worry about "breaking" if there would be a refactoring for the Listeners. No compatibility-guarantee for this mechanism +var GetListener = DefaultGetListener + func init() { DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB) } diff --git a/modules/log/event_writer_file.go b/modules/log/event_writer_file.go index 4f41b96453a0..fd73d7d30a04 100644 --- a/modules/log/event_writer_file.go +++ b/modules/log/event_writer_file.go @@ -4,6 +4,8 @@ package log import ( + "io" + "code.gitea.io/gitea/modules/util/rotatingfilewriter" ) @@ -19,7 +21,7 @@ type WriterFileOption struct { type eventWriterFile struct { *EventWriterBaseImpl - fileWriter *rotatingfilewriter.RotatingFileWriter + fileWriter io.WriteCloser } var _ EventWriter = (*eventWriterFile)(nil) @@ -37,7 +39,10 @@ func NewEventWriterFile(name string, mode WriterMode) EventWriter { CompressionLevel: opt.CompressionLevel, }) if err != nil { + // if the log file can't be opened, what should it do? panic/exit? ignore logs? fallback to stderr? + // it seems that "fallback to stderr" is slightly better than others .... FallbackErrorf("unable to open log file %q: %v", opt.FileName, err) + w.fileWriter = nopCloser{Writer: LoggerToWriter(FallbackErrorf)} } w.OutputWriteCloser = w.fileWriter return w diff --git a/modules/packages/debian/metadata.go b/modules/packages/debian/metadata.go index dee524c8ff8b..bb77f7524bf6 100644 --- a/modules/packages/debian/metadata.go +++ b/modules/packages/debian/metadata.go @@ -80,7 +80,9 @@ func ParsePackage(r io.Reader) (*Package, error) { if strings.HasPrefix(hd.Name, controlTar) { var inner io.Reader - switch hd.Name[len(controlTar):] { + // https://man7.org/linux/man-pages/man5/deb-split.5.html#FORMAT + // The file names might contain a trailing slash (since dpkg 1.15.6). + switch strings.TrimSuffix(hd.Name[len(controlTar):], "/") { case "": inner = arr case ".gz": diff --git a/modules/packages/debian/metadata_test.go b/modules/packages/debian/metadata_test.go index 69fd51ea7900..26c2a6fc6880 100644 --- a/modules/packages/debian/metadata_test.go +++ b/modules/packages/debian/metadata_test.go @@ -69,57 +69,71 @@ func TestParsePackage(t *testing.T) { tw.Write([]byte("Package: gitea\nVersion: 1.0.0\nArchitecture: amd64\n")) tw.Close() - t.Run("None", func(t *testing.T) { - data := createArchive(map[string][]byte{"control.tar": buf.Bytes()}) - - p, err := ParsePackage(data) - assert.NotNil(t, p) - assert.NoError(t, err) - assert.Equal(t, "gitea", p.Name) - }) - - t.Run("gz", func(t *testing.T) { - var zbuf bytes.Buffer - zw := gzip.NewWriter(&zbuf) - zw.Write(buf.Bytes()) - zw.Close() - - data := createArchive(map[string][]byte{"control.tar.gz": zbuf.Bytes()}) - - p, err := ParsePackage(data) - assert.NotNil(t, p) - assert.NoError(t, err) - assert.Equal(t, "gitea", p.Name) - }) - - t.Run("xz", func(t *testing.T) { - var xbuf bytes.Buffer - xw, _ := xz.NewWriter(&xbuf) - xw.Write(buf.Bytes()) - xw.Close() - - data := createArchive(map[string][]byte{"control.tar.xz": xbuf.Bytes()}) - - p, err := ParsePackage(data) - assert.NotNil(t, p) - assert.NoError(t, err) - assert.Equal(t, "gitea", p.Name) - }) + cases := []struct { + Extension string + WriterFactory func(io.Writer) io.WriteCloser + }{ + { + Extension: "", + WriterFactory: func(w io.Writer) io.WriteCloser { + return nopCloser{w} + }, + }, + { + Extension: ".gz", + WriterFactory: func(w io.Writer) io.WriteCloser { + return gzip.NewWriter(w) + }, + }, + { + Extension: ".xz", + WriterFactory: func(w io.Writer) io.WriteCloser { + xw, _ := xz.NewWriter(w) + return xw + }, + }, + { + Extension: ".zst", + WriterFactory: func(w io.Writer) io.WriteCloser { + zw, _ := zstd.NewWriter(w) + return zw + }, + }, + } - t.Run("zst", func(t *testing.T) { - var zbuf bytes.Buffer - zw, _ := zstd.NewWriter(&zbuf) - zw.Write(buf.Bytes()) - zw.Close() + for _, c := range cases { + t.Run(c.Extension, func(t *testing.T) { + var cbuf bytes.Buffer + w := c.WriterFactory(&cbuf) + w.Write(buf.Bytes()) + w.Close() + + data := createArchive(map[string][]byte{"control.tar" + c.Extension: cbuf.Bytes()}) + + p, err := ParsePackage(data) + assert.NotNil(t, p) + assert.NoError(t, err) + assert.Equal(t, "gitea", p.Name) + + t.Run("TrailingSlash", func(t *testing.T) { + data := createArchive(map[string][]byte{"control.tar" + c.Extension + "/": cbuf.Bytes()}) + + p, err := ParsePackage(data) + assert.NotNil(t, p) + assert.NoError(t, err) + assert.Equal(t, "gitea", p.Name) + }) + }) + } + }) +} - data := createArchive(map[string][]byte{"control.tar.zst": zbuf.Bytes()}) +type nopCloser struct { + io.Writer +} - p, err := ParsePackage(data) - assert.NotNil(t, p) - assert.NoError(t, err) - assert.Equal(t, "gitea", p.Name) - }) - }) +func (nopCloser) Close() error { + return nil } func TestParseControlFile(t *testing.T) { diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go index 784a99582d4c..4d9424f9971f 100644 --- a/modules/setting/lfs.go +++ b/modules/setting/lfs.go @@ -58,7 +58,7 @@ func loadLFSFrom(rootCfg ConfigProvider) error { LFS.JWTSecretBytes = make([]byte, 32) n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64)) - if err != nil || n != 32 { + if (err != nil || n != 32) && InstallLock { LFS.JWTSecretBase64, err = generate.NewJwtSecretBase64() if err != nil { return fmt.Errorf("error generating JWT Secret for custom config: %v", err) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5f671676b585..8784ab6878fe 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -640,7 +640,7 @@ update_language_success = Language has been updated. update_profile_success = Your profile has been updated. change_username = Your username has been changed. change_username_prompt = Note: username changes also change your account URL. -change_username_redirect_prompt = The old username will redirect until it is claimed. +change_username_redirect_prompt = The old username will redirect until someone claims it. continue = Continue cancel = Cancel language = Language @@ -1448,7 +1448,7 @@ issues.context.quote_reply = Quote Reply issues.context.reference_issue = Reference in New Issue issues.context.edit = Edit issues.context.delete = Delete -issues.no_content = There is no content yet. +issues.no_content = No description provided. issues.close = Close Issue issues.comment_pull_merged_at = merged commit %[1]s into %[2]s %[3]s issues.comment_manually_pull_merged_at = manually merged commit %[1]s into %[2]s %[3]s @@ -2545,7 +2545,7 @@ settings.visibility.private_shortname = Private settings.update_settings = Update Settings settings.update_setting_success = Organization settings have been updated. -settings.change_orgname_prompt = Note: changing the organization name also changes the organization's URL. +settings.change_orgname_prompt = Note: Changing the organization name will also change your organization's URL and free the old name. settings.change_orgname_redirect_prompt = The old name will redirect until it is claimed. settings.update_avatar_success = The organization's avatar has been updated. settings.delete = Delete Organization @@ -2627,10 +2627,13 @@ teams.invite.description = Please click the button below to join the team. [admin] dashboard = Dashboard +identity_access = Identity & Access users = User Accounts organizations = Organizations +assets = Code Assets repositories = Repositories hooks = Webhooks +integrations = Integrations authentication = Authentication Sources emails = User Emails config = Configuration diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 34e6488db9b9..20ef4cde6981 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1673,7 +1673,7 @@ pulls.is_empty=As modificações feitas neste ramo já existem no ramo de destin pulls.required_status_check_failed=Algumas das verificações obrigatórias não foram bem sucedidas. pulls.required_status_check_missing=Estão faltando algumas verificações necessárias. pulls.required_status_check_administrator=Uma vez que é administrador, ainda pode realizar a integração deste pedido. -pulls.blocked_by_approvals=Este pedido de integração ainda não tem aprovações suficientes. Já foram concedidas %d de um total de%d aprovações. +pulls.blocked_by_approvals=Este pedido de integração ainda não tem aprovações suficientes. Já foram concedidas %d de um total de %d aprovações. pulls.blocked_by_rejection=Este pedido de integração tem modificações solicitadas por um revisor oficial. pulls.blocked_by_official_review_requests=Este pedido de integração tem pedidos de revisão oficiais. pulls.blocked_by_outdated_branch=Este pedido de integração foi bloqueado por ser obsoleto. diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index d28ed95fd1d4..c070e5fbc139 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -640,6 +640,7 @@ language=Dil ui=Tema hidden_comment_types=Gizli yorum türleri hidden_comment_types_description=Burada işaretlenen yorum türleri konu sayfalarında görüntülenmeyecektir. Örneğin "Etiket" seçildiğinde tüm ",