Merge branch 'main' into pacman-packages

This commit is contained in:
Danila Fominykh 2023-07-24 15:38:58 +03:00 committed by GitHub
commit e22a3c7bb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 519 additions and 202 deletions

View File

@ -409,6 +409,11 @@
"path": "github.com/go-chi/cors/LICENSE", "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" "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", "name": "github.com/go-enry/go-enry/v2",
"path": "github.com/go-enry/go-enry/v2/LICENSE", "path": "github.com/go-enry/go-enry/v2/LICENSE",
@ -484,11 +489,6 @@
"path": "github.com/gogs/chardet/LICENSE", "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" "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", "name": "github.com/gogs/go-gogs-client",
"path": "github.com/gogs/go-gogs-client/LICENSE", "path": "github.com/gogs/go-gogs-client/LICENSE",
@ -909,6 +909,11 @@
"path": "github.com/robfig/cron/LICENSE", "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" "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", "name": "github.com/rs/xid",
"path": "github.com/rs/xid/LICENSE", "path": "github.com/rs/xid/LICENSE",

View File

@ -5,6 +5,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"net/url" "net/url"
@ -297,10 +298,12 @@ var (
&cli.BoolFlag{ &cli.BoolFlag{
Name: "force-smtps", Name: "force-smtps",
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.", Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
Value: true,
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "skip-verify", Name: "skip-verify",
Usage: "Skip TLS verify.", Usage: "Skip TLS verify.",
Value: true,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "helo-hostname", Name: "helo-hostname",
@ -310,6 +313,7 @@ var (
&cli.BoolFlag{ &cli.BoolFlag{
Name: "disable-helo", Name: "disable-helo",
Usage: "Disable SMTP helo.", Usage: "Disable SMTP helo.",
Value: true,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "allowed-domains", Name: "allowed-domains",
@ -319,10 +323,12 @@ var (
&cli.BoolFlag{ &cli.BoolFlag{
Name: "skip-local-2fa", Name: "skip-local-2fa",
Usage: "Skip 2FA to log on.", Usage: "Skip 2FA to log on.",
Value: true,
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "active", Name: "active",
Usage: "This Authentication Source is Activated.", Usage: "This Authentication Source is Activated.",
Value: true,
}, },
} }
@ -373,7 +379,7 @@ func runRepoSyncReleases(_ *cli.Context) error {
continue continue
} }
oldnum, err := getReleaseCount(repo.ID) oldnum, err := getReleaseCount(ctx, repo.ID)
if err != nil { if err != nil {
log.Warn(" GetReleaseCountByRepoID: %v", err) log.Warn(" GetReleaseCountByRepoID: %v", err)
} }
@ -385,7 +391,7 @@ func runRepoSyncReleases(_ *cli.Context) error {
continue continue
} }
count, err = getReleaseCount(repo.ID) count, err = getReleaseCount(ctx, repo.ID)
if err != nil { if err != nil {
log.Warn(" GetReleaseCountByRepoID: %v", err) log.Warn(" GetReleaseCountByRepoID: %v", err)
gitRepo.Close() gitRepo.Close()
@ -401,9 +407,9 @@ func runRepoSyncReleases(_ *cli.Context) error {
return nil return nil
} }
func getReleaseCount(id int64) (int64, error) { func getReleaseCount(ctx context.Context, id int64) (int64, error) {
return repo_model.GetReleaseCountByRepoID( return repo_model.GetReleaseCountByRepoID(
db.DefaultContext, ctx,
id, id,
repo_model.FindReleasesOptions{ repo_model.FindReleasesOptions{
IncludeTags: true, IncludeTags: true,

View File

@ -117,6 +117,7 @@ var (
Name: "rotate", Name: "rotate",
Aliases: []string{"r"}, Aliases: []string{"r"},
Usage: "Rotate logs", Usage: "Rotate logs",
Value: true,
}, },
&cli.Int64Flag{ &cli.Int64Flag{
Name: "max-size", Name: "max-size",
@ -127,6 +128,7 @@ var (
Name: "daily", Name: "daily",
Aliases: []string{"d"}, Aliases: []string{"d"},
Usage: "Rotate logs daily", Usage: "Rotate logs daily",
Value: true,
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "max-days", Name: "max-days",
@ -137,6 +139,7 @@ var (
Name: "compress", Name: "compress",
Aliases: []string{"z"}, Aliases: []string{"z"},
Usage: "Compress rotated logs", Usage: "Compress rotated logs",
Value: true,
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "compression-level", Name: "compression-level",

3
go.mod
View File

@ -41,6 +41,7 @@ require (
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.0.8 github.com/go-chi/chi/v5 v5.0.8
github.com/go-chi/cors v1.2.1 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-enry/go-enry/v2 v2.8.4
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e
github.com/go-git/go-billy/v5 v5.4.1 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/go-webauthn/webauthn v0.8.6
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f 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/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v5 v5.0.0 github.com/golang-jwt/jwt/v5 v5.0.0
github.com/google/go-github/v53 v53.2.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/rhysd/actionlint v1.6.25 // indirect
github.com/rivo/uniseg v0.4.4 // indirect github.com/rivo/uniseg v0.4.4 // indirect
github.com/robfig/cron v1.2.0 // 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/rogpeppe/go-internal v1.11.0 // indirect
github.com/rs/xid v1.5.0 // indirect github.com/rs/xid v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect

10
go.sum
View File

@ -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/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 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= 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 h1:QrY3hx/RiqCJJRbdU0MOcjfTM1a586J0WSooqdlJIhs=
github.com/go-enry/go-enry/v2 v2.8.4/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8= 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= 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/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 h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= 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 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0=
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU= 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= 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/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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 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/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 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= 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/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.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.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.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.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 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 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.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.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.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 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 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= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=

View File

@ -195,6 +195,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
} }
runJobs := make([]*ActionRunJob, 0, len(jobs)) runJobs := make([]*ActionRunJob, 0, len(jobs))
var hasWaiting bool
for _, v := range jobs { for _, v := range jobs {
id, job := v.Job() id, job := v.Job()
needs := job.Needs() needs := job.Needs()
@ -205,6 +206,8 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
status := StatusWaiting status := StatusWaiting
if len(needs) > 0 || run.NeedApproval { if len(needs) > 0 || run.NeedApproval {
status = StatusBlocked status = StatusBlocked
} else {
hasWaiting = true
} }
job.Name, _ = util.SplitStringAtByteN(job.Name, 255) job.Name, _ = util.SplitStringAtByteN(job.Name, 255)
runJobs = append(runJobs, &ActionRunJob{ runJobs = append(runJobs, &ActionRunJob{
@ -225,6 +228,13 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
return err 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() return commiter.Commit()
} }

View File

@ -111,6 +111,13 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
return affected, nil 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 { if job.RunID == 0 {
var err error var err error
if job, err = GetRunJobByID(ctx, job.ID); err != nil { if job, err = GetRunJobByID(ctx, job.ID); err != nil {

View File

@ -215,12 +215,11 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro
} }
func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) { 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 { if err != nil {
return nil, false, err return nil, false, err
} }
defer commiter.Close() defer commiter.Close()
ctx = dbCtx.WithContext(ctx)
e := db.GetEngine(ctx) e := db.GetEngine(ctx)

View File

@ -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()
}

View File

@ -515,6 +515,8 @@ var migrations = []Migration{
NewMigration("Alter Actions Artifact table", v1_21.AlterActionArtifactTable), NewMigration("Alter Actions Artifact table", v1_21.AlterActionArtifactTable),
// v266 -> v267 // v266 -> v267
NewMigration("Reduce commit status", v1_21.ReduceCommitStatus), NewMigration("Reduce commit status", v1_21.ReduceCommitStatus),
// v267 -> v268
NewMigration("Add action_tasks_version table", v1_21.CreateActionTasksVersionTable),
} }
// GetCurrentDBVersion returns the current db version // GetCurrentDBVersion returns the current db version

View File

@ -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))
}

View File

@ -150,11 +150,13 @@ func CloseProvidedListeners() error {
return returnableError 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 // a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket". It
// returns an provided net.Listener for the matching network and address, or // returns an provided net.Listener for the matching network and address, or
// creates a new one using net.Listen. // creates a new one using net.Listen. This function can be replaced by changing the
func GetListener(network, address string) (net.Listener, error) { // 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 // Add a deferral to say that we've tried to grab a listener
defer GetManager().InformCleanup() defer GetManager().InformCleanup()
switch network { switch network {

View File

@ -9,9 +9,11 @@ package graceful
import "net" import "net"
// GetListener obtains a listener for the local network address. // DefaultGetListener obtains a listener for the local network address.
// On windows this is basically just a shim around net.Listen. // On windows this is basically just a shim around net.Listen. This function
func GetListener(network, address string) (net.Listener, error) { // 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 // Add a deferral to say that we've tried to grab a listener
defer GetManager().InformCleanup() defer GetManager().InformCleanup()

View File

@ -33,6 +33,14 @@ var (
PerWriteWriteTimeoutKbTime = 10 * time.Second 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() { func init() {
DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB) DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB)
} }

View File

@ -4,6 +4,8 @@
package log package log
import ( import (
"io"
"code.gitea.io/gitea/modules/util/rotatingfilewriter" "code.gitea.io/gitea/modules/util/rotatingfilewriter"
) )
@ -19,7 +21,7 @@ type WriterFileOption struct {
type eventWriterFile struct { type eventWriterFile struct {
*EventWriterBaseImpl *EventWriterBaseImpl
fileWriter *rotatingfilewriter.RotatingFileWriter fileWriter io.WriteCloser
} }
var _ EventWriter = (*eventWriterFile)(nil) var _ EventWriter = (*eventWriterFile)(nil)
@ -37,7 +39,10 @@ func NewEventWriterFile(name string, mode WriterMode) EventWriter {
CompressionLevel: opt.CompressionLevel, CompressionLevel: opt.CompressionLevel,
}) })
if err != nil { 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) FallbackErrorf("unable to open log file %q: %v", opt.FileName, err)
w.fileWriter = nopCloser{Writer: LoggerToWriter(FallbackErrorf)}
} }
w.OutputWriteCloser = w.fileWriter w.OutputWriteCloser = w.fileWriter
return w return w

View File

@ -640,7 +640,7 @@ update_language_success = Language has been updated.
update_profile_success = Your profile has been updated. update_profile_success = Your profile has been updated.
change_username = Your username has been changed. change_username = Your username has been changed.
change_username_prompt = Note: username changes also change your account URL. 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 continue = Continue
cancel = Cancel cancel = Cancel
language = Language language = Language
@ -2545,7 +2545,7 @@ settings.visibility.private_shortname = Private
settings.update_settings = Update Settings settings.update_settings = Update Settings
settings.update_setting_success = Organization settings have been updated. 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.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.update_avatar_success = The organization's avatar has been updated.
settings.delete = Delete Organization settings.delete = Delete Organization
@ -2627,10 +2627,13 @@ teams.invite.description = Please click the button below to join the team.
[admin] [admin]
dashboard = Dashboard dashboard = Dashboard
identity_access = Identity & Access
users = User Accounts users = User Accounts
organizations = Organizations organizations = Organizations
assets = Code Assets
repositories = Repositories repositories = Repositories
hooks = Webhooks hooks = Webhooks
integrations = Integrations
authentication = Authentication Sources authentication = Authentication Sources
emails = User Emails emails = User Emails
config = Configuration config = Configuration

View File

@ -127,20 +127,39 @@ func (s *Service) Declare(
// FetchTask assigns a task to the runner // FetchTask assigns a task to the runner
func (s *Service) FetchTask( func (s *Service) FetchTask(
ctx context.Context, ctx context.Context,
_ *connect.Request[runnerv1.FetchTaskRequest], req *connect.Request[runnerv1.FetchTaskRequest],
) (*connect.Response[runnerv1.FetchTaskResponse], error) { ) (*connect.Response[runnerv1.FetchTaskResponse], error) {
runner := GetRunner(ctx) runner := GetRunner(ctx)
var task *runnerv1.Task var task *runnerv1.Task
tasksVersion := req.Msg.TasksVersion // task version from runner
latestVersion, err := actions_model.GetTasksVersionByScope(ctx, runner.OwnerID, runner.RepoID)
if err != nil {
return nil, status.Errorf(codes.Internal, "query tasks version failed: %v", err)
} else if latestVersion == 0 {
if err := actions_model.IncreaseTaskVersion(ctx, runner.OwnerID, runner.RepoID); err != nil {
return nil, status.Errorf(codes.Internal, "fail to increase task version: %v", err)
}
// if we don't increase the value of `latestVersion` here,
// the response of FetchTask will return tasksVersion as zero.
// and the runner will treat it as an old version of Gitea.
latestVersion++
}
if tasksVersion != latestVersion {
// if the task version in request is not equal to the version in db,
// it means there may still be some tasks not be assgined.
// try to pick a task for the runner that send the request.
if t, ok, err := pickTask(ctx, runner); err != nil { if t, ok, err := pickTask(ctx, runner); err != nil {
log.Error("pick task failed: %v", err) log.Error("pick task failed: %v", err)
return nil, status.Errorf(codes.Internal, "pick task: %v", err) return nil, status.Errorf(codes.Internal, "pick task: %v", err)
} else if ok { } else if ok {
task = t task = t
} }
}
res := connect.NewResponse(&runnerv1.FetchTaskResponse{ res := connect.NewResponse(&runnerv1.FetchTaskResponse{
Task: task, Task: task,
TasksVersion: latestVersion,
}) })
return res, nil return res, nil
} }

View File

@ -114,7 +114,7 @@ func SearchPackages(ctx *context.Context) {
// EnumeratePackages lists all package names // EnumeratePackages lists all package names
// https://packagist.org/apidoc#list-packages // https://packagist.org/apidoc#list-packages
func EnumeratePackages(ctx *context.Context) { func EnumeratePackages(ctx *context.Context) {
ps, err := packages_model.GetPackagesByType(db.DefaultContext, ctx.Package.Owner.ID, packages_model.TypeComposer) ps, err := packages_model.GetPackagesByType(ctx, ctx.Package.Owner.ID, packages_model.TypeComposer)
if err != nil { if err != nil {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return

View File

@ -4,6 +4,7 @@
package conan package conan
import ( import (
std_ctx "context"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -602,18 +603,16 @@ func DeletePackageV2(ctx *context.Context) {
} }
func deleteRecipeOrPackage(apictx *context.Context, rref *conan_module.RecipeReference, ignoreRecipeRevision bool, pref *conan_module.PackageReference, ignorePackageRevision bool) error { func deleteRecipeOrPackage(apictx *context.Context, rref *conan_module.RecipeReference, ignoreRecipeRevision bool, pref *conan_module.PackageReference, ignorePackageRevision bool) error {
ctx, committer, err := db.TxContext(db.DefaultContext) var pd *packages_model.PackageDescriptor
if err != nil { versionDeleted := false
return err
}
defer committer.Close()
err := db.WithTx(apictx, func(ctx std_ctx.Context) error {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, apictx.Package.Owner.ID, packages_model.TypeConan, rref.Name, rref.Version) pv, err := packages_model.GetVersionByNameAndVersion(ctx, apictx.Package.Owner.ID, packages_model.TypeConan, rref.Name, rref.Version)
if err != nil { if err != nil {
return err return err
} }
pd, err := packages_model.GetPackageDescriptor(ctx, pv) pd, err = packages_model.GetPackageDescriptor(ctx, pv)
if err != nil { if err != nil {
return err return err
} }
@ -648,8 +647,6 @@ func deleteRecipeOrPackage(apictx *context.Context, rref *conan_module.RecipeRef
return err return err
} }
} }
versionDeleted := false
has, err := packages_model.HasVersionFileReferences(ctx, pv.ID) has, err := packages_model.HasVersionFileReferences(ctx, pv.ID)
if err != nil { if err != nil {
return err return err
@ -657,12 +654,11 @@ func deleteRecipeOrPackage(apictx *context.Context, rref *conan_module.RecipeRef
if !has { if !has {
versionDeleted = true versionDeleted = true
if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil { return packages_service.DeletePackageVersionAndReferences(ctx, pv)
return err
} }
} return nil
})
if err := committer.Commit(); err != nil { if err != nil {
return err return err
} }

View File

@ -26,19 +26,19 @@ var uploadVersionMutex sync.Mutex
// saveAsPackageBlob creates a package blob from an upload // saveAsPackageBlob creates a package blob from an upload
// The uploaded blob gets stored in a special upload version to link them to the package/image // The uploaded blob gets stored in a special upload version to link them to the package/image
func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pci *packages_service.PackageCreationInfo) (*packages_model.PackageBlob, error) { func saveAsPackageBlob(ctx context.Context, hsr packages_module.HashedSizeReader, pci *packages_service.PackageCreationInfo) (*packages_model.PackageBlob, error) {
pb := packages_service.NewPackageBlob(hsr) pb := packages_service.NewPackageBlob(hsr)
exists := false exists := false
contentStore := packages_module.NewContentStore() contentStore := packages_module.NewContentStore()
uploadVersion, err := getOrCreateUploadVersion(&pci.PackageInfo) uploadVersion, err := getOrCreateUploadVersion(ctx, &pci.PackageInfo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = db.WithTx(db.DefaultContext, func(ctx context.Context) error { err = db.WithTx(ctx, func(ctx context.Context) error {
if err := packages_service.CheckSizeQuotaExceeded(ctx, pci.Creator, pci.Owner, packages_model.TypeContainer, hsr.Size()); err != nil { if err := packages_service.CheckSizeQuotaExceeded(ctx, pci.Creator, pci.Owner, packages_model.TypeContainer, hsr.Size()); err != nil {
return err return err
} }
@ -79,24 +79,24 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pci *packages_servi
} }
// mountBlob mounts the specific blob to a different package // mountBlob mounts the specific blob to a different package
func mountBlob(pi *packages_service.PackageInfo, pb *packages_model.PackageBlob) error { func mountBlob(ctx context.Context, pi *packages_service.PackageInfo, pb *packages_model.PackageBlob) error {
uploadVersion, err := getOrCreateUploadVersion(pi) uploadVersion, err := getOrCreateUploadVersion(ctx, pi)
if err != nil { if err != nil {
return err return err
} }
return db.WithTx(db.DefaultContext, func(ctx context.Context) error { return db.WithTx(ctx, func(ctx context.Context) error {
return createFileForBlob(ctx, uploadVersion, pb) return createFileForBlob(ctx, uploadVersion, pb)
}) })
} }
func getOrCreateUploadVersion(pi *packages_service.PackageInfo) (*packages_model.PackageVersion, error) { func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageInfo) (*packages_model.PackageVersion, error) {
var uploadVersion *packages_model.PackageVersion var uploadVersion *packages_model.PackageVersion
// FIXME: Replace usage of mutex with database transaction // FIXME: Replace usage of mutex with database transaction
// https://github.com/go-gitea/gitea/pull/21862 // https://github.com/go-gitea/gitea/pull/21862
uploadVersionMutex.Lock() uploadVersionMutex.Lock()
err := db.WithTx(db.DefaultContext, func(ctx context.Context) error { err := db.WithTx(ctx, func(ctx context.Context) error {
created := true created := true
p := &packages_model.Package{ p := &packages_model.Package{
OwnerID: pi.Owner.ID, OwnerID: pi.Owner.ID,
@ -172,8 +172,8 @@ func createFileForBlob(ctx context.Context, pv *packages_model.PackageVersion, p
return nil return nil
} }
func deleteBlob(ownerID int64, image, digest string) error { func deleteBlob(ctx context.Context, ownerID int64, image, digest string) error {
return db.WithTx(db.DefaultContext, func(ctx context.Context) error { return db.WithTx(ctx, func(ctx context.Context) error {
pfds, err := container_model.GetContainerBlobs(ctx, &container_model.BlobSearchOptions{ pfds, err := container_model.GetContainerBlobs(ctx, &container_model.BlobSearchOptions{
OwnerID: ownerID, OwnerID: ownerID,
Image: image, Image: image,

View File

@ -210,7 +210,7 @@ func InitiateUploadBlob(ctx *context.Context) {
} }
if accessible { if accessible {
if err := mountBlob(&packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}, blob.Blob); err != nil { if err := mountBlob(ctx, &packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}, blob.Blob); err != nil {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
@ -239,7 +239,7 @@ func InitiateUploadBlob(ctx *context.Context) {
return return
} }
if _, err := saveAsPackageBlob( if _, err := saveAsPackageBlob(ctx,
buf, buf,
&packages_service.PackageCreationInfo{ &packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{ PackageInfo: packages_service.PackageInfo{
@ -384,7 +384,7 @@ func EndUploadBlob(ctx *context.Context) {
return return
} }
if _, err := saveAsPackageBlob( if _, err := saveAsPackageBlob(ctx,
uploader, uploader,
&packages_service.PackageCreationInfo{ &packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{ PackageInfo: packages_service.PackageInfo{
@ -502,7 +502,7 @@ func DeleteBlob(ctx *context.Context) {
return return
} }
if err := deleteBlob(ctx.Package.Owner.ID, ctx.Params("image"), d); err != nil { if err := deleteBlob(ctx, ctx.Package.Owner.ID, ctx.Params("image"), d); err != nil {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
@ -543,7 +543,7 @@ func UploadManifest(ctx *context.Context) {
return return
} }
digest, err := processManifest(mci, buf) digest, err := processManifest(ctx, mci, buf)
if err != nil { if err != nil {
var namedError *namedError var namedError *namedError
if errors.As(err, &namedError) { if errors.As(err, &namedError) {

View File

@ -50,7 +50,7 @@ type manifestCreationInfo struct {
Properties map[string]string Properties map[string]string
} }
func processManifest(mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) { func processManifest(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
var index oci.Index var index oci.Index
if err := json.NewDecoder(buf).Decode(&index); err != nil { if err := json.NewDecoder(buf).Decode(&index); err != nil {
return "", err return "", err
@ -72,14 +72,14 @@ func processManifest(mci *manifestCreationInfo, buf *packages_module.HashedBuffe
} }
if isImageManifestMediaType(mci.MediaType) { if isImageManifestMediaType(mci.MediaType) {
return processImageManifest(mci, buf) return processImageManifest(ctx, mci, buf)
} else if isImageIndexMediaType(mci.MediaType) { } else if isImageIndexMediaType(mci.MediaType) {
return processImageManifestIndex(mci, buf) return processImageManifestIndex(ctx, mci, buf)
} }
return "", errManifestInvalid return "", errManifestInvalid
} }
func processImageManifest(mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) { func processImageManifest(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
manifestDigest := "" manifestDigest := ""
err := func() error { err := func() error {
@ -92,7 +92,7 @@ func processImageManifest(mci *manifestCreationInfo, buf *packages_module.Hashed
return err return err
} }
ctx, committer, err := db.TxContext(db.DefaultContext) ctx, committer, err := db.TxContext(ctx)
if err != nil { if err != nil {
return err return err
} }
@ -181,7 +181,7 @@ func processImageManifest(mci *manifestCreationInfo, buf *packages_module.Hashed
return err return err
} }
if err := notifyPackageCreate(mci.Creator, pv); err != nil { if err := notifyPackageCreate(ctx, mci.Creator, pv); err != nil {
return err return err
} }
@ -196,7 +196,7 @@ func processImageManifest(mci *manifestCreationInfo, buf *packages_module.Hashed
return manifestDigest, nil return manifestDigest, nil
} }
func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) { func processImageManifestIndex(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
manifestDigest := "" manifestDigest := ""
err := func() error { err := func() error {
@ -209,7 +209,7 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
return err return err
} }
ctx, committer, err := db.TxContext(db.DefaultContext) ctx, committer, err := db.TxContext(ctx)
if err != nil { if err != nil {
return err return err
} }
@ -285,7 +285,7 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
return err return err
} }
if err := notifyPackageCreate(mci.Creator, pv); err != nil { if err := notifyPackageCreate(ctx, mci.Creator, pv); err != nil {
return err return err
} }
@ -300,13 +300,13 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
return manifestDigest, nil return manifestDigest, nil
} }
func notifyPackageCreate(doer *user_model.User, pv *packages_model.PackageVersion) error { func notifyPackageCreate(ctx context.Context, doer *user_model.User, pv *packages_model.PackageVersion) error {
pd, err := packages_model.GetPackageDescriptor(db.DefaultContext, pv) pd, err := packages_model.GetPackageDescriptor(ctx, pv)
if err != nil { if err != nil {
return err return err
} }
notification.NotifyPackageCreate(db.DefaultContext, doer, pd) notification.NotifyPackageCreate(ctx, doer, pd)
return nil return nil
} }

View File

@ -5,6 +5,7 @@ package npm
import ( import (
"bytes" "bytes"
std_ctx "context"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -222,7 +223,7 @@ func UploadPackage(ctx *context.Context) {
} }
for _, tag := range npmPackage.DistTags { for _, tag := range npmPackage.DistTags {
if err := setPackageTag(tag, pv, false); err != nil { if err := setPackageTag(ctx, tag, pv, false); err != nil {
if err == errInvalidTagName { if err == errInvalidTagName {
apiError(ctx, http.StatusBadRequest, err) apiError(ctx, http.StatusBadRequest, err)
return return
@ -345,7 +346,7 @@ func AddPackageTag(ctx *context.Context) {
return return
} }
if err := setPackageTag(ctx.Params("tag"), pv, false); err != nil { if err := setPackageTag(ctx, ctx.Params("tag"), pv, false); err != nil {
if err == errInvalidTagName { if err == errInvalidTagName {
apiError(ctx, http.StatusBadRequest, err) apiError(ctx, http.StatusBadRequest, err)
return return
@ -366,7 +367,7 @@ func DeletePackageTag(ctx *context.Context) {
} }
if len(pvs) != 0 { if len(pvs) != 0 {
if err := setPackageTag(ctx.Params("tag"), pvs[0], true); err != nil { if err := setPackageTag(ctx, ctx.Params("tag"), pvs[0], true); err != nil {
if err == errInvalidTagName { if err == errInvalidTagName {
apiError(ctx, http.StatusBadRequest, err) apiError(ctx, http.StatusBadRequest, err)
return return
@ -377,7 +378,7 @@ func DeletePackageTag(ctx *context.Context) {
} }
} }
func setPackageTag(tag string, pv *packages_model.PackageVersion, deleteOnly bool) error { func setPackageTag(ctx std_ctx.Context, tag string, pv *packages_model.PackageVersion, deleteOnly bool) error {
if tag == "" { if tag == "" {
return errInvalidTagName return errInvalidTagName
} }
@ -386,12 +387,7 @@ func setPackageTag(tag string, pv *packages_model.PackageVersion, deleteOnly boo
return errInvalidTagName return errInvalidTagName
} }
ctx, committer, err := db.TxContext(db.DefaultContext) return db.WithTx(ctx, func(ctx std_ctx.Context) error {
if err != nil {
return err
}
defer committer.Close()
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
PackageID: pv.PackageID, PackageID: pv.PackageID,
Properties: map[string]string{ Properties: map[string]string{
@ -425,8 +421,8 @@ func setPackageTag(tag string, pv *packages_model.PackageVersion, deleteOnly boo
return err return err
} }
} }
return nil
return committer.Commit() })
} }
func PackageSearch(ctx *context.Context) { func PackageSearch(ctx *context.Context) {

View File

@ -312,6 +312,8 @@ func DeleteUser(ctx *context.APIContext) {
// "$ref": "#/responses/empty" // "$ref": "#/responses/empty"
// "403": // "403":
// "$ref": "#/responses/forbidden" // "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "422": // "422":
// "$ref": "#/responses/validationError" // "$ref": "#/responses/validationError"

View File

@ -8,7 +8,6 @@ import (
"net/http" "net/http"
asymkey_model "code.gitea.io/gitea/models/asymkey" asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@ -31,7 +30,7 @@ func appendPrivateInformation(ctx std_ctx.Context, apiKey *api.PublicKey, key *a
if defaultUser.ID == key.OwnerID { if defaultUser.ID == key.OwnerID {
apiKey.Owner = convert.ToUser(ctx, defaultUser, defaultUser) apiKey.Owner = convert.ToUser(ctx, defaultUser, defaultUser)
} else { } else {
user, err := user_model.GetUserByID(db.DefaultContext, key.OwnerID) user, err := user_model.GetUserByID(ctx, key.OwnerID)
if err != nil { if err != nil {
return apiKey, err return apiKey, err
} }

View File

@ -85,7 +85,7 @@ func Packages(ctx *context.Context) {
// DeletePackageVersion deletes a package version // DeletePackageVersion deletes a package version
func DeletePackageVersion(ctx *context.Context) { func DeletePackageVersion(ctx *context.Context) {
pv, err := packages_model.GetVersionByID(db.DefaultContext, ctx.FormInt64("id")) pv, err := packages_model.GetVersionByID(ctx, ctx.FormInt64("id"))
if err != nil { if err != nil {
ctx.ServerError("GetRepositoryByID", err) ctx.ServerError("GetRepositoryByID", err)
return return

View File

@ -4,6 +4,7 @@
package healthcheck package healthcheck
import ( import (
"context"
"net/http" "net/http"
"os" "os"
"time" "time"
@ -72,7 +73,7 @@ func Check(w http.ResponseWriter, r *http.Request) {
statuses := make([]status, 0) statuses := make([]status, 0)
if setting.InstallLock { if setting.InstallLock {
statuses = append(statuses, checkDatabase(rsp.Checks)) statuses = append(statuses, checkDatabase(r.Context(), rsp.Checks))
statuses = append(statuses, checkCache(rsp.Checks)) statuses = append(statuses, checkCache(rsp.Checks))
} }
for _, s := range statuses { for _, s := range statuses {
@ -89,9 +90,9 @@ func Check(w http.ResponseWriter, r *http.Request) {
} }
// database checks gitea database status // database checks gitea database status
func checkDatabase(checks checks) status { func checkDatabase(ctx context.Context, checks checks) status {
st := componentStatus{} st := componentStatus{}
if err := db.GetEngine(db.DefaultContext).Ping(); err != nil { if err := db.GetEngine(ctx).Ping(); err != nil {
st.Status = fail st.Status = fail
st.Time = getCheckTime() st.Time = getCheckTime()
log.Error("database ping failed with error: %v", err) log.Error("database ping failed with error: %v", err)

View File

@ -198,7 +198,7 @@ type fileInfo struct {
st typesniffer.SniffedType st typesniffer.SniffedType
} }
func getFileReader(repoID int64, blob *git.Blob) ([]byte, io.ReadCloser, *fileInfo, error) { func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, io.ReadCloser, *fileInfo, error) {
dataRc, err := blob.DataAsync() dataRc, err := blob.DataAsync()
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
@ -221,7 +221,7 @@ func getFileReader(repoID int64, blob *git.Blob) ([]byte, io.ReadCloser, *fileIn
return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil
} }
meta, err := git_model.GetLFSMetaObjectByOid(db.DefaultContext, repoID, pointer.Oid) meta, err := git_model.GetLFSMetaObjectByOid(ctx, repoID, pointer.Oid)
if err != nil && err != git_model.ErrLFSObjectNotExist { // fallback to plain file if err != nil && err != git_model.ErrLFSObjectNotExist { // fallback to plain file
return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil
} }
@ -265,7 +265,7 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr
ctx.Data["ReadmeExist"] = true ctx.Data["ReadmeExist"] = true
ctx.Data["FileIsSymlink"] = readmeFile.IsLink() ctx.Data["FileIsSymlink"] = readmeFile.IsLink()
buf, dataRc, fInfo, err := getFileReader(ctx.Repo.Repository.ID, target.Blob()) buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, target.Blob())
if err != nil { if err != nil {
ctx.ServerError("getFileReader", err) ctx.ServerError("getFileReader", err)
return return
@ -328,7 +328,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
ctx.Data["IsViewFile"] = true ctx.Data["IsViewFile"] = true
ctx.Data["HideRepoInfo"] = true ctx.Data["HideRepoInfo"] = true
blob := entry.Blob() blob := entry.Blob()
buf, dataRc, fInfo, err := getFileReader(ctx.Repo.Repository.ID, blob) buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, blob)
if err != nil { if err != nil {
ctx.ServerError("getFileReader", err) ctx.ServerError("getFileReader", err)
return return

View File

@ -14,10 +14,10 @@ import (
"code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/translation"
"github.com/gogs/cron" "github.com/go-co-op/gocron"
) )
var c = cron.New() var scheduler = gocron.NewScheduler(time.Local)
// Prevent duplicate running tasks. // Prevent duplicate running tasks.
var taskStatusTable = sync.NewStatusTable() var taskStatusTable = sync.NewStatusTable()
@ -39,11 +39,11 @@ func NewContext(original context.Context) {
} }
} }
c.Start() scheduler.StartAsync()
started = true started = true
lock.Unlock() lock.Unlock()
graceful.GetManager().RunAtShutdown(context.Background(), func() { graceful.GetManager().RunAtShutdown(context.Background(), func() {
c.Stop() scheduler.Stop()
lock.Lock() lock.Lock()
started = false started = false
lock.Unlock() lock.Unlock()
@ -77,13 +77,20 @@ type TaskTable []*TaskTableRow
// ListTasks returns all running cron tasks. // ListTasks returns all running cron tasks.
func ListTasks() TaskTable { func ListTasks() TaskTable {
entries := c.Entries() jobs := scheduler.Jobs()
eMap := map[string]*cron.Entry{} jobMap := map[string]*gocron.Job{}
for _, e := range entries { for _, job := range jobs {
eMap[e.Description] = e // the first tag is the task name
tags := job.Tags()
if len(tags) == 0 { // should never happen
continue
} }
jobMap[job.Tags()[0]] = job
}
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
tTable := make([]*TaskTableRow, 0, len(tasks)) tTable := make([]*TaskTableRow, 0, len(tasks))
for _, task := range tasks { for _, task := range tasks {
spec := "-" spec := "-"
@ -91,10 +98,13 @@ func ListTasks() TaskTable {
next time.Time next time.Time
prev time.Time prev time.Time
) )
if e, ok := eMap[task.Name]; ok { if e, ok := jobMap[task.Name]; ok {
spec = e.Spec tags := e.Tags()
next = e.Next if len(tags) > 1 {
prev = e.Prev spec = tags[1] // the second tag is the task spec
}
next = e.NextRun()
prev = e.PreviousRun()
} }
task.lock.Lock() task.lock.Lock()
tTable = append(tTable, &TaskTableRow{ tTable = append(tTable, &TaskTableRow{

View File

@ -7,6 +7,7 @@ import (
"context" "context"
"fmt" "fmt"
"reflect" "reflect"
"strings"
"sync" "sync"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@ -176,8 +177,7 @@ func RegisterTask(name string, config Config, fun func(context.Context, *user_mo
if config.IsEnabled() { if config.IsEnabled() {
// We cannot use the entry return as there is no way to lock it // We cannot use the entry return as there is no way to lock it
if _, err = c.AddJob(name, config.GetSchedule(), task); err != nil { if err := addTaskToScheduler(task); err != nil {
log.Error("Unable to register cron task with name: %s Error: %v", name, err)
return err return err
} }
} }
@ -199,3 +199,21 @@ func RegisterTaskFatal(name string, config Config, fun func(context.Context, *us
log.Fatal("Unable to register cron task %s Error: %v", name, err) log.Fatal("Unable to register cron task %s Error: %v", name, err)
} }
} }
func addTaskToScheduler(task *Task) error {
tags := []string{task.Name, task.config.GetSchedule()} // name and schedule can't be get from job, so we add them as tag
if scheduleHasSeconds(task.config.GetSchedule()) {
scheduler = scheduler.CronWithSeconds(task.config.GetSchedule())
} else {
scheduler = scheduler.Cron(task.config.GetSchedule())
}
if _, err := scheduler.Tag(tags...).Do(task.Run); err != nil {
log.Error("Unable to register cron task with name: %s Error: %v", task.Name, err)
return err
}
return nil
}
func scheduleHasSeconds(schedule string) bool {
return len(strings.Fields(schedule)) >= 6
}

View File

@ -0,0 +1,61 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cron
import (
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAddTaskToScheduler(t *testing.T) {
assert.Len(t, scheduler.Jobs(), 0)
defer scheduler.Clear()
// no seconds
err := addTaskToScheduler(&Task{
Name: "task 1",
config: &BaseConfig{
Schedule: "5 4 * * *",
},
})
assert.NoError(t, err)
assert.Len(t, scheduler.Jobs(), 1)
assert.Equal(t, "task 1", scheduler.Jobs()[0].Tags()[0])
assert.Equal(t, "5 4 * * *", scheduler.Jobs()[0].Tags()[1])
// with seconds
err = addTaskToScheduler(&Task{
Name: "task 2",
config: &BaseConfig{
Schedule: "30 5 4 * * *",
},
})
assert.NoError(t, err)
assert.Len(t, scheduler.Jobs(), 2)
assert.Equal(t, "task 2", scheduler.Jobs()[1].Tags()[0])
assert.Equal(t, "30 5 4 * * *", scheduler.Jobs()[1].Tags()[1])
}
func TestScheduleHasSeconds(t *testing.T) {
tests := []struct {
schedule string
hasSecond bool
}{
{"* * * * * *", true},
{"* * * * *", false},
{"5 4 * * *", false},
{"5 4 * * *", false},
{"5,8 4 * * *", false},
{"* * * * * *", true},
{"5,8 4 * * *", false},
}
for i, test := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
assert.Equal(t, test.hasSecond, scheduleHasSeconds(test.schedule))
})
}
}

View File

@ -4,36 +4,61 @@
<a class="{{if .PageIsAdminDashboard}}active {{end}}item" href="{{AppSubUrl}}/admin"> <a class="{{if .PageIsAdminDashboard}}active {{end}}item" href="{{AppSubUrl}}/admin">
{{.locale.Tr "admin.dashboard"}} {{.locale.Tr "admin.dashboard"}}
</a> </a>
<a class="{{if .PageIsAdminUsers}}active {{end}}item" href="{{AppSubUrl}}/admin/users"> <details class="item toggleable-item" {{if or .PageIsAdminUsers .PageIsAdminEmails .PageIsAdminOrganizations .PageIsAdminAuthentications}}open{{end}}>
{{.locale.Tr "admin.users"}} <summary>{{.locale.Tr "admin.identity_access"}}</summary>
<div class="menu">
<a class="{{if .PageIsAdminAuthentications}}active {{end}}item" href="{{AppSubUrl}}/admin/auths">
{{.locale.Tr "admin.authentication"}}
</a> </a>
<a class="{{if .PageIsAdminOrganizations}}active {{end}}item" href="{{AppSubUrl}}/admin/orgs"> <a class="{{if .PageIsAdminOrganizations}}active {{end}}item" href="{{AppSubUrl}}/admin/orgs">
{{.locale.Tr "admin.organizations"}} {{.locale.Tr "admin.organizations"}}
</a> </a>
<a class="{{if .PageIsAdminRepositories}}active {{end}}item" href="{{AppSubUrl}}/admin/repos"> <a class="{{if .PageIsAdminUsers}}active {{end}}item" href="{{AppSubUrl}}/admin/users">
{{.locale.Tr "admin.repositories"}} {{.locale.Tr "admin.users"}}
</a> </a>
<a class="{{if .PageIsAdminEmails}}active {{end}}item" href="{{AppSubUrl}}/admin/emails">
{{.locale.Tr "admin.emails"}}
</a>
</div>
</details>
<details class="item toggleable-item" {{if or .PageIsAdminRepositories (and .EnablePackages .PageIsAdminPackages)}}open{{end}}>
<summary>{{.locale.Tr "admin.assets"}}</summary>
<div class="menu">
{{if .EnablePackages}} {{if .EnablePackages}}
<a class="{{if .PageIsAdminPackages}}active {{end}}item" href="{{AppSubUrl}}/admin/packages"> <a class="{{if .PageIsAdminPackages}}active {{end}}item" href="{{AppSubUrl}}/admin/packages">
{{.locale.Tr "packages.title"}} {{.locale.Tr "packages.title"}}
</a> </a>
{{end}} {{end}}
<a class="{{if .PageIsAdminRepositories}}active {{end}}item" href="{{AppSubUrl}}/admin/repos">
{{.locale.Tr "admin.repositories"}}
</a>
</div>
</details>
<!-- Webhooks and OAuth can be both disabled here, so add this if statement to display different ui -->
{{if and (not DisableWebhooks) .EnableOAuth2}}
<details class="item toggleable-item" {{if or .PageIsAdminDefaultHooks .PageIsAdminSystemHooks .PageIsAdminApplications}}open{{end}}>
<summary>{{.locale.Tr "admin.integrations"}}</summary>
<div class="menu">
<a class="{{if .PageIsAdminApplications}}active {{end}}item" href="{{AppSubUrl}}/admin/applications">
{{.locale.Tr "settings.applications"}}
</a>
<a class="{{if or .PageIsAdminDefaultHooks .PageIsAdminSystemHooks}}active {{end}}item" href="{{AppSubUrl}}/admin/hooks">
{{.locale.Tr "admin.hooks"}}
</a>
</div>
</details>
{{else}}
{{if not DisableWebhooks}} {{if not DisableWebhooks}}
<a class="{{if or .PageIsAdminDefaultHooks .PageIsAdminSystemHooks}}active {{end}}item" href="{{AppSubUrl}}/admin/hooks"> <a class="{{if or .PageIsAdminDefaultHooks .PageIsAdminSystemHooks}}active {{end}}item" href="{{AppSubUrl}}/admin/hooks">
{{.locale.Tr "admin.hooks"}} {{.locale.Tr "admin.hooks"}}
</a> </a>
{{end}} {{end}}
<a class="{{if .PageIsAdminAuthentications}}active {{end}}item" href="{{AppSubUrl}}/admin/auths">
{{.locale.Tr "admin.authentication"}}
</a>
<a class="{{if .PageIsAdminEmails}}active {{end}}item" href="{{AppSubUrl}}/admin/emails">
{{.locale.Tr "admin.emails"}}
</a>
{{if .EnableOAuth2}} {{if .EnableOAuth2}}
<a class="{{if .PageIsAdminApplications}}active {{end}}item" href="{{AppSubUrl}}/admin/applications"> <a class="{{if .PageIsAdminApplications}}active {{end}}item" href="{{AppSubUrl}}/admin/applications">
{{.locale.Tr "settings.applications"}} {{.locale.Tr "settings.applications"}}
</a> </a>
{{end}} {{end}}
{{end}}
{{if .EnableActions}} {{if .EnableActions}}
<details class="item toggleable-item" {{if .PageIsSharedSettingsRunners}}open{{end}}> <details class="item toggleable-item" {{if .PageIsSharedSettingsRunners}}open{{end}}>
<summary>{{.locale.Tr "actions.actions"}}</summary> <summary>{{.locale.Tr "actions.actions"}}</summary>

View File

@ -10,7 +10,7 @@
{{else}} {{else}}
{{$referenceUrl = printf "%s/files#%s" .ctxData.Issue.Link .item.HashTag}} {{$referenceUrl = printf "%s/files#%s" .ctxData.Issue.Link .item.HashTag}}
{{end}} {{end}}
<div class="item context js-aria-clickable" data-clipboard-text-type="url" data-clipboard-text="{{AppSubUrl}}{{$referenceUrl}}">{{.ctxData.locale.Tr "repo.issues.context.copy_link"}}</div> <div class="item context js-aria-clickable" data-clipboard-text-type="url" data-clipboard-text="{{$referenceUrl}}">{{.ctxData.locale.Tr "repo.issues.context.copy_link"}}</div>
<div class="item context js-aria-clickable quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-raw">{{.ctxData.locale.Tr "repo.issues.context.quote_reply"}}</div> <div class="item context js-aria-clickable quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-raw">{{.ctxData.locale.Tr "repo.issues.context.quote_reply"}}</div>
{{if not .ctxData.UnitIssuesGlobalDisabled}} {{if not .ctxData.UnitIssuesGlobalDisabled}}
<div class="item context js-aria-clickable reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{.ctxData.locale.Tr "repo.issues.context.reference_issue"}}</div> <div class="item context js-aria-clickable reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{.ctxData.locale.Tr "repo.issues.context.reference_issue"}}</div>

View File

@ -620,6 +620,9 @@
"403": { "403": {
"$ref": "#/responses/forbidden" "$ref": "#/responses/forbidden"
}, },
"404": {
"$ref": "#/responses/notFound"
},
"422": { "422": {
"$ref": "#/responses/validationError" "$ref": "#/responses/validationError"
} }