From 8f80fc4e2c283c41cb1525b88996bd07743cbf31 Mon Sep 17 00:00:00 2001 From: Sally Young Date: Wed, 11 Aug 2021 17:28:44 +0100 Subject: [PATCH] Issue #519: Allow includes to be optional --- docs/usage.md | 18 ++++++++++ task_test.go | 35 +++++++++++++++++++ taskfile/included_taskfile.go | 5 ++- taskfile/read/taskfile.go | 4 +++ testdata/includes_optional/.gitignore | 1 + testdata/includes_optional/Taskfile.yml | 11 ++++++ .../Taskfile.yml | 11 ++++++ .../Taskfile.yml | 9 +++++ 8 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 testdata/includes_optional/.gitignore create mode 100644 testdata/includes_optional/Taskfile.yml create mode 100644 testdata/includes_optional_explicit_false/Taskfile.yml create mode 100644 testdata/includes_optional_implicit_false/Taskfile.yml diff --git a/docs/usage.md b/docs/usage.md index f3a704a2..5495c422 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -154,6 +154,24 @@ includes: > This was a deliberate decision to keep use and implementation simple. > If you disagree, open an GitHub issue and explain your use case. =) +### Optional includes + +Includes marked as optional will allow Task to continue execution as normal if +the included file is missing. + +```yaml +version: '3' + +includes: + tests: + taskfile: ./tests/Taskfile.yml + optional: true +tasks: + greet: + cmds: + - echo "This command can still be successfully executed if ./tests/Taskfile.yml does not exist" +``` + ## Task directory By default, tasks will be executed in the directory where the Taskfile is diff --git a/task_test.go b/task_test.go index d35b8a39..3ed80c15 100644 --- a/task_test.go +++ b/task_test.go @@ -755,6 +755,41 @@ func TestIncludesCallingRoot(t *testing.T) { tt.Run(t) } +func TestIncludesOptional(t *testing.T) { + tt := fileContentTest{ + Dir: "testdata/includes_optional", + Target: "default", + TrimSpace: true, + Files: map[string]string{ + "called_dep.txt": "called_dep", + }} + tt.Run(t) +} + +func TestIncludesOptionalImplicitFalse(t *testing.T) { + e := task.Executor{ + Dir: "testdata/includes_optional_implicit_false", + Stdout: ioutil.Discard, + Stderr: ioutil.Discard, + } + + err := e.Setup() + assert.Error(t, err) + assert.Equal(t, "stat testdata/includes_optional_implicit_false/TaskfileOptional.yml: no such file or directory", err.Error()) +} + +func TestIncludesOptionalExplicitFalse(t *testing.T) { + e := task.Executor{ + Dir: "testdata/includes_optional_explicit_false", + Stdout: ioutil.Discard, + Stderr: ioutil.Discard, + } + + err := e.Setup() + assert.Error(t, err) + assert.Equal(t, "stat testdata/includes_optional_explicit_false/TaskfileOptional.yml: no such file or directory", err.Error()) +} + func TestSummary(t *testing.T) { const dir = "testdata/summary" diff --git a/taskfile/included_taskfile.go b/taskfile/included_taskfile.go index a5eaba86..a0dd8866 100644 --- a/taskfile/included_taskfile.go +++ b/taskfile/included_taskfile.go @@ -10,6 +10,7 @@ import ( type IncludedTaskfile struct { Taskfile string Dir string + Optional bool AdvancedImport bool } @@ -92,12 +93,14 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err var includedTaskfile struct { Taskfile string Dir string + Optional bool } if err := unmarshal(&includedTaskfile); err != nil { return err } - it.Dir = includedTaskfile.Dir it.Taskfile = includedTaskfile.Taskfile + it.Dir = includedTaskfile.Dir + it.Optional = includedTaskfile.Optional it.AdvancedImport = true return nil } diff --git a/taskfile/read/taskfile.go b/taskfile/read/taskfile.go index 780edd8e..f33cdb47 100644 --- a/taskfile/read/taskfile.go +++ b/taskfile/read/taskfile.go @@ -42,6 +42,7 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) { includedTask = taskfile.IncludedTaskfile{ Taskfile: tr.Replace(includedTask.Taskfile), Dir: tr.Replace(includedTask.Dir), + Optional: includedTask.Optional, AdvancedImport: includedTask.AdvancedImport, } if err := tr.Err(); err != nil { @@ -56,6 +57,9 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) { } info, err := os.Stat(path) + if err != nil && includedTask.Optional { + return nil + } if err != nil { return err } diff --git a/testdata/includes_optional/.gitignore b/testdata/includes_optional/.gitignore new file mode 100644 index 00000000..2211df63 --- /dev/null +++ b/testdata/includes_optional/.gitignore @@ -0,0 +1 @@ +*.txt diff --git a/testdata/includes_optional/Taskfile.yml b/testdata/includes_optional/Taskfile.yml new file mode 100644 index 00000000..0791bdfe --- /dev/null +++ b/testdata/includes_optional/Taskfile.yml @@ -0,0 +1,11 @@ +version: '3' + +includes: + included: + taskfile: TaskfileOptional.yml + optional: true + +tasks: + default: + cmds: + - echo "called_dep" > called_dep.txt diff --git a/testdata/includes_optional_explicit_false/Taskfile.yml b/testdata/includes_optional_explicit_false/Taskfile.yml new file mode 100644 index 00000000..54985e14 --- /dev/null +++ b/testdata/includes_optional_explicit_false/Taskfile.yml @@ -0,0 +1,11 @@ +version: '3' + +includes: + included: + taskfile: TaskfileOptional.yml + optional: false + +tasks: + default: + cmds: + - echo "Hello, world!" diff --git a/testdata/includes_optional_implicit_false/Taskfile.yml b/testdata/includes_optional_implicit_false/Taskfile.yml new file mode 100644 index 00000000..996ffec4 --- /dev/null +++ b/testdata/includes_optional_implicit_false/Taskfile.yml @@ -0,0 +1,9 @@ +version: '3' + +includes: + included: TaskfileOptional.yml + +tasks: + default: + cmds: + - echo "Hello, world!"