Add env for workflow and step name (#1693)

closes #1681
This commit is contained in:
Anbraten 2023-04-08 13:15:28 +02:00 committed by GitHub
parent 6af9371011
commit 36b5ae3459
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 80 additions and 50 deletions

View file

@ -112,7 +112,7 @@ func execWithAxis(c *cli.Context, file, repoPath string, axis matrix.Axis) error
metadata := metadataFromContext(c, axis) metadata := metadataFromContext(c, axis)
environ := metadata.Environ() environ := metadata.Environ()
var secrets []compiler.Secret var secrets []compiler.Secret
for key, val := range metadata.Step.Matrix { for key, val := range metadata.Workflow.Matrix {
environ[key] = val environ[key] = val
secrets = append(secrets, compiler.Secret{ secrets = append(secrets, compiler.Secret{
Name: key, Name: key,
@ -218,7 +218,7 @@ func execWithAxis(c *cli.Context, file, repoPath string, axis matrix.Axis) error
ctx, cancel := context.WithTimeout(context.Background(), c.Duration("timeout")) ctx, cancel := context.WithTimeout(context.Background(), c.Duration("timeout"))
defer cancel() defer cancel()
ctx = utils.WithContextSigtermCallback(ctx, func() { ctx = utils.WithContextSigtermCallback(ctx, func() {
println("ctrl+c received, terminating process") fmt.Println("ctrl+c received, terminating process")
}) })
return pipeline.New(compiled, return pipeline.New(compiled,
@ -290,10 +290,15 @@ func metadataFromContext(c *cli.Context, axis matrix.Axis) frontend.Metadata {
}, },
}, },
}, },
Step: frontend.Step{ Workflow: frontend.Workflow{
Number: c.Int("step-number"), Name: c.String("workflow-name"),
Number: c.Int("workflow-number"),
Matrix: axis, Matrix: axis,
}, },
Step: frontend.Step{
Name: c.String("step-name"),
Number: c.Int("step-number"),
},
Sys: frontend.System{ Sys: frontend.System{
Name: c.String("system-name"), Name: c.String("system-name"),
Link: c.String("system-link"), Link: c.String("system-link"),

View file

@ -260,8 +260,16 @@ var flags = []cli.Flag{
Name: "prev-commit-author-email", Name: "prev-commit-author-email",
}, },
&cli.IntFlag{ &cli.IntFlag{
EnvVars: []string{"CI_STEP_NUMBER", "CI_JOB_NUMBER"}, EnvVars: []string{"CI_WORKFLOW_NAME"},
Name: "step-number", Name: "workflow-name",
},
&cli.IntFlag{
EnvVars: []string{"CI_WORKFLOW_NUMBER", "CI_JOB_NUMBER"},
Name: "workflow-number",
},
&cli.IntFlag{
EnvVars: []string{"CI_STEP_NAME", "CI_JOB_NUMBER"},
Name: "step-name",
}, },
&cli.StringSliceFlag{ &cli.StringSliceFlag{
EnvVars: []string{"CI_ENV"}, EnvVars: []string{"CI_ENV"},

View file

@ -47,7 +47,7 @@ pipeline:
This is the reference list of all environment variables available to your pipeline containers. These are injected into your pipeline step and plugins containers, at runtime. This is the reference list of all environment variables available to your pipeline containers. These are injected into your pipeline step and plugins containers, at runtime.
| NAME | Description | | NAME | Description |
|----------------------------------|----------------------------------------------------------------------------------------------| | -------------------------------- | -------------------------------------------------------------------------------------------- |
| `CI=woodpecker` | environment is woodpecker | | `CI=woodpecker` | environment is woodpecker |
| | **Repository** | | | **Repository** |
| `CI_REPO` | repository full name `<owner>/<name>` | | `CI_REPO` | repository full name `<owner>/<name>` |
@ -84,8 +84,10 @@ This is the reference list of all environment variables available to your pipeli
| `CI_PIPELINE_CREATED` | pipeline created UNIX timestamp | | `CI_PIPELINE_CREATED` | pipeline created UNIX timestamp |
| `CI_PIPELINE_STARTED` | pipeline started UNIX timestamp | | `CI_PIPELINE_STARTED` | pipeline started UNIX timestamp |
| `CI_PIPELINE_FINISHED` | pipeline finished UNIX timestamp | | `CI_PIPELINE_FINISHED` | pipeline finished UNIX timestamp |
| | **Current workflow** |
| `CI_WORKFLOW_NAME` | workflow name |
| | **Current step** | | | **Current step** |
| `CI_STEP_NUMBER` | step number | | `CI_STEP_NAME` | step name |
| `CI_STEP_STATUS` | step status (success, failure) | | `CI_STEP_STATUS` | step status (success, failure) |
| `CI_STEP_STARTED` | step started UNIX timestamp | | `CI_STEP_STARTED` | step started UNIX timestamp |
| `CI_STEP_FINISHED` | step finished UNIX timestamp | | `CI_STEP_FINISHED` | step finished UNIX timestamp |

View file

@ -42,12 +42,13 @@ const (
type ( type (
// Metadata defines runtime m. // Metadata defines runtime m.
Metadata struct { Metadata struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Repo Repo `json:"repo,omitempty"` Repo Repo `json:"repo,omitempty"`
Curr Pipeline `json:"curr,omitempty"` Curr Pipeline `json:"curr,omitempty"`
Prev Pipeline `json:"prev,omitempty"` Prev Pipeline `json:"prev,omitempty"`
Step Step `json:"step,omitempty"` Workflow Workflow `json:"workflow,omitempty"`
Sys System `json:"sys,omitempty"` Step Step `json:"step,omitempty"`
Sys System `json:"sys,omitempty"`
} }
// Repo defines runtime metadata for a repository. // Repo defines runtime metadata for a repository.
@ -96,12 +97,19 @@ type (
Avatar string `json:"avatar,omitempty"` Avatar string `json:"avatar,omitempty"`
} }
// Step defines runtime metadata for a step. // Workflow defines runtime metadata for a workflow.
Step struct { Workflow struct {
Name string `json:"name,omitempty"`
Number int `json:"number,omitempty"` Number int `json:"number,omitempty"`
Matrix map[string]string `json:"matrix,omitempty"` Matrix map[string]string `json:"matrix,omitempty"`
} }
// Step defines runtime metadata for a step.
Step struct {
Name string `json:"name,omitempty"`
Number int `json:"number,omitempty"`
}
// Secret defines a runtime secret // Secret defines a runtime secret
Secret struct { Secret struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
@ -180,6 +188,10 @@ func (m *Metadata) Environ() map[string]string {
"CI_PIPELINE_STARTED": strconv.FormatInt(m.Curr.Started, 10), "CI_PIPELINE_STARTED": strconv.FormatInt(m.Curr.Started, 10),
"CI_PIPELINE_FINISHED": strconv.FormatInt(m.Curr.Finished, 10), "CI_PIPELINE_FINISHED": strconv.FormatInt(m.Curr.Finished, 10),
"CI_WORKFLOW_NAME": m.Workflow.Name,
"CI_WORKFLOW_NUMBER": strconv.Itoa(m.Workflow.Number),
"CI_STEP_NAME": m.Step.Name,
"CI_STEP_NUMBER": strconv.Itoa(m.Step.Number), "CI_STEP_NUMBER": strconv.Itoa(m.Step.Number),
"CI_STEP_STATUS": "", // will be set by agent "CI_STEP_STATUS": "", // will be set by agent
"CI_STEP_STARTED": "", // will be set by agent "CI_STEP_STARTED": "", // will be set by agent

View file

@ -62,6 +62,7 @@ func (c *Compiler) createProcess(name string, container *yaml.Container, section
} }
environment["CI_WORKSPACE"] = path.Join(c.base, c.path) environment["CI_WORKSPACE"] = path.Join(c.base, c.path)
environment["CI_STEP_NAME"] = name
if section == "services" || container.Detached { if section == "services" || container.Detached {
detached = true detached = true

View file

@ -144,7 +144,7 @@ func (c *Constraint) Match(metadata frontend.Metadata, global bool) (bool, error
c.SetDefaultEventFilter() c.SetDefaultEventFilter()
// apply step only filters // apply step only filters
match = c.Matrix.Match(metadata.Step.Matrix) match = c.Matrix.Match(metadata.Workflow.Matrix)
} }
match = match && c.Platform.Match(metadata.Sys.Platform) && match = match && c.Platform.Match(metadata.Sys.Platform) &&

View file

@ -50,7 +50,7 @@ type StepBuilder struct {
} }
type Item struct { type Item struct {
Step *model.Step Workflow *model.Step
Platform string Platform string
Labels map[string]string Labels map[string]string
DependsOn []string DependsOn []string
@ -76,7 +76,7 @@ func (b *StepBuilder) Build() ([]*Item, error) {
} }
for _, axis := range axes { for _, axis := range axes {
step := &model.Step{ workflow := &model.Step{
PipelineID: b.Curr.ID, PipelineID: b.Curr.ID,
PID: pidSequence, PID: pidSequence,
PGID: pidSequence, PGID: pidSequence,
@ -85,7 +85,7 @@ func (b *StepBuilder) Build() ([]*Item, error) {
Name: SanitizePath(y.Name), Name: SanitizePath(y.Name),
} }
metadata := metadataFromStruct(b.Repo, b.Curr, b.Last, step, b.Link) metadata := metadataFromStruct(b.Repo, b.Curr, b.Last, workflow, b.Link)
environ := b.environmentVariables(metadata, axis) environ := b.environmentVariables(metadata, axis)
// add global environment variables for substituting // add global environment variables for substituting
@ -118,12 +118,12 @@ func (b *StepBuilder) Build() ([]*Item, error) {
// checking if filtered. // checking if filtered.
if match, err := parsed.When.Match(metadata, true); !match && err == nil { if match, err := parsed.When.Match(metadata, true); !match && err == nil {
log.Debug().Str("pipeline", step.Name).Msg( log.Debug().Str("pipeline", workflow.Name).Msg(
"Marked as skipped, dose not match metadata", "Marked as skipped, dose not match metadata",
) )
step.State = model.StatusSkipped workflow.State = model.StatusSkipped
} else if err != nil { } else if err != nil {
log.Debug().Str("pipeline", step.Name).Msg( log.Debug().Str("pipeline", workflow.Name).Msg(
"Pipeline config could not be parsed", "Pipeline config could not be parsed",
) )
return nil, err return nil, err
@ -131,13 +131,13 @@ func (b *StepBuilder) Build() ([]*Item, error) {
// TODO: deprecated branches filter => remove after some time // TODO: deprecated branches filter => remove after some time
if !parsed.Branches.Match(b.Curr.Branch) && (b.Curr.Event != model.EventDeploy && b.Curr.Event != model.EventTag) { if !parsed.Branches.Match(b.Curr.Branch) && (b.Curr.Event != model.EventDeploy && b.Curr.Event != model.EventTag) {
log.Debug().Str("pipeline", step.Name).Msg( log.Debug().Str("pipeline", workflow.Name).Msg(
"Marked as skipped, dose not match branch", "Marked as skipped, dose not match branch",
) )
step.State = model.StatusSkipped workflow.State = model.StatusSkipped
} }
ir, err := b.toInternalRepresentation(parsed, environ, metadata, step.ID) ir, err := b.toInternalRepresentation(parsed, environ, metadata, workflow.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -147,7 +147,7 @@ func (b *StepBuilder) Build() ([]*Item, error) {
} }
item := &Item{ item := &Item{
Step: step, Workflow: workflow,
Config: ir, Config: ir,
Labels: parsed.Labels, Labels: parsed.Labels,
DependsOn: parsed.DependsOn, DependsOn: parsed.DependsOn,
@ -175,7 +175,7 @@ func (b *StepBuilder) Build() ([]*Item, error) {
func stepListContainsItemsToRun(items []*Item) bool { func stepListContainsItemsToRun(items []*Item) bool {
for i := range items { for i := range items {
if items[i].Step.State == model.StatusPending { if items[i].Workflow.State == model.StatusPending {
return true return true
} }
} }
@ -196,7 +196,7 @@ func filterItemsWithMissingDependencies(items []*Item) []*Item {
if len(itemsToRemove) > 0 { if len(itemsToRemove) > 0 {
filtered := make([]*Item, 0) filtered := make([]*Item, 0)
for _, item := range items { for _, item := range items {
if !containsItemWithName(item.Step.Name, itemsToRemove) { if !containsItemWithName(item.Workflow.Name, itemsToRemove) {
filtered = append(filtered, item) filtered = append(filtered, item)
} }
} }
@ -209,7 +209,7 @@ func filterItemsWithMissingDependencies(items []*Item) []*Item {
func containsItemWithName(name string, items []*Item) bool { func containsItemWithName(name string, items []*Item) bool {
for _, item := range items { for _, item := range items {
if name == item.Step.Name { if name == item.Workflow.Name {
return true return true
} }
} }
@ -295,9 +295,9 @@ func (b *StepBuilder) toInternalRepresentation(parsed *yaml.Config, environ map[
func SetPipelineStepsOnPipeline(pipeline *model.Pipeline, pipelineItems []*Item) *model.Pipeline { func SetPipelineStepsOnPipeline(pipeline *model.Pipeline, pipelineItems []*Item) *model.Pipeline {
var pidSequence int var pidSequence int
for _, item := range pipelineItems { for _, item := range pipelineItems {
pipeline.Steps = append(pipeline.Steps, item.Step) pipeline.Steps = append(pipeline.Steps, item.Workflow)
if pidSequence < item.Step.PID { if pidSequence < item.Workflow.PID {
pidSequence = item.Step.PID pidSequence = item.Workflow.PID
} }
} }
@ -313,11 +313,11 @@ func SetPipelineStepsOnPipeline(pipeline *model.Pipeline, pipelineItems []*Item)
PipelineID: pipeline.ID, PipelineID: pipeline.ID,
Name: step.Alias, Name: step.Alias,
PID: pidSequence, PID: pidSequence,
PPID: item.Step.PID, PPID: item.Workflow.PID,
PGID: gid, PGID: gid,
State: model.StatusPending, State: model.StatusPending,
} }
if item.Step.State == model.StatusSkipped { if item.Workflow.State == model.StatusSkipped {
step.State = model.StatusSkipped step.State = model.StatusSkipped
} }
pipeline.Steps = append(pipeline.Steps, step) pipeline.Steps = append(pipeline.Steps, step)
@ -329,7 +329,7 @@ func SetPipelineStepsOnPipeline(pipeline *model.Pipeline, pipelineItems []*Item)
} }
// return the metadata from the cli context. // return the metadata from the cli context.
func metadataFromStruct(repo *model.Repo, pipeline, last *model.Pipeline, step *model.Step, link string) frontend.Metadata { func metadataFromStruct(repo *model.Repo, pipeline, last *model.Pipeline, workflow *model.Step, link string) frontend.Metadata {
host := link host := link
uri, err := url.Parse(link) uri, err := url.Parse(link)
if err == nil { if err == nil {
@ -345,10 +345,12 @@ func metadataFromStruct(repo *model.Repo, pipeline, last *model.Pipeline, step *
}, },
Curr: metadataPipelineFromModelPipeline(pipeline, true), Curr: metadataPipelineFromModelPipeline(pipeline, true),
Prev: metadataPipelineFromModelPipeline(last, false), Prev: metadataPipelineFromModelPipeline(last, false),
Step: frontend.Step{ Workflow: frontend.Workflow{
Number: step.PID, Name: workflow.Name,
Matrix: step.Environ, Number: workflow.PID,
Matrix: workflow.Environ,
}, },
Step: frontend.Step{},
Sys: frontend.System{ Sys: frontend.System{
Name: "woodpecker", Name: "woodpecker",
Link: link, Link: link,

View file

@ -273,7 +273,7 @@ pipeline:
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
pipelineNames := []string{pipelineItems[0].Step.Name, pipelineItems[1].Step.Name} pipelineNames := []string{pipelineItems[0].Workflow.Name, pipelineItems[1].Workflow.Name}
if !containsItemWithName("lint", pipelineItems) || !containsItemWithName("test", pipelineItems) { if !containsItemWithName("lint", pipelineItems) || !containsItemWithName("test", pipelineItems) {
t.Fatalf("Pipeline name should be 'lint' and 'test' but are '%v'", pipelineNames) t.Fatalf("Pipeline name should be 'lint' and 'test' but are '%v'", pipelineNames)
} }
@ -312,15 +312,15 @@ pipeline:
if len(pipelineItems) != 2 { if len(pipelineItems) != 2 {
t.Fatal("Should have generated 2 pipeline") t.Fatal("Should have generated 2 pipeline")
} }
if pipelineItems[0].Step.State != model.StatusSkipped { if pipelineItems[0].Workflow.State != model.StatusSkipped {
t.Fatal("Should not run on dev branch") t.Fatal("Should not run on dev branch")
} }
for _, child := range pipelineItems[0].Step.Children { for _, child := range pipelineItems[0].Workflow.Children {
if child.State != model.StatusSkipped { if child.State != model.StatusSkipped {
t.Fatal("Children should skipped status too") t.Fatal("Children should skipped status too")
} }
} }
if pipelineItems[1].Step.State != model.StatusPending { if pipelineItems[1].Workflow.State != model.StatusPending {
t.Fatal("Should run on dev branch") t.Fatal("Should run on dev branch")
} }
} }
@ -448,7 +448,7 @@ depends_on: [ zerostep ]
if len(pipelineItems) != 1 { if len(pipelineItems) != 1 {
t.Fatal("Zerostep and the step that depends on it should not generate a pipeline item") t.Fatal("Zerostep and the step that depends on it should not generate a pipeline item")
} }
if pipelineItems[0].Step.Name != "justastep" { if pipelineItems[0].Workflow.Name != "justastep" {
t.Fatal("justastep should have been generated") t.Fatal("justastep should have been generated")
} }
} }
@ -502,7 +502,7 @@ depends_on: [ shouldbefiltered ]
if len(pipelineItems) != 1 { if len(pipelineItems) != 1 {
t.Fatal("Zerostep and the step that depends on it, and the one depending on it should not generate a pipeline item") t.Fatal("Zerostep and the step that depends on it, and the one depending on it should not generate a pipeline item")
} }
if pipelineItems[0].Step.Name != "justastep" { if pipelineItems[0].Workflow.Name != "justastep" {
t.Fatal("justastep should have been generated") t.Fatal("justastep should have been generated")
} }
} }

View file

@ -28,11 +28,11 @@ import (
func queuePipeline(repo *model.Repo, pipelineItems []*pipeline.Item) error { func queuePipeline(repo *model.Repo, pipelineItems []*pipeline.Item) error {
var tasks []*model.Task var tasks []*model.Task
for _, item := range pipelineItems { for _, item := range pipelineItems {
if item.Step.State == model.StatusSkipped { if item.Workflow.State == model.StatusSkipped {
continue continue
} }
task := new(model.Task) task := new(model.Task)
task.ID = fmt.Sprint(item.Step.ID) task.ID = fmt.Sprint(item.Workflow.ID)
task.Labels = map[string]string{} task.Labels = map[string]string{}
for k, v := range item.Labels { for k, v := range item.Labels {
task.Labels[k] = v task.Labels[k] = v
@ -44,7 +44,7 @@ func queuePipeline(repo *model.Repo, pipelineItems []*pipeline.Item) error {
task.DepStatus = make(map[string]model.StatusValue) task.DepStatus = make(map[string]model.StatusValue)
task.Data, _ = json.Marshal(rpc.Pipeline{ task.Data, _ = json.Marshal(rpc.Pipeline{
ID: fmt.Sprint(item.Step.ID), ID: fmt.Sprint(item.Workflow.ID),
Config: item.Config, Config: item.Config,
Timeout: repo.Timeout, Timeout: repo.Timeout,
}) })
@ -60,8 +60,8 @@ func queuePipeline(repo *model.Repo, pipelineItems []*pipeline.Item) error {
func taskIds(dependsOn []string, pipelineItems []*pipeline.Item) (taskIds []string) { func taskIds(dependsOn []string, pipelineItems []*pipeline.Item) (taskIds []string) {
for _, dep := range dependsOn { for _, dep := range dependsOn {
for _, pipelineItem := range pipelineItems { for _, pipelineItem := range pipelineItems {
if pipelineItem.Step.Name == dep { if pipelineItem.Workflow.Name == dep {
taskIds = append(taskIds, fmt.Sprint(pipelineItem.Step.ID)) taskIds = append(taskIds, fmt.Sprint(pipelineItem.Workflow.ID))
} }
} }
} }