diff --git a/README.md b/README.md index 98d3b857..919c7e5d 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. + ## Output syntax By default, Task just redirect the STDOUT and STDERR of the running commands diff --git a/cmd/task/task.go b/cmd/task/task.go index 6afde2d8..18a4e2bd 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 + dryRun 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(&dryRun, "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, + DryRun: dryRun, 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 48092e48..019faeea 100644 --- a/task.go +++ b/task.go @@ -35,6 +35,7 @@ type Executor struct { Watch bool Verbose bool Silent bool + DryRun 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.DryRun { + 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 6f0214c9..cac097b0 100644 --- a/task_test.go +++ b/task_test.go @@ -432,3 +432,25 @@ func TestExpand(t *testing.T) { assert.NoError(t, e.Run(taskfile.Call{Task: "pwd"})) assert.Equal(t, home, strings.TrimSpace(buff.String())) } + +func TestDryRun(t *testing.T) { + const dir = "testdata/dryrun" + + file := filepath.Join(dir, "file.txt") + _ = os.Remove(file) + + var buff bytes.Buffer + + e := task.Executor{ + Dir: dir, + Stdout: &buff, + Stderr: &buff, + DryRun: true, + } + assert.NoError(t, e.Setup()) + assert.NoError(t, e.Run(taskfile.Call{Task: "build"})) + + if _, err := os.Stat(file); err == nil { + t.Errorf("File should not exist %s", file) + } +} diff --git a/testdata/dryrun/Taskfile.yml b/testdata/dryrun/Taskfile.yml new file mode 100644 index 00000000..62c6dde7 --- /dev/null +++ b/testdata/dryrun/Taskfile.yml @@ -0,0 +1,3 @@ +build: + cmds: + - touch file.txt \ No newline at end of file