diff --git a/README.md b/README.md index 7737d21..4e599e9 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ myke solves all these problems in a single tiny binary, to avoid reinventing the * Built-in templating using golang text/template and 50+ functions provided by [sprig](https://github.com/Masterminds/sprig) * Mixin ymls to share tasks, envvars, etc * Runtime arguments like `myke task1 --key1=val1 task2 --key2=val2 ...` -* `before/after` hooks to perform cleanups, chains with mixins, etc (`error` hook in the roadmap) +* `before/after/error` hooks to perform cleanups, chains with mixins, etc * `retry` support with max and delay for failing tasks * and a lot of small utilities packed in diff --git a/core/execution.go b/core/execution.go index 1b5723b..4f5dcd9 100644 --- a/core/execution.go +++ b/core/execution.go @@ -77,6 +77,10 @@ func (e *Execution) executeTask() error { err = e.executeCmd(e.Task.After) } } + + if err != nil && len(e.Task.Error) > 0 { + e.executeCmd(e.Task.Error) + } return err } diff --git a/core/task.go b/core/task.go index 8be0922..c73452b 100644 --- a/core/task.go +++ b/core/task.go @@ -15,6 +15,7 @@ type Task struct { Cmd string Before string After string + Error string Shell string Retry int RetryDelay time.Duration @@ -38,6 +39,9 @@ func loadTaskJSON(name string, json gjson.Result) Task { if j := json.Get("after"); j.Exists() { t.After = strings.TrimSpace(j.String()) } + if j := json.Get("error"); j.Exists() { + t.Error = strings.TrimSpace(j.String()) + } if j := json.Get("shell"); j.Exists() { t.Shell = strings.TrimSpace(j.String()) } @@ -68,5 +72,6 @@ func mixinTask(taskName string, child Task, parent Task) Task { } child.Before = strings.TrimSpace(child.Before + "\n" + parent.Before) child.After = strings.TrimSpace(child.After + "\n" + parent.After) + child.Error = strings.TrimSpace(child.Error + "\n" + parent.Error) return child } diff --git a/examples/env/package_test.go b/examples/env/package_test.go index c226ed4..a776217 100644 --- a/examples/env/package_test.go +++ b/examples/env/package_test.go @@ -6,12 +6,12 @@ import ( ) var tests = []TestTable{ - {`yml`, `yml`, `envvar from yml is value_from_yml`}, - {`file_default`, `file_default`, `envvar from myke.env is value_from_myke.env`}, - {`file_default_local`, `file_default_local`, `envvar from myke.env.local is value_from_myke.env.local`}, - {`file_custom`, `file_custom`, `envvar from test.env is value_from_test.env`}, - {`file_custom_local`, `file_custom_local`, `envvar from test.env.local is value_from_test.env.local`}, - {`path`, `path`, `PATH is [^:]+env/path_from_myke.env.local:[^:]+env/path_from_myke.env:[^:]+env/path_from_test.env.local:[^:]+env/path_from_test.env:[^:]+env/path_from_yml:[^:]+env/bin`}, + {Arg: `yml`, Out: `envvar from yml is value_from_yml`}, + {Arg: `file_default`, Out: `envvar from myke.env is value_from_myke.env`}, + {Arg: `file_default_local`, Out: `envvar from myke.env.local is value_from_myke.env.local`}, + {Arg: `file_custom`, Out: `envvar from test.env is value_from_test.env`}, + {Arg: `file_custom_local`, Out: `envvar from test.env.local is value_from_test.env.local`}, + {Arg: `path`, Out: `PATH is [^:]+env/path_from_myke.env.local:[^:]+env/path_from_myke.env:[^:]+env/path_from_test.env.local:[^:]+env/path_from_test.env:[^:]+env/path_from_yml:[^:]+env/bin`}, } func Test(t *testing.T) { diff --git a/examples/hooks/myke.yml b/examples/hooks/myke.yml index 62dd8a7..f841350 100644 --- a/examples/hooks/myke.yml +++ b/examples/hooks/myke.yml @@ -8,7 +8,6 @@ tasks: cmd: echo running before after: cmd: echo running after - before_after: - cmd: echo running cmd - before: $myke before - after: $myke after + error: + cmd: foobar + error: echo there was an error diff --git a/examples/hooks/package_test.go b/examples/hooks/package_test.go index 71afb17..09ecaec 100644 --- a/examples/hooks/package_test.go +++ b/examples/hooks/package_test.go @@ -6,9 +6,9 @@ import ( ) var tests = []TestTable{ - {`before`, `before`, `running before`}, - {`after`, `after`, `running after`}, - // {`before_after`, `before_after`, `running before.+running cmd.+running after`}, + {Arg: `before`, Out: `running before`}, + {Arg: `after`, Out: `running after`}, + {Arg: `error`, Out: `(?s)foobar.*there was an error`, Err: true}, } func Test(t *testing.T) { diff --git a/examples/mixin/package_test.go b/examples/mixin/package_test.go index bd8e0ef..4f48206 100644 --- a/examples/mixin/package_test.go +++ b/examples/mixin/package_test.go @@ -6,10 +6,10 @@ import ( ) var tests = []TestTable{ - {`task1`, `task1`, `parent says value_parent_1`}, - {`task2`, `task2`, `(?s)parent says value_child_2.*?child says value_child_2`}, - {`task3`, `task3`, `child says value_child_3`}, - {`path`, `path`, `PATH is [^:]+mixin/path_child:[^:]+mixin/bin:[^:]+mixin/parent/path_parent:[^:]+mixin/parent/bin`}, + {Arg: `task1`, Out: `parent says value_parent_1`}, + {Arg: `task2`, Out: `(?s)parent says value_child_2.*?child says value_child_2`}, + {Arg: `task3`, Out: `child says value_child_3`}, + {Arg: `path`, Out: `PATH is [^:]+mixin/path_child:[^:]+mixin/bin:[^:]+mixin/parent/path_parent:[^:]+mixin/parent/bin`}, } func Test(t *testing.T) { diff --git a/examples/package_test.go b/examples/package_test.go index 8ac1da6..d98692c 100644 --- a/examples/package_test.go +++ b/examples/package_test.go @@ -6,14 +6,14 @@ import ( ) var tests = []TestTable{ - {`heading`, ``, `(?m)^\s*PROJECT\s*\|\s*TAGS\s*\|\s*TASKS\s*$`}, - {`env`, ``, `(?m)^\s*env\s*\|\s*\|\s*file_custom, file_custom_local, file_default, file_default_local, path, yml\s*$`}, - {`hooks`, ``, `(?m)^\s*hooks\s*\|\s*\|\s*after, before, before_after\s*$`}, - {`mixin`, ``, `(?m)^\s*mixin\s*\|\s*\|\s*path, task1, task2, task3\s*$`}, - {`retry`, ``, `(?m)^\s*retry\s*\|\s*\|\s*retry\s*$`}, - {`tags1`, ``, `(?m)^\s*tags1\s*\|\s*tagA, tagB\s*\|\s*tag\s*$`}, - {`tags2`, ``, `(?m)^\s*tags2\s*\|\s*tagB, tagC\s*\|\s*tag\s*$`}, - {`template`, ``, `(?m)^\s*template\s*\|\s*\|\s*args, file\s*$`}, + {Arg: ``, Out: `(?m)^\s*PROJECT\s*\|\s*TAGS\s*\|\s*TASKS\s*$`}, + {Arg: ``, Out: `(?m)^\s*env\s*\|\s*\|\s*file_custom, file_custom_local, file_default, file_default_local, path, yml\s*$`}, + {Arg: ``, Out: `(?m)^\s*hooks\s*\|\s*\|\s*after, before, error\s*$`}, + {Arg: ``, Out: `(?m)^\s*mixin\s*\|\s*\|\s*path, task1, task2, task3\s*$`}, + {Arg: ``, Out: `(?m)^\s*retry\s*\|\s*\|\s*retry\s*$`}, + {Arg: ``, Out: `(?m)^\s*tags1\s*\|\s*tagA, tagB\s*\|\s*tag\s*$`}, + {Arg: ``, Out: `(?m)^\s*tags2\s*\|\s*tagB, tagC\s*\|\s*tag\s*$`}, + {Arg: ``, Out: `(?m)^\s*template\s*\|\s*\|\s*args, file\s*$`}, } func Test(t *testing.T) { diff --git a/examples/retry/package_test.go b/examples/retry/package_test.go index 9e2eac4..aecf116 100644 --- a/examples/retry/package_test.go +++ b/examples/retry/package_test.go @@ -6,8 +6,8 @@ import ( ) var tests = []TestTable{ - {`retry_debug`, `-vv retry`, `(?s)false.*retry/retry: Failed, Retrying 1/5 in 10ms.*false.*Retrying 2/5.*false.*Retrying 3/5.*false.*Retrying 4/5.*false.*retry/retry: Failed`}, - {`retry`, `retry`, `(Retrying \d+){0}`}, + {Arg: `-vv retry`, Out: `(?s)false.*retry/retry: Failed, Retrying 1/5 in 10ms.*false.*Retrying 2/5.*false.*Retrying 3/5.*false.*Retrying 4/5.*false.*retry/retry: Failed`, Err: true}, + {Arg: `retry`, Out: `(Retrying \d+){0}`, Err: true}, } func Test(t *testing.T) { diff --git a/examples/tag/package_test.go b/examples/tag/package_test.go index def468f..1b99e29 100644 --- a/examples/tag/package_test.go +++ b/examples/tag/package_test.go @@ -6,15 +6,15 @@ import ( ) var tests = []TestTable{ - {`tag`, `tag`, `tags1/tag`}, - {`tag`, `tag`, `tags2/tag`}, - {`tag`, `--dry-run tag`, `(?s)tags1/tag: Will run.*tags2/tag: Will run`}, - {`tagA/tag`, `tagA/tag`, `tags1 tag`}, - {`tagA/tag`, `tagA/tag`, `(tags2){0}`}, - {`tagB/tag`, `tagB/tag`, `tags1/tag`}, - {`tagB/tag`, `tagB/tag`, `tags2/tag`}, - {`tagC/tag`, `tagC/tag`, `(tags1){0}`}, - {`tagC/tag`, `tagC/tag`, `tags2 tag`}, + {Arg: `tag`, Out: `tags1/tag`}, + {Arg: `tag`, Out: `tags2/tag`}, + {Arg: `--dry-run tag`, Out: `(?s)tags1/tag: Will run.*tags2/tag: Will run`}, + {Arg: `tagA/tag`, Out: `tags1 tag`}, + {Arg: `tagA/tag`, Out: `(tags2){0}`}, + {Arg: `tagB/tag`, Out: `tags1/tag`}, + {Arg: `tagB/tag`, Out: `tags2/tag`}, + {Arg: `tagC/tag`, Out: `(tags1){0}`}, + {Arg: `tagC/tag`, Out: `tags2 tag`}, } func Test(t *testing.T) { diff --git a/examples/template/package_test.go b/examples/template/package_test.go index 2e06ec8..24e15df 100644 --- a/examples/template/package_test.go +++ b/examples/template/package_test.go @@ -6,12 +6,12 @@ import ( ) var tests = []TestTable{ - {`args`, `args`, `(?s)template/args: Failed`}, - {`args_from`, `args --from=a`, `from=a to=something_to`}, - {`args_from_to`, `args --from=a --to=b`, `from=a to=b`}, - {`args_multiple_tasks`, `args --from=a args --from=b`, `(?s).*from=a to=something_to.*from=b to=something_to`}, + {Arg: `args`, Out: `(?s)template/args: Failed`, Err: true}, + {Arg: `args --from=a`, Out: `from=a to=something_to`}, + {Arg: `args --from=a --to=b`, Out: `from=a to=b`}, + {Arg: `args --from=a args --from=b`, Out: `(?s).*from=a to=something_to.*from=b to=something_to`}, // Cannot invoke myke subcommand in a test - // {`file`, `file`, `(?s)I am a template.*PARAM1=value1.*PARAM2=value2`}, + // {Arg:`file`, Out:`(?s)I am a template.*PARAM1=value1.*PARAM2=value2`}, } func Test(t *testing.T) { diff --git a/examples/util/test_util.go b/examples/util/test_util.go index 210cda2..93d23ff 100644 --- a/examples/util/test_util.go +++ b/examples/util/test_util.go @@ -15,9 +15,9 @@ import ( // TestTable represents a table-driven test type TestTable struct { - Desc string - Args string - Expected string + Arg string + Out string + Err bool } // RunCliTests runs myke CLI with the given table tests @@ -32,15 +32,14 @@ func RunCliTests(t *testing.T, dir string, tests []TestTable) { func runTest(t *testing.T, tt TestTable) { actual, err := captureStdout(func() error { - args := strings.Split(tt.Args, " ") + args := strings.Split(tt.Arg, " ") return cmd.Exec(args) }) - // TODO: Add error verification - if assert.Regexp(t, tt.Expected, actual) { - t.Logf("myke(%s): passed", tt.Desc) + if tt.Err == (err != nil) && assert.Regexp(t, tt.Out, actual) { + t.Logf("myke(%s): passed", tt.Arg) } else { - t.Errorf("myke(%s): failed %s", tt.Desc, err) + t.Errorf("myke(%s): failed %s", tt.Arg, err) } }