diff --git a/README.md b/README.md index 09735bc8..e2bbd19a 100644 --- a/README.md +++ b/README.md @@ -635,6 +635,11 @@ tasks: - echo "This will print nothing" > /dev/null ``` +## Dry Run Mode + +Dry run mode (`--dry`) compiles and steps through each task, printing the commands +that would be run without executing them. This is useful for debugging your Taskfiles. + ## Ignore errors You have the option to ignore errors during command execution. diff --git a/cmd/task/task.go b/cmd/task/task.go index 6afde2d8..74c54697 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -17,7 +17,7 @@ var ( version = "master" ) -const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [task...] +const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--dry] [task...] Runs the specified task(s). Falls back to the "default" task if no task name was specified, or lists all tasks if an unknown task name was specified. @@ -55,6 +55,7 @@ func main() { watch bool verbose bool silent bool + dry bool dir string ) @@ -66,6 +67,7 @@ func main() { pflag.BoolVarP(&watch, "watch", "w", false, "enables watch of the given task") pflag.BoolVarP(&verbose, "verbose", "v", false, "enables verbose mode") pflag.BoolVarP(&silent, "silent", "s", false, "disables echoing") + pflag.BoolVar(&dry, "dry", false, "compiles and prints tasks in the order that they would be run, without executing them") pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution") pflag.Parse() @@ -91,6 +93,7 @@ func main() { Verbose: verbose, Silent: silent, Dir: dir, + Dry: dry, Context: getSignalContext(), diff --git a/completion/zsh/_task b/completion/zsh/_task index 362ee9ac..438465ce 100644 --- a/completion/zsh/_task +++ b/completion/zsh/_task @@ -12,6 +12,7 @@ function __list() { _arguments \ '(-d --dir)'{-d,--dir}': :_files' \ + '(--dry)'--dry \ '(-f --force)'{-f,--force} \ '(-i --init)'{-i,--init} \ '(-l --list)'{-l,--list} \ diff --git a/task.go b/task.go index d1e89a66..ab62c691 100644 --- a/task.go +++ b/task.go @@ -35,6 +35,7 @@ type Executor struct { Watch bool Verbose bool Silent bool + Dry bool Context context.Context @@ -211,6 +212,10 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi e.Logger.Errf(cmd.Cmd) } + if e.Dry { + return nil + } + stdOut := e.Output.WrapWriter(e.Stdout, t.Prefix) stdErr := e.Output.WrapWriter(e.Stderr, t.Prefix) defer stdOut.Close() diff --git a/task_test.go b/task_test.go index f3c8ec6b..84e2d570 100644 --- a/task_test.go +++ b/task_test.go @@ -455,3 +455,26 @@ func TestExpand(t *testing.T) { assert.NoError(t, e.Run(taskfile.Call{Task: "pwd"})) assert.Equal(t, home, strings.TrimSpace(buff.String())) } + +func TestDry(t *testing.T) { + const dir = "testdata/dry" + + file := filepath.Join(dir, "file.txt") + _ = os.Remove(file) + + var buff bytes.Buffer + + e := task.Executor{ + Dir: dir, + Stdout: &buff, + Stderr: &buff, + Dry: true, + } + assert.NoError(t, e.Setup()) + assert.NoError(t, e.Run(taskfile.Call{Task: "build"})) + + assert.Equal(t, "touch file.txt", strings.TrimSpace(buff.String())) + if _, err := os.Stat(file); err == nil { + t.Errorf("File should not exist %s", file) + } +} diff --git a/testdata/dry/Taskfile.yml b/testdata/dry/Taskfile.yml new file mode 100644 index 00000000..b4e932ed --- /dev/null +++ b/testdata/dry/Taskfile.yml @@ -0,0 +1,6 @@ +version: '2' + +tasks: + build: + cmds: + - touch file.txt