From 1b43b0bf20c03e63fb270453d2ffa5afed456fb2 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 17 Mar 2023 03:43:04 +0100 Subject: [PATCH] Add pull request labels as environment variable (#1321) Closes #1308 Co-authored-by: Anbraten --- docs/docs/20-usage/20-pipeline-syntax.md | 7 ++++ docs/docs/20-usage/50-environment.md | 1 + pipeline/frontend/metadata.go | 43 +++++++++++++----------- pipeline/stepBuilder.go | 3 +- server/forge/gitea/helper.go | 9 +++++ server/forge/github/convert.go | 10 ++++++ server/forge/github/parse.go | 1 + server/forge/gitlab/convert.go | 9 +++++ server/model/pipeline.go | 3 +- 9 files changed, 64 insertions(+), 22 deletions(-) diff --git a/docs/docs/20-usage/20-pipeline-syntax.md b/docs/docs/20-usage/20-pipeline-syntax.md index 81266a3af..4956b47b4 100644 --- a/docs/docs/20-usage/20-pipeline-syntax.md +++ b/docs/docs/20-usage/20-pipeline-syntax.md @@ -475,6 +475,13 @@ when: - evaluate: 'not (CI_COMMIT_MESSAGE contains "please ignore me")' ``` +Run on pull requests with the label `deploy`: + +```yaml +when: + - evaluate: 'CI_COMMIT_PULL_REQUEST_LABELS contains "deploy"' +``` + ### `group` - Parallel execution Woodpecker supports parallel step execution for same-machine fan-in and fan-out. Parallel steps are configured using the `group` attribute. This instructs the pipeline runner to execute the named group in parallel. diff --git a/docs/docs/20-usage/50-environment.md b/docs/docs/20-usage/50-environment.md index ee5448bf4..c4466350c 100644 --- a/docs/docs/20-usage/50-environment.md +++ b/docs/docs/20-usage/50-environment.md @@ -68,6 +68,7 @@ This is the reference list of all environment variables available to your pipeli | `CI_COMMIT_TARGET_BRANCH` | commit target branch | | `CI_COMMIT_TAG` | commit tag name (empty if event is not `tag`) | | `CI_COMMIT_PULL_REQUEST` | commit pull request number (empty if event is not `pull_request`) | +| `CI_COMMIT_PULL_REQUEST_LABELS` | labels assigned to pull request (empty if event is not `pull_request`) | | `CI_COMMIT_LINK` | commit link in forge | | `CI_COMMIT_MESSAGE` | commit message | | `CI_COMMIT_AUTHOR` | commit author username | diff --git a/pipeline/frontend/metadata.go b/pipeline/frontend/metadata.go index 7797c09cf..6399570e0 100644 --- a/pipeline/frontend/metadata.go +++ b/pipeline/frontend/metadata.go @@ -79,13 +79,14 @@ type ( // Commit defines runtime metadata for a commit. Commit struct { - Sha string `json:"sha,omitempty"` - Ref string `json:"ref,omitempty"` - Refspec string `json:"refspec,omitempty"` - Branch string `json:"branch,omitempty"` - Message string `json:"message,omitempty"` - Author Author `json:"author,omitempty"` - ChangedFiles []string `json:"changed_files,omitempty"` + Sha string `json:"sha,omitempty"` + Ref string `json:"ref,omitempty"` + Refspec string `json:"refspec,omitempty"` + Branch string `json:"branch,omitempty"` + Message string `json:"message,omitempty"` + Author Author `json:"author,omitempty"` + ChangedFiles []string `json:"changed_files,omitempty"` + PullRequestLabels []string `json:"labels,omitempty"` } // Author defines runtime metadata for a commit author. @@ -154,19 +155,20 @@ func (m *Metadata) Environ() map[string]string { "CI_REPO_PRIVATE": strconv.FormatBool(m.Repo.Private), "CI_REPO_TRUSTED": "false", // TODO should this be added? - "CI_COMMIT_SHA": m.Curr.Commit.Sha, - "CI_COMMIT_REF": m.Curr.Commit.Ref, - "CI_COMMIT_REFSPEC": m.Curr.Commit.Refspec, - "CI_COMMIT_BRANCH": m.Curr.Commit.Branch, - "CI_COMMIT_SOURCE_BRANCH": sourceBranch, - "CI_COMMIT_TARGET_BRANCH": targetBranch, - "CI_COMMIT_LINK": m.Curr.Link, - "CI_COMMIT_MESSAGE": m.Curr.Commit.Message, - "CI_COMMIT_AUTHOR": m.Curr.Commit.Author.Name, - "CI_COMMIT_AUTHOR_EMAIL": m.Curr.Commit.Author.Email, - "CI_COMMIT_AUTHOR_AVATAR": m.Curr.Commit.Author.Avatar, - "CI_COMMIT_TAG": "", // will be set if event is tag - "CI_COMMIT_PULL_REQUEST": "", // will be set if event is pr + "CI_COMMIT_SHA": m.Curr.Commit.Sha, + "CI_COMMIT_REF": m.Curr.Commit.Ref, + "CI_COMMIT_REFSPEC": m.Curr.Commit.Refspec, + "CI_COMMIT_BRANCH": m.Curr.Commit.Branch, + "CI_COMMIT_SOURCE_BRANCH": sourceBranch, + "CI_COMMIT_TARGET_BRANCH": targetBranch, + "CI_COMMIT_LINK": m.Curr.Link, + "CI_COMMIT_MESSAGE": m.Curr.Commit.Message, + "CI_COMMIT_AUTHOR": m.Curr.Commit.Author.Name, + "CI_COMMIT_AUTHOR_EMAIL": m.Curr.Commit.Author.Email, + "CI_COMMIT_AUTHOR_AVATAR": m.Curr.Commit.Author.Avatar, + "CI_COMMIT_TAG": "", // will be set if event is tag + "CI_COMMIT_PULL_REQUEST": "", // will be set if event is pr + "CI_COMMIT_PULL_REQUEST_LABELS": "", // will be set if event is pr "CI_PIPELINE_NUMBER": strconv.FormatInt(m.Curr.Number, 10), "CI_PIPELINE_PARENT": strconv.FormatInt(m.Curr.Parent, 10), @@ -244,6 +246,7 @@ func (m *Metadata) Environ() map[string]string { } if m.Curr.Event == EventPull { params["CI_COMMIT_PULL_REQUEST"] = pullRegexp.FindString(m.Curr.Commit.Ref) + params["CI_COMMIT_PULL_REQUEST_LABELS"] = strings.Join(m.Curr.Commit.PullRequestLabels, ",") } return params diff --git a/pipeline/stepBuilder.go b/pipeline/stepBuilder.go index 5d560b269..ded1e4fce 100644 --- a/pipeline/stepBuilder.go +++ b/pipeline/stepBuilder.go @@ -388,7 +388,8 @@ func metadataPipelineFromModelPipeline(pipeline *model.Pipeline, includeParent b Email: pipeline.Email, Avatar: pipeline.Avatar, }, - ChangedFiles: pipeline.ChangedFiles, + ChangedFiles: pipeline.ChangedFiles, + PullRequestLabels: pipeline.PullRequestLabels, }, Cron: cron, } diff --git a/server/forge/gitea/helper.go b/server/forge/gitea/helper.go index e9a7acec1..fb792581b 100644 --- a/server/forge/gitea/helper.go +++ b/server/forge/gitea/helper.go @@ -160,6 +160,7 @@ func pipelineFromPullRequest(hook *pullRequestHook) *model.Pipeline { hook.PullRequest.Head.Ref, hook.PullRequest.Base.Ref, ), + PullRequestLabels: convertLabels(hook.PullRequest.Labels), } return pipeline } @@ -229,3 +230,11 @@ func matchingHooks(hooks []*gitea.Hook, rawurl string) *gitea.Hook { } return nil } + +func convertLabels(from []*gitea.Label) []string { + labels := make([]string, len(from)) + for i, label := range from { + labels[i] = label.Name + } + return labels +} diff --git a/server/forge/github/convert.go b/server/forge/github/convert.go index 53fb9c30a..b02d2c601 100644 --- a/server/forge/github/convert.go +++ b/server/forge/github/convert.go @@ -164,3 +164,13 @@ func convertRepoHook(eventRepo *github.PushEventRepository) *model.Repo { } return repo } + +// covertLabels is a helper function used to convert a GitHub label list to +// the common Woodpecker label structure. +func convertLabels(from []*github.Label) []string { + labels := make([]string, len(from)) + for i, label := range from { + labels[i] = *label.Name + } + return labels +} diff --git a/server/forge/github/parse.go b/server/forge/github/parse.go index b2c50c67c..ebdd95a70 100644 --- a/server/forge/github/parse.go +++ b/server/forge/github/parse.go @@ -170,6 +170,7 @@ func parsePullHook(hook *github.PullRequestEvent, merge bool) (*github.PullReque hook.GetPullRequest().GetHead().GetRef(), hook.GetPullRequest().GetBase().GetRef(), ), + PullRequestLabels: convertLabels(hook.GetPullRequest().Labels), } if merge { pipeline.Ref = fmt.Sprintf(mergeRefs, hook.GetPullRequest().GetNumber()) diff --git a/server/forge/gitlab/convert.go b/server/forge/gitlab/convert.go index bf171e3be..40f181e05 100644 --- a/server/forge/gitlab/convert.go +++ b/server/forge/gitlab/convert.go @@ -129,6 +129,7 @@ func convertMergeRequestHook(hook *gitlab.MergeEvent, req *http.Request) (int, * pipeline.Title = obj.Title pipeline.Link = obj.URL + pipeline.PullRequestLabels = convertLabels(hook.Labels) return obj.IID, repo, pipeline, nil } @@ -250,3 +251,11 @@ func extractFromPath(str string) (string, string, error) { } return s[0], s[1], nil } + +func convertLabels(from []*gitlab.Label) []string { + labels := make([]string, len(from)) + for i, label := range from { + labels[i] = label.Name + } + return labels +} diff --git a/server/model/pipeline.go b/server/model/pipeline.go index c6d8fcd3b..6ea9b368a 100644 --- a/server/model/pipeline.go +++ b/server/model/pipeline.go @@ -36,7 +36,7 @@ type Pipeline struct { Branch string `json:"branch" xorm:"pipeline_branch"` Ref string `json:"ref" xorm:"pipeline_ref"` Refspec string `json:"refspec" xorm:"pipeline_refspec"` - CloneURL string `json:"clone_url" xorm:"pipeline_clone_url"` + CloneURL string `json:"clone_url" xorm:"pipeline_clone_url"` Title string `json:"title" xorm:"pipeline_title"` Message string `json:"message" xorm:"TEXT 'pipeline_message'"` Timestamp int64 `json:"timestamp" xorm:"pipeline_timestamp"` @@ -52,6 +52,7 @@ type Pipeline struct { Files []*File `json:"files,omitempty" xorm:"-"` ChangedFiles []string `json:"changed_files,omitempty" xorm:"json 'changed_files'"` AdditionalVariables map[string]string `json:"variables,omitempty" xorm:"json 'additional_variables'"` + PullRequestLabels []string `json:"pr_labels,omitempty" xorm:"json 'pr_labels'"` } // TableName return database table name for xorm