From 8174778871db16971fbd145de913afc10a0d7036 Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Mon, 30 Mar 2026 16:24:51 +0200 Subject: [PATCH] Separate cron field (#6346) --- cmd/server/openapi/docs.go | 4 ++ docs/src/pages/migrations.md | 8 ++++ server/api/pipeline.go | 23 +++++++---- server/api/user.go | 8 +++- server/cron/cron.go | 3 +- server/cron/cron_test.go | 3 +- server/model/pipeline.go | 20 +++++++++ server/model/repo.go | 2 +- server/pipeline/step_builder/metadata.go | 7 +--- .../datastore/migration/027_add_cron_field.go | 41 +++++++++++++++++++ server/store/datastore/migration/migration.go | 1 + 11 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 server/store/datastore/migration/027_add_cron_field.go diff --git a/cmd/server/openapi/docs.go b/cmd/server/openapi/docs.go index 1babef7ab8..5bc9d82522 100644 --- a/cmd/server/openapi/docs.go +++ b/cmd/server/openapi/docs.go @@ -5011,6 +5011,10 @@ const docTemplate = `{ "created": { "type": "integer" }, + "cron": { + "description": "name of the cron job", + "type": "string" + }, "deploy_task": { "type": "string" }, diff --git a/docs/src/pages/migrations.md b/docs/src/pages/migrations.md index fb799505d2..d95ae2cd34 100644 --- a/docs/src/pages/migrations.md +++ b/docs/src/pages/migrations.md @@ -1,3 +1,5 @@ + + # Migrations To enhance the usability of Woodpecker and meet evolving security standards, occasional migrations are necessary. While we aim to minimize these changes, some are unavoidable. If you experience significant issues during a migration to a new version, please let us know so maintainers can reassess the updates. @@ -18,6 +20,12 @@ To enhance the usability of Woodpecker and meet evolving security standards, occ Extension HTTP calls (as of now the configuration extension) will by default only be allowed to contact external hosts. Set `WOODPECKER_EXTENSIONS_ALLOWED_HOSTS` accordingly to allow additional hosts as needed. +### API changes + +- The pipeline model has been changed to use nested objects grouped based on the event (e.g. instead of a generic `title` it now uses `pr.title`). Following properties are deprecated and should be replaced by the their new counterparts: + - `sender` => + - `cron` for cron events + ### Internal changes - Renamed the server flag `config-service-endpoint` to `config-extension-endpoint` diff --git a/server/api/pipeline.go b/server/api/pipeline.go index d36626de0c..406007984a 100644 --- a/server/api/pipeline.go +++ b/server/api/pipeline.go @@ -78,8 +78,12 @@ func CreatePipeline(c *gin.Context) { pl, err := pipeline.Create(c, _store, repo, tmpPipeline) if err != nil { handlePipelineErr(c, err) + } + + if pl != nil { + c.JSON(http.StatusOK, pl.ToAPIModel()) } else { - c.JSON(http.StatusOK, pl) + c.Status(http.StatusNoContent) } } @@ -175,7 +179,12 @@ func GetPipelines(c *gin.Context) { _ = c.AbortWithError(http.StatusInternalServerError, err) return } - c.JSON(http.StatusOK, pipelines) + + pls := make([]*model.APIPipeline, len(pipelines)) + for i, p := range pipelines { + pls[i] = p.ToAPIModel() + } + c.JSON(http.StatusOK, pls) } // DeletePipeline @@ -252,7 +261,7 @@ func GetPipeline(c *gin.Context) { return } - c.JSON(http.StatusOK, pl) + c.JSON(http.StatusOK, pl.ToAPIModel()) } func GetPipelineLastByBranch(c *gin.Context) { @@ -270,7 +279,7 @@ func GetPipelineLastByBranch(c *gin.Context) { _ = c.AbortWithError(http.StatusInternalServerError, err) return } - c.JSON(http.StatusOK, pl) + c.JSON(http.StatusOK, pl.ToAPIModel()) } // GetStepLogs @@ -530,7 +539,7 @@ func PostApproval(c *gin.Context) { if err != nil { handlePipelineErr(c, err) } else { - c.JSON(http.StatusOK, newPipeline) + c.JSON(http.StatusOK, newPipeline.ToAPIModel()) } } @@ -562,7 +571,7 @@ func PostDecline(c *gin.Context) { if err != nil { handlePipelineErr(c, err) } else { - c.JSON(http.StatusOK, pl) + c.JSON(http.StatusOK, pl.ToAPIModel()) } } @@ -662,7 +671,7 @@ func PostPipeline(c *gin.Context) { if err != nil { handlePipelineErr(c, err) } else { - c.JSON(http.StatusOK, newPipeline) + c.JSON(http.StatusOK, newPipeline.ToAPIModel()) } } diff --git a/server/api/user.go b/server/api/user.go index 8d69d18914..24dc359607 100644 --- a/server/api/user.go +++ b/server/api/user.go @@ -171,9 +171,15 @@ func GetRepos(c *gin.Context) { repos := make([]*model.RepoLastPipeline, len(activeRepos)) for i, repo := range activeRepos { + var lastAPIPipeline *model.APIPipeline + lastPipeline, ok := latestPipelines[repo.ID] + if ok { + lastAPIPipeline = lastPipeline.ToAPIModel() + } + repos[i] = &model.RepoLastPipeline{ Repo: repo, - LastPipeline: latestPipelines[repo.ID], + LastPipeline: lastAPIPipeline, } } diff --git a/server/cron/cron.go b/server/cron/cron.go index cb175b18b1..f6e05330d2 100644 --- a/server/cron/cron.go +++ b/server/cron/cron.go @@ -141,9 +141,8 @@ func CreatePipeline(ctx context.Context, store store.Store, cron *model.Cron) (* Commit: commit.SHA, Ref: "refs/heads/" + cron.Branch, Branch: cron.Branch, - Message: cron.Name, Timestamp: cron.NextExec, - Sender: cron.Name, + Cron: cron.Name, ForgeURL: commit.ForgeURL, AdditionalVariables: cron.Variables, }, nil diff --git a/server/cron/cron_test.go b/server/cron/cron_test.go index c44d82aa14..72525cda5e 100644 --- a/server/cron/cron_test.go +++ b/server/cron/cron_test.go @@ -66,9 +66,8 @@ func TestCreatePipeline(t *testing.T) { Commit: "sha1", Event: "cron", ForgeURL: "https://example.com/sha1", - Message: "test", Ref: "refs/heads/default", - Sender: "test", + Cron: "test", }, pipeline) } diff --git a/server/model/pipeline.go b/server/model/pipeline.go index 2a7c67ea9a..4c9c97e756 100644 --- a/server/model/pipeline.go +++ b/server/model/pipeline.go @@ -54,9 +54,15 @@ type Pipeline struct { AdditionalVariables map[string]string `json:"variables,omitempty" xorm:"json 'additional_variables'"` PullRequestLabels []string `json:"pr_labels,omitempty" xorm:"json 'pr_labels'"` PullRequestMilestone string `json:"pr_milestone,omitempty" xorm:"pr_milestone"` + Cron string `json:"cron,omitempty" xorm:"cron"` // name of the cron job IsPrerelease bool `json:"is_prerelease,omitempty" xorm:"is_prerelease"` FromFork bool `json:"from_fork,omitempty" xorm:"from_fork"` Version string `json:"version" xorm:"'version'"` +} + +// APIPipeline TODO remove deprecated properties in next major. +type APIPipeline struct { + *Pipeline } // @name Pipeline // TableName return database table name for xorm. @@ -64,6 +70,20 @@ func (Pipeline) TableName() string { return "pipelines" } +func (p *Pipeline) ToAPIModel() *APIPipeline { + ap := &APIPipeline{ + Pipeline: p, + } + + switch p.Event { //nolint:gocritic + case EventCron: + ap.Message = p.Cron + ap.Sender = p.Cron + } + + return ap +} + type PipelineFilter struct { Before int64 After int64 diff --git a/server/model/repo.go b/server/model/repo.go index 1c9672582c..8aec1d95a5 100644 --- a/server/model/repo.go +++ b/server/model/repo.go @@ -175,5 +175,5 @@ type TrustedConfigurationPatch struct { // RepoLastPipeline represents a repository with last pipeline execution information. type RepoLastPipeline struct { *Repo - LastPipeline *Pipeline `json:"last_pipeline,omitempty"` + LastPipeline *APIPipeline `json:"last_pipeline,omitempty"` } // @name RepoLastPipeline diff --git a/server/pipeline/step_builder/metadata.go b/server/pipeline/step_builder/metadata.go index 3335c96a0e..b7b57d9ad1 100644 --- a/server/pipeline/step_builder/metadata.go +++ b/server/pipeline/step_builder/metadata.go @@ -100,11 +100,6 @@ func metadataPipelineFromModelPipeline(pipeline *model.Pipeline, includeParent b return metadata.Pipeline{} } - cron := "" - if pipeline.Event == model.EventCron { - cron = pipeline.Sender - } - parent := int64(0) if includeParent { parent = pipeline.Parent @@ -138,7 +133,7 @@ func metadataPipelineFromModelPipeline(pipeline *model.Pipeline, includeParent b PullRequestMilestone: pipeline.PullRequestMilestone, IsPrerelease: pipeline.IsPrerelease, }, - Cron: cron, + Cron: pipeline.Cron, Author: pipeline.Author, Avatar: pipeline.Avatar, } diff --git a/server/store/datastore/migration/027_add_cron_field.go b/server/store/datastore/migration/027_add_cron_field.go new file mode 100644 index 0000000000..057838aac3 --- /dev/null +++ b/server/store/datastore/migration/027_add_cron_field.go @@ -0,0 +1,41 @@ +// Copyright 2026 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 ( + "src.techknowlogick.com/xormigrate" + "xorm.io/xorm" + + "go.woodpecker-ci.org/woodpecker/v3/server/model" +) + +var addCronField = xormigrate.Migration{ + ID: "add-cron-field", + MigrateSession: func(sess *xorm.Session) error { + type pipelines struct { + ID int64 `xorm:"pk autoincr 'id'"` + + // new cron field + Cron string `xorm:"cron"` + } + + if err := sess.Sync(new(pipelines)); err != nil { + return err + } + + _, err := sess.Exec("UPDATE pipelines SET cron = sender, sender = '', message = '' WHERE event = ?", model.EventCron) + return err + }, +} diff --git a/server/store/datastore/migration/migration.go b/server/store/datastore/migration/migration.go index e5e72ea789..17f075ece8 100644 --- a/server/store/datastore/migration/migration.go +++ b/server/store/datastore/migration/migration.go @@ -55,6 +55,7 @@ var migrationTasks = []*xormigrate.Migration{ &unsanitizeOrgAndUserNames, &replaceZeroForgeIDsInOrgs, &fixForgeColumns, + &addCronField, } var allBeans = []any{