From 8e181e874841697dd392468358723e4c75be3ace Mon Sep 17 00:00:00 2001 From: qwerty287 Date: Wed, 26 Jun 2024 06:05:35 +0000 Subject: [PATCH 1/6] Translated using Weblate (German) Currently translated at 100.0% (318 of 318 strings) Translation: Woodpecker CI/UI Translate-URL: http://translate.woodpecker-ci.org/projects/woodpecker-ci/ui/de/ --- web/src/assets/locales/de.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/src/assets/locales/de.json b/web/src/assets/locales/de.json index 2dc204168..06d37ef9e 100644 --- a/web/src/assets/locales/de.json +++ b/web/src/assets/locales/de.json @@ -561,5 +561,9 @@ "edit": "Geheimnis bearbeiten", "delete": "Geheimnis löschen" }, - "settings": "Einstellungen" + "settings": "Einstellungen", + "oauth_error": "Fehler bei der Authentifizierung mit OAuth-Anbieter", + "internal_error": "Ein interner Fehler ist aufgetreten", + "registration_closed": "Die Registrierung ist geschlossen", + "access_denied": "Du darfst nicht auf diese Instanz zugreifen" } From 4dcdebd78627f91c5b1a6d597a44a8362f2c5d41 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 26 Jun 2024 15:07:19 +0200 Subject: [PATCH 2/6] Let webhook pass on pipeline parsion error (#3829) --- server/pipeline/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/pipeline/create.go b/server/pipeline/create.go index 127c3aa44..2692d1117 100644 --- a/server/pipeline/create.go +++ b/server/pipeline/create.go @@ -94,7 +94,7 @@ func Create(ctx context.Context, _store store.Store, repo *model.Repo, pipeline pipelineItems, parseErr := parsePipeline(_forge, _store, pipeline, repoUser, repo, forgeYamlConfigs, nil) if pipeline_errors.HasBlockingErrors(parseErr) { log.Debug().Str("repo", repo.FullName).Err(parseErr).Msg("failed to parse yaml") - return nil, updatePipelineWithErr(ctx, _forge, _store, pipeline, repo, repoUser, parseErr) + return pipeline, updatePipelineWithErr(ctx, _forge, _store, pipeline, repo, repoUser, parseErr) } else if parseErr != nil { pipeline.Errors = pipeline_errors.GetPipelineErrors(parseErr) } From a5ee50619aa691b737f8ee2b0f0eed105e8c8879 Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Wed, 26 Jun 2024 17:09:10 +0200 Subject: [PATCH 3/6] Update forgejo sdk (#3840) --- go.mod | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 21ad95e3b..2a9e4b43e 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( code.gitea.io/sdk/gitea v0.18.0 codeberg.org/6543/go-yaml2json v1.0.0 codeberg.org/6543/xyaml v1.1.0 - codeberg.org/mvdkleijn/forgejo-sdk/forgejo v0.0.0-20240607215151-168c988b82c1 + codeberg.org/mvdkleijn/forgejo-sdk/forgejo v1.1.0 github.com/6543/logfile-open v1.2.1 github.com/adrg/xdg v0.4.0 github.com/alessio/shellescape v1.4.2 @@ -61,12 +61,12 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 github.com/zalando/go-keyring v0.2.4 go.uber.org/multierr v1.11.0 - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.24.0 golang.org/x/net v0.25.0 golang.org/x/oauth2 v0.20.0 golang.org/x/sync v0.7.0 - golang.org/x/term v0.20.0 - golang.org/x/text v0.15.0 + golang.org/x/term v0.21.0 + golang.org/x/text v0.16.0 google.golang.org/grpc v1.64.0 google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v3 v3.0.1 @@ -128,7 +128,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -176,9 +176,9 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/arch v0.8.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/sys v0.21.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 12a411114..bed7c4a80 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ codeberg.org/6543/go-yaml2json v1.0.0 h1:heGqo9VEi7gY2yNqjj7X4ADs5nzlFIbGsJtgYDL codeberg.org/6543/go-yaml2json v1.0.0/go.mod h1:mz61q14LWF4ZABrgMEDMmk3t9dPi6zgR1uBh2VKV2RQ= codeberg.org/6543/xyaml v1.1.0 h1:0PWTy8OUqshshjrrnAXFWXSPUEa8R49DIh2ah07SxFc= codeberg.org/6543/xyaml v1.1.0/go.mod h1:jI7afXLZUxeL4rNNsG1SlHh78L+gma9lK1bIebyFZwA= -codeberg.org/mvdkleijn/forgejo-sdk/forgejo v0.0.0-20240607215151-168c988b82c1 h1:fwiekuoe+B8uz+Jpk0URaIOfklb2MWM8Pe0lNQck32A= -codeberg.org/mvdkleijn/forgejo-sdk/forgejo v0.0.0-20240607215151-168c988b82c1/go.mod h1:09wAYX9H0+wBo1baX9DdSqdfreZc6ji5aELsnu9m14M= +codeberg.org/mvdkleijn/forgejo-sdk/forgejo v1.1.0 h1:t5vPmL8JNfg1syWSZUS7miw8pwUHW+l2AB2IkFZuOiw= +codeberg.org/mvdkleijn/forgejo-sdk/forgejo v1.1.0/go.mod h1:09wAYX9H0+wBo1baX9DdSqdfreZc6ji5aELsnu9m14M= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= @@ -218,8 +218,8 @@ github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOs github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -526,8 +526,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -590,14 +590,14 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -605,8 +605,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -623,8 +623,8 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From ea8976bf88ea407a3815bf7b8a20ec78c8e61235 Mon Sep 17 00:00:00 2001 From: Anbraten <6918444+anbraten@users.noreply.github.com> Date: Wed, 26 Jun 2024 20:54:12 +0200 Subject: [PATCH 4/6] Add mastodon verification (#3843) --- docs/docusaurus.config.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 5057fa018..e49a1efb5 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -15,6 +15,15 @@ const config: Config = { organizationName: 'woodpecker-ci', projectName: 'woodpecker-ci.github.io', trailingSlash: false, + headTags: [ + { + tagName: 'link', + attributes: { + href: 'https://floss.social/@WoodpeckerCI', + rel: 'me', + }, + }, + ], themeConfig: { navbar: { title: 'Woodpecker', From b8b6efb3528799ca139a92f00a8694b225f5b5c2 Mon Sep 17 00:00:00 2001 From: Anbraten <6918444+anbraten@users.noreply.github.com> Date: Thu, 27 Jun 2024 00:08:59 +0200 Subject: [PATCH 5/6] Enhance token checking (#3842) --- server/api/hook.go | 2 +- server/router/middleware/session/agent.go | 13 ++--- server/router/middleware/session/user.go | 4 +- shared/token/token.go | 56 ++++++++++++-------- shared/token/token_test.go | 62 +++++++++++++++++++++++ 5 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 shared/token/token_test.go diff --git a/server/api/hook.go b/server/api/hook.go index 40a93ef35..0ed75a4f3 100644 --- a/server/api/hook.go +++ b/server/api/hook.go @@ -178,7 +178,7 @@ func PostHook(c *gin.Context) { // // get the token and verify the hook is authorized - parsedToken, err := token.ParseRequest(c.Request, func(_ *token.Token) (string, error) { + parsedToken, err := token.ParseRequest([]token.Type{token.HookToken}, c.Request, func(_ *token.Token) (string, error) { return repo.Hash, nil }) if err != nil { diff --git a/server/router/middleware/session/agent.go b/server/router/middleware/session/agent.go index cacaf0d44..0cbfa2a33 100644 --- a/server/router/middleware/session/agent.go +++ b/server/router/middleware/session/agent.go @@ -31,17 +31,14 @@ func AuthorizeAgent(c *gin.Context) { return } - parsed, err := token.ParseRequest(c.Request, func(_ *token.Token) (string, error) { + _, err := token.ParseRequest([]token.Type{token.AgentToken}, c.Request, func(_ *token.Token) (string, error) { return secret, nil }) - switch { - case err != nil: + if err != nil { c.String(http.StatusInternalServerError, "invalid or empty token. %s", err) c.Abort() - case parsed.Kind != token.AgentToken: - c.String(http.StatusForbidden, "invalid token. please use an agent token") - c.Abort() - default: - c.Next() + return } + + c.Next() } diff --git a/server/router/middleware/session/user.go b/server/router/middleware/session/user.go index b413cf528..2f098b5cb 100644 --- a/server/router/middleware/session/user.go +++ b/server/router/middleware/session/user.go @@ -43,7 +43,7 @@ func SetUser() gin.HandlerFunc { return func(c *gin.Context) { var user *model.User - t, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) { + t, err := token.ParseRequest([]token.Type{token.UserToken, token.SessToken}, c.Request, func(t *token.Token) (string, error) { var err error userID, err := strconv.ParseInt(t.Get("user-id"), 10, 64) if err != nil { @@ -58,7 +58,7 @@ func SetUser() gin.HandlerFunc { // if this is a session token (ie not the API token) // this means the user is accessing with a web browser, // so we should implement CSRF protection measures. - if t.Kind == token.SessToken { + if t.Type == token.SessToken { err = token.CheckCsrf(c.Request, func(_ *token.Token) (string, error) { return user.Hash, nil }) diff --git a/shared/token/token.go b/shared/token/token.go index 545107f59..f893babfe 100644 --- a/shared/token/token.go +++ b/shared/token/token.go @@ -24,36 +24,52 @@ import ( type SecretFunc func(*Token) (string, error) +type Type string + const ( - UserToken = "user" - SessToken = "sess" - HookToken = "hook" - CsrfToken = "csrf" - AgentToken = "agent" + UserToken Type = "user" // user token (exp cli) + SessToken Type = "sess" // session token (ui token requires csrf check) + HookToken Type = "hook" // repo hook token + CsrfToken Type = "csrf" + AgentToken Type = "agent" ) // SignerAlgo id default algorithm used to sign JWT tokens. const SignerAlgo = "HS256" type Token struct { - Kind string + Type Type claims jwt.MapClaims } -func parse(raw string, fn SecretFunc) (*Token, error) { +func Parse(allowedTypes []Type, raw string, fn SecretFunc) (*Token, error) { token := &Token{ claims: jwt.MapClaims{}, } parsed, err := jwt.Parse(raw, keyFunc(token, fn)) if err != nil { return nil, err - } else if !parsed.Valid { + } + if !parsed.Valid { return nil, jwt.ErrTokenUnverifiable } + + hasAllowedType := false + for _, k := range allowedTypes { + if k == token.Type { + hasAllowedType = true + break + } + } + + if !hasAllowedType { + return nil, jwt.ErrInvalidType + } + return token, nil } -func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) { +func ParseRequest(allowedTypes []Type, r *http.Request, fn SecretFunc) (*Token, error) { // first we attempt to get the token from the // authorization header. token := r.Header.Get("Authorization") @@ -63,19 +79,19 @@ func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) { if _, err := fmt.Sscanf(token, "Bearer %s", &bearer); err != nil { return nil, err } - return parse(bearer, fn) + return Parse(allowedTypes, bearer, fn) } token = r.Header.Get("X-Gitlab-Token") if len(token) != 0 { - return parse(token, fn) + return Parse(allowedTypes, token, fn) } // then we attempt to get the token from the // access_token url query parameter token = r.FormValue("access_token") if len(token) != 0 { - return parse(token, fn) + return Parse(allowedTypes, token, fn) } // and finally we attempt to get the token from @@ -84,7 +100,7 @@ func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) { if err != nil { return nil, err } - return parse(cookie.Value, fn) + return Parse(allowedTypes, cookie.Value, fn) } func CheckCsrf(r *http.Request, fn SecretFunc) error { @@ -97,12 +113,12 @@ func CheckCsrf(r *http.Request, fn SecretFunc) error { // parse the raw CSRF token value and validate raw := r.Header.Get("X-CSRF-TOKEN") - _, err := parse(raw, fn) + _, err := Parse([]Type{CsrfToken}, raw, fn) return err } -func New(kind string) *Token { - return &Token{Kind: kind, claims: jwt.MapClaims{}} +func New(tokenType Type) *Token { + return &Token{Type: tokenType, claims: jwt.MapClaims{}} } // Sign signs the token using the given secret hash @@ -124,7 +140,7 @@ func (t *Token) SignExpires(secret string, exp int64) (string, error) { claims[k] = v } - claims["type"] = t.Kind + claims["type"] = t.Type if exp > 0 { claims["exp"] = float64(exp) } @@ -157,12 +173,12 @@ func keyFunc(token *Token, fn SecretFunc) jwt.Keyfunc { return nil, jwt.ErrSignatureInvalid } - // extract the token kind and cast to the expected type - kind, ok := claims["type"] + // extract the token type and cast to the expected type + tokenType, ok := claims["type"].(string) if !ok { return nil, jwt.ErrInvalidType } - token.Kind, _ = kind.(string) + token.Type = Type(tokenType) // copy custom claims for k, v := range claims { diff --git a/shared/token/token_test.go b/shared/token/token_test.go new file mode 100644 index 000000000..fb77ef609 --- /dev/null +++ b/shared/token/token_test.go @@ -0,0 +1,62 @@ +package token_test + +import ( + "testing" + + "github.com/franela/goblin" + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" + "github.com/stretchr/testify/assert" + + "go.woodpecker-ci.org/woodpecker/v2/shared/token" +) + +func TestToken(t *testing.T) { + gin.SetMode(gin.TestMode) + + g := goblin.Goblin(t) + g.Describe("Token", func() { + jwtSecret := "secret-to-sign-the-token" + + g.It("should parse a valid token", func() { + _token := token.New(token.UserToken) + _token.Set("user-id", "1") + signedToken, err := _token.Sign(jwtSecret) + assert.NoError(g, err) + + parsed, err := token.Parse([]token.Type{token.UserToken}, signedToken, func(_ *token.Token) (string, error) { + return jwtSecret, nil + }) + + assert.NoError(g, err) + assert.NotNil(g, parsed) + assert.Equal(g, "1", parsed.Get("user-id")) + }) + + g.It("should fail to parse a token with a wrong type", func() { + _token := token.New(token.UserToken) + _token.Set("user-id", "1") + signedToken, err := _token.Sign(jwtSecret) + assert.NoError(g, err) + + _, err = token.Parse([]token.Type{token.AgentToken}, signedToken, func(_ *token.Token) (string, error) { + return jwtSecret, nil + }) + + assert.ErrorIs(g, err, jwt.ErrInvalidType) + }) + + g.It("should fail to parse a token with a wrong secret", func() { + _token := token.New(token.UserToken) + _token.Set("user-id", "1") + signedToken, err := _token.Sign(jwtSecret) + assert.NoError(g, err) + + _, err = token.Parse([]token.Type{token.UserToken}, signedToken, func(_ *token.Token) (string, error) { + return "this-is-a-wrong-secret", nil + }) + + assert.ErrorIs(g, err, jwt.ErrSignatureInvalid) + }) + }) +} From 92cd0d04a399980c152d74de1c34e2734eb32550 Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Thu, 27 Jun 2024 09:32:06 +0200 Subject: [PATCH 6/6] Unify DB tables/columns (#3806) Co-authored-by: Anbraten <6918444+anbraten@users.noreply.github.com> --- Makefile | 2 +- cmd/server/docs/docs.go | 9 + docs/docs/92-development/03-ui.md | 2 +- docs/docs/92-development/06-conventions.md | 7 + .../{06-guides.md => 07-guides.md} | 0 ...{07-translations.md => 08-translations.md} | 0 .../{08-swagger.md => 09-swagger.md} | 0 server/model/config.go | 18 +- server/model/cron.go | 12 +- server/model/feed.go | 34 +- server/model/forge.go | 5 + server/model/log.go | 6 +- server/model/perm.go | 12 +- server/model/pipeline.go | 66 +- server/model/redirection.go | 2 +- server/model/registry.go | 14 +- server/model/repo.go | 46 +- server/model/secret.go | 14 +- server/model/server_config.go | 9 +- server/model/step.go | 26 +- server/model/task.go | 12 +- server/model/user.go | 20 +- server/model/workflow.go | 24 +- server/store/datastore/config.go | 8 +- server/store/datastore/feed.go | 58 +- .../021_parent_steps_to_workflows.go | 4 +- .../store/datastore/migration/022_add_orgs.go | 4 +- .../datastore/migration/023_add_org_id.go | 4 +- .../migration/030_set_default_forge_id.go | 75 ++- .../migration/031_unify_columns_tables.go | 637 ++++++++++++++++++ server/store/datastore/migration/migration.go | 1 + server/store/datastore/org.go | 6 +- server/store/datastore/permission.go | 10 +- server/store/datastore/pipeline.go | 34 +- server/store/datastore/registry.go | 4 +- server/store/datastore/repo.go | 28 +- server/store/datastore/secret.go | 18 +- server/store/datastore/step.go | 18 +- server/store/datastore/task.go | 2 +- server/store/datastore/user.go | 6 +- server/store/datastore/workflow.go | 8 +- 41 files changed, 1005 insertions(+), 260 deletions(-) create mode 100644 docs/docs/92-development/06-conventions.md rename docs/docs/92-development/{06-guides.md => 07-guides.md} (100%) rename docs/docs/92-development/{07-translations.md => 08-translations.md} (100%) rename docs/docs/92-development/{08-swagger.md => 09-swagger.md} (100%) create mode 100644 server/store/datastore/migration/031_unify_columns_tables.go diff --git a/Makefile b/Makefile index b1f7a4f02..0c6299a26 100644 --- a/Makefile +++ b/Makefile @@ -173,7 +173,7 @@ test-cli: ## Test cli code test-server-datastore: ## Test server datastore go test -timeout 120s -tags 'test $(TAGS)' -run TestMigrate go.woodpecker-ci.org/woodpecker/v2/server/store/... - go test -race -timeout 30s -tags 'test $(TAGS)' -skip TestMigrate go.woodpecker-ci.org/woodpecker/v2/server/store/... + go test -race -timeout 45s -tags 'test $(TAGS)' -skip TestMigrate go.woodpecker-ci.org/woodpecker/v2/server/store/... test-server-datastore-coverage: ## Test server datastore with coverage report go test -race -cover -coverprofile datastore-coverage.out -timeout 180s -tags 'test $(TAGS)' go.woodpecker-ci.org/woodpecker/v2/server/store/... diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 7ea2dd04f..d1a23505b 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -4014,6 +4014,7 @@ const docTemplate = `{ "type": "string" }, "created_at": { + "description": "TODO change JSON field to \"created\" in 3.0", "type": "integer" }, "creator_id": { @@ -4056,12 +4057,14 @@ const docTemplate = `{ "type": "string" }, "created_at": { + "description": "TODO change JSON field to \"created\" in 3.0", "type": "integer" }, "event": { "type": "string" }, "finished_at": { + "description": "TODO change JSON field to \"finished\" in 3.0", "type": "integer" }, "id": { @@ -4083,6 +4086,7 @@ const docTemplate = `{ "type": "integer" }, "started_at": { + "description": "TODO change JSON field to \"started\" in 3.0", "type": "integer" }, "status": { @@ -4240,6 +4244,7 @@ const docTemplate = `{ "type": "string" }, "created_at": { + "description": "TODO change JSON field to \"created\" in 3.0", "type": "integer" }, "deploy_task": { @@ -4258,6 +4263,7 @@ const docTemplate = `{ "$ref": "#/definitions/WebhookEvent" }, "finished_at": { + "description": "TODO change JSON field to \"finished\" in 3.0", "type": "integer" }, "forge_url": { @@ -4291,6 +4297,7 @@ const docTemplate = `{ "type": "string" }, "reviewed_at": { + "description": "TODO change JSON field to \"reviewed\" in 3.0", "type": "integer" }, "reviewed_by": { @@ -4301,6 +4308,7 @@ const docTemplate = `{ "type": "string" }, "started_at": { + "description": "TODO change JSON field to \"started\" in 3.0", "type": "integer" }, "status": { @@ -4313,6 +4321,7 @@ const docTemplate = `{ "type": "string" }, "updated_at": { + "description": "TODO change JSON field to \"updated\" in 3.0", "type": "integer" }, "variables": { diff --git a/docs/docs/92-development/03-ui.md b/docs/docs/92-development/03-ui.md index e8999b0be..6a01584c2 100644 --- a/docs/docs/92-development/03-ui.md +++ b/docs/docs/92-development/03-ui.md @@ -36,4 +36,4 @@ The following list contains some tools and frameworks used by the Woodpecker UI. Woodpecker uses [Vue I18n](https://vue-i18n.intlify.dev/) as translation library. New translations have to be added to `web/src/assets/locales/en.json`. The English source file will be automatically imported into [Weblate](https://translate.woodpecker-ci.org/) (the translation system used by Woodpecker) where all other languages will be translated by the community based on the English source. You must not provide translations except English in PRs, otherwise weblate could put git into conflicts (when someone has translated in that language file and changes are not into main branch yet) -For more information about translations see [Translations](./07-translations.md). +For more information about translations see [Translations](./08-translations.md). diff --git a/docs/docs/92-development/06-conventions.md b/docs/docs/92-development/06-conventions.md new file mode 100644 index 000000000..e94a90c43 --- /dev/null +++ b/docs/docs/92-development/06-conventions.md @@ -0,0 +1,7 @@ +# Conventions + +## Database naming + +Database tables are named plural, columns don't have any prefix. + +Example: Table name `agent`, columns `id`, `name`. diff --git a/docs/docs/92-development/06-guides.md b/docs/docs/92-development/07-guides.md similarity index 100% rename from docs/docs/92-development/06-guides.md rename to docs/docs/92-development/07-guides.md diff --git a/docs/docs/92-development/07-translations.md b/docs/docs/92-development/08-translations.md similarity index 100% rename from docs/docs/92-development/07-translations.md rename to docs/docs/92-development/08-translations.md diff --git a/docs/docs/92-development/08-swagger.md b/docs/docs/92-development/09-swagger.md similarity index 100% rename from docs/docs/92-development/08-swagger.md rename to docs/docs/92-development/09-swagger.md diff --git a/server/model/config.go b/server/model/config.go index ee8cd479e..7103a5999 100644 --- a/server/model/config.go +++ b/server/model/config.go @@ -17,15 +17,23 @@ package model // Config represents a pipeline configuration. type Config struct { - ID int64 `json:"-" xorm:"pk autoincr 'config_id'"` - RepoID int64 `json:"-" xorm:"UNIQUE(s) 'config_repo_id'"` - Hash string `json:"hash" xorm:"UNIQUE(s) 'config_hash'"` - Name string `json:"name" xorm:"UNIQUE(s) 'config_name'"` - Data []byte `json:"data" xorm:"LONGBLOB 'config_data'"` + ID int64 `json:"-" xorm:"pk autoincr 'id'"` + RepoID int64 `json:"-" xorm:"UNIQUE(s) 'repo_id'"` + Hash string `json:"hash" xorm:"UNIQUE(s) 'hash'"` + Name string `json:"name" xorm:"UNIQUE(s) 'name'"` + Data []byte `json:"data" xorm:"LONGBLOB 'data'"` } // @name Config +func (Config) TableName() string { + return "configs" +} + // PipelineConfig is the n:n relation between Pipeline and Config. type PipelineConfig struct { ConfigID int64 `json:"-" xorm:"UNIQUE(s) NOT NULL 'config_id'"` PipelineID int64 `json:"-" xorm:"UNIQUE(s) NOT NULL 'pipeline_id'"` } + +func (PipelineConfig) TableName() string { + return "pipeline_configs" +} diff --git a/server/model/cron.go b/server/model/cron.go index 6fa213724..42467de35 100644 --- a/server/model/cron.go +++ b/server/model/cron.go @@ -21,14 +21,14 @@ import ( ) type Cron struct { - ID int64 `json:"id" xorm:"pk autoincr"` - Name string `json:"name" xorm:"UNIQUE(s) INDEX"` + ID int64 `json:"id" xorm:"pk autoincr 'id'"` + Name string `json:"name" xorm:"name UNIQUE(s) INDEX"` RepoID int64 `json:"repo_id" xorm:"repo_id UNIQUE(s) INDEX"` CreatorID int64 `json:"creator_id" xorm:"creator_id INDEX"` - NextExec int64 `json:"next_exec"` - Schedule string `json:"schedule" xorm:"NOT NULL"` // @weekly, 3min, ... - Created int64 `json:"created_at" xorm:"created NOT NULL DEFAULT 0"` - Branch string `json:"branch"` + NextExec int64 `json:"next_exec" xorm:"next_exec"` + Schedule string `json:"schedule" xorm:"schedule NOT NULL"` // @weekly, 3min, ... + Created int64 `json:"created_at" xorm:"created NOT NULL DEFAULT 0"` // TODO change JSON field to "created" in 3.0 + Branch string `json:"branch" xorm:"branch"` } // @name Cron // TableName returns the database table name for xorm. diff --git a/server/model/feed.go b/server/model/feed.go index 0ff5296a0..0dd4b5d3a 100644 --- a/server/model/feed.go +++ b/server/model/feed.go @@ -17,21 +17,21 @@ package model // Feed represents an item in the user's feed or timeline. type Feed struct { - RepoID int64 `json:"repo_id" xorm:"feed_repo_id"` - ID int64 `json:"id,omitempty" xorm:"feed_pipeline_id"` - Number int64 `json:"number,omitempty" xorm:"feed_pipeline_number"` - Event string `json:"event,omitempty" xorm:"feed_pipeline_event"` - Status string `json:"status,omitempty" xorm:"feed_pipeline_status"` - Created int64 `json:"created_at,omitempty" xorm:"feed_pipeline_created"` - Started int64 `json:"started_at,omitempty" xorm:"feed_pipeline_started"` - Finished int64 `json:"finished_at,omitempty" xorm:"feed_pipeline_finished"` - Commit string `json:"commit,omitempty" xorm:"feed_pipeline_commit"` - Branch string `json:"branch,omitempty" xorm:"feed_pipeline_branch"` - Ref string `json:"ref,omitempty" xorm:"feed_pipeline_ref"` - Refspec string `json:"refspec,omitempty" xorm:"feed_pipeline_refspec"` - Title string `json:"title,omitempty" xorm:"feed_pipeline_title"` - Message string `json:"message,omitempty" xorm:"feed_pipeline_message"` - Author string `json:"author,omitempty" xorm:"feed_pipeline_author"` - Avatar string `json:"author_avatar,omitempty" xorm:"feed_pipeline_avatar"` - Email string `json:"author_email,omitempty" xorm:"feed_pipeline_email"` + RepoID int64 `json:"repo_id" xorm:"repo_id"` + ID int64 `json:"id,omitempty" xorm:"pipeline_id"` + Number int64 `json:"number,omitempty" xorm:"pipeline_number"` + Event string `json:"event,omitempty" xorm:"pipeline_event"` + Status string `json:"status,omitempty" xorm:"pipeline_status"` + Created int64 `json:"created_at,omitempty" xorm:"pipeline_created"` // TODO change JSON field to "created" in 3.0 + Started int64 `json:"started_at,omitempty" xorm:"pipeline_started"` // TODO change JSON field to "started" in 3.0 + Finished int64 `json:"finished_at,omitempty" xorm:"pipeline_finished"` // TODO change JSON field to "finished" in 3.0 + Commit string `json:"commit,omitempty" xorm:"pipeline_commit"` + Branch string `json:"branch,omitempty" xorm:"pipeline_branch"` + Ref string `json:"ref,omitempty" xorm:"pipeline_ref"` + Refspec string `json:"refspec,omitempty" xorm:"pipeline_refspec"` + Title string `json:"title,omitempty" xorm:"pipeline_title"` + Message string `json:"message,omitempty" xorm:"pipeline_message"` + Author string `json:"author,omitempty" xorm:"pipeline_author"` + Avatar string `json:"author_avatar,omitempty" xorm:"pipeline_avatar"` + Email string `json:"author_email,omitempty" xorm:"pipeline_email"` } // @name Feed diff --git a/server/model/forge.go b/server/model/forge.go index 39a1af292..87854dd3e 100644 --- a/server/model/forge.go +++ b/server/model/forge.go @@ -37,6 +37,11 @@ type Forge struct { AdditionalOptions map[string]any `json:"additional_options,omitempty" xorm:"json"` } // @name Forge +// TableName returns the database table name for xorm. +func (Forge) TableName() string { + return "forges" +} + // PublicCopy returns a copy of the forge without sensitive information and technical details. func (f *Forge) PublicCopy() *Forge { forge := &Forge{ diff --git a/server/model/log.go b/server/model/log.go index 1e06c8075..9ac618063 100644 --- a/server/model/log.go +++ b/server/model/log.go @@ -28,11 +28,11 @@ const ( type LogEntry struct { ID int64 `json:"id" xorm:"pk autoincr 'id'"` StepID int64 `json:"step_id" xorm:"INDEX 'step_id'"` - Time int64 `json:"time"` - Line int `json:"line"` + Time int64 `json:"time" xorm:"'time'"` + Line int `json:"line" xorm:"'line'"` Data []byte `json:"data" xorm:"LONGBLOB"` Created int64 `json:"-" xorm:"created"` - Type LogEntryType `json:"type"` + Type LogEntryType `json:"type" xorm:"'type'"` } // @name LogEntry // TODO: store info what specific command the line belongs to (must be optional and impl. by backend) diff --git a/server/model/perm.go b/server/model/perm.go index d0e7c4339..f25c23812 100644 --- a/server/model/perm.go +++ b/server/model/perm.go @@ -17,13 +17,13 @@ package model // Perm defines a repository permission for an individual user. type Perm struct { - UserID int64 `json:"-" xorm:"UNIQUE(s) INDEX NOT NULL 'perm_user_id'"` - RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX NOT NULL 'perm_repo_id'"` + UserID int64 `json:"-" xorm:"UNIQUE(s) INDEX NOT NULL 'user_id'"` + RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX NOT NULL 'repo_id'"` Repo *Repo `json:"-" xorm:"-"` - Pull bool `json:"pull" xorm:"perm_pull"` - Push bool `json:"push" xorm:"perm_push"` - Admin bool `json:"admin" xorm:"perm_admin"` - Synced int64 `json:"synced" xorm:"perm_synced"` + Pull bool `json:"pull" xorm:"pull"` + Push bool `json:"push" xorm:"push"` + Admin bool `json:"admin" xorm:"admin"` + Synced int64 `json:"synced" xorm:"synced"` Created int64 `json:"created" xorm:"created"` Updated int64 `json:"updated" xorm:"updated"` } // @name Perm diff --git a/server/model/pipeline.go b/server/model/pipeline.go index f2a4557ee..674069cfd 100644 --- a/server/model/pipeline.go +++ b/server/model/pipeline.go @@ -20,50 +20,50 @@ import ( ) type Pipeline struct { - ID int64 `json:"id" xorm:"pk autoincr 'pipeline_id'"` - RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX 'pipeline_repo_id'"` - Number int64 `json:"number" xorm:"UNIQUE(s) 'pipeline_number'"` - Author string `json:"author" xorm:"INDEX 'pipeline_author'"` - Parent int64 `json:"parent" xorm:"pipeline_parent"` - Event WebhookEvent `json:"event" xorm:"pipeline_event"` - Status StatusValue `json:"status" xorm:"INDEX 'pipeline_status'"` - Errors []*types.PipelineError `json:"errors" xorm:"json 'pipeline_errors'"` - Created int64 `json:"created_at" xorm:"pipeline_created"` - Updated int64 `json:"updated_at" xorm:"updated NOT NULL DEFAULT 0 'updated'"` - Started int64 `json:"started_at" xorm:"pipeline_started"` - Finished int64 `json:"finished_at" xorm:"pipeline_finished"` - Deploy string `json:"deploy_to" xorm:"pipeline_deploy"` - DeployTask string `json:"deploy_task" xorm:"pipeline_deploy_task"` - Commit string `json:"commit" xorm:"pipeline_commit"` - Branch string `json:"branch" xorm:"pipeline_branch"` - Ref string `json:"ref" xorm:"pipeline_ref"` - Refspec string `json:"refspec" xorm:"pipeline_refspec"` - Title string `json:"title" xorm:"pipeline_title"` - Message string `json:"message" xorm:"TEXT 'pipeline_message'"` - Timestamp int64 `json:"timestamp" xorm:"pipeline_timestamp"` - Sender string `json:"sender" xorm:"pipeline_sender"` // uses reported user for webhooks and name of cron for cron pipelines - Avatar string `json:"author_avatar" xorm:"pipeline_avatar"` - Email string `json:"author_email" xorm:"pipeline_email"` - ForgeURL string `json:"forge_url" xorm:"pipeline_forge_url"` - Reviewer string `json:"reviewed_by" xorm:"pipeline_reviewer"` - Reviewed int64 `json:"reviewed_at" xorm:"pipeline_reviewed"` + ID int64 `json:"id" xorm:"pk autoincr 'id'"` + RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX 'repo_id'"` + Number int64 `json:"number" xorm:"UNIQUE(s) 'number'"` + Author string `json:"author" xorm:"INDEX 'author'"` + Parent int64 `json:"parent" xorm:"parent"` + Event WebhookEvent `json:"event" xorm:"event"` + Status StatusValue `json:"status" xorm:"INDEX 'status'"` + Errors []*types.PipelineError `json:"errors" xorm:"json 'errors'"` + Created int64 `json:"created_at" xorm:"'created' NOT NULL DEFAULT 0 created"` // TODO change JSON field to "created" in 3.0 + Updated int64 `json:"updated_at" xorm:"'updated' NOT NULL DEFAULT 0 updated"` // TODO change JSON field to "updated" in 3.0 + Started int64 `json:"started_at" xorm:"started"` // TODO change JSON field to "started" in 3.0 + Finished int64 `json:"finished_at" xorm:"finished"` // TODO change JSON field to "finished" in 3.0 + Deploy string `json:"deploy_to" xorm:"deploy"` + DeployTask string `json:"deploy_task" xorm:"deploy_task"` + Commit string `json:"commit" xorm:"commit"` + Branch string `json:"branch" xorm:"branch"` + Ref string `json:"ref" xorm:"ref"` + Refspec string `json:"refspec" xorm:"refspec"` + Title string `json:"title" xorm:"title"` + Message string `json:"message" xorm:"TEXT 'message'"` + Timestamp int64 `json:"timestamp" xorm:"'timestamp'"` + Sender string `json:"sender" xorm:"sender"` // uses reported user for webhooks and name of cron for cron pipelines + Avatar string `json:"author_avatar" xorm:"avatar"` + Email string `json:"author_email" xorm:"email"` + ForgeURL string `json:"forge_url" xorm:"forge_url"` + Reviewer string `json:"reviewed_by" xorm:"reviewer"` + Reviewed int64 `json:"reviewed_at" xorm:"reviewed"` // TODO change JSON field to "reviewed" in 3.0 Workflows []*Workflow `json:"workflows,omitempty" xorm:"-"` ChangedFiles []string `json:"changed_files,omitempty" xorm:"LONGTEXT 'changed_files'"` AdditionalVariables map[string]string `json:"variables,omitempty" xorm:"json 'additional_variables'"` PullRequestLabels []string `json:"pr_labels,omitempty" xorm:"json 'pr_labels'"` - IsPrerelease bool `json:"is_prerelease,omitempty" xorm:"is_prerelease"` + IsPrerelease bool `json:"is_prerelease,omitempty" xorm:"is_prerelease"` } // @name Pipeline -type PipelineFilter struct { - Before int64 - After int64 -} - // TableName return database table name for xorm. func (Pipeline) TableName() string { return "pipelines" } +type PipelineFilter struct { + Before int64 + After int64 +} + // IsMultiPipeline checks if step list contain more than one parent step. func (p Pipeline) IsMultiPipeline() bool { return len(p.Workflows) > 1 diff --git a/server/model/redirection.go b/server/model/redirection.go index fa780fe17..5cdb8779e 100644 --- a/server/model/redirection.go +++ b/server/model/redirection.go @@ -15,7 +15,7 @@ package model type Redirection struct { - ID int64 `xorm:"pk autoincr 'redirection_id'"` + ID int64 `xorm:"pk autoincr 'id'"` RepoID int64 `xorm:"'repo_id'"` FullName string `xorm:"UNIQUE INDEX 'repo_full_name'"` } diff --git a/server/model/registry.go b/server/model/registry.go index e5772b824..c2df9bac4 100644 --- a/server/model/registry.go +++ b/server/model/registry.go @@ -28,13 +28,17 @@ var ( // Registry represents a docker registry with credentials. type Registry struct { - ID int64 `json:"id" xorm:"pk autoincr 'registry_id'"` - RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX 'registry_repo_id'"` - Address string `json:"address" xorm:"UNIQUE(s) INDEX 'registry_addr'"` - Username string `json:"username" xorm:"varchar(2000) 'registry_username'"` - Password string `json:"password" xorm:"TEXT 'registry_password'"` + ID int64 `json:"id" xorm:"pk autoincr 'id'"` + RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX 'repo_id'"` + Address string `json:"address" xorm:"UNIQUE(s) INDEX 'address'"` + Username string `json:"username" xorm:"varchar(2000) 'username'"` + Password string `json:"password" xorm:"TEXT 'password'"` } // @name Registry +func (r Registry) TableName() string { + return "registries" +} + // Validate validates the registry information. func (r *Registry) Validate() error { switch { diff --git a/server/model/repo.go b/server/model/repo.go index b1e8b19e8..8cbdb86db 100644 --- a/server/model/repo.go +++ b/server/model/repo.go @@ -22,32 +22,32 @@ import ( // Repo represents a repository. type Repo struct { - ID int64 `json:"id,omitempty" xorm:"pk autoincr 'repo_id'"` - UserID int64 `json:"-" xorm:"repo_user_id"` + ID int64 `json:"id,omitempty" xorm:"pk autoincr 'id'"` + UserID int64 `json:"-" xorm:"user_id"` ForgeID int64 `json:"forge_id,omitempty" xorm:"forge_id"` // ForgeRemoteID is the unique identifier for the repository on the forge. ForgeRemoteID ForgeRemoteID `json:"forge_remote_id" xorm:"forge_remote_id"` - OrgID int64 `json:"org_id" xorm:"repo_org_id"` - Owner string `json:"owner" xorm:"UNIQUE(name) 'repo_owner'"` - Name string `json:"name" xorm:"UNIQUE(name) 'repo_name'"` - FullName string `json:"full_name" xorm:"UNIQUE 'repo_full_name'"` - Avatar string `json:"avatar_url,omitempty" xorm:"varchar(500) 'repo_avatar'"` - ForgeURL string `json:"forge_url,omitempty" xorm:"varchar(1000) 'repo_forge_url'"` - Clone string `json:"clone_url,omitempty" xorm:"varchar(1000) 'repo_clone'"` - CloneSSH string `json:"clone_url_ssh" xorm:"varchar(1000) 'repo_clone_ssh'"` - Branch string `json:"default_branch,omitempty" xorm:"varchar(500) 'repo_branch'"` - SCMKind SCMKind `json:"scm,omitempty" xorm:"varchar(50) 'repo_scm'"` - PREnabled bool `json:"pr_enabled" xorm:"DEFAULT TRUE 'repo_pr_enabled'"` - Timeout int64 `json:"timeout,omitempty" xorm:"repo_timeout"` - Visibility RepoVisibility `json:"visibility" xorm:"varchar(10) 'repo_visibility'"` - IsSCMPrivate bool `json:"private" xorm:"repo_private"` - IsTrusted bool `json:"trusted" xorm:"repo_trusted"` - IsGated bool `json:"gated" xorm:"repo_gated"` - IsActive bool `json:"active" xorm:"repo_active"` - AllowPull bool `json:"allow_pr" xorm:"repo_allow_pr"` - AllowDeploy bool `json:"allow_deploy" xorm:"repo_allow_deploy"` - Config string `json:"config_file" xorm:"varchar(500) 'repo_config_path'"` - Hash string `json:"-" xorm:"varchar(500) 'repo_hash'"` + OrgID int64 `json:"org_id" xorm:"org_id"` + Owner string `json:"owner" xorm:"UNIQUE(name) 'owner'"` + Name string `json:"name" xorm:"UNIQUE(name) 'name'"` + FullName string `json:"full_name" xorm:"UNIQUE 'full_name'"` + Avatar string `json:"avatar_url,omitempty" xorm:"varchar(500) 'avatar'"` + ForgeURL string `json:"forge_url,omitempty" xorm:"varchar(1000) 'forge_url'"` + Clone string `json:"clone_url,omitempty" xorm:"varchar(1000) 'clone'"` + CloneSSH string `json:"clone_url_ssh" xorm:"varchar(1000) 'clone_ssh'"` + Branch string `json:"default_branch,omitempty" xorm:"varchar(500) 'branch'"` + SCMKind SCMKind `json:"scm,omitempty" xorm:"varchar(50) 'scm'"` + PREnabled bool `json:"pr_enabled" xorm:"DEFAULT TRUE 'pr_enabled'"` + Timeout int64 `json:"timeout,omitempty" xorm:"timeout"` + Visibility RepoVisibility `json:"visibility" xorm:"varchar(10) 'visibility'"` + IsSCMPrivate bool `json:"private" xorm:"private"` + IsTrusted bool `json:"trusted" xorm:"trusted"` + IsGated bool `json:"gated" xorm:"gated"` + IsActive bool `json:"active" xorm:"active"` + AllowPull bool `json:"allow_pr" xorm:"allow_pr"` + AllowDeploy bool `json:"allow_deploy" xorm:"allow_deploy"` + Config string `json:"config_file" xorm:"varchar(500) 'config_path'"` + Hash string `json:"-" xorm:"varchar(500) 'hash'"` Perm *Perm `json:"-" xorm:"-"` CancelPreviousPipelineEvents []WebhookEvent `json:"cancel_previous_pipeline_events" xorm:"json 'cancel_previous_pipeline_events'"` NetrcOnlyTrusted bool `json:"netrc_only_trusted" xorm:"NOT NULL DEFAULT true 'netrc_only_trusted'"` diff --git a/server/model/secret.go b/server/model/secret.go index 327da66b0..d9806145b 100644 --- a/server/model/secret.go +++ b/server/model/secret.go @@ -45,13 +45,13 @@ type SecretStore interface { // Secret represents a secret variable, such as a password or token. type Secret struct { - ID int64 `json:"id" xorm:"pk autoincr 'secret_id'"` - OrgID int64 `json:"org_id" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'secret_org_id'"` - RepoID int64 `json:"repo_id" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'secret_repo_id'"` - Name string `json:"name" xorm:"NOT NULL UNIQUE(s) INDEX 'secret_name'"` - Value string `json:"value,omitempty" xorm:"TEXT 'secret_value'"` - Images []string `json:"images" xorm:"json 'secret_images'"` - Events []WebhookEvent `json:"events" xorm:"json 'secret_events'"` + ID int64 `json:"id" xorm:"pk autoincr 'id'"` + OrgID int64 `json:"org_id" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'org_id'"` + RepoID int64 `json:"repo_id" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'repo_id'"` + Name string `json:"name" xorm:"NOT NULL UNIQUE(s) INDEX 'name'"` + Value string `json:"value,omitempty" xorm:"TEXT 'value'"` + Images []string `json:"images" xorm:"json 'images'"` + Events []WebhookEvent `json:"events" xorm:"json 'events'"` } // @name Secret // TableName return database table name for xorm. diff --git a/server/model/server_config.go b/server/model/server_config.go index 2c18bbdfd..ac85b0c86 100644 --- a/server/model/server_config.go +++ b/server/model/server_config.go @@ -16,6 +16,11 @@ package model // ServerConfig represents a key-value pair for storing server configurations. type ServerConfig struct { - Key string `json:"key" xorm:"pk"` - Value string `json:"value" xorm:""` + Key string `json:"key" xorm:"pk 'key'"` + Value string `json:"value" xorm:"value"` +} + +// TableName return database table name for xorm. +func (ServerConfig) TableName() string { + return "server_configs" } diff --git a/server/model/step.go b/server/model/step.go index 7ceb81300..7c0379fdb 100644 --- a/server/model/step.go +++ b/server/model/step.go @@ -26,19 +26,19 @@ const ( // Step represents a process in the pipeline. type Step struct { - ID int64 `json:"id" xorm:"pk autoincr 'step_id'"` - UUID string `json:"uuid" xorm:"INDEX 'step_uuid'"` - PipelineID int64 `json:"pipeline_id" xorm:"UNIQUE(s) INDEX 'step_pipeline_id'"` - PID int `json:"pid" xorm:"UNIQUE(s) 'step_pid'"` - PPID int `json:"ppid" xorm:"step_ppid"` - Name string `json:"name" xorm:"step_name"` - State StatusValue `json:"state" xorm:"step_state"` - Error string `json:"error,omitempty" xorm:"TEXT 'step_error'"` - Failure string `json:"-" xorm:"step_failure"` - ExitCode int `json:"exit_code" xorm:"step_exit_code"` - Started int64 `json:"start_time,omitempty" xorm:"step_started"` - Stopped int64 `json:"end_time,omitempty" xorm:"step_stopped"` - Type StepType `json:"type,omitempty" xorm:"step_type"` + ID int64 `json:"id" xorm:"pk autoincr 'id'"` + UUID string `json:"uuid" xorm:"INDEX 'uuid'"` + PipelineID int64 `json:"pipeline_id" xorm:"UNIQUE(s) INDEX 'pipeline_id'"` + PID int `json:"pid" xorm:"UNIQUE(s) 'pid'"` + PPID int `json:"ppid" xorm:"ppid"` + Name string `json:"name" xorm:"name"` + State StatusValue `json:"state" xorm:"state"` + Error string `json:"error,omitempty" xorm:"TEXT 'error'"` + Failure string `json:"-" xorm:"failure"` + ExitCode int `json:"exit_code" xorm:"exit_code"` + Started int64 `json:"start_time,omitempty" xorm:"started"` + Stopped int64 `json:"end_time,omitempty" xorm:"stopped"` + Type StepType `json:"type,omitempty" xorm:"type"` } // @name Step // TableName return database table name for xorm. diff --git a/server/model/task.go b/server/model/task.go index c9b5a7801..bd24ca665 100644 --- a/server/model/task.go +++ b/server/model/task.go @@ -21,12 +21,12 @@ import ( // Task defines scheduled pipeline Task. type Task struct { - ID string `json:"id" xorm:"PK UNIQUE 'task_id'"` - Data []byte `json:"data" xorm:"LONGBLOB 'task_data'"` - Labels map[string]string `json:"labels" xorm:"json 'task_labels'"` - Dependencies []string `json:"dependencies" xorm:"json 'task_dependencies'"` - RunOn []string `json:"run_on" xorm:"json 'task_run_on'"` - DepStatus map[string]StatusValue `json:"dep_status" xorm:"json 'task_dep_status'"` + ID string `json:"id" xorm:"PK UNIQUE 'id'"` + Data []byte `json:"data" xorm:"LONGBLOB 'data'"` + Labels map[string]string `json:"labels" xorm:"json 'labels'"` + Dependencies []string `json:"dependencies" xorm:"json 'dependencies'"` + RunOn []string `json:"run_on" xorm:"json 'run_on'"` + DepStatus map[string]StatusValue `json:"dep_status" xorm:"json 'dependencies_status'"` AgentID int64 `json:"agent_id" xorm:"'agent_id'"` } // @name Task diff --git a/server/model/user.go b/server/model/user.go index 66371f772..78166b45e 100644 --- a/server/model/user.go +++ b/server/model/user.go @@ -32,7 +32,7 @@ type User struct { // the id for this user. // // required: true - ID int64 `json:"id" xorm:"pk autoincr 'user_id'"` + ID int64 `json:"id" xorm:"pk autoincr 'id'"` ForgeID int64 `json:"forge_id,omitempty" xorm:"forge_id"` @@ -41,36 +41,36 @@ type User struct { // Login is the username for this user. // // required: true - Login string `json:"login" xorm:"UNIQUE 'user_login'"` + Login string `json:"login" xorm:"UNIQUE 'login'"` // Token is the oauth2 token. - Token string `json:"-" xorm:"TEXT 'user_token'"` + Token string `json:"-" xorm:"TEXT 'token'"` // Secret is the oauth2 token secret. - Secret string `json:"-" xorm:"TEXT 'user_secret'"` + Secret string `json:"-" xorm:"TEXT 'secret'"` // Expiry is the token and secret expiration timestamp. - Expiry int64 `json:"-" xorm:"user_expiry"` + Expiry int64 `json:"-" xorm:"expiry"` // Email is the email address for this user. // // required: true - Email string `json:"email" xorm:" varchar(500) 'user_email'"` + Email string `json:"email" xorm:" varchar(500) 'email'"` // the avatar url for this user. - Avatar string `json:"avatar_url" xorm:" varchar(500) 'user_avatar'"` + Avatar string `json:"avatar_url" xorm:" varchar(500) 'avatar'"` // Admin indicates the user is a system administrator. // // NOTE: If the username is part of the WOODPECKER_ADMIN // environment variable, this value will be set to true on login. - Admin bool `json:"admin,omitempty" xorm:"user_admin"` + Admin bool `json:"admin,omitempty" xorm:"admin"` // Hash is a unique token used to sign tokens. - Hash string `json:"-" xorm:"UNIQUE varchar(500) 'user_hash'"` + Hash string `json:"-" xorm:"UNIQUE varchar(500) 'hash'"` // OrgID is the of the user as model.Org. - OrgID int64 `json:"org_id" xorm:"user_org_id"` + OrgID int64 `json:"org_id" xorm:"org_id"` } // @name User // TableName return database table name for xorm. diff --git a/server/model/workflow.go b/server/model/workflow.go index e75b4385a..52df70233 100644 --- a/server/model/workflow.go +++ b/server/model/workflow.go @@ -17,18 +17,18 @@ package model // Workflow represents a workflow in the pipeline. type Workflow struct { - ID int64 `json:"id" xorm:"pk autoincr 'workflow_id'"` - PipelineID int64 `json:"pipeline_id" xorm:"UNIQUE(s) INDEX 'workflow_pipeline_id'"` - PID int `json:"pid" xorm:"UNIQUE(s) 'workflow_pid'"` - Name string `json:"name" xorm:"workflow_name"` - State StatusValue `json:"state" xorm:"workflow_state"` - Error string `json:"error,omitempty" xorm:"TEXT 'workflow_error'"` - Started int64 `json:"start_time,omitempty" xorm:"workflow_started"` - Stopped int64 `json:"end_time,omitempty" xorm:"workflow_stopped"` - AgentID int64 `json:"agent_id,omitempty" xorm:"workflow_agent_id"` - Platform string `json:"platform,omitempty" xorm:"workflow_platform"` - Environ map[string]string `json:"environ,omitempty" xorm:"json 'workflow_environ'"` - AxisID int `json:"-" xorm:"workflow_axis_id"` + ID int64 `json:"id" xorm:"pk autoincr 'id'"` + PipelineID int64 `json:"pipeline_id" xorm:"UNIQUE(s) INDEX 'pipeline_id'"` + PID int `json:"pid" xorm:"UNIQUE(s) 'pid'"` + Name string `json:"name" xorm:"name"` + State StatusValue `json:"state" xorm:"state"` + Error string `json:"error,omitempty" xorm:"TEXT 'error'"` + Started int64 `json:"start_time,omitempty" xorm:"started"` + Stopped int64 `json:"end_time,omitempty" xorm:"stopped"` + AgentID int64 `json:"agent_id,omitempty" xorm:"agent_id"` + Platform string `json:"platform,omitempty" xorm:"platform"` + Environ map[string]string `json:"environ,omitempty" xorm:"json 'environ'"` + AxisID int `json:"-" xorm:"axis_id"` Children []*Step `json:"children,omitempty" xorm:"-"` } diff --git a/server/store/datastore/config.go b/server/store/datastore/config.go index eda41bd25..b16c415a8 100644 --- a/server/store/datastore/config.go +++ b/server/store/datastore/config.go @@ -29,16 +29,16 @@ import ( func (s storage) ConfigsForPipeline(pipelineID int64) ([]*model.Config, error) { configs := make([]*model.Config, 0, perPage) return configs, s.engine. - Table("config"). - Join("LEFT", "pipeline_config", "config.config_id = pipeline_config.config_id"). - Where("pipeline_config.pipeline_id = ?", pipelineID). + Table("configs"). + Join("LEFT", "pipeline_configs", "configs.id = pipeline_configs.config_id"). + Where("pipeline_configs.pipeline_id = ?", pipelineID). Find(&configs) } func (s storage) configFindIdentical(sess *xorm.Session, repoID int64, hash, name string) (*model.Config, error) { conf := new(model.Config) if err := wrapGet(sess.Where( - builder.Eq{"config_repo_id": repoID, "config_hash": hash, "config_name": name}, + builder.Eq{"repo_id": repoID, "hash": hash, "name": name}, ).Get(conf)); err != nil { return nil, err } diff --git a/server/store/datastore/feed.go b/server/store/datastore/feed.go index 926809386..d06897bf8 100644 --- a/server/store/datastore/feed.go +++ b/server/store/datastore/feed.go @@ -20,30 +20,30 @@ import ( "go.woodpecker-ci.org/woodpecker/v2/server/model" ) -var feedItemSelect = `repos.repo_id as feed_repo_id, -pipelines.pipeline_id as feed_pipeline_id, -pipelines.pipeline_number as feed_pipeline_number, -pipelines.pipeline_event as feed_pipeline_event, -pipelines.pipeline_status as feed_pipeline_status, -pipelines.pipeline_created as feed_pipeline_created, -pipelines.pipeline_started as feed_pipeline_started, -pipelines.pipeline_finished as feed_pipeline_finished, -pipelines.pipeline_commit as feed_pipeline_commit, -pipelines.pipeline_branch as feed_pipeline_branch, -pipelines.pipeline_ref as feed_pipeline_ref, -pipelines.pipeline_refspec as feed_pipeline_refspec, -pipelines.pipeline_title as feed_pipeline_title, -pipelines.pipeline_message as feed_pipeline_message, -pipelines.pipeline_author as feed_pipeline_author, -pipelines.pipeline_email as feed_pipeline_email, -pipelines.pipeline_avatar as feed_pipeline_avatar` +var feedItemSelect = `repos.id as repo_id, +pipelines.id as pipeline_id, +pipelines.number as pipeline_number, +pipelines.event as pipeline_event, +pipelines.status as pipeline_status, +pipelines.created as pipeline_created, +pipelines.started as pipeline_started, +pipelines.finished as pipeline_finished, +'pipelines.commit' as pipeline_commit, +pipelines.branch as pipeline_branch, +pipelines.ref as pipeline_ref, +pipelines.refspec as pipeline_refspec, +pipelines.title as pipeline_title, +pipelines.message as pipeline_message, +pipelines.author as pipeline_author, +pipelines.email as pipeline_email, +pipelines.avatar as pipeline_avatar` func (s storage) GetPipelineQueue() ([]*model.Feed, error) { feed := make([]*model.Feed, 0, perPage) err := s.engine.Table("pipelines"). Select(feedItemSelect). - Join("INNER", "repos", "pipelines.pipeline_repo_id = repos.repo_id"). - In("pipelines.pipeline_status", model.StatusPending, model.StatusRunning). + Join("INNER", "repos", "pipelines.repo_id = repos.id"). + In("pipelines.status", model.StatusPending, model.StatusRunning). Find(&feed) return feed, err } @@ -52,10 +52,10 @@ func (s storage) UserFeed(user *model.User) ([]*model.Feed, error) { feed := make([]*model.Feed, 0, perPage) err := s.engine.Table("repos"). Select(feedItemSelect). - Join("INNER", "perms", "repos.repo_id = perms.perm_repo_id"). - Join("INNER", "pipelines", "repos.repo_id = pipelines.pipeline_repo_id"). + Join("INNER", "perms", "repos.id = perms.repo_id"). + Join("INNER", "pipelines", "repos.id = pipelines.repo_id"). Where(userPushOrAdminCondition(user.ID)). - Desc("pipelines.pipeline_id"). + Desc("pipelines.id"). Limit(perPage). Find(&feed) @@ -67,16 +67,16 @@ func (s storage) RepoListLatest(user *model.User) ([]*model.Feed, error) { err := s.engine.Table("repos"). Select(feedItemSelect). - Join("INNER", "perms", "repos.repo_id = perms.perm_repo_id"). - Join("LEFT", "pipelines", "pipelines.pipeline_id = "+`( - SELECT pipelines.pipeline_id FROM pipelines - WHERE pipelines.pipeline_repo_id = repos.repo_id - ORDER BY pipelines.pipeline_id DESC + Join("INNER", "perms", "repos.id = perms.repo_id"). + Join("LEFT", "pipelines", "pipelines.id = "+`( + SELECT pipelines.id FROM pipelines + WHERE pipelines.repo_id = repos.id + ORDER BY pipelines.id DESC LIMIT 1 )`). Where(userPushOrAdminCondition(user.ID)). - And(builder.Eq{"repos.repo_active": true}). - Asc("repos.repo_full_name"). + And(builder.Eq{"repos.active": true}). + Asc("repos.full_name"). Find(&feed) return feed, err diff --git a/server/store/datastore/migration/021_parent_steps_to_workflows.go b/server/store/datastore/migration/021_parent_steps_to_workflows.go index a9831a19e..a97efb1d2 100644 --- a/server/store/datastore/migration/021_parent_steps_to_workflows.go +++ b/server/store/datastore/migration/021_parent_steps_to_workflows.go @@ -43,7 +43,7 @@ func (oldStep021) TableName() string { var parentStepsToWorkflows = xormigrate.Migration{ ID: "parent-steps-to-workflows", MigrateSession: func(sess *xorm.Session) error { - if err := sess.Sync(new(model.Workflow)); err != nil { + if err := sess.Sync(new(workflowV031)); err != nil { return err } // make sure the columns exist before removing them @@ -58,7 +58,7 @@ var parentStepsToWorkflows = xormigrate.Migration{ } for _, p := range parentSteps { - asWorkflow := &model.Workflow{ + asWorkflow := &workflowV031{ PipelineID: p.PipelineID, PID: p.PID, Name: p.Name, diff --git a/server/store/datastore/migration/022_add_orgs.go b/server/store/datastore/migration/022_add_orgs.go index 35064fef6..9d93e0537 100644 --- a/server/store/datastore/migration/022_add_orgs.go +++ b/server/store/datastore/migration/022_add_orgs.go @@ -66,7 +66,7 @@ var addOrgs = xormigrate.Migration{ } } - if err := sess.Sync(new(model.Org), new(syncRepo022), new(model.User)); err != nil { + if err := sess.Sync(new(model.Org), new(syncRepo022), new(userV031)); err != nil { return fmt.Errorf("sync new models failed: %w", err) } @@ -88,7 +88,7 @@ var addOrgs = xormigrate.Migration{ // check if it's a registered user if _, ok := users[orgName]; !ok { - exist, err := sess.Where("user_login = ?", orgName).Exist(new(model.User)) + exist, err := sess.Where("user_login = ?", orgName).Exist(new(userV031)) if err != nil { return fmt.Errorf("check if user '%s' exist failed: %w", orgName, err) } diff --git a/server/store/datastore/migration/023_add_org_id.go b/server/store/datastore/migration/023_add_org_id.go index da055421b..37d59833a 100644 --- a/server/store/datastore/migration/023_add_org_id.go +++ b/server/store/datastore/migration/023_add_org_id.go @@ -26,12 +26,12 @@ import ( var addOrgID = xormigrate.Migration{ ID: "add-org-id", MigrateSession: func(sess *xorm.Session) error { - if err := sess.Sync(new(model.User)); err != nil { + if err := sess.Sync(new(userV031)); err != nil { return fmt.Errorf("sync new models failed: %w", err) } // get all users - var users []*model.User + var users []*userV031 if err := sess.Find(&users); err != nil { return fmt.Errorf("find all repos failed: %w", err) } diff --git a/server/store/datastore/migration/030_set_default_forge_id.go b/server/store/datastore/migration/030_set_default_forge_id.go index 994ebee16..bbd9300c3 100644 --- a/server/store/datastore/migration/030_set_default_forge_id.go +++ b/server/store/datastore/migration/030_set_default_forge_id.go @@ -23,14 +23,83 @@ import ( "go.woodpecker-ci.org/woodpecker/v2/server/model" ) +type userV030 struct { + ID int64 `xorm:"pk autoincr 'user_id'"` + ForgeID int64 `xorm:"forge_id"` + ForgeRemoteID model.ForgeRemoteID `xorm:"forge_remote_id"` + Login string `xorm:"UNIQUE 'user_login'"` + Token string `xorm:"TEXT 'user_token'"` + Secret string `xorm:"TEXT 'user_secret'"` + Expiry int64 `xorm:"user_expiry"` + Email string `xorm:" varchar(500) 'user_email'"` + Avatar string `xorm:" varchar(500) 'user_avatar'"` + Admin bool `xorm:"user_admin"` + Hash string `xorm:"UNIQUE varchar(500) 'user_hash'"` + OrgID int64 `xorm:"user_org_id"` +} + +func (userV030) TableName() string { + return "users" +} + +type repoV030 struct { + ID int64 `xorm:"pk autoincr 'repo_id'"` + UserID int64 `xorm:"repo_user_id"` + ForgeID int64 `xorm:"forge_id"` + ForgeRemoteID model.ForgeRemoteID `xorm:"forge_remote_id"` + OrgID int64 `xorm:"repo_org_id"` + Owner string `xorm:"UNIQUE(name) 'repo_owner'"` + Name string `xorm:"UNIQUE(name) 'repo_name'"` + FullName string `xorm:"UNIQUE 'repo_full_name'"` + Avatar string `xorm:"varchar(500) 'repo_avatar'"` + ForgeURL string `xorm:"varchar(1000) 'repo_forge_url'"` + Clone string `xorm:"varchar(1000) 'repo_clone'"` + CloneSSH string `xorm:"varchar(1000) 'repo_clone_ssh'"` + Branch string `xorm:"varchar(500) 'repo_branch'"` + SCMKind model.SCMKind `xorm:"varchar(50) 'repo_scm'"` + PREnabled bool `xorm:"DEFAULT TRUE 'repo_pr_enabled'"` + Timeout int64 `xorm:"repo_timeout"` + Visibility model.RepoVisibility `xorm:"varchar(10) 'repo_visibility'"` + IsSCMPrivate bool `xorm:"repo_private"` + IsTrusted bool `xorm:"repo_trusted"` + IsGated bool `xorm:"repo_gated"` + IsActive bool `xorm:"repo_active"` + AllowPull bool `xorm:"repo_allow_pr"` + AllowDeploy bool `xorm:"repo_allow_deploy"` + Config string `xorm:"varchar(500) 'repo_config_path'"` + Hash string `xorm:"varchar(500) 'repo_hash'"` + Perm *model.Perm `xorm:"-"` + CancelPreviousPipelineEvents []model.WebhookEvent `xorm:"json 'cancel_previous_pipeline_events'"` + NetrcOnlyTrusted bool `xorm:"NOT NULL DEFAULT true 'netrc_only_trusted'"` +} + +func (repoV030) TableName() string { + return "repos" +} + +type forgeV030 struct { + ID int64 `xorm:"pk autoincr 'id'"` + Type model.ForgeType `xorm:"VARCHAR(250) 'type'"` + URL string `xorm:"VARCHAR(500) 'url'"` + Client string `xorm:"VARCHAR(250) 'client'"` + ClientSecret string `xorm:"VARCHAR(250) 'client_secret'"` + SkipVerify bool `xorm:"bool 'skip_verify'"` + OAuthHost string `xorm:"VARCHAR(250) 'oauth_host'"` // public url for oauth if different from url + AdditionalOptions map[string]any `xorm:"json 'additional_options'"` +} + +func (forgeV030) TableName() string { + return "forge" +} + var setForgeID = xormigrate.Migration{ ID: "set-forge-id", MigrateSession: func(sess *xorm.Session) (err error) { - if err := sess.Sync(new(model.User), new(model.Repo), new(model.Forge), new(model.Org)); err != nil { + if err := sess.Sync(new(userV030), new(repoV030), new(forgeV030), new(model.Org)); err != nil { return fmt.Errorf("sync new models failed: %w", err) } - _, err = sess.Exec(fmt.Sprintf("UPDATE `%s` SET forge_id=1;", model.User{}.TableName())) + _, err = sess.Exec(fmt.Sprintf("UPDATE `%s` SET forge_id=1;", userV030{}.TableName())) if err != nil { return err } @@ -40,7 +109,7 @@ var setForgeID = xormigrate.Migration{ return err } - _, err = sess.Exec(fmt.Sprintf("UPDATE `%s` SET forge_id=1;", model.Repo{}.TableName())) + _, err = sess.Exec(fmt.Sprintf("UPDATE `%s` SET forge_id=1;", repoV030{}.TableName())) return err }, } diff --git a/server/store/datastore/migration/031_unify_columns_tables.go b/server/store/datastore/migration/031_unify_columns_tables.go new file mode 100644 index 000000000..881832b0f --- /dev/null +++ b/server/store/datastore/migration/031_unify_columns_tables.go @@ -0,0 +1,637 @@ +// Copyright 2024 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package migration + +import ( + "fmt" + + "src.techknowlogick.com/xormigrate" + "xorm.io/xorm" + + "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types" + "go.woodpecker-ci.org/woodpecker/v2/server/model" +) + +type configV031 struct { + ID int64 `xorm:"pk autoincr 'config_id'"` + RepoID int64 `xorm:"UNIQUE(s) 'config_repo_id'"` + Hash string `xorm:"UNIQUE(s) 'config_hash'"` + Name string `xorm:"UNIQUE(s) 'config_name'"` + Data []byte `xorm:"LONGBLOB 'config_data'"` +} + +func (configV031) TableName() string { + return "config" +} + +type cronV031 struct { + ID int64 `xorm:"pk autoincr 'i_d'"` + Name string `xorm:"name UNIQUE(s) INDEX"` + RepoID int64 `xorm:"repo_id UNIQUE(s) INDEX"` + CreatorID int64 `xorm:"creator_id INDEX"` + NextExec int64 `xorm:"next_exec"` + Schedule string `xorm:"schedule NOT NULL"` + Created int64 `xorm:"created NOT NULL DEFAULT 0"` + Branch string `xorm:"branch"` +} + +func (cronV031) TableName() string { + return "crons" +} + +type permV031 struct { + UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL 'perm_user_id'"` + RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL 'perm_repo_id'"` + Pull bool `xorm:"perm_pull"` + Push bool `xorm:"perm_push"` + Admin bool `xorm:"perm_admin"` + Synced int64 `xorm:"perm_synced"` +} + +func (permV031) TableName() string { + return "perms" +} + +type pipelineV031 struct { + ID int64 `xorm:"pk autoincr 'pipeline_id'"` + RepoID int64 `xorm:"UNIQUE(s) INDEX 'pipeline_repo_id'"` + Number int64 `xorm:"UNIQUE(s) 'pipeline_number'"` + Author string `xorm:"INDEX 'pipeline_author'"` + Parent int64 `xorm:"pipeline_parent"` + Event model.WebhookEvent `xorm:"pipeline_event"` + Status model.StatusValue `xorm:"INDEX 'pipeline_status'"` + Errors []*types.PipelineError `xorm:"json 'pipeline_errors'"` + Created int64 `xorm:"pipeline_created"` + Started int64 `xorm:"pipeline_started"` + Finished int64 `xorm:"pipeline_finished"` + Deploy string `xorm:"pipeline_deploy"` + DeployTask string `xorm:"pipeline_deploy_task"` + Commit string `xorm:"pipeline_commit"` + Branch string `xorm:"pipeline_branch"` + Ref string `xorm:"pipeline_ref"` + Refspec string `xorm:"pipeline_refspec"` + Title string `xorm:"pipeline_title"` + Message string `xorm:"TEXT 'pipeline_message'"` + Timestamp int64 `xorm:"pipeline_timestamp"` + Sender string `xorm:"pipeline_sender"` // uses reported user for webhooks and name of cron for cron pipelines + Avatar string `xorm:"pipeline_avatar"` + Email string `xorm:"pipeline_email"` + ForgeURL string `xorm:"pipeline_forge_url"` + Reviewer string `xorm:"pipeline_reviewer"` + Reviewed int64 `xorm:"pipeline_reviewed"` +} + +func (pipelineV031) TableName() string { + return "pipelines" +} + +type redirectionV031 struct { + ID int64 `xorm:"pk autoincr 'redirection_id'"` +} + +func (r redirectionV031) TableName() string { + return "redirections" +} + +type registryV031 struct { + ID int64 `xorm:"pk autoincr 'registry_id'"` + RepoID int64 `xorm:"UNIQUE(s) INDEX 'registry_repo_id'"` + Address string `xorm:"UNIQUE(s) INDEX 'registry_addr'"` + Username string `xorm:"varchar(2000) 'registry_username'"` + Password string `xorm:"TEXT 'registry_password'"` +} + +type repoV031 struct { + ID int64 `xorm:"pk autoincr 'repo_id'"` + UserID int64 `xorm:"repo_user_id"` + OrgID int64 `xorm:"repo_org_id"` + Owner string `xorm:"UNIQUE(name) 'repo_owner'"` + Name string `xorm:"UNIQUE(name) 'repo_name'"` + FullName string `xorm:"UNIQUE 'repo_full_name'"` + Avatar string `xorm:"varchar(500) 'repo_avatar'"` + ForgeURL string `xorm:"varchar(1000) 'repo_forge_url'"` + Clone string `xorm:"varchar(1000) 'repo_clone'"` + CloneSSH string `xorm:"varchar(1000) 'repo_clone_ssh'"` + Branch string `xorm:"varchar(500) 'repo_branch'"` + SCMKind model.SCMKind `xorm:"varchar(50) 'repo_scm'"` + PREnabled bool `xorm:"DEFAULT TRUE 'repo_pr_enabled'"` + Timeout int64 `xorm:"repo_timeout"` + Visibility model.RepoVisibility `xorm:"varchar(10) 'repo_visibility'"` + IsSCMPrivate bool `xorm:"repo_private"` + IsTrusted bool `xorm:"repo_trusted"` + IsGated bool `xorm:"repo_gated"` + IsActive bool `xorm:"repo_active"` + AllowPull bool `xorm:"repo_allow_pr"` + AllowDeploy bool `xorm:"repo_allow_deploy"` + Config string `xorm:"varchar(500) 'repo_config_path'"` + Hash string `xorm:"varchar(500) 'repo_hash'"` +} + +func (repoV031) TableName() string { + return "repos" +} + +type secretV031 struct { + ID int64 `xorm:"pk autoincr 'secret_id'"` + OrgID int64 `xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'secret_org_id'"` + RepoID int64 `xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'secret_repo_id'"` + Name string `xorm:"NOT NULL UNIQUE(s) INDEX 'secret_name'"` + Value string `xorm:"TEXT 'secret_value'"` + Images []string `xorm:"json 'secret_images'"` + Events []model.WebhookEvent `xorm:"json 'secret_events'"` +} + +func (secretV031) TableName() string { + return "secrets" +} + +type stepV031 struct { + ID int64 `xorm:"pk autoincr 'step_id'"` + UUID string `xorm:"INDEX 'step_uuid'"` + PipelineID int64 `xorm:"UNIQUE(s) INDEX 'step_pipeline_id'"` + PID int `xorm:"UNIQUE(s) 'step_pid'"` + PPID int `xorm:"step_ppid"` + Name string `xorm:"step_name"` + State model.StatusValue `xorm:"step_state"` + Error string `xorm:"TEXT 'step_error'"` + Failure string `xorm:"step_failure"` + ExitCode int `xorm:"step_exit_code"` + Started int64 `xorm:"step_started"` + Stopped int64 `xorm:"step_stopped"` + Type model.StepType `xorm:"step_type"` +} + +func (stepV031) TableName() string { + return "steps" +} + +type taskV031 struct { + ID string `xorm:"PK UNIQUE 'task_id'"` + Data []byte `xorm:"LONGBLOB 'task_data'"` + Labels map[string]string `xorm:"json 'task_labels'"` + Dependencies []string `xorm:"json 'task_dependencies'"` + RunOn []string `xorm:"json 'task_run_on'"` + DepStatus map[string]model.StatusValue `xorm:"json 'task_dep_status'"` +} + +func (taskV031) TableName() string { + return "tasks" +} + +type userV031 struct { + ID int64 `xorm:"pk autoincr 'user_id'"` + Login string `xorm:"UNIQUE 'user_login'"` + Token string `xorm:"TEXT 'user_token'"` + Secret string `xorm:"TEXT 'user_secret'"` + Expiry int64 `xorm:"user_expiry"` + Email string `xorm:" varchar(500) 'user_email'"` + Avatar string `xorm:" varchar(500) 'user_avatar'"` + Admin bool `xorm:"user_admin"` + Hash string `xorm:"UNIQUE varchar(500) 'user_hash'"` + OrgID int64 `xorm:"user_org_id"` +} + +func (userV031) TableName() string { + return "users" +} + +type workflowV031 struct { + ID int64 `xorm:"pk autoincr 'workflow_id'"` + PipelineID int64 `xorm:"UNIQUE(s) INDEX 'workflow_pipeline_id'"` + PID int `xorm:"UNIQUE(s) 'workflow_pid'"` + Name string `xorm:"workflow_name"` + State model.StatusValue `xorm:"workflow_state"` + Error string `xorm:"TEXT 'workflow_error'"` + Started int64 `xorm:"workflow_started"` + Stopped int64 `xorm:"workflow_stopped"` + AgentID int64 `xorm:"workflow_agent_id"` + Platform string `xorm:"workflow_platform"` + Environ map[string]string `xorm:"json 'workflow_environ'"` + AxisID int `xorm:"workflow_axis_id"` +} + +func (workflowV031) TableName() string { + return "workflows" +} + +type serverConfigV031 struct { + Key string `xorm:"pk 'key'"` + Value string `xorm:"value"` +} + +func (serverConfigV031) TableName() string { + return "server_config" +} + +var unifyColumnsTables = xormigrate.Migration{ + ID: "unify-columns-tables", + MigrateSession: func(sess *xorm.Session) (err error) { + if err := sess.Sync(new(configV031), new(cronV031), new(permV031), new(pipelineV031), new(redirectionV031), new(registryV031), new(repoV031), new(secretV031), new(stepV031), new(taskV031), new(userV031), new(workflowV031), new(serverConfigV031)); err != nil { + return fmt.Errorf("sync models failed: %w", err) + } + + // Config + if err := renameColumn(sess, "config", "config_id", "id"); err != nil { + return err + } + if err := renameColumn(sess, "config", "config_repo_id", "repo_id"); err != nil { + return err + } + if err := renameColumn(sess, "config", "config_hash", "hash"); err != nil { + return err + } + if err := renameColumn(sess, "config", "config_name", "name"); err != nil { + return err + } + if err := renameColumn(sess, "config", "config_data", "data"); err != nil { + return err + } + if err := renameTable(sess, "config", "configs"); err != nil { + return err + } + + // PipelineConfig + if err := renameTable(sess, "pipeline_config", "pipeline_configs"); err != nil { + return err + } + + // Cron + if err := renameColumn(sess, "crons", "i_d", "id"); err != nil { + return err + } + + // Forge + if err := renameTable(sess, "forge", "forges"); err != nil { + return err + } + + // Perm + if err := renameColumn(sess, "perms", "perm_user_id", "user_id"); err != nil { + return err + } + if err := renameColumn(sess, "perms", "perm_repo_id", "repo_id"); err != nil { + return err + } + if err := renameColumn(sess, "perms", "perm_pull", "pull"); err != nil { + return err + } + if err := renameColumn(sess, "perms", "perm_push", "push"); err != nil { + return err + } + if err := renameColumn(sess, "perms", "perm_admin", "admin"); err != nil { + return err + } + if err := renameColumn(sess, "perms", "perm_synced", "synced"); err != nil { + return err + } + + // Pipeline + if err := renameColumn(sess, "pipelines", "pipeline_id", "id"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_repo_id", "repo_id"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_number", "number"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_author", "author"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_parent", "parent"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_event", "event"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_status", "status"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_errors", "errors"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_created", "created"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_started", "started"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_finished", "finished"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_deploy", "deploy"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_deploy_task", "deploy_task"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_commit", "commit"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_branch", "branch"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_ref", "ref"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_refspec", "refspec"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_title", "title"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_message", "message"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_timestamp", "timestamp"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_sender", "sender"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_avatar", "avatar"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_email", "email"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_forge_url", "forge_url"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_reviewer", "reviewer"); err != nil { + return err + } + if err := renameColumn(sess, "pipelines", "pipeline_reviewed", "reviewed"); err != nil { + return err + } + + // Redirection + if err := renameColumn(sess, "redirections", "redirection_id", "id"); err != nil { + return err + } + + // Registry + if err := renameColumn(sess, "registry", "registry_id", "id"); err != nil { + return err + } + if err := renameColumn(sess, "registry", "registry_repo_id", "repo_id"); err != nil { + return err + } + if err := renameColumn(sess, "registry", "registry_addr", "address"); err != nil { + return err + } + if err := renameColumn(sess, "registry", "registry_username", "username"); err != nil { + return err + } + if err := renameColumn(sess, "registry", "registry_password", "password"); err != nil { + return err + } + if err := renameTable(sess, "registry", "registries"); err != nil { + return err + } + + // Repo + if err := renameColumn(sess, "repos", "repo_id", "id"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_user_id", "user_id"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_org_id", "org_id"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_owner", "owner"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_name", "name"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_full_name", "full_name"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_avatar", "avatar"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_forge_url", "forge_url"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_clone", "clone"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_clone_ssh", "clone_ssh"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_branch", "branch"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_scm", "scm"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_pr_enabled", "pr_enabled"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_timeout", "timeout"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_visibility", "visibility"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_private", "private"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_trusted", "trusted"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_gated", "gated"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_active", "active"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_allow_pr", "allow_pr"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_allow_deploy", "allow_deploy"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_config_path", "config_path"); err != nil { + return err + } + if err := renameColumn(sess, "repos", "repo_hash", "hash"); err != nil { + return err + } + + // Secrets + if err := renameColumn(sess, "secrets", "secret_id", "id"); err != nil { + return err + } + if err := renameColumn(sess, "secrets", "secret_org_id", "org_id"); err != nil { + return err + } + if err := renameColumn(sess, "secrets", "secret_repo_id", "repo_id"); err != nil { + return err + } + if err := renameColumn(sess, "secrets", "secret_name", "name"); err != nil { + return err + } + if err := renameColumn(sess, "secrets", "secret_value", "value"); err != nil { + return err + } + if err := renameColumn(sess, "secrets", "secret_images", "images"); err != nil { + return err + } + if err := renameColumn(sess, "secrets", "secret_events", "events"); err != nil { + return err + } + + // ServerConfig + if err := renameTable(sess, "server_config", "server_configs"); err != nil { + return err + } + + // Step + if err := renameColumn(sess, "steps", "step_id", "id"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_uuid", "uuid"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_pipeline_id", "pipeline_id"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_pid", "pid"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_ppid", "ppid"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_name", "name"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_state", "state"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_error", "error"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_failure", "failure"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_exit_code", "exit_code"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_started", "started"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_stopped", "stopped"); err != nil { + return err + } + if err := renameColumn(sess, "steps", "step_type", "type"); err != nil { + return err + } + + // Task + if err := renameColumn(sess, "tasks", "task_id", "id"); err != nil { + return err + } + if err := renameColumn(sess, "tasks", "task_data", "data"); err != nil { + return err + } + if err := renameColumn(sess, "tasks", "task_labels", "labels"); err != nil { + return err + } + if err := renameColumn(sess, "tasks", "task_dependencies", "dependencies"); err != nil { + return err + } + if err := renameColumn(sess, "tasks", "task_run_on", "run_on"); err != nil { + return err + } + if err := renameColumn(sess, "tasks", "task_dep_status", "dependencies_status"); err != nil { + return err + } + + // User + if err := renameColumn(sess, "users", "user_id", "id"); err != nil { + return err + } + if err := renameColumn(sess, "users", "user_login", "login"); err != nil { + return err + } + if err := renameColumn(sess, "users", "user_token", "token"); err != nil { + return err + } + if err := renameColumn(sess, "users", "user_secret", "secret"); err != nil { + return err + } + if err := renameColumn(sess, "users", "user_expiry", "expiry"); err != nil { + return err + } + if err := renameColumn(sess, "users", "user_email", "email"); err != nil { + return err + } + if err := renameColumn(sess, "users", "user_avatar", "avatar"); err != nil { + return err + } + if err := renameColumn(sess, "users", "user_admin", "admin"); err != nil { + return err + } + if err := renameColumn(sess, "users", "user_hash", "hash"); err != nil { + return err + } + if err := renameColumn(sess, "users", "user_org_id", "org_id"); err != nil { + return err + } + + // Workflow + if err := renameColumn(sess, "workflows", "workflow_id", "id"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_pipeline_id", "pipeline_id"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_pid", "pid"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_name", "name"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_state", "state"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_error", "error"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_started", "started"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_stopped", "stopped"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_agent_id", "agent_id"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_platform", "platform"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_environ", "environ"); err != nil { + return err + } + if err := renameColumn(sess, "workflows", "workflow_axis_id", "axis_id"); err != nil { + return err + } + + return nil + }, +} diff --git a/server/store/datastore/migration/migration.go b/server/store/datastore/migration/migration.go index ac21feb1c..bcaac4e31 100644 --- a/server/store/datastore/migration/migration.go +++ b/server/store/datastore/migration/migration.go @@ -60,6 +60,7 @@ var migrationTasks = []*xormigrate.Migration{ &renameLinkToURL, &cleanRegistryPipeline, &setForgeID, + &unifyColumnsTables, } var allBeans = []any{ diff --git a/server/store/datastore/org.go b/server/store/datastore/org.go index 05cdbb434..b432fb65c 100644 --- a/server/store/datastore/org.go +++ b/server/store/datastore/org.go @@ -56,12 +56,12 @@ func (s storage) OrgDelete(id int64) error { } func (s storage) orgDelete(sess *xorm.Session, id int64) error { - if _, err := sess.Where("secret_org_id = ?", id).Delete(new(model.Secret)); err != nil { + if _, err := sess.Where("org_id = ?", id).Delete(new(model.Secret)); err != nil { return err } var repos []*model.Repo - if err := sess.Where("repo_org_id = ?", id).Find(&repos); err != nil { + if err := sess.Where("org_id = ?", id).Find(&repos); err != nil { return err } @@ -84,7 +84,7 @@ func (s storage) OrgFindByName(name string) (*model.Org, error) { func (s storage) OrgRepoList(org *model.Org, p *model.ListOptions) ([]*model.Repo, error) { var repos []*model.Repo - return repos, s.paginate(p).OrderBy("repo_id").Where("repo_org_id = ?", org.ID).Find(&repos) + return repos, s.paginate(p).OrderBy("id").Where("org_id = ?", org.ID).Find(&repos) } func (s storage) OrgList(p *model.ListOptions) ([]*model.Org, error) { diff --git a/server/store/datastore/permission.go b/server/store/datastore/permission.go index 16e95edb6..22736fdc3 100644 --- a/server/store/datastore/permission.go +++ b/server/store/datastore/permission.go @@ -26,7 +26,7 @@ import ( func (s storage) PermFind(user *model.User, repo *model.Repo) (*model.Perm, error) { perm := new(model.Perm) return perm, wrapGet(s.engine. - Where(builder.Eq{"perm_user_id": user.ID, "perm_repo_id": repo.ID}). + Where(builder.Eq{"user_id": user.ID, "repo_id": repo.ID}). Get(perm)) } @@ -75,11 +75,11 @@ func (s storage) permUpsert(sess *xorm.Session, perm *model.Perm) error { // userPushOrAdminCondition return condition where user must have push or admin rights // if used make sure to have permission table ("perms") joined. func userPushOrAdminCondition(userID int64) builder.Cond { - return builder.Eq{"perms.perm_user_id": userID}. - And(builder.Eq{"perms.perm_push": true}. - Or(builder.Eq{"perms.perm_admin": true})) + return builder.Eq{"perms.user_id": userID}. + And(builder.Eq{"perms.push": true}. + Or(builder.Eq{"perms.admin": true})) } func userIDAndRepoIDCond(perm *model.Perm) builder.Cond { - return builder.Eq{"perm_user_id": perm.UserID, "perm_repo_id": perm.RepoID} + return builder.Eq{"user_id": perm.UserID, "repo_id": perm.RepoID} } diff --git a/server/store/datastore/pipeline.go b/server/store/datastore/pipeline.go index fc210a43e..41cc1e780 100644 --- a/server/store/datastore/pipeline.go +++ b/server/store/datastore/pipeline.go @@ -31,44 +31,44 @@ func (s storage) GetPipeline(id int64) (*model.Pipeline, error) { func (s storage) GetPipelineNumber(repo *model.Repo, num int64) (*model.Pipeline, error) { pipeline := new(model.Pipeline) return pipeline, wrapGet(s.engine.Where( - builder.Eq{"pipeline_repo_id": repo.ID, "pipeline_number": num}, + builder.Eq{"repo_id": repo.ID, "number": num}, ).Get(pipeline)) } func (s storage) GetPipelineLast(repo *model.Repo, branch string) (*model.Pipeline, error) { pipeline := new(model.Pipeline) return pipeline, wrapGet(s.engine. - Desc("pipeline_number"). - Where(builder.Eq{"pipeline_repo_id": repo.ID, "pipeline_branch": branch, "pipeline_event": model.EventPush}). + Desc("number"). + Where(builder.Eq{"repo_id": repo.ID, "branch": branch, "event": model.EventPush}). Get(pipeline)) } func (s storage) GetPipelineLastBefore(repo *model.Repo, branch string, num int64) (*model.Pipeline, error) { pipeline := new(model.Pipeline) return pipeline, wrapGet(s.engine. - Desc("pipeline_number"). - Where(builder.Lt{"pipeline_id": num}. - And(builder.Eq{"pipeline_repo_id": repo.ID, "pipeline_branch": branch})). + Desc("number"). + Where(builder.Lt{"id": num}. + And(builder.Eq{"repo_id": repo.ID, "branch": branch})). Get(pipeline)) } func (s storage) GetPipelineList(repo *model.Repo, p *model.ListOptions, f *model.PipelineFilter) ([]*model.Pipeline, error) { pipelines := make([]*model.Pipeline, 0, 16) - cond := builder.NewCond().And(builder.Eq{"pipeline_repo_id": repo.ID}) + cond := builder.NewCond().And(builder.Eq{"repo_id": repo.ID}) if f != nil { if f.After != 0 { - cond = cond.And(builder.Gt{"pipeline_created": f.After}) + cond = cond.And(builder.Gt{"created": f.After}) } if f.Before != 0 { - cond = cond.And(builder.Lt{"pipeline_created": f.Before}) + cond = cond.And(builder.Lt{"created": f.Before}) } } return pipelines, s.paginate(p).Where(cond). - Desc("pipeline_number"). + Desc("number"). Find(&pipelines) } @@ -76,9 +76,9 @@ func (s storage) GetPipelineList(repo *model.Repo, p *model.ListOptions, f *mode func (s storage) GetActivePipelineList(repo *model.Repo) ([]*model.Pipeline, error) { pipelines := make([]*model.Pipeline, 0) query := s.engine. - Where("pipeline_repo_id = ?", repo.ID). - In("pipeline_status", model.StatusPending, model.StatusRunning, model.StatusBlocked). - Desc("pipeline_number") + Where("repo_id = ?", repo.ID). + In("status", model.StatusPending, model.StatusRunning, model.StatusBlocked). + Desc("number") return pipelines, query.Find(&pipelines) } @@ -93,7 +93,7 @@ func (s storage) CreatePipeline(pipeline *model.Pipeline, stepList ...*model.Ste return err } - repoExist, err := sess.Where("repo_id = ?", pipeline.RepoID).Exist(&model.Repo{}) + repoExist, err := sess.Where("id = ?", pipeline.RepoID).Exist(&model.Repo{}) if err != nil { return err } @@ -104,9 +104,9 @@ func (s storage) CreatePipeline(pipeline *model.Pipeline, stepList ...*model.Ste // calc pipeline number var number int64 - if _, err := sess.Select("MAX(pipeline_number)"). + if _, err := sess.Select("MAX(number)"). Table(new(model.Pipeline)). - Where("pipeline_repo_id = ?", pipeline.RepoID). + Where("repo_id = ?", pipeline.RepoID). Get(&number); err != nil { return err } @@ -154,7 +154,7 @@ func (s storage) deletePipeline(sess *xorm.Session, pipelineID int64) error { } if !exist { // this config is only used for this pipeline. so delete it - if _, err := sess.Where(builder.Eq{"config_id": confID}).Delete(new(model.Config)); err != nil { + if _, err := sess.Where(builder.Eq{"id": confID}).Delete(new(model.Config)); err != nil { return err } } diff --git a/server/store/datastore/registry.go b/server/store/datastore/registry.go index 782d98c4d..924b9a7e1 100644 --- a/server/store/datastore/registry.go +++ b/server/store/datastore/registry.go @@ -23,13 +23,13 @@ import ( func (s storage) RegistryFind(repo *model.Repo, addr string) (*model.Registry, error) { reg := new(model.Registry) return reg, wrapGet(s.engine.Where( - builder.Eq{"registry_repo_id": repo.ID, "registry_addr": addr}, + builder.Eq{"repo_id": repo.ID, "address": addr}, ).Get(reg)) } func (s storage) RegistryList(repo *model.Repo, p *model.ListOptions) ([]*model.Registry, error) { var regs []*model.Registry - return regs, s.paginate(p).OrderBy("registry_id").Where("registry_repo_id = ?", repo.ID).Find(®s) + return regs, s.paginate(p).OrderBy("id").Where("repo_id = ?", repo.ID).Find(®s) } func (s storage) RegistryCreate(registry *model.Registry) error { diff --git a/server/store/datastore/repo.go b/server/store/datastore/repo.go index fdc2043fb..f373b17cd 100644 --- a/server/store/datastore/repo.go +++ b/server/store/datastore/repo.go @@ -73,11 +73,11 @@ func (s storage) GetRepoName(fullName string) (*model.Repo, error) { func (s storage) getRepoName(e *xorm.Session, fullName string) (*model.Repo, error) { repo := new(model.Repo) - return repo, wrapGet(e.Where("LOWER(repo_full_name) = ?", strings.ToLower(fullName)).Get(repo)) + return repo, wrapGet(e.Where("LOWER(full_name) = ?", strings.ToLower(fullName)).Get(repo)) } func (s storage) GetRepoCount() (int64, error) { - return s.engine.Where(builder.Eq{"repo_active": true}).Count(new(model.Repo)) + return s.engine.Where(builder.Eq{"active": true}).Count(new(model.Repo)) } func (s storage) CreateRepo(repo *model.Repo) error { @@ -105,16 +105,16 @@ func (s storage) DeleteRepo(repo *model.Repo) error { func (s storage) deleteRepo(sess *xorm.Session, repo *model.Repo) error { const batchSize = perPage - if _, err := sess.Where("config_repo_id = ?", repo.ID).Delete(new(model.Config)); err != nil { + if _, err := sess.Where("repo_id = ?", repo.ID).Delete(new(model.Config)); err != nil { return err } - if _, err := sess.Where("perm_repo_id = ?", repo.ID).Delete(new(model.Perm)); err != nil { + if _, err := sess.Where("repo_id = ?", repo.ID).Delete(new(model.Perm)); err != nil { return err } - if _, err := sess.Where("registry_repo_id = ?", repo.ID).Delete(new(model.Registry)); err != nil { + if _, err := sess.Where("repo_id = ?", repo.ID).Delete(new(model.Registry)); err != nil { return err } - if _, err := sess.Where("secret_repo_id = ?", repo.ID).Delete(new(model.Secret)); err != nil { + if _, err := sess.Where("repo_id = ?", repo.ID).Delete(new(model.Secret)); err != nil { return err } if _, err := sess.Where("repo_id = ?", repo.ID).Delete(new(model.Redirection)); err != nil { @@ -124,7 +124,7 @@ func (s storage) deleteRepo(sess *xorm.Session, repo *model.Repo) error { // delete related pipelines for startPipelines := 0; ; startPipelines += batchSize { pipelineIDs := make([]int64, 0, batchSize) - if err := sess.Limit(batchSize, startPipelines).Table("pipelines").Cols("pipeline_id").Where("pipeline_repo_id = ?", repo.ID).Find(&pipelineIDs); err != nil { + if err := sess.Limit(batchSize, startPipelines).Table("pipelines").Cols("id").Where("repo_id = ?", repo.ID).Find(&pipelineIDs); err != nil { return err } if len(pipelineIDs) == 0 { @@ -146,16 +146,16 @@ func (s storage) deleteRepo(sess *xorm.Session, repo *model.Repo) error { func (s storage) RepoList(user *model.User, owned, active bool) ([]*model.Repo, error) { repos := make([]*model.Repo, 0) sess := s.engine.Table("repos"). - Join("INNER", "perms", "perms.perm_repo_id = repos.repo_id"). - Where("perms.perm_user_id = ?", user.ID) + Join("INNER", "perms", "perms.repo_id = repos.id"). + Where("perms.user_id = ?", user.ID) if owned { - sess = sess.And(builder.Eq{"perms.perm_push": true}.Or(builder.Eq{"perms.perm_admin": true})) + sess = sess.And(builder.Eq{"perms.push": true}.Or(builder.Eq{"perms.admin": true})) } if active { - sess = sess.And(builder.Eq{"repos.repo_active": true}) + sess = sess.And(builder.Eq{"repos.active": true}) } return repos, sess. - Asc("repo_full_name"). + Asc("full_name"). Find(&repos) } @@ -164,9 +164,9 @@ func (s storage) RepoListAll(active bool, p *model.ListOptions) ([]*model.Repo, repos := make([]*model.Repo, 0) sess := s.paginate(p).Table("repos") if active { - sess = sess.And(builder.Eq{"repos.repo_active": true}) + sess = sess.And(builder.Eq{"repos.active": true}) } return repos, sess. - Asc("repo_full_name"). + Asc("full_name"). Find(&repos) } diff --git a/server/store/datastore/secret.go b/server/store/datastore/secret.go index 85100e49a..9a481ab1b 100644 --- a/server/store/datastore/secret.go +++ b/server/store/datastore/secret.go @@ -20,21 +20,21 @@ import ( "go.woodpecker-ci.org/woodpecker/v2/server/model" ) -const orderSecretsBy = "secret_name" +const orderSecretsBy = "name" func (s storage) SecretFind(repo *model.Repo, name string) (*model.Secret, error) { secret := new(model.Secret) return secret, wrapGet(s.engine.Where( - builder.Eq{"secret_repo_id": repo.ID, "secret_name": name}, + builder.Eq{"repo_id": repo.ID, "name": name}, ).Get(secret)) } func (s storage) SecretList(repo *model.Repo, includeGlobalAndOrgSecrets bool, p *model.ListOptions) ([]*model.Secret, error) { var secrets []*model.Secret - var cond builder.Cond = builder.Eq{"secret_repo_id": repo.ID} + var cond builder.Cond = builder.Eq{"repo_id": repo.ID} if includeGlobalAndOrgSecrets { - cond = cond.Or(builder.Eq{"secret_org_id": repo.OrgID}). - Or(builder.And(builder.Eq{"secret_org_id": 0}, builder.Eq{"secret_repo_id": 0})) + cond = cond.Or(builder.Eq{"org_id": repo.OrgID}). + Or(builder.And(builder.Eq{"org_id": 0}, builder.Eq{"repo_id": 0})) } return secrets, s.paginate(p).Where(cond).OrderBy(orderSecretsBy).Find(&secrets) } @@ -62,25 +62,25 @@ func (s storage) SecretDelete(secret *model.Secret) error { func (s storage) OrgSecretFind(orgID int64, name string) (*model.Secret, error) { secret := new(model.Secret) return secret, wrapGet(s.engine.Where( - builder.Eq{"secret_org_id": orgID, "secret_name": name}, + builder.Eq{"org_id": orgID, "name": name}, ).Get(secret)) } func (s storage) OrgSecretList(orgID int64, p *model.ListOptions) ([]*model.Secret, error) { secrets := make([]*model.Secret, 0) - return secrets, s.paginate(p).Where("secret_org_id = ?", orgID).OrderBy(orderSecretsBy).Find(&secrets) + return secrets, s.paginate(p).Where("org_id = ?", orgID).OrderBy(orderSecretsBy).Find(&secrets) } func (s storage) GlobalSecretFind(name string) (*model.Secret, error) { secret := new(model.Secret) return secret, wrapGet(s.engine.Where( - builder.Eq{"secret_org_id": 0, "secret_repo_id": 0, "secret_name": name}, + builder.Eq{"org_id": 0, "repo_id": 0, "name": name}, ).Get(secret)) } func (s storage) GlobalSecretList(p *model.ListOptions) ([]*model.Secret, error) { secrets := make([]*model.Secret, 0) return secrets, s.paginate(p).Where( - builder.Eq{"secret_org_id": 0, "secret_repo_id": 0}, + builder.Eq{"org_id": 0, "repo_id": 0}, ).OrderBy(orderSecretsBy).Find(&secrets) } diff --git a/server/store/datastore/step.go b/server/store/datastore/step.go index f7739f4ad..3a4a43b80 100644 --- a/server/store/datastore/step.go +++ b/server/store/datastore/step.go @@ -29,29 +29,29 @@ func (s storage) StepLoad(id int64) (*model.Step, error) { func (s storage) StepFind(pipeline *model.Pipeline, pid int) (*model.Step, error) { step := new(model.Step) return step, wrapGet(s.engine.Where( - builder.Eq{"step_pipeline_id": pipeline.ID, "step_pid": pid}, + builder.Eq{"pipeline_id": pipeline.ID, "pid": pid}, ).Get(step)) } func (s storage) StepByUUID(uuid string) (*model.Step, error) { step := new(model.Step) return step, wrapGet(s.engine.Where( - builder.Eq{"step_uuid": uuid}, + builder.Eq{"uuid": uuid}, ).Get(step)) } func (s storage) StepChild(pipeline *model.Pipeline, ppid int, child string) (*model.Step, error) { step := new(model.Step) return step, wrapGet(s.engine.Where( - builder.Eq{"step_pipeline_id": pipeline.ID, "step_ppid": ppid, "step_name": child}, + builder.Eq{"pipeline_id": pipeline.ID, "ppid": ppid, "name": child}, ).Get(step)) } func (s storage) StepList(pipeline *model.Pipeline) ([]*model.Step, error) { stepList := make([]*model.Step, 0) return stepList, s.engine. - Where("step_pipeline_id = ?", pipeline.ID). - OrderBy("step_pid"). + Where("pipeline_id = ?", pipeline.ID). + OrderBy("pid"). Find(&stepList) } @@ -62,9 +62,9 @@ func (s storage) StepListFromWorkflowFind(workflow *model.Workflow) ([]*model.St func (s storage) stepListWorkflow(sess *xorm.Session, workflow *model.Workflow) ([]*model.Step, error) { stepList := make([]*model.Step, 0) return stepList, sess. - Where("step_pipeline_id = ?", workflow.PipelineID). - Where("step_ppid = ?", workflow.PID). - OrderBy("step_pid"). + Where("pipeline_id = ?", workflow.PipelineID). + Where("ppid = ?", workflow.PID). + OrderBy("pid"). Find(&stepList) } @@ -84,7 +84,7 @@ func (s storage) StepUpdate(step *model.Step) error { } func deleteStep(sess *xorm.Session, stepID int64) error { - if _, err := sess.Where("step_id = ?", stepID).Delete(new(model.LogEntry)); err != nil { + if _, err := sess.Where("id = ?", stepID).Delete(new(model.LogEntry)); err != nil { return err } return wrapDelete(sess.ID(stepID).Delete(new(model.Step))) diff --git a/server/store/datastore/task.go b/server/store/datastore/task.go index 67e8d9797..44296dff1 100644 --- a/server/store/datastore/task.go +++ b/server/store/datastore/task.go @@ -30,5 +30,5 @@ func (s storage) TaskInsert(task *model.Task) error { } func (s storage) TaskDelete(id string) error { - return wrapDelete(s.engine.Where("task_id = ?", id).Delete(new(model.Task))) + return wrapDelete(s.engine.Where("id = ?", id).Delete(new(model.Task))) } diff --git a/server/store/datastore/user.go b/server/store/datastore/user.go index 39f09af75..8db676f1d 100644 --- a/server/store/datastore/user.go +++ b/server/store/datastore/user.go @@ -41,12 +41,12 @@ func (s storage) GetUserLogin(login string) (*model.User, error) { func (s storage) getUserLogin(sess *xorm.Session, login string) (*model.User, error) { user := new(model.User) - return user, wrapGet(sess.Where("user_login=?", login).Get(user)) + return user, wrapGet(sess.Where("login=?", login).Get(user)) } func (s storage) GetUserList(p *model.ListOptions) ([]*model.User, error) { var users []*model.User - return users, s.paginate(p).OrderBy("user_id").Find(&users) + return users, s.paginate(p).OrderBy("id").Find(&users) } func (s storage) GetUserCount() (int64, error) { @@ -89,7 +89,7 @@ func (s storage) DeleteUser(user *model.User) error { return err } - if _, err := sess.Where("perm_user_id = ?", user.ID).Delete(new(model.Perm)); err != nil { + if _, err := sess.Where("user_id = ?", user.ID).Delete(new(model.Perm)); err != nil { return err } diff --git a/server/store/datastore/workflow.go b/server/store/datastore/workflow.go index b3325b9a0..8e76df90c 100644 --- a/server/store/datastore/workflow.go +++ b/server/store/datastore/workflow.go @@ -87,7 +87,7 @@ func (s storage) workflowsDelete(sess *xorm.Session, pipelineID int64) error { // delete related steps for startSteps := 0; ; startSteps += perPage { stepIDs := make([]int64, 0, perPage) - if err := sess.Limit(perPage, startSteps).Table("steps").Cols("step_id").Where("step_pipeline_id = ?", pipelineID).Find(&stepIDs); err != nil { + if err := sess.Limit(perPage, startSteps).Table("steps").Cols("id").Where("pipeline_id = ?", pipelineID).Find(&stepIDs); err != nil { return err } if len(stepIDs) == 0 { @@ -101,7 +101,7 @@ func (s storage) workflowsDelete(sess *xorm.Session, pipelineID int64) error { } } - _, err := sess.Where("workflow_pipeline_id = ?", pipelineID).Delete(new(model.Workflow)) + _, err := sess.Where("pipeline_id = ?", pipelineID).Delete(new(model.Workflow)) return err } @@ -112,8 +112,8 @@ func (s storage) WorkflowList(pipeline *model.Pipeline) ([]*model.Workflow, erro // workflowList lists workflows without child steps. func (s storage) workflowList(sess *xorm.Session, pipeline *model.Pipeline) ([]*model.Workflow, error) { var wfList []*model.Workflow - err := sess.Where("workflow_pipeline_id = ?", pipeline.ID). - OrderBy("workflow_pid"). + err := sess.Where("pipeline_id = ?", pipeline.ID). + OrderBy("pid"). Find(&wfList) if err != nil { return nil, err