mirror of
				https://github.com/go-task/task.git
				synced 2025-10-30 23:58:01 +02:00 
			
		
		
		
	Add Preconditions to Tasks
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -21,3 +21,6 @@ dist/ | |||||||
|  |  | ||||||
| # intellij idea/goland | # intellij idea/goland | ||||||
| .idea/ | .idea/ | ||||||
|  |  | ||||||
|  | # exuberant ctags | ||||||
|  | tags | ||||||
|   | |||||||
| @@ -141,6 +141,21 @@ includes: | |||||||
|   docker: ./DockerTasks.yml |   docker: ./DockerTasks.yml | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | ## Version 2.3 | ||||||
|  |  | ||||||
|  | Version 2.3 comes with `preconditions` stanza in tasks. | ||||||
|  |  | ||||||
|  | ```yaml | ||||||
|  | version: '2' | ||||||
|  |  | ||||||
|  | tasks: | ||||||
|  |   upload_environment: | ||||||
|  |     preconditions: | ||||||
|  |       - test -f .env | ||||||
|  |     cmds: | ||||||
|  |       - aws s3 cp .env s3://myenvironment | ||||||
|  | ``` | ||||||
|  |  | ||||||
| Please check the [documentation][includes] | Please check the [documentation][includes] | ||||||
|  |  | ||||||
| [output]: usage.md#output-syntax | [output]: usage.md#output-syntax | ||||||
|   | |||||||
| @@ -344,6 +344,53 @@ up-to-date. | |||||||
| Also, `task --status [tasks]...` will exit with a non-zero exit code if any of | Also, `task --status [tasks]...` will exit with a non-zero exit code if any of | ||||||
| the tasks are not up-to-date. | the tasks are not up-to-date. | ||||||
|  |  | ||||||
|  | If you need a certain set of conditions to be _true_ you can use the | ||||||
|  | `preconditions` stanza.  `preconditions` are very similar to `status` | ||||||
|  | lines except they support `sh` expansion and they SHOULD all return 0 | ||||||
|  |  | ||||||
|  | ```yaml | ||||||
|  | version: '2' | ||||||
|  |  | ||||||
|  | tasks: | ||||||
|  |   generate-files: | ||||||
|  |     cmds: | ||||||
|  |       - mkdir directory | ||||||
|  |       - touch directory/file1.txt | ||||||
|  |       - touch directory/file2.txt | ||||||
|  |     # test existence of files | ||||||
|  |     preconditions: | ||||||
|  |       - test -f .env | ||||||
|  |       - sh: "[ 1 = 0 ]" | ||||||
|  |         msg: "One doesn't equal Zero, Halting" | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Preconditions can set specific failure messages that can tell | ||||||
|  | a user what to do using the `msg` field. | ||||||
|  |  | ||||||
|  | If a task has a dependency on a sub-task with a precondition, and that | ||||||
|  | precondition is not met - the calling task will fail.  Adding `ignore_errors` | ||||||
|  | to the precondition will cause parent tasks to execute even if the sub task | ||||||
|  | can not run.  Note that a task executed directly with a failing precondition | ||||||
|  | will not run unless `--force` is given. | ||||||
|  |  | ||||||
|  | ```yaml | ||||||
|  | version: '2' | ||||||
|  | tasks: | ||||||
|  |   task_will_fail: | ||||||
|  |     preconditions: | ||||||
|  |       - sh: "exit 1" | ||||||
|  |         ignore_errors: true | ||||||
|  |  | ||||||
|  |   task_will_succeed: | ||||||
|  |   deps: | ||||||
|  |     - task_will_fail | ||||||
|  |  | ||||||
|  |   task_will_succeed: | ||||||
|  |   cmds: | ||||||
|  |     - task: task_will_fail | ||||||
|  |     - echo "I will run" | ||||||
|  | ``` | ||||||
|  |  | ||||||
| ## Variables | ## Variables | ||||||
|  |  | ||||||
| When doing interpolation of variables, Task will look for the below. | When doing interpolation of variables, Task will look for the below. | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								internal/taskfile/precondition.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								internal/taskfile/precondition.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | package taskfile | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// ErrCantUnmarshalPrecondition is returned for invalid precond YAML. | ||||||
|  | 	ErrCantUnmarshalPrecondition = errors.New("task: can't unmarshal precondition value") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Precondition represents a precondition necessary for a task to run | ||||||
|  | type Precondition struct { | ||||||
|  | 	Sh          string | ||||||
|  | 	Msg         string | ||||||
|  | 	IgnoreError bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UnmarshalYAML implements yaml.Unmarshaler interface. | ||||||
|  | func (p *Precondition) UnmarshalYAML(unmarshal func(interface{}) error) error { | ||||||
|  | 	var cmd string | ||||||
|  |  | ||||||
|  | 	if err := unmarshal(&cmd); err == nil { | ||||||
|  | 		p.Sh = cmd | ||||||
|  | 		p.Msg = fmt.Sprintf("`%s` failed", cmd) | ||||||
|  | 		p.IgnoreError = false | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var sh struct { | ||||||
|  | 		Sh          string | ||||||
|  | 		Msg         string | ||||||
|  | 		IgnoreError bool `yaml:"ignore_error"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err := unmarshal(&sh) | ||||||
|  |  | ||||||
|  | 	if err == nil { | ||||||
|  | 		p.Sh = sh.Sh | ||||||
|  | 		p.Msg = sh.Msg | ||||||
|  | 		if p.Msg == "" { | ||||||
|  | 			p.Msg = fmt.Sprintf("%s failed", sh.Sh) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		p.IgnoreError = sh.IgnoreError | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return err | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								internal/taskfile/precondition_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								internal/taskfile/precondition_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | package taskfile_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/go-task/task/v2/internal/taskfile" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"gopkg.in/yaml.v2" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestPreconditionParse(t *testing.T) { | ||||||
|  | 	tests := []struct { | ||||||
|  | 		content  string | ||||||
|  | 		v        interface{} | ||||||
|  | 		expected interface{} | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			"test -f foo.txt", | ||||||
|  | 			&taskfile.Precondition{}, | ||||||
|  | 			&taskfile.Precondition{Sh: `test -f foo.txt`, Msg: "`test -f foo.txt` failed", IgnoreError: false}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"sh: '[ 1 = 0 ]'", | ||||||
|  | 			&taskfile.Precondition{}, | ||||||
|  | 			&taskfile.Precondition{Sh: "[ 1 = 0 ]", Msg: "[ 1 = 0 ] failed", IgnoreError: false}, | ||||||
|  | 		}, | ||||||
|  | 		{` | ||||||
|  | sh: "[ 1 = 2 ]" | ||||||
|  | msg: "1 is not 2" | ||||||
|  | `, | ||||||
|  | 			&taskfile.Precondition{}, | ||||||
|  | 			&taskfile.Precondition{Sh: "[ 1 = 2 ]", Msg: "1 is not 2", IgnoreError: false}, | ||||||
|  | 		}, | ||||||
|  | 		{` | ||||||
|  | sh: "[ 1 = 2 ]" | ||||||
|  | msg: "1 is not 2" | ||||||
|  | ignore_error: true | ||||||
|  | `, | ||||||
|  | 			&taskfile.Precondition{}, | ||||||
|  | 			&taskfile.Precondition{Sh: "[ 1 = 2 ]", Msg: "1 is not 2", IgnoreError: true}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	for _, test := range tests { | ||||||
|  | 		err := yaml.Unmarshal([]byte(test.content), test.v) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 		assert.Equal(t, test.expected, test.v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -5,19 +5,20 @@ type Tasks map[string]*Task | |||||||
|  |  | ||||||
| // Task represents a task | // Task represents a task | ||||||
| type Task struct { | type Task struct { | ||||||
| 	Task        string | 	Task         string | ||||||
| 	Cmds        []*Cmd | 	Cmds         []*Cmd | ||||||
| 	Deps        []*Dep | 	Deps         []*Dep | ||||||
| 	Desc        string | 	Desc         string | ||||||
| 	Summary     string | 	Summary      string | ||||||
| 	Sources     []string | 	Sources      []string | ||||||
| 	Generates   []string | 	Generates    []string | ||||||
| 	Status      []string | 	Status       []string | ||||||
| 	Dir         string | 	Precondition []*Precondition | ||||||
| 	Vars        Vars | 	Dir          string | ||||||
| 	Env         Vars | 	Vars         Vars | ||||||
| 	Silent      bool | 	Env          Vars | ||||||
| 	Method      string | 	Silent       bool | ||||||
| 	Prefix      string | 	Method       string | ||||||
| 	IgnoreError bool `yaml:"ignore_error"` | 	Prefix       string | ||||||
|  | 	IgnoreError  bool `yaml:"ignore_error"` | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,6 +10,8 @@ var ( | |||||||
| 	v21 = mustVersion("2.1") | 	v21 = mustVersion("2.1") | ||||||
| 	v22 = mustVersion("2.2") | 	v22 = mustVersion("2.2") | ||||||
| 	v23 = mustVersion("2.3") | 	v23 = mustVersion("2.3") | ||||||
|  | 	v24 = mustVersion("2.4") | ||||||
|  | 	v25 = mustVersion("2.5") | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // IsV1 returns if is a given Taskfile version is version 1 | // IsV1 returns if is a given Taskfile version is version 1 | ||||||
| @@ -37,6 +39,16 @@ func IsV23(v *semver.Constraints) bool { | |||||||
| 	return v.Check(v23) | 	return v.Check(v23) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // IsV24 returns if is a given Taskfile version is at least version 2.4 | ||||||
|  | func IsV24(v *semver.Constraints) bool { | ||||||
|  | 	return v.Check(v24) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsV25 returns if is a given Taskfile version is at least version 2.5 | ||||||
|  | func IsV25(v *semver.Constraints) bool { | ||||||
|  | 	return v.Check(v25) | ||||||
|  | } | ||||||
|  |  | ||||||
| func mustVersion(s string) *semver.Version { | func mustVersion(s string) *semver.Version { | ||||||
| 	v, err := semver.NewVersion(s) | 	v, err := semver.NewVersion(s) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								precondition.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								precondition.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | // Package task provides ... | ||||||
|  | package task | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  |  | ||||||
|  | 	"github.com/go-task/task/v2/internal/execext" | ||||||
|  | 	"github.com/go-task/task/v2/internal/taskfile" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// ErrNecessaryPreconditionFailed is returned when a precondition fails | ||||||
|  | 	ErrNecessaryPreconditionFailed = errors.New("task: precondition not met") | ||||||
|  | 	// ErrOptionalPreconditionFailed is returned when a precondition fails | ||||||
|  | 	// that has ignore_error set to true | ||||||
|  | 	ErrOptionalPreconditionFailed = errors.New("task: optional precondition not met") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (e *Executor) areTaskPreconditionsMet(ctx context.Context, t *taskfile.Task) (bool, error) { | ||||||
|  | 	var optionalPreconditionFailed bool | ||||||
|  | 	for _, p := range t.Precondition { | ||||||
|  | 		err := execext.RunCommand(ctx, &execext.RunCommandOptions{ | ||||||
|  | 			Command: p.Sh, | ||||||
|  | 			Dir:     t.Dir, | ||||||
|  | 			Env:     getEnviron(t), | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			e.Logger.Outf(p.Msg) | ||||||
|  | 			if p.IgnoreError == true { | ||||||
|  | 				optionalPreconditionFailed = true | ||||||
|  | 			} else { | ||||||
|  | 				return false, ErrNecessaryPreconditionFailed | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if optionalPreconditionFailed == true { | ||||||
|  | 		return true, ErrOptionalPreconditionFailed | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
| @@ -78,8 +78,10 @@ func (e *Executor) isTaskUpToDateStatus(ctx context.Context, t *taskfile.Task) ( | |||||||
| 			Env:     getEnviron(t), | 			Env:     getEnviron(t), | ||||||
| 		}) | 		}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | 			e.Logger.VerboseOutf("task: status command %s exited non-zero: %s", s, err) | ||||||
| 			return false, nil | 			return false, nil | ||||||
| 		} | 		} | ||||||
|  | 		e.Logger.VerboseOutf("task: status command %s exited zero", s) | ||||||
| 	} | 	} | ||||||
| 	return true, nil | 	return true, nil | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								task.go
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								task.go
									
									
									
									
									
								
							| @@ -119,7 +119,7 @@ func (e *Executor) Setup() error { | |||||||
| 			Vars:   e.taskvars, | 			Vars:   e.taskvars, | ||||||
| 			Logger: e.Logger, | 			Logger: e.Logger, | ||||||
| 		} | 		} | ||||||
| 	case version.IsV2(v), version.IsV21(v), version.IsV22(v): | 	case version.IsV2(v), version.IsV21(v), version.IsV22(v), version.IsV23(v): | ||||||
| 		e.Compiler = &compilerv2.CompilerV2{ | 		e.Compiler = &compilerv2.CompilerV2{ | ||||||
| 			Dir:          e.Dir, | 			Dir:          e.Dir, | ||||||
| 			Taskvars:     e.taskvars, | 			Taskvars:     e.taskvars, | ||||||
| @@ -127,8 +127,9 @@ func (e *Executor) Setup() error { | |||||||
| 			Expansions:   e.Taskfile.Expansions, | 			Expansions:   e.Taskfile.Expansions, | ||||||
| 			Logger:       e.Logger, | 			Logger:       e.Logger, | ||||||
| 		} | 		} | ||||||
| 	case version.IsV23(v): |  | ||||||
| 		return fmt.Errorf(`task: Taskfile versions greater than v2.3 not implemented in the version of Task`) | 	case version.IsV24(v): | ||||||
|  | 		return fmt.Errorf(`task: Taskfile versions greater than v2.4 not implemented in the version of Task`) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if !version.IsV21(v) && e.Taskfile.Output != "" { | 	if !version.IsV21(v) && e.Taskfile.Output != "" { | ||||||
| @@ -192,7 +193,13 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error { | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		if upToDate { |  | ||||||
|  | 		preCondMet, err := e.areTaskPreconditionsMet(ctx, t) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if upToDate && preCondMet { | ||||||
| 			if !e.Silent { | 			if !e.Silent { | ||||||
| 				e.Logger.Errf(`task: Task "%s" is up to date`, t.Task) | 				e.Logger.Errf(`task: Task "%s" is up to date`, t.Task) | ||||||
| 			} | 			} | ||||||
| @@ -224,7 +231,15 @@ func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error { | |||||||
| 		d := d | 		d := d | ||||||
|  |  | ||||||
| 		g.Go(func() error { | 		g.Go(func() error { | ||||||
| 			return e.RunTask(ctx, taskfile.Call{Task: d.Task, Vars: d.Vars}) | 			err := e.RunTask(ctx, taskfile.Call{Task: d.Task, Vars: d.Vars}) | ||||||
|  | 			if err != nil { | ||||||
|  | 				if err == ErrOptionalPreconditionFailed { | ||||||
|  | 					e.Logger.Errf("%s", err) | ||||||
|  | 				} else { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			return nil | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -236,7 +251,15 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi | |||||||
|  |  | ||||||
| 	switch { | 	switch { | ||||||
| 	case cmd.Task != "": | 	case cmd.Task != "": | ||||||
| 		return e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars}) | 		err := e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			if err == ErrOptionalPreconditionFailed { | ||||||
|  | 				e.Logger.Errf("%s", err) | ||||||
|  | 			} else { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
| 	case cmd.Cmd != "": | 	case cmd.Cmd != "": | ||||||
| 		if e.Verbose || (!cmd.Silent && !t.Silent && !e.Silent) { | 		if e.Verbose || (!cmd.Silent && !t.Silent && !e.Silent) { | ||||||
| 			e.Logger.Errf(cmd.Cmd) | 			e.Logger.Errf(cmd.Cmd) | ||||||
|   | |||||||
							
								
								
									
										62
									
								
								task_test.go
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								task_test.go
									
									
									
									
									
								
							| @@ -273,6 +273,68 @@ func TestStatus(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestPrecondition(t *testing.T) { | ||||||
|  | 	const dir = "testdata/precondition" | ||||||
|  |  | ||||||
|  | 	var buff bytes.Buffer | ||||||
|  | 	e := &task.Executor{ | ||||||
|  | 		Dir:    dir, | ||||||
|  | 		Stdout: &buff, | ||||||
|  | 		Stderr: &buff, | ||||||
|  | 		Silent: false, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// A precondition that has been met | ||||||
|  | 	assert.NoError(t, e.Setup()) | ||||||
|  | 	assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "foo"})) | ||||||
|  | 	if buff.String() != "" { | ||||||
|  | 		t.Errorf("Got Output when none was expected: %s", buff.String()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// A precondition that was not met | ||||||
|  | 	assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "impossible"})) | ||||||
|  |  | ||||||
|  | 	if buff.String() != "1 != 0\n" { | ||||||
|  | 		t.Errorf("Wrong output message: %s", buff.String()) | ||||||
|  | 	} | ||||||
|  | 	buff.Reset() | ||||||
|  |  | ||||||
|  | 	// Calling a task with a precondition in a dependency fails the task | ||||||
|  | 	assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "depends_on_imposssible"})) | ||||||
|  | 	if buff.String() != "1 != 0\n" { | ||||||
|  | 		t.Errorf("Wrong output message: %s", buff.String()) | ||||||
|  | 	} | ||||||
|  | 	buff.Reset() | ||||||
|  |  | ||||||
|  | 	// Calling a task with a precondition in a cmd fails the task | ||||||
|  | 	assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "executes_failing_task_as_cmd"})) | ||||||
|  | 	if buff.String() != "1 != 0\n" { | ||||||
|  | 		t.Errorf("Wrong output message: %s", buff.String()) | ||||||
|  | 	} | ||||||
|  | 	buff.Reset() | ||||||
|  |  | ||||||
|  | 	// A task with a failing precondition and ignore_errors on still fails | ||||||
|  | 	assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "impossible_but_i_dont_care"})) | ||||||
|  | 	if buff.String() != "2 != 1\n" { | ||||||
|  | 		t.Errorf("Wrong output message: %s", buff.String()) | ||||||
|  | 	} | ||||||
|  | 	buff.Reset() | ||||||
|  |  | ||||||
|  | 	// If a precondition has ignore errors, then it will allow _dependent_ tasks to execute | ||||||
|  | 	assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "depends_on_failure_of_impossible"})) | ||||||
|  | 	if buff.String() != "2 != 1\ntask: optional precondition not met\n" { | ||||||
|  | 		t.Errorf("Wrong output message: %s", buff.String()) | ||||||
|  | 	} | ||||||
|  | 	buff.Reset() | ||||||
|  |  | ||||||
|  | 	// If a precondition has ignore errors, then it will allow tasks calling it to execute | ||||||
|  | 	assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "executes_failing_task_as_cmd_but_succeeds"})) | ||||||
|  | 	if buff.String() != "2 != 1\ntask: optional precondition not met\n" { | ||||||
|  | 		t.Errorf("Wrong output message: %s", buff.String()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestGenerates(t *testing.T) { | func TestGenerates(t *testing.T) { | ||||||
| 	const ( | 	const ( | ||||||
| 		srcTask        = "sub/src.txt" | 		srcTask        = "sub/src.txt" | ||||||
|   | |||||||
							
								
								
									
										34
									
								
								testdata/precondition/Taskfile.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								testdata/precondition/Taskfile.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | version: '2' | ||||||
|  |  | ||||||
|  | tasks: | ||||||
|  |   foo: | ||||||
|  |     precondition: | ||||||
|  |       - test -f foo.txt | ||||||
|  |  | ||||||
|  |   impossible: | ||||||
|  |     precondition: | ||||||
|  |       - sh: "[ 1 = 0 ]" | ||||||
|  |         msg: "1 != 0" | ||||||
|  |  | ||||||
|  |   impossible_but_i_dont_care: | ||||||
|  |     precondition: | ||||||
|  |       - sh: "[ 2 = 1 ]" | ||||||
|  |         msg: "2 != 1" | ||||||
|  |         ignore_error: true | ||||||
|  |  | ||||||
|  |   depends_on_imposssible: | ||||||
|  |     deps: | ||||||
|  |       - impossible | ||||||
|  |  | ||||||
|  |   executes_failing_task_as_cmd: | ||||||
|  |     cmds: | ||||||
|  |       - task: impossible | ||||||
|  |  | ||||||
|  |   depends_on_failure_of_impossible: | ||||||
|  |     deps: | ||||||
|  |       - impossible_but_i_dont_care | ||||||
|  |  | ||||||
|  |   executes_failing_task_as_cmd_but_succeeds: | ||||||
|  |     cmds: | ||||||
|  |       - task: impossible_but_i_dont_care | ||||||
|  |  | ||||||
							
								
								
									
										0
									
								
								testdata/precondition/foo.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								testdata/precondition/foo.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
								
								
									
										11
									
								
								variables.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								variables.go
									
									
									
									
									
								
							| @@ -73,6 +73,7 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) { | |||||||
| 				IgnoreError: cmd.IgnoreError, | 				IgnoreError: cmd.IgnoreError, | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
| 	if len(origTask.Deps) > 0 { | 	if len(origTask.Deps) > 0 { | ||||||
| 		new.Deps = make([]*taskfile.Dep, len(origTask.Deps)) | 		new.Deps = make([]*taskfile.Dep, len(origTask.Deps)) | ||||||
| @@ -83,6 +84,16 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	if len(origTask.Precondition) > 0 { | ||||||
|  | 		new.Precondition = make([]*taskfile.Precondition, len(origTask.Precondition)) | ||||||
|  | 		for i, precond := range origTask.Precondition { | ||||||
|  | 			new.Precondition[i] = &taskfile.Precondition{ | ||||||
|  | 				Sh:          r.Replace(precond.Sh), | ||||||
|  | 				Msg:         r.Replace(precond.Msg), | ||||||
|  | 				IgnoreError: precond.IgnoreError, | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return &new, r.Err() | 	return &new, r.Err() | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user