You've already forked woodpecker
							
							
				mirror of
				https://github.com/woodpecker-ci/woodpecker.git
				synced 2025-10-30 23:27:39 +02:00 
			
		
		
		
	Introducing runs_on to run pipelines on failure
This commit is contained in:
		| @@ -23,6 +23,7 @@ type ( | ||||
| 		Volumes   Volumes | ||||
| 		Labels    libcompose.SliceorMap | ||||
| 		DependsOn []string `yaml:"depends_on,omitempty"` | ||||
| 		RunsOn    []string `yaml:"runs_on,omitempty"` | ||||
| 	} | ||||
|  | ||||
| 	// Workspace defines a pipeline workspace. | ||||
|   | ||||
| @@ -40,6 +40,8 @@ func TestParse(t *testing.T) { | ||||
| 				g.Assert(out.Labels["com.example.type"]).Equal("build") | ||||
| 				g.Assert(out.DependsOn[0]).Equal("lint") | ||||
| 				g.Assert(out.DependsOn[1]).Equal("test") | ||||
| 				g.Assert(out.RunsOn[0]).Equal("success") | ||||
| 				g.Assert(out.RunsOn[1]).Equal("failure") | ||||
| 			}) | ||||
| 			// Check to make sure variable expansion works in yaml.MapSlice | ||||
| 			// g.It("Should unmarshal variables", func() { | ||||
| @@ -99,6 +101,9 @@ labels: | ||||
| depends_on: | ||||
|   - lint | ||||
|   - test | ||||
| runs_on: | ||||
|   - success | ||||
|   - failure | ||||
| ` | ||||
|  | ||||
| var sampleVarYaml = ` | ||||
|   | ||||
| @@ -127,7 +127,7 @@ func TestFifoDependencies(t *testing.T) { | ||||
| 	task2 := &Task{ | ||||
| 		ID:           "2", | ||||
| 		Dependencies: []string{"1"}, | ||||
| 		DepStatus: make(map[string]bool), | ||||
| 		DepStatus:    make(map[string]bool), | ||||
| 	} | ||||
|  | ||||
| 	q := New().(*fifo) | ||||
| @@ -157,14 +157,14 @@ func TestFifoErrors(t *testing.T) { | ||||
| 	task2 := &Task{ | ||||
| 		ID:           "2", | ||||
| 		Dependencies: []string{"1"}, | ||||
| 		DepStatus: make(map[string]bool), | ||||
| 		DepStatus:    make(map[string]bool), | ||||
| 	} | ||||
|  | ||||
| 	task3 := &Task{ | ||||
| 		ID:           "3", | ||||
| 		Dependencies: []string{"1"}, | ||||
| 		DepStatus: make(map[string]bool), | ||||
| 		RunOn: []string{"success", "failure"}, | ||||
| 		DepStatus:    make(map[string]bool), | ||||
| 		RunOn:        []string{"success", "failure"}, | ||||
| 	} | ||||
|  | ||||
| 	q := New().(*fifo) | ||||
| @@ -202,3 +202,69 @@ func TestFifoErrors(t *testing.T) { | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestShouldRun(t *testing.T) { | ||||
| 	task := &Task{ | ||||
| 		ID:           "2", | ||||
| 		Dependencies: []string{"1"}, | ||||
| 		DepStatus: map[string]bool{ | ||||
| 			"1": true, | ||||
| 		}, | ||||
| 		RunOn: []string{"failure"}, | ||||
| 	} | ||||
| 	if task.ShouldRun() { | ||||
| 		t.Errorf("expect task to not run, it runs on failure only") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	task = &Task{ | ||||
| 		ID:           "2", | ||||
| 		Dependencies: []string{"1"}, | ||||
| 		DepStatus: map[string]bool{ | ||||
| 			"1": true, | ||||
| 		}, | ||||
| 		RunOn: []string{"failure", "success"}, | ||||
| 	} | ||||
| 	if !task.ShouldRun() { | ||||
| 		t.Errorf("expect task to run") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	task = &Task{ | ||||
| 		ID:           "2", | ||||
| 		Dependencies: []string{"1"}, | ||||
| 		DepStatus: map[string]bool{ | ||||
| 			"1": false, | ||||
| 		}, | ||||
| 	} | ||||
| 	if task.ShouldRun() { | ||||
| 		t.Errorf("expect task to not run") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	task = &Task{ | ||||
| 		ID:           "2", | ||||
| 		Dependencies: []string{"1"}, | ||||
| 		DepStatus: map[string]bool{ | ||||
| 			"1": true, | ||||
| 		}, | ||||
| 		RunOn: []string{"success"}, | ||||
| 	} | ||||
| 	if !task.ShouldRun() { | ||||
| 		t.Errorf("expect task to run") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	task = &Task{ | ||||
| 		ID:           "2", | ||||
| 		Dependencies: []string{"1"}, | ||||
| 		DepStatus: map[string]bool{ | ||||
| 			"1": false, | ||||
| 		}, | ||||
| 		RunOn: []string{"failure"}, | ||||
| 	} | ||||
| 	if !task.ShouldRun() { | ||||
| 		t.Errorf("expect task to run") | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -36,17 +36,29 @@ type Task struct { | ||||
|  | ||||
| // ShouldRun tells if a task should be run or skipped, based on dependencies | ||||
| func (t *Task) ShouldRun() bool { | ||||
| 	if runsOnFailure(t.RunOn) { | ||||
| 	if runsOnFailure(t.RunOn) && runsOnSuccess(t.RunOn) { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	for _, success := range t.DepStatus { | ||||
| 		if !success { | ||||
| 			return false | ||||
| 	if !runsOnFailure(t.RunOn) && runsOnSuccess(t.RunOn) { | ||||
| 		for _, success := range t.DepStatus { | ||||
| 			if !success { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	return true | ||||
| 	if runsOnFailure(t.RunOn) && !runsOnSuccess(t.RunOn) { | ||||
| 		for _, success := range t.DepStatus { | ||||
| 			if success { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func runsOnFailure(runsOn []string) bool { | ||||
| @@ -58,6 +70,19 @@ func runsOnFailure(runsOn []string) bool { | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func runsOnSuccess(runsOn []string) bool { | ||||
| 	if len(runsOn) == 0 { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	for _, status := range runsOn { | ||||
| 		if status == "success" { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // InfoT provides runtime information. | ||||
| type InfoT struct { | ||||
| 	Pending []*Task `json:"pending"` | ||||
|   | ||||
| @@ -321,6 +321,7 @@ func queueBuild(build *model.Build, repo *model.Repo, buildItems []*buildItem) { | ||||
| 		task.Labels["platform"] = item.Platform | ||||
| 		task.Labels["repo"] = repo.FullName | ||||
| 		task.Dependencies = taskIds(item.DependsOn, buildItems) | ||||
| 		task.RunOn = item.RunsOn | ||||
| 		task.DepStatus = make(map[string]bool) | ||||
|  | ||||
| 		task.Data, _ = json.Marshal(rpc.Pipeline{ | ||||
|   | ||||
| @@ -50,6 +50,7 @@ type buildItem struct { | ||||
| 	Platform  string | ||||
| 	Labels    map[string]string | ||||
| 	DependsOn []string | ||||
| 	RunsOn    []string | ||||
| 	Config    *backend.Config | ||||
| } | ||||
|  | ||||
| @@ -111,6 +112,7 @@ func (b *procBuilder) Build() ([]*buildItem, error) { | ||||
| 				Config:    ir, | ||||
| 				Labels:    parsed.Labels, | ||||
| 				DependsOn: parsed.DependsOn, | ||||
| 				RunsOn:    parsed.RunsOn, | ||||
| 				Platform:  metadata.Sys.Arch, | ||||
| 			} | ||||
| 			if item.Labels == nil { | ||||
|   | ||||
| @@ -124,3 +124,37 @@ depends_on: | ||||
| 		t.Fatal("Should depend on test") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRunsOn(t *testing.T) { | ||||
| 	b := procBuilder{ | ||||
| 		Repo:  &model.Repo{}, | ||||
| 		Curr:  &model.Build{}, | ||||
| 		Last:  &model.Build{}, | ||||
| 		Netrc: &model.Netrc{}, | ||||
| 		Secs:  []*model.Secret{}, | ||||
| 		Regs:  []*model.Registry{}, | ||||
| 		Link:  "", | ||||
| 		Yamls: []*remote.FileMeta{ | ||||
| 			&remote.FileMeta{Data: []byte(` | ||||
| pipeline: | ||||
|   deploy: | ||||
|     image: scratch | ||||
|  | ||||
| runs_on: | ||||
|   - success | ||||
|   - failure | ||||
| `)}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	buildItems, err := b.Build() | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if len(buildItems[0].RunsOn) != 2 { | ||||
| 		t.Fatal("Should run on success and failure") | ||||
| 	} | ||||
| 	if buildItems[0].RunsOn[1] != "failure" { | ||||
| 		t.Fatal("Should run on failure") | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user