diff --git a/docs/usage.md b/docs/usage.md index 48f52242..5e23d236 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -266,6 +266,8 @@ The above syntax is also supported in `deps`. ## Prevent unnecessary work +### By fingerprinting locally generated files and their sources + If a task generates something, you can inform Task the source and generated files, so Task will prevent to run them if not necessary. @@ -321,6 +323,9 @@ tasks: > TIP: method `none` skips any validation and always run the task. +### Using programmatic checks to indicate a task is up to date. + + Alternatively, you can inform a sequence of tests as `status`. If no error is returned (exit status 0), the task is considered up-to-date: @@ -340,7 +345,8 @@ tasks: - test -f directory/file2.txt ``` -Normally, you would use either `status` or `sources` in combination with + +Normally, you would use `sources` in combination with `generates` - but for tasks that generate remote artifacts (Docker images, deploys, CD releases) the checksum source and timestamps require either access to the artifact or for an out-of-band refresh of the `.checksum` @@ -356,9 +362,13 @@ up-to-date. Also, `task --status [tasks]...` will exit with a non-zero exit code if any of 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. +### Using programmatic checks to cancel execution of an task and it's dependencies + +In addition to `status` checks, there are also `preconditions` checks, which are +the logical inverse of `status` checks. That is, if you need a certain set of +conditions to be _true_ you can use the `preconditions` stanza. +`preconditions` are similar to `status` lines except they support `sh` +expansion and they SHOULD all return 0. ```yaml version: '2' diff --git a/go.mod b/go.mod index 1b479dd7..abcc7eb8 100644 --- a/go.mod +++ b/go.mod @@ -14,10 +14,8 @@ require ( github.com/radovskyb/watcher v1.0.5 github.com/spf13/pflag v1.0.3 github.com/stretchr/testify v1.3.0 - golang.org/x/crypto v0.0.0-20180830192347-182538f80094 // indirect - golang.org/x/net v0.0.0-20180826012351-8a410e7b638d // indirect - golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f - golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789 // indirect + golang.org/x/sync v0.0.0-20190423024810-112230192c58 + golang.org/x/tools/gopls v0.1.3 // indirect gopkg.in/yaml.v2 v2.2.1 mvdan.cc/sh v2.6.4+incompatible ) diff --git a/go.sum b/go.sum index 18f0ea37..c298de7f 100644 --- a/go.sum +++ b/go.sum @@ -33,12 +33,24 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/crypto v0.0.0-20180830192347-182538f80094 h1:rVTAlhYa4+lCfNxmAIEOGQRoD23UqP72M3+rSWVGDTg= golang.org/x/crypto v0.0.0-20180830192347-182538f80094/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789 h1:T8D7l6WB3tLu+VpKvw06ieD/OhBi1XpJmG1U/FtttZg= golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190710153321-831012c29e42 h1:4IOeC7p+OItq3+O5BWkcmVu2uBe3jekXau5S4QZX9DU= +golang.org/x/tools v0.0.0-20190710153321-831012c29e42/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools/gopls v0.1.3 h1:CB5ECiPysqZrwxcyRjN+exyZpY0gODTZvNiqQi3lpeo= +golang.org/x/tools/gopls v0.1.3/go.mod h1:vrCQzOKxvuiZLjCKSmbbov04oeBQQOb4VQqwYK2PWIY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= diff --git a/internal/status/checksum.go b/internal/status/checksum.go index f33c1d22..bdd2aff9 100644 --- a/internal/status/checksum.go +++ b/internal/status/checksum.go @@ -46,10 +46,6 @@ func (c *Checksum) IsUpToDate() (bool, error) { return oldMd5 == newMd5, nil } -func (t *Checksum) Kind() string { - return "checksum" -} - func (c *Checksum) checksum(files ...string) (string, error) { h := md5.New() @@ -77,7 +73,7 @@ func (c *Checksum) checksum(files ...string) (string, error) { return fmt.Sprintf("%x", h.Sum(nil)), nil } -// Value implements the Chcker Interface +// Value implements the Checker Interface func (c *Checksum) Value() (string, error) { return c.checksum() } @@ -87,6 +83,11 @@ func (c *Checksum) OnError() error { return os.Remove(c.checksumFilePath()) } +// Kind implements the Checker Interface +func (t *Checksum) Kind() string { + return "checksum" +} + func (c *Checksum) checksumFilePath() string { return filepath.Join(c.Dir, ".task", "checksum", c.normalizeFilename(c.Task)) } diff --git a/internal/templater/templater.go b/internal/templater/templater.go index 65a2fde2..d601c99a 100644 --- a/internal/templater/templater.go +++ b/internal/templater/templater.go @@ -18,6 +18,10 @@ type Templater struct { err error } +func (r *Templater) RefreshStringMap() { + r.strMap = r.Vars.ToStringMap() +} + func (r *Templater) Replace(str string) string { if r.err != nil || str == "" { return "" diff --git a/status.go b/status.go index 74f25489..7ebb5f84 100644 --- a/status.go +++ b/status.go @@ -32,7 +32,7 @@ func (e *Executor) isTaskUpToDate(ctx context.Context, t *taskfile.Task) (bool, return e.isTaskUpToDateStatus(ctx, t) } - checker, err := e.GetStatusChecker(t) + checker, err := e.getStatusChecker(t) if err != nil { return false, err } @@ -41,14 +41,14 @@ func (e *Executor) isTaskUpToDate(ctx context.Context, t *taskfile.Task) (bool, } func (e *Executor) statusOnError(t *taskfile.Task) error { - checker, err := e.GetStatusChecker(t) + checker, err := e.getStatusChecker(t) if err != nil { return err } return checker.OnError() } -func (e *Executor) GetStatusChecker(t *taskfile.Task) (status.Checker, error) { +func (e *Executor) getStatusChecker(t *taskfile.Task) (status.Checker, error) { switch t.Method { case "", "timestamp": return &status.Timestamp{ diff --git a/variables.go b/variables.go index d90b331a..57e7ae78 100644 --- a/variables.go +++ b/variables.go @@ -1,7 +1,6 @@ package task import ( - "io/ioutil" "path/filepath" "strings" @@ -64,30 +63,6 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) { new.Env[k] = taskfile.Var{Static: static} } - if len(origTask.Status) > 0 { - e := &Executor{ - Dir: new.Dir, - Stdout: ioutil.Discard, - Stderr: ioutil.Discard, - Dry: true, - } - - checker, err := e.GetStatusChecker(&new) - if err != nil { - return nil, err - } - - value, err := checker.Value() - if err != nil { - return nil, err - } - - vars[strings.ToUpper(checker.Kind())] = taskfile.Var{Static: value} - - statusTemplater := templater.Templater{Vars: vars} - new.Status = statusTemplater.ReplaceSlice(origTask.Status) - } - if len(origTask.Cmds) > 0 { new.Cmds = make([]*taskfile.Cmd, len(origTask.Cmds)) for i, cmd := range origTask.Cmds { @@ -120,5 +95,24 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) { } } + if len(origTask.Status) > 0 { + checker, err := e.getStatusChecker(&new) + if err != nil { + return nil, err + } + + value, err := checker.Value() + if err != nil { + return nil, err + } + + vars[strings.ToUpper(checker.Kind())] = taskfile.Var{Static: value} + // Adding new static variables, requires us to refresh the templaters + // cache of the the static values + r.RefreshStringMap() + + new.Status = r.ReplaceSlice(origTask.Status) + } + return &new, r.Err() }