Compare commits

...

45 Commits

Author SHA1 Message Date
Andrey Nering
bc99509395 Merge branch 'master' into v3 2019-09-22 18:43:45 -03:00
Andrey Nering
d3060b0060 Add CHANGELOG for #216 2019-09-14 18:09:34 -03:00
Andrey Nering
14d7f04a81 Always expode .TIMESTAMP and .STATUS when using status: 2019-09-14 18:04:41 -03:00
Andrey Nering
1a28e5e0d4 Few code improvements on #216 2019-09-14 17:54:41 -03:00
Andrey Nering
884cd0d636 Merge branch 'CypherpunkArmory-report-timestamp-to-status' into v3 2019-09-14 17:18:42 -03:00
Andrey Nering
6a7a3c0ae8 Merge pull request #246 from go-task/method-on-v3
Add global "method:" option to allow setting a default for all tasks. Change default from "timestamp" to "checksum"
2019-09-08 23:02:40 -03:00
Andrey Nering
948e6bd57c Update v3 CHANGELOG 2019-09-08 22:59:27 -03:00
Andrey Nering
78595fba0b Make "checksum" the default method in v3 2019-09-08 22:51:56 -03:00
Andrey Nering
8020284b12 Add global method: option to set default method 2019-09-08 22:51:14 -03:00
Andrey Nering
d6a49da870 Merge branch 'master' into v3 2019-09-08 22:12:02 -03:00
Andrey Nering
bae1e1ee9f Update .gitignore 2019-09-07 14:46:46 -03:00
Andrey Nering
7138785500 Merge branch 'master' into v3 2019-09-07 14:44:21 -03:00
Andrey Nering
1a33f9168b Merge branch 'report-timestamp-to-status' of https://github.com/CypherpunkArmory/task into CypherpunkArmory-report-timestamp-to-status 2019-09-01 21:44:23 -03:00
Andrey Nering
ccae3d7383 Update CHANGELOG 2019-08-25 18:17:33 -03:00
Andrey Nering
847651a90a Improve note wording a bit 2019-08-25 18:15:26 -03:00
Andrey Nering
1b8998e7a2 Merge pull request #237 from jaedle/v3
Remove all code related support of version 1
2019-08-25 18:11:47 -03:00
Andrey Nering
dc8fb79759 Merge branch 'master' into v3 2019-08-25 18:05:40 -03:00
Stephen Prater
6b0935d6cf Fix tests 2019-08-25 13:47:29 -07:00
Stephen Prater
d1183ce272 Merge branch 'master' into report-timestamp-to-status
* master:
  Update CHANGELOG
  Small improvements to #228
  Fix a typo
  Fix Checksum.IsUpToDate
  Remove directory check
  Update glob.go
  Separate error handlings for readability
  Re-run the task if generated files do not exist
2019-08-25 13:46:02 -07:00
Stephen Prater
a1aec8178a Export Time Struct to Template 2019-08-25 13:36:48 -07:00
Stephen Prater
cb6fe4bb59 Merge remote-tracking branch 'upstream/v3' into report-timestamp-to-status
* upstream/v3:
  v3.0.0-preview1
  Update v3 changelog
  Only have colored output on v3
  Add --color=false flag to disable colored output
  Update documentation about sprig
  Update CHANGELOG
  Migrate from sprig to slim-sprig
  Fix build after merging master
  Use colors for some output messages
2019-08-25 10:33:13 -07:00
Stephen Prater
db36bc67f1 Changes per feedback 2019-08-25 10:30:00 -07:00
Stephen Prater
e0f72a6193 Apply suggestions from code review
Co-Authored-By: Andrey Nering <andrey.nering@gmail.com>
2019-08-25 09:39:39 -07:00
Stephen Prater
1ee684b7c0 Expose timestamp and checksum to status 2019-08-25 09:39:39 -07:00
jaedle
93005512b4 cleanp taskfile reader 2019-08-24 06:28:12 +02:00
jaedle
b8094fd771 bump version of auto generated taskfile 2019-08-19 21:01:36 +02:00
jaedle
af5d9c952d assert error message 2019-08-19 21:01:01 +02:00
jaedle
ce4e187cbc show documentation of version 1, add deprecation notice 2019-08-19 20:59:02 +02:00
jaedle
821c80b61e Fix error message
Co-Authored-By: Andrey Nering <andrey.nering@gmail.com>
2019-08-19 20:53:35 +02:00
jaedle
5a6fb7c973 Bump version of taskfile
Co-Authored-By: Andrey Nering <andrey.nering@gmail.com>
2019-08-19 20:53:20 +02:00
jaedle
0f385f9f4e remove v1 2019-08-18 17:37:21 +02:00
Andrey Nering
c47c15ee47 v3.0.0-preview1 2019-08-11 23:43:27 -03:00
Andrey Nering
6803ad2e59 Update v3 changelog 2019-08-11 23:10:12 -03:00
Andrey Nering
d5a791b470 Merge branch 'master' into v3 2019-08-11 23:03:58 -03:00
Andrey Nering
e6255081a8 Merge pull request #207 from go-task/colored-output
Use colors for some output messages
2019-07-07 14:41:25 -03:00
Andrey Nering
623db0ed94 Only have colored output on v3 2019-07-07 14:18:02 -03:00
Andrey Nering
0e575e9c25 Add --color=false flag to disable colored output 2019-07-07 14:13:53 -03:00
Andrey Nering
fb23ba9878 Merge branch 'v3' into colored-output 2019-07-07 14:04:12 -03:00
Andrey Nering
4e09fc7f43 Update documentation about sprig 2019-06-22 22:48:53 -03:00
Andrey Nering
64cfdd815f Update CHANGELOG 2019-06-22 22:47:23 -03:00
Andrey Nering
f6f31e0a8d Merge pull request #219 from go-task/migrate-from-sprig-to-slim-sprig
Migrate from sprig to slim-sprig
2019-06-22 22:37:42 -03:00
Andrey Nering
bd5fb9be03 Migrate from sprig to slim-sprig 2019-06-22 22:17:24 -03:00
Andrey Nering
dd9cdb0ec9 Fix build after merging master 2019-06-15 22:47:15 -03:00
Andrey Nering
7f082a821d Merge branch 'v3' into colored-output 2019-06-15 22:42:15 -03:00
Andrey Nering
8efc38ad82 Use colors for some output messages 2019-05-26 18:36:39 -03:00
44 changed files with 526 additions and 549 deletions

1
.gitignore vendored
View File

@@ -26,3 +26,4 @@ dist/
tags tags
/bin /bin
/testdata/vars/v1

View File

@@ -1,5 +1,32 @@
# Changelog # Changelog
# v3.0.0 - Preview 2
- Taskfiles in version 1 are not supported anymore
([#237](https://github.com/go-task/task/pull/237)).
- Added global `method:` option. With this option, you can set a default
method to all tasks in a Taskfile
([#246](https://github.com/go-task/task/issues/246)).
- Changed default method from `timestamp` to `checksum`
([#246](https://github.com/go-task/task/issues/246)).
- New magic variables are now available when using `status:`:
`.TIMESTAMP` which contains the greatest modification date
from the files listed in `sources:`, and `.CHECKSUM`, which
contains a checksum of all files listed in `status:`.
This is useful for manual checking when using external, or even remote,
artifacts when using `status:`
([#216](https://github.com/go-task/task/pull/216)).
## v3.0.0 - Preview 1
- We're now using [slim-sprig](https://github.com/go-task/slim-sprig) instead of
[sprig](https://github.com/Masterminds/sprig), which allowed a file size
reduction of about 22%
([#219](https://github.com/go-task/task/pull/219)).
- We now use some colors on Task output to better distinguish message types -
commands are green, errors are red, etc
([#207](https://github.com/go-task/task/pull/207)).
## v2.7.0 - 2019-09-22 ## v2.7.0 - 2019-09-22
- Fixed panic bug when assigning a global variable - Fixed panic bug when assigning a global variable

View File

@@ -1,4 +1,4 @@
version: '2' version: '3'
includes: includes:
docs: ./docs docs: ./docs

View File

@@ -10,6 +10,7 @@ import (
"github.com/go-task/task/v2" "github.com/go-task/task/v2"
"github.com/go-task/task/v2/internal/args" "github.com/go-task/task/v2/internal/args"
"github.com/go-task/task/v2/internal/logger"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
@@ -27,12 +28,14 @@ Example: 'task hello' with the following 'Taskfile.yml' file will generate an
'output.txt' file with the content "hello". 'output.txt' file with the content "hello".
''' '''
hello: version: '3'
cmds: tasks:
- echo "I am going to write a file named 'output.txt' now." hello:
- echo "hello" > output.txt cmds:
generates: - echo "I am going to write a file named 'output.txt' now."
- output.txt - echo "hello" > output.txt
generates:
- output.txt
''' '''
Options: Options:
@@ -61,6 +64,7 @@ func main() {
dir string dir string
entrypoint string entrypoint string
output string output string
color bool
) )
pflag.BoolVar(&versionFlag, "version", false, "show Task version") pflag.BoolVar(&versionFlag, "version", false, "show Task version")
@@ -76,6 +80,7 @@ func main() {
pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution") pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
pflag.StringVarP(&entrypoint, "taskfile", "t", "", `choose which Taskfile to run. Defaults to "Taskfile.yml"`) pflag.StringVarP(&entrypoint, "taskfile", "t", "", `choose which Taskfile to run. Defaults to "Taskfile.yml"`)
pflag.StringVarP(&output, "output", "o", "", "sets output style: [interleaved|group|prefixed]") pflag.StringVarP(&output, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
pflag.BoolVarP(&color, "color", "c", true, "colored output")
pflag.Parse() pflag.Parse()
if versionFlag { if versionFlag {
@@ -114,6 +119,7 @@ func main() {
Dry: dry, Dry: dry,
Entrypoint: entrypoint, Entrypoint: entrypoint,
Summary: summary, Summary: summary,
Color: color,
Stdin: os.Stdin, Stdin: os.Stdin,
Stdout: os.Stdout, Stdout: os.Stdout,
@@ -132,7 +138,7 @@ func main() {
arguments := pflag.Args() arguments := pflag.Args()
if len(arguments) == 0 { if len(arguments) == 0 {
log.Println("task: No argument given, trying default task") e.Logger.Errf(logger.Yellow, "task: No argument given, trying default task")
arguments = []string{"default"} arguments = []string{"default"}
} }
@@ -154,7 +160,8 @@ func main() {
} }
if err := e.Run(ctx, calls...); err != nil { if err := e.Run(ctx, calls...); err != nil {
log.Fatal(err) e.Logger.Errf(logger.Red, "%v", err)
os.Exit(1)
} }
} }

View File

@@ -1,4 +1,4 @@
version: '2' version: '3'
tasks: tasks:
install: install:

View File

@@ -15,6 +15,8 @@ available, but not `3.0.0+`.
## Version 1 ## Version 1
> NOTE: Taskfiles in version 1 are not supported on Task >= v3.0.0 anymore.
In the first version of the `Taskfile`, the `version:` key was not available, In the first version of the `Taskfile`, the `version:` key was not available,
because the tasks was in the root of the YAML document. Like this: because the tasks was in the root of the YAML document. Like this:

View File

@@ -266,6 +266,8 @@ The above syntax is also supported in `deps`.
## Prevent unnecessary work ## Prevent unnecessary work
### By fingerprinting locally generated files and their sources
If a task generates something, you can inform Task the source and generated If a task generates something, you can inform Task the source and generated
files, so Task will prevent to run them if not necessary. files, so Task will prevent to run them if not necessary.
@@ -321,6 +323,8 @@ tasks:
> TIP: method `none` skips any validation and always run the task. > 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 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: is returned (exit status 0), the task is considered up-to-date:
@@ -340,15 +344,34 @@ tasks:
- test -f directory/file2.txt - test -f directory/file2.txt
``` ```
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`
fingerprint file.
Two special variables `{{.CHECKSUM}}` and `{{.TIMESTAMP}}` are available
for interpolation within `status` commands, depending on the method assigned
to fingerprint the sources. Only `source` globs are fingerprinted.
Note that the `{{.TIMESTAMP}}` variable is a "live" Go `time.Time` struct, and
can be formatted using any of the methods that `time.Time` responds to.
See [the Go Time documentation](https://golang.org/pkg/time/) for more information.
You can use `--force` or `-f` if you want to force a task to run even when You can use `--force` or `-f` if you want to force a task to run even when
up-to-date. up-to-date.
Also, `task --status [tasks]...` will exit with a non-zero exit code if any of Also, `task --status [tasks]...` will exit with a non-zero exit code if any of
the tasks are not up-to-date. the tasks are not up-to-date.
If you need a certain set of conditions to be _true_ you can use the ### Using programmatic checks to cancel execution of an task and it's dependencies
`preconditions` stanza. `preconditions` are very similar to `status`
lines except they support `sh` expansion and they SHOULD all return 0. 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 ```yaml
version: '2' version: '2'
@@ -512,7 +535,7 @@ This works for all types of variables.
Task parse commands as [Go's template engine][gotemplate] before executing Task parse commands as [Go's template engine][gotemplate] before executing
them. Variables are accessible through dot syntax (`.VARNAME`). them. Variables are accessible through dot syntax (`.VARNAME`).
All functions by the Go's [sprig lib](http://masterminds.github.io/sprig/) All functions by the Go's [slim-sprig lib](https://go-task.github.io/slim-sprig/)
are available. The following example gets the current date in a given format: are available. The following example gets the current date in a given format:
```yaml ```yaml

10
go.mod
View File

@@ -1,13 +1,9 @@
module github.com/go-task/task/v2 module github.com/go-task/task/v2
require ( require (
github.com/Masterminds/semver v1.4.2 // indirect github.com/fatih/color v1.7.0
github.com/Masterminds/sprig v2.16.0+incompatible github.com/go-task/slim-sprig v0.0.0-20190623010546-24867827a98b
github.com/aokoli/goutils v1.0.1 // indirect github.com/mattn/go-colorable v0.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.0.0 // indirect
github.com/huandu/xstrings v1.1.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/mattn/go-zglob v0.0.1 github.com/mattn/go-zglob v0.0.1
github.com/radovskyb/watcher v1.0.5 github.com/radovskyb/watcher v1.0.5
github.com/spf13/pflag v1.0.3 github.com/spf13/pflag v1.0.3

22
go.sum
View File

@@ -1,23 +1,19 @@
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.16.0+incompatible h1:QZbMUPxRQ50EKAq3LFMnxddMu88/EUUG3qmxwtDmPsY=
github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg=
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/huandu/xstrings v1.1.0 h1:9oZY6Z/H3A1gytJxzuicbmV5QoR8M1TAPcn9WTg7vqg= github.com/go-task/slim-sprig v0.0.0-20190623010546-24867827a98b h1:z6iCP1USASmEZtKTzynd/rP4vOtBLlsD3v24wItbJIs=
github.com/huandu/xstrings v1.1.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/go-task/slim-sprig v0.0.0-20190623010546-24867827a98b/go.mod h1:XLIiFDBy2M8pA/fEL5rx9xr2EAzrDEO0S5brm5iekOE=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY= github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY=
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -28,6 +24,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20180319223459-c679ae2cc0cb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -37,6 +34,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= 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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -5,6 +5,7 @@ import (
"sort" "sort"
"text/tabwriter" "text/tabwriter"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/taskfile" "github.com/go-task/task/v2/internal/taskfile"
) )
@@ -12,10 +13,10 @@ import (
func (e *Executor) PrintTasksHelp() { func (e *Executor) PrintTasksHelp() {
tasks := e.tasksWithDesc() tasks := e.tasksWithDesc()
if len(tasks) == 0 { if len(tasks) == 0 {
e.Logger.Outf("task: No tasks with description available") e.Logger.Outf(logger.Yellow, "task: No tasks with description available")
return return
} }
e.Logger.Outf("task: Available tasks for this project:") e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
// Format in tab-separated columns with a tab stop of 8. // Format in tab-separated columns with a tab stop of 8.
w := tabwriter.NewWriter(e.Stdout, 0, 8, 0, '\t', 0) w := tabwriter.NewWriter(e.Stdout, 0, 8, 0, '\t', 0)

View File

@@ -10,7 +10,7 @@ import (
const defaultTaskfile = `# https://taskfile.dev const defaultTaskfile = `# https://taskfile.dev
version: '2' version: '3'
vars: vars:
GREETING: Hello, World! GREETING: Hello, World!

View File

@@ -1,137 +0,0 @@
package v1
import (
"bytes"
"context"
"fmt"
"strings"
"sync"
"github.com/go-task/task/v2/internal/compiler"
"github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v2/internal/templater"
)
var _ compiler.Compiler = &CompilerV1{}
type CompilerV1 struct {
Dir string
Vars taskfile.Vars
Logger *logger.Logger
dynamicCache map[string]string
muDynamicCache sync.Mutex
}
// GetVariables returns fully resolved variables following the priority order:
// 1. Call variables (should already have been resolved)
// 2. Environment (should not need to be resolved)
// 3. Task variables, resolved with access to:
// - call, taskvars and environment variables
// 4. Taskvars variables, resolved with access to:
// - environment variables
func (c *CompilerV1) GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error) {
merge := func(dest taskfile.Vars, srcs ...taskfile.Vars) {
for _, src := range srcs {
for k, v := range src {
dest[k] = v
}
}
}
varsKeys := func(srcs ...taskfile.Vars) []string {
m := make(map[string]struct{})
for _, src := range srcs {
for k := range src {
m[k] = struct{}{}
}
}
lst := make([]string, 0, len(m))
for k := range m {
lst = append(lst, k)
}
return lst
}
replaceVars := func(dest taskfile.Vars, keys []string) error {
r := templater.Templater{Vars: dest}
for _, k := range keys {
v := dest[k]
dest[k] = taskfile.Var{
Static: r.Replace(v.Static),
Sh: r.Replace(v.Sh),
}
}
return r.Err()
}
resolveShell := func(dest taskfile.Vars, keys []string) error {
for _, k := range keys {
v := dest[k]
static, err := c.HandleDynamicVar(v)
if err != nil {
return err
}
dest[k] = taskfile.Var{Static: static}
}
return nil
}
update := func(dest taskfile.Vars, srcs ...taskfile.Vars) error {
merge(dest, srcs...)
// updatedKeys ensures template evaluation is run only once.
updatedKeys := varsKeys(srcs...)
if err := replaceVars(dest, updatedKeys); err != nil {
return err
}
return resolveShell(dest, updatedKeys)
}
// Resolve taskvars variables to "result" with environment override variables.
override := compiler.GetEnviron()
result := make(taskfile.Vars, len(c.Vars)+len(t.Vars)+len(override))
if err := update(result, c.Vars, override); err != nil {
return nil, err
}
// Resolve task variables to "result" with environment and call override variables.
merge(override, call.Vars)
if err := update(result, t.Vars, override); err != nil {
return nil, err
}
return result, nil
}
func (c *CompilerV1) HandleDynamicVar(v taskfile.Var) (string, error) {
if v.Static != "" || v.Sh == "" {
return v.Static, nil
}
c.muDynamicCache.Lock()
defer c.muDynamicCache.Unlock()
if c.dynamicCache == nil {
c.dynamicCache = make(map[string]string, 30)
}
if result, ok := c.dynamicCache[v.Sh]; ok {
return result, nil
}
var stdout bytes.Buffer
opts := &execext.RunCommandOptions{
Command: v.Sh,
Dir: c.Dir,
Stdout: &stdout,
Stderr: c.Logger.Stderr,
}
if err := execext.RunCommand(context.Background(), opts); err != nil {
return "", fmt.Errorf(`task: Command "%s" in taskvars file failed: %s`, opts.Command, err)
}
// Trim a single trailing newline from the result to make most command
// output easier to use in shell commands.
result := strings.TrimSuffix(stdout.String(), "\n")
c.dynamicCache[v.Sh] = result
c.Logger.VerboseErrf(`task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
return result, nil
}

View File

@@ -103,7 +103,7 @@ func (c *CompilerV2) HandleDynamicVar(v taskfile.Var) (string, error) {
result := strings.TrimSuffix(stdout.String(), "\n") result := strings.TrimSuffix(stdout.String(), "\n")
c.dynamicCache[v.Sh] = result c.dynamicCache[v.Sh] = result
c.Logger.VerboseErrf(`task: dynamic variable: '%s' result: '%s'`, v.Sh, result) c.Logger.VerboseErrf(logger.Magenta, `task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
return result, nil return result, nil
} }

View File

@@ -1,38 +1,65 @@
package logger package logger
import ( import (
"fmt"
"io" "io"
"github.com/fatih/color"
) )
type PrintFunc func(io.Writer, string, ...interface{})
var (
Default PrintFunc = color.New(color.Reset).FprintfFunc()
Bold PrintFunc = color.New(color.Bold).FprintfFunc()
Blue PrintFunc = color.New(color.FgBlue, color.Bold).FprintfFunc()
Green PrintFunc = color.New(color.FgGreen, color.Bold).FprintfFunc()
Cyan PrintFunc = color.New(color.FgCyan, color.Bold).FprintfFunc()
Yellow PrintFunc = color.New(color.FgYellow, color.Bold).FprintfFunc()
Magenta PrintFunc = color.New(color.FgMagenta, color.Bold).FprintfFunc()
Red PrintFunc = color.New(color.FgRed, color.Bold).FprintfFunc()
)
// Logger is just a wrapper that prints stuff to STDOUT or STDERR,
// with optional color.
type Logger struct { type Logger struct {
Stdout io.Writer Stdout io.Writer
Stderr io.Writer Stderr io.Writer
Verbose bool Verbose bool
Color bool
} }
func (l *Logger) Outf(s string, args ...interface{}) { // Outf prints stuff to STDOUT.
func (l *Logger) Outf(print PrintFunc, s string, args ...interface{}) {
if len(args) == 0 { if len(args) == 0 {
s, args = "%s", []interface{}{s} s, args = "%s", []interface{}{s}
} }
fmt.Fprintf(l.Stdout, s+"\n", args...) if !l.Color {
print = Default
}
print(l.Stdout, s+"\n", args...)
} }
func (l *Logger) VerboseOutf(s string, args ...interface{}) { // VerboseOutf prints stuff to STDOUT if verbose mode is enabled.
func (l *Logger) VerboseOutf(print PrintFunc, s string, args ...interface{}) {
if l.Verbose { if l.Verbose {
l.Outf(s, args...) l.Outf(print, s, args...)
} }
} }
func (l *Logger) Errf(s string, args ...interface{}) { // Errf prints stuff to STDERR.
func (l *Logger) Errf(print PrintFunc, s string, args ...interface{}) {
if len(args) == 0 { if len(args) == 0 {
s, args = "%s", []interface{}{s} s, args = "%s", []interface{}{s}
} }
fmt.Fprintf(l.Stderr, s+"\n", args...) if !l.Color {
print = Default
}
print(l.Stderr, s+"\n", args...)
} }
func (l *Logger) VerboseErrf(s string, args ...interface{}) { // VerboseErrf prints stuff to STDERR if verbose mode is enabled.
func (l *Logger) VerboseErrf(print PrintFunc, s string, args ...interface{}) {
if l.Verbose { if l.Verbose {
l.Errf(s, args...) l.Errf(print, s, args...)
} }
} }

View File

@@ -23,6 +23,10 @@ type Checksum struct {
// IsUpToDate implements the Checker interface // IsUpToDate implements the Checker interface
func (c *Checksum) IsUpToDate() (bool, error) { func (c *Checksum) IsUpToDate() (bool, error) {
if len(c.Sources) == 0 {
return false, nil
}
checksumFile := c.checksumFilePath() checksumFile := c.checksumFilePath()
data, _ := ioutil.ReadFile(checksumFile) data, _ := ioutil.ReadFile(checksumFile)
@@ -84,11 +88,21 @@ func (c *Checksum) checksum(files ...string) (string, error) {
return fmt.Sprintf("%x", h.Sum(nil)), nil return fmt.Sprintf("%x", h.Sum(nil)), nil
} }
// Value implements the Checker Interface
func (c *Checksum) Value() (interface{}, error) {
return c.checksum()
}
// OnError implements the Checker interface // OnError implements the Checker interface
func (c *Checksum) OnError() error { func (c *Checksum) OnError() error {
return os.Remove(c.checksumFilePath()) return os.Remove(c.checksumFilePath())
} }
// Kind implements the Checker Interface
func (*Checksum) Kind() string {
return "checksum"
}
func (c *Checksum) checksumFilePath() string { func (c *Checksum) checksumFilePath() string {
return filepath.Join(c.Dir, ".task", "checksum", c.normalizeFilename(c.Task)) return filepath.Join(c.Dir, ".task", "checksum", c.normalizeFilename(c.Task))
} }

View File

@@ -8,6 +8,15 @@ func (None) IsUpToDate() (bool, error) {
return false, nil return false, nil
} }
// Value implements the Checker interface
func (None) Value() (interface{}, error) {
return "", nil
}
func (None) Kind() string {
return "none"
}
// OnError implements the Checker interface // OnError implements the Checker interface
func (None) OnError() error { func (None) OnError() error {
return nil return nil

View File

@@ -9,5 +9,7 @@ var (
// Checker is an interface that checks if the status is up-to-date // Checker is an interface that checks if the status is up-to-date
type Checker interface { type Checker interface {
IsUpToDate() (bool, error) IsUpToDate() (bool, error)
Value() (interface{}, error)
OnError() error OnError() error
Kind() string
} }

View File

@@ -41,6 +41,29 @@ func (t *Timestamp) IsUpToDate() (bool, error) {
return !generatesMinTime.Before(sourcesMaxTime), nil return !generatesMinTime.Before(sourcesMaxTime), nil
} }
func (t *Timestamp) Kind() string {
return "timestamp"
}
// Value implements the Checker Interface
func (t *Timestamp) Value() (interface{}, error) {
sources, err := globs(t.Dir, t.Sources)
if err != nil {
return time.Now(), err
}
sourcesMaxTime, err := getMaxTime(sources...)
if err != nil {
return time.Now(), err
}
if sourcesMaxTime.IsZero() {
return time.Unix(0, 0), nil
}
return sourcesMaxTime, nil
}
func getMinTime(files ...string) (time.Time, error) { func getMinTime(files ...string) (time.Time, error) {
var t time.Time var t time.Time
for _, f := range files { for _, f := range files {

View File

@@ -20,8 +20,8 @@ func printSpaceBetweenSummaries(l *logger.Logger, i int) {
return return
} }
l.Outf("") l.Outf(logger.Default, "")
l.Outf("") l.Outf(logger.Default, "")
} }
func PrintTask(l *logger.Logger, t *taskfile.Task) { func PrintTask(l *logger.Logger, t *taskfile.Task) {
@@ -50,14 +50,14 @@ func printTaskSummary(l *logger.Logger, t *taskfile.Task) {
for i, line := range lines { for i, line := range lines {
notLastLine := i+1 < len(lines) notLastLine := i+1 < len(lines)
if notLastLine || line != "" { if notLastLine || line != "" {
l.Outf(line) l.Outf(logger.Default, line)
} }
} }
} }
func printTaskName(l *logger.Logger, t *taskfile.Task) { func printTaskName(l *logger.Logger, t *taskfile.Task) {
l.Outf("task: %s", t.Task) l.Outf(logger.Default, "task: %s", t.Task)
l.Outf("") l.Outf(logger.Default, "")
} }
func hasDescription(t *taskfile.Task) bool { func hasDescription(t *taskfile.Task) bool {
@@ -65,11 +65,11 @@ func hasDescription(t *taskfile.Task) bool {
} }
func printTaskDescription(l *logger.Logger, t *taskfile.Task) { func printTaskDescription(l *logger.Logger, t *taskfile.Task) {
l.Outf(t.Desc) l.Outf(logger.Default, t.Desc)
} }
func printNoDescriptionOrSummary(l *logger.Logger) { func printNoDescriptionOrSummary(l *logger.Logger) {
l.Outf("(task does not have description or summary)") l.Outf(logger.Default, "(task does not have description or summary)")
} }
func printTaskDependencies(l *logger.Logger, t *taskfile.Task) { func printTaskDependencies(l *logger.Logger, t *taskfile.Task) {
@@ -77,11 +77,11 @@ func printTaskDependencies(l *logger.Logger, t *taskfile.Task) {
return return
} }
l.Outf("") l.Outf(logger.Default, "")
l.Outf("dependencies:") l.Outf(logger.Default, "dependencies:")
for _, d := range t.Deps { for _, d := range t.Deps {
l.Outf(" - %s", d.Task) l.Outf(logger.Default, " - %s", d.Task)
} }
} }
@@ -90,14 +90,14 @@ func printTaskCommands(l *logger.Logger, t *taskfile.Task) {
return return
} }
l.Outf("") l.Outf(logger.Default, "")
l.Outf("commands:") l.Outf(logger.Default, "commands:")
for _, c := range t.Cmds { for _, c := range t.Cmds {
isCommand := c.Cmd != "" isCommand := c.Cmd != ""
if isCommand { if isCommand {
l.Outf(" - %s", c.Cmd) l.Outf(logger.Default, " - %s", c.Cmd)
} else { } else {
l.Outf(" - Task: %s", c.Task) l.Outf(logger.Default, " - Task: %s", c.Task)
} }
} }
} }

View File

@@ -5,6 +5,7 @@ type Taskfile struct {
Version string Version string
Expansions int Expansions int
Output string Output string
Method string
Includes map[string]string Includes map[string]string
Vars Vars Vars Vars
Env Vars Env Vars
@@ -13,15 +14,11 @@ type Taskfile struct {
// UnmarshalYAML implements yaml.Unmarshaler interface // UnmarshalYAML implements yaml.Unmarshaler interface
func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error { func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&tf.Tasks); err == nil {
tf.Version = "1"
return nil
}
var taskfile struct { var taskfile struct {
Version string Version string
Expansions int Expansions int
Output string Output string
Method string
Includes map[string]string Includes map[string]string
Vars Vars Vars Vars
Env Vars Env Vars
@@ -33,6 +30,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
tf.Version = taskfile.Version tf.Version = taskfile.Version
tf.Expansions = taskfile.Expansions tf.Expansions = taskfile.Expansions
tf.Output = taskfile.Output tf.Output = taskfile.Output
tf.Method = taskfile.Method
tf.Includes = taskfile.Includes tf.Includes = taskfile.Includes
tf.Vars = taskfile.Vars tf.Vars = taskfile.Vars
tf.Env = taskfile.Env tf.Env = taskfile.Env

View File

@@ -13,17 +13,22 @@ var (
// Vars is a string[string] variables map. // Vars is a string[string] variables map.
type Vars map[string]Var type Vars map[string]Var
// ToStringMap converts Vars to a string map containing only the static // ToCacheMap converts Vars to a map containing only the static
// variables // variables
func (vs Vars) ToStringMap() (m map[string]string) { func (vs Vars) ToCacheMap() (m map[string]interface{}) {
m = make(map[string]string, len(vs)) m = make(map[string]interface{}, len(vs))
for k, v := range vs { for k, v := range vs {
if v.Sh != "" { if v.Sh != "" {
// Dynamic variable is not yet resolved; trigger // Dynamic variable is not yet resolved; trigger
// <no value> to be used in templates. // <no value> to be used in templates.
continue continue
} }
m[k] = v.Static
if v.Live != nil {
m[k] = v.Live
} else {
m[k] = v.Static
}
} }
return return
} }
@@ -31,6 +36,7 @@ func (vs Vars) ToStringMap() (m map[string]string) {
// Var represents either a static or dynamic variable. // Var represents either a static or dynamic variable.
type Var struct { type Var struct {
Static string Static string
Live interface{}
Sh string Sh string
} }

View File

@@ -6,7 +6,7 @@ import (
"strings" "strings"
"text/template" "text/template"
"github.com/Masterminds/sprig" "github.com/go-task/slim-sprig"
) )
var ( var (

View File

@@ -14,8 +14,12 @@ import (
type Templater struct { type Templater struct {
Vars taskfile.Vars Vars taskfile.Vars
strMap map[string]string cacheMap map[string]interface{}
err error err error
}
func (r *Templater) ResetCache() {
r.cacheMap = r.Vars.ToCacheMap()
} }
func (r *Templater) Replace(str string) string { func (r *Templater) Replace(str string) string {
@@ -29,12 +33,12 @@ func (r *Templater) Replace(str string) string {
return "" return ""
} }
if r.strMap == nil { if r.cacheMap == nil {
r.strMap = r.Vars.ToStringMap() r.cacheMap = r.Vars.ToCacheMap()
} }
var b bytes.Buffer var b bytes.Buffer
if err = templ.Execute(&b, r.strMap); err != nil { if err = templ.Execute(&b, r.cacheMap); err != nil {
r.err = err r.err = err
return "" return ""
} }
@@ -62,6 +66,7 @@ func (r *Templater) ReplaceVars(vars taskfile.Vars) taskfile.Vars {
for k, v := range vars { for k, v := range vars {
new[k] = taskfile.Var{ new[k] = taskfile.Var{
Static: r.Replace(v.Static), Static: r.Replace(v.Static),
Live: v.Live,
Sh: r.Replace(v.Sh), Sh: r.Replace(v.Sh),
} }
} }

View File

@@ -5,6 +5,7 @@ import (
"errors" "errors"
"github.com/go-task/task/v2/internal/execext" "github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/taskfile" "github.com/go-task/task/v2/internal/taskfile"
) )
@@ -22,7 +23,7 @@ func (e *Executor) areTaskPreconditionsMet(ctx context.Context, t *taskfile.Task
}) })
if err != nil { if err != nil {
e.Logger.Errf("task: %s", p.Msg) e.Logger.Errf(logger.Magenta, "task: %s", p.Msg)
return false, ErrPreconditionFailed return false, ErrPreconditionFailed
} }
} }

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/go-task/task/v2/internal/execext" "github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/status" "github.com/go-task/task/v2/internal/status"
"github.com/go-task/task/v2/internal/taskfile" "github.com/go-task/task/v2/internal/taskfile"
) )
@@ -49,25 +50,37 @@ func (e *Executor) statusOnError(t *taskfile.Task) error {
} }
func (e *Executor) getStatusChecker(t *taskfile.Task) (status.Checker, error) { func (e *Executor) getStatusChecker(t *taskfile.Task) (status.Checker, error) {
switch t.Method { method := t.Method
case "", "timestamp": if method == "" {
return &status.Timestamp{ method = e.Taskfile.Method
Dir: t.Dir, }
Sources: t.Sources, switch method {
Generates: t.Generates, case "timestamp":
}, nil return e.timestampChecker(t), nil
case "checksum": case "checksum":
return &status.Checksum{ return e.checksumChecker(t), nil
Dir: t.Dir,
Task: t.Task,
Sources: t.Sources,
Generates: t.Generates,
Dry: e.Dry,
}, nil
case "none": case "none":
return status.None{}, nil return status.None{}, nil
default: default:
return nil, fmt.Errorf(`task: invalid method "%s"`, t.Method) return nil, fmt.Errorf(`task: invalid method "%s"`, method)
}
}
func (e *Executor) timestampChecker(t *taskfile.Task) status.Checker {
return &status.Timestamp{
Dir: t.Dir,
Sources: t.Sources,
Generates: t.Generates,
}
}
func (e *Executor) checksumChecker(t *taskfile.Task) status.Checker {
return &status.Checksum{
Dir: t.Dir,
Task: t.Task,
Sources: t.Sources,
Generates: t.Generates,
Dry: e.Dry,
} }
} }
@@ -79,10 +92,10 @@ func (e *Executor) isTaskUpToDateStatus(ctx context.Context, t *taskfile.Task) (
Env: getEnviron(t), Env: getEnviron(t),
}) })
if err != nil { if err != nil {
e.Logger.VerboseOutf("task: status command %s exited non-zero: %s", s, err) e.Logger.VerboseOutf(logger.Yellow, "task: status command %s exited non-zero: %s", s, err)
return false, nil return false, nil
} }
e.Logger.VerboseOutf("task: status command %s exited zero", s) e.Logger.VerboseOutf(logger.Yellow, "task: status command %s exited zero", s)
} }
return true, nil return true, nil
} }

66
task.go
View File

@@ -11,7 +11,6 @@ import (
"sync/atomic" "sync/atomic"
"github.com/go-task/task/v2/internal/compiler" "github.com/go-task/task/v2/internal/compiler"
compilerv1 "github.com/go-task/task/v2/internal/compiler/v1"
compilerv2 "github.com/go-task/task/v2/internal/compiler/v2" compilerv2 "github.com/go-task/task/v2/internal/compiler/v2"
"github.com/go-task/task/v2/internal/execext" "github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/logger" "github.com/go-task/task/v2/internal/logger"
@@ -41,6 +40,7 @@ type Executor struct {
Silent bool Silent bool
Dry bool Dry bool
Summary bool Summary bool
Color bool
Stdin io.Reader Stdin io.Reader
Stdout io.Writer Stdout io.Writer
@@ -114,38 +114,38 @@ func (e *Executor) Setup() error {
Stdout: e.Stdout, Stdout: e.Stdout,
Stderr: e.Stderr, Stderr: e.Stderr,
Verbose: e.Verbose, Verbose: e.Verbose,
Color: e.Color,
} }
v, err := strconv.ParseFloat(e.Taskfile.Version, 64) v, err := strconv.ParseFloat(e.Taskfile.Version, 64)
if err != nil { if err != nil {
return fmt.Errorf(`task: Could not parse taskfile version "%s": %v`, e.Taskfile.Version, err) return fmt.Errorf(`task: Could not parse taskfile version "%s": %v`, e.Taskfile.Version, err)
} }
if v < 2 {
return fmt.Errorf(`task: Taskfile versions prior to v2 are not supported anymore`)
}
// consider as equal to the greater version if round // consider as equal to the greater version if round
if v == 2.0 { if v == 2.0 {
v = 2.6 v = 2.6
} }
if v < 1 { if v > 3.0 {
return fmt.Errorf(`task: Taskfile version should be greater or equal to v1`) return fmt.Errorf(`task: Taskfile versions greater than v3.0 not implemented in the version of Task`)
}
if v > 2.6 {
return fmt.Errorf(`task: Taskfile versions greater than v2.6 not implemented in the version of Task`)
} }
if v < 2 { // Color available only on v3
e.Compiler = &compilerv1.CompilerV1{ if v < 3 {
Dir: e.Dir, e.Logger.Color = false
Vars: e.taskvars, }
Logger: e.Logger,
} e.Compiler = &compilerv2.CompilerV2{
} else { // v >= 2 Dir: e.Dir,
e.Compiler = &compilerv2.CompilerV2{ Taskvars: e.taskvars,
Dir: e.Dir, TaskfileVars: e.Taskfile.Vars,
Taskvars: e.taskvars, Expansions: e.Taskfile.Expansions,
TaskfileVars: e.Taskfile.Vars, Logger: e.Logger,
Expansions: e.Taskfile.Expansions,
Logger: e.Logger,
}
} }
if v < 2.1 && e.Taskfile.Output != "" { if v < 2.1 && e.Taskfile.Output != "" {
@@ -169,6 +169,14 @@ func (e *Executor) Setup() error {
return fmt.Errorf(`task: output option "%s" not recognized`, e.Taskfile.Output) return fmt.Errorf(`task: output option "%s" not recognized`, e.Taskfile.Output)
} }
if e.Taskfile.Method == "" {
if v >= 3 {
e.Taskfile.Method = "checksum"
} else {
e.Taskfile.Method = "timestamp"
}
}
if v <= 2.1 { if v <= 2.1 {
err := errors.New(`task: Taskfile option "ignore_error" is only available starting on Taskfile version v2.1`) err := errors.New(`task: Taskfile option "ignore_error" is only available starting on Taskfile version v2.1`)
@@ -228,24 +236,24 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
if upToDate && preCondMet { if upToDate && preCondMet {
if !e.Silent { if !e.Silent {
e.Logger.Errf(`task: Task "%s" is up to date`, t.Task) e.Logger.Errf(logger.Magenta, `task: Task "%s" is up to date`, t.Task)
} }
return nil return nil
} }
} }
if err := e.mkdir(t); err != nil { if err := e.mkdir(t); err != nil {
e.Logger.Errf("task: cannot make directory %q: %v", t.Dir, err) e.Logger.Errf(logger.Red, "task: cannot make directory %q: %v", t.Dir, err)
} }
for i := range t.Cmds { for i := range t.Cmds {
if err := e.runCommand(ctx, t, call, i); err != nil { if err := e.runCommand(ctx, t, call, i); err != nil {
if err2 := e.statusOnError(t); err2 != nil { if err2 := e.statusOnError(t); err2 != nil {
e.Logger.VerboseErrf("task: error cleaning status on error: %v", err2) e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v", err2)
} }
if execext.IsExitError(err) && t.IgnoreError { if execext.IsExitError(err) && t.IgnoreError {
e.Logger.VerboseErrf("task: task error ignored: %v", err) e.Logger.VerboseErrf(logger.Yellow, "task: task error ignored: %v", err)
continue continue
} }
@@ -302,7 +310,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
return nil return nil
case cmd.Cmd != "": case cmd.Cmd != "":
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Silent) { if e.Verbose || (!cmd.Silent && !t.Silent && !e.Silent) {
e.Logger.Errf(cmd.Cmd) e.Logger.Errf(logger.Green, "task: %s", cmd.Cmd)
} }
if e.Dry { if e.Dry {
@@ -333,7 +341,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
Stderr: stdErr, Stderr: stdErr,
}) })
if execext.IsExitError(err) && cmd.IgnoreError { if execext.IsExitError(err) && cmd.IgnoreError {
e.Logger.VerboseErrf("task: command error ignored: %v", err) e.Logger.VerboseErrf(logger.Yellow, "task: command error ignored: %v", err)
return nil return nil
} }
return err return err
@@ -348,8 +356,10 @@ func getEnviron(t *taskfile.Task) []string {
} }
environ := os.Environ() environ := os.Environ()
for k, v := range t.Env.ToStringMap() { for k, v := range t.Env.ToCacheMap() {
environ = append(environ, fmt.Sprintf("%s=%s", k, v)) if s, ok := v.(string); ok {
environ = append(environ, fmt.Sprintf("%s=%s", k, s))
}
} }
return environ return environ
} }

View File

@@ -69,43 +69,6 @@ func TestEnv(t *testing.T) {
tt.Run(t) tt.Run(t)
} }
func TestVarsV1(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/vars/v1",
Target: "default",
TrimSpace: true,
Files: map[string]string{
// hello task:
"foo.txt": "foo",
"bar.txt": "bar",
"baz.txt": "baz",
"tmpl_foo.txt": "foo",
"tmpl_bar.txt": "<no value>",
"tmpl_foo2.txt": "foo2",
"tmpl_bar2.txt": "bar2",
"shtmpl_foo.txt": "foo",
"shtmpl_foo2.txt": "foo2",
"nestedtmpl_foo.txt": "{{.FOO}}",
"nestedtmpl_foo2.txt": "foo2",
"foo2.txt": "foo2",
"bar2.txt": "bar2",
"baz2.txt": "baz2",
"tmpl2_foo.txt": "<no value>",
"tmpl2_foo2.txt": "foo2",
"tmpl2_bar.txt": "<no value>",
"tmpl2_bar2.txt": "<no value>",
"shtmpl2_foo.txt": "<no value>",
"shtmpl2_foo2.txt": "foo2",
"nestedtmpl2_foo2.txt": "{{.FOO2}}",
"override.txt": "bar",
},
}
tt.Run(t)
// Ensure identical results when running hello task directly.
tt.Target = "hello"
tt.Run(t)
}
func TestVarsV2(t *testing.T) { func TestVarsV2(t *testing.T) {
tt := fileContentTest{ tt := fileContentTest{
Dir: "testdata/vars/v2", Dir: "testdata/vars/v2",
@@ -144,7 +107,7 @@ func TestVarsV2(t *testing.T) {
} }
func TestMultilineVars(t *testing.T) { func TestMultilineVars(t *testing.T) {
for _, dir := range []string{"testdata/vars/v1/multiline", "testdata/vars/v2/multiline"} { for _, dir := range []string{"testdata/vars/v2/multiline"} {
tt := fileContentTest{ tt := fileContentTest{
Dir: dir, Dir: dir,
Target: "default", Target: "default",
@@ -168,7 +131,7 @@ func TestMultilineVars(t *testing.T) {
func TestVarsInvalidTmpl(t *testing.T) { func TestVarsInvalidTmpl(t *testing.T) {
const ( const (
dir = "testdata/vars/v1" dir = "testdata/vars/v2"
target = "invalid-var-tmpl" target = "invalid-var-tmpl"
expectError = "template: :1: unexpected EOF" expectError = "template: :1: unexpected EOF"
) )
@@ -406,6 +369,34 @@ func TestStatusChecksum(t *testing.T) {
assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String()) assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String())
} }
func TestStatusVariables(t *testing.T) {
const dir = "testdata/status_vars"
_ = os.RemoveAll(filepath.Join(dir, ".task"))
_ = os.Remove(filepath.Join(dir, "generated.txt"))
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: false,
Verbose: true,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
assert.Contains(t, buff.String(), "d41d8cd98f00b204e9800998ecf8427e")
inf, err := os.Stat(filepath.Join(dir, "source.txt"))
assert.NoError(t, err)
ts := fmt.Sprintf("%d", inf.ModTime().Unix())
tf := fmt.Sprintf("%s", inf.ModTime())
assert.Contains(t, buff.String(), ts)
assert.Contains(t, buff.String(), tf)
}
func TestInit(t *testing.T) { func TestInit(t *testing.T) {
const dir = "testdata/init" const dir = "testdata/init"
var file = filepath.Join(dir, "Taskfile.yml") var file = filepath.Join(dir, "Taskfile.yml")
@@ -441,7 +432,6 @@ func TestTaskVersion(t *testing.T) {
Dir string Dir string
Version string Version string
}{ }{
{"testdata/version/v1", "1"},
{"testdata/version/v2", "2"}, {"testdata/version/v2", "2"},
} }
@@ -511,7 +501,7 @@ func TestDry(t *testing.T) {
assert.NoError(t, e.Setup()) assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"})) assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
assert.Equal(t, "touch file.txt", strings.TrimSpace(buff.String())) assert.Equal(t, "task: touch file.txt", strings.TrimSpace(buff.String()))
if _, err := os.Stat(file); err == nil { if _, err := os.Stat(file); err == nil {
t.Errorf("File should not exist %s", file) t.Errorf("File should not exist %s", file)
} }
@@ -668,7 +658,7 @@ func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) {
} }
// Ensure that the directory to be created doesn't actually exist. // Ensure that the directory to be created doesn't actually exist.
_ = os.Remove(toBeCreated) _ = os.RemoveAll(toBeCreated)
if _, err := os.Stat(toBeCreated); err == nil { if _, err := os.Stat(toBeCreated); err == nil {
t.Errorf("Directory should not exist: %v", err) t.Errorf("Directory should not exist: %v", err)
} }
@@ -679,5 +669,17 @@ func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) {
assert.Equal(t, expected, got, "Mismatch in the working directory") assert.Equal(t, expected, got, "Mismatch in the working directory")
// Clean-up after ourselves only if no error. // Clean-up after ourselves only if no error.
_ = os.Remove(toBeCreated) _ = os.RemoveAll(toBeCreated)
}
func TestDisplaysErrorOnUnsupportedVersion(t *testing.T) {
e := task.Executor{
Dir: "testdata/version/v1",
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
}
err := e.Setup()
assert.Error(t, err)
assert.Equal(t, "task: Taskfile versions prior to v2 are not supported anymore", err.Error())
} }

View File

@@ -1,9 +1,12 @@
build: version: '2'
cmds:
- cp ./source.txt ./generated.txt tasks:
sources: build:
- ./**/glob-with-inexistent-file.txt cmds:
- ./source.txt - cp ./source.txt ./generated.txt
generates: sources:
- ./generated.txt - ./**/glob-with-inexistent-file.txt
method: checksum - ./source.txt
generates:
- ./generated.txt
method: checksum

View File

@@ -1,7 +1,10 @@
task-1: version: '2'
deps:
- task: task-2
task-2: tasks:
deps: task-1:
- task: task-1 deps:
- task: task-2
task-2:
deps:
- task: task-1

View File

@@ -1,53 +1,56 @@
default: version: '2'
deps: [d1, d2, d3]
d1: tasks:
deps: [d11, d12, d13] default:
cmds: deps: [d1, d2, d3]
- echo 'Text' > d1.txt
d2: d1:
deps: [d21, d22, d23] deps: [d11, d12, d13]
cmds: cmds:
- echo 'Text' > d2.txt - echo 'Text' > d1.txt
d3: d2:
deps: [d31, d32, d33] deps: [d21, d22, d23]
cmds: cmds:
- echo 'Text' > d3.txt - echo 'Text' > d2.txt
d11: d3:
cmds: deps: [d31, d32, d33]
- echo 'Text' > d11.txt cmds:
- echo 'Text' > d3.txt
d12: d11:
cmds: cmds:
- echo 'Text' > d12.txt - echo 'Text' > d11.txt
d13: d12:
cmds: cmds:
- echo 'Text' > d13.txt - echo 'Text' > d12.txt
d21: d13:
cmds: cmds:
- echo 'Text' > d21.txt - echo 'Text' > d13.txt
d22: d21:
cmds: cmds:
- echo 'Text' > d22.txt - echo 'Text' > d21.txt
d23: d22:
cmds: cmds:
- echo 'Text' > d23.txt - echo 'Text' > d22.txt
d31: d23:
cmds: cmds:
- echo 'Text' > d31.txt - echo 'Text' > d23.txt
d32: d31:
cmds: cmds:
- echo 'Text' > d32.txt - echo 'Text' > d31.txt
d33: d32:
cmds: cmds:
- echo 'Text' > d33.txt - echo 'Text' > d32.txt
d33:
cmds:
- echo 'Text' > d33.txt

View File

@@ -1,41 +1,48 @@
abs.txt: version: '2'
desc: generates dest file based on absolute paths
deps:
- sub/src.txt
dir: sub
cmds:
- cat src.txt > '{{.BUILD_DIR}}/abs.txt'
sources:
- src.txt
generates:
- "{{.BUILD_DIR}}/abs.txt"
rel.txt: tasks:
desc: generates dest file based on relative paths abs.txt:
deps: desc: generates dest file based on absolute paths
- sub/src.txt deps:
dir: sub - sub/src.txt
cmds: dir: sub
- cat src.txt > '../rel.txt' cmds:
sources: - cat src.txt > '{{.BUILD_DIR}}/abs.txt'
- src.txt method: timestamp
generates: sources:
- "../rel.txt" - src.txt
generates:
- "{{.BUILD_DIR}}/abs.txt"
sub/src.txt: rel.txt:
desc: generate source file desc: generates dest file based on relative paths
cmds: deps:
- mkdir -p sub - sub/src.txt
- echo "hello world" > sub/src.txt dir: sub
status: cmds:
- test -f sub/src.txt - cat src.txt > '../rel.txt'
method: timestamp
sources:
- src.txt
generates:
- "../rel.txt"
'my text file.txt': sub/src.txt:
desc: generate file with spaces in the name desc: generate source file
deps: [sub/src.txt] cmds:
cmds: - mkdir -p sub
- cat sub/src.txt > 'my text file.txt' - echo "hello world" > sub/src.txt
sources: method: timestamp
- sub/src.txt status:
generates: - test -f sub/src.txt
- 'my text file.txt'
'my text file.txt':
desc: generate file with spaces in the name
deps: [sub/src.txt]
cmds:
- cat sub/src.txt > 'my text file.txt'
method: timestamp
sources:
- sub/src.txt
generates:
- 'my text file.txt'

View File

@@ -1,37 +1,40 @@
default: version: '2'
vars:
SPANISH: ¡Holla mundo!
PORTUGUESE: "{{.PORTUGUESE_HELLO_WORLD}}"
GERMAN: "Welt!"
deps:
- task: write-file
vars: {CONTENT: Dependence1, FILE: dep1.txt}
- task: write-file
vars: {CONTENT: Dependence2, FILE: dep2.txt}
- task: write-file
vars: {CONTENT: "{{.SPANISH|replace \"mundo\" \"dependencia\"}}", FILE: spanish-dep.txt}
cmds:
- task: write-file
vars: {CONTENT: Hello, FILE: hello.txt}
- task: write-file
vars: {CONTENT: "$echo 'World'", FILE: world.txt}
- task: write-file
vars: {CONTENT: "!", FILE: exclamation.txt}
- task: write-file
vars: {CONTENT: "{{.SPANISH}}", FILE: spanish.txt}
- task: write-file
vars: {CONTENT: "{{.PORTUGUESE}}", FILE: portuguese.txt}
- task: write-file
vars: {CONTENT: "{{.GERMAN}}", FILE: german.txt}
- task: non-default
write-file: tasks:
cmds: default:
- echo {{.CONTENT}} > {{.FILE}} vars:
SPANISH: ¡Holla mundo!
PORTUGUESE: "{{.PORTUGUESE_HELLO_WORLD}}"
GERMAN: "Welt!"
deps:
- task: write-file
vars: {CONTENT: Dependence1, FILE: dep1.txt}
- task: write-file
vars: {CONTENT: Dependence2, FILE: dep2.txt}
- task: write-file
vars: {CONTENT: "{{.SPANISH|replace \"mundo\" \"dependencia\"}}", FILE: spanish-dep.txt}
cmds:
- task: write-file
vars: {CONTENT: Hello, FILE: hello.txt}
- task: write-file
vars: {CONTENT: "$echo 'World'", FILE: world.txt}
- task: write-file
vars: {CONTENT: "!", FILE: exclamation.txt}
- task: write-file
vars: {CONTENT: "{{.SPANISH}}", FILE: spanish.txt}
- task: write-file
vars: {CONTENT: "{{.PORTUGUESE}}", FILE: portuguese.txt}
- task: write-file
vars: {CONTENT: "{{.GERMAN}}", FILE: german.txt}
- task: non-default
non-default: write-file:
vars: cmds:
PORTUGUESE: "{{.PORTUGUESE_HELLO_WORLD}}" - echo {{.CONTENT}} > {{.FILE}}
cmds:
- task: write-file non-default:
vars: {CONTENT: "{{.PORTUGUESE}}", FILE: portuguese2.txt} vars:
PORTUGUESE: "{{.PORTUGUESE_HELLO_WORLD}}"
cmds:
- task: write-file
vars: {CONTENT: "{{.PORTUGUESE}}", FILE: portuguese2.txt}

View File

@@ -1,5 +1,8 @@
gen-foo: version: '2'
cmds:
- touch foo.txt tasks:
status: gen-foo:
- test -f foo.txt cmds:
- touch foo.txt
status:
- test -f foo.txt

1
testdata/status_vars/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
generated.txt

10
testdata/status_vars/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
version: '3'
tasks:
build:
sources:
- ./source.txt
status:
- echo "{{.CHECKSUM}}"
- echo '{{.TIMESTAMP.Unix}}'
- echo '{{.TIMESTAMP}}'

1
testdata/status_vars/source.txt vendored Normal file
View File

@@ -0,0 +1 @@
Hello, World!

View File

@@ -1,4 +1,4 @@
version: 2 version: '2'
tasks: tasks:
task-with-summary: task-with-summary:

View File

@@ -1 +0,0 @@
*.txt

View File

@@ -1,48 +0,0 @@
default:
deps: [hello]
hello:
cmds:
- echo {{.FOO}} > foo.txt
- echo {{.BAR}} > bar.txt
- echo {{.BAZ}} > baz.txt
- echo '{{.TMPL_FOO}}' > tmpl_foo.txt
- echo '{{.TMPL_BAR}}' > tmpl_bar.txt
- echo '{{.TMPL_FOO2}}' > tmpl_foo2.txt
- echo '{{.TMPL_BAR2}}' > tmpl_bar2.txt
- echo '{{.SHTMPL_FOO}}' > shtmpl_foo.txt
- echo '{{.SHTMPL_FOO2}}' > shtmpl_foo2.txt
- echo '{{.NESTEDTMPL_FOO}}' > nestedtmpl_foo.txt
- echo '{{.NESTEDTMPL_FOO2}}' > nestedtmpl_foo2.txt
- echo {{.FOO2}} > foo2.txt
- echo {{.BAR2}} > bar2.txt
- echo {{.BAZ2}} > baz2.txt
- echo '{{.TMPL2_FOO}}' > tmpl2_foo.txt
- echo '{{.TMPL2_BAR}}' > tmpl2_bar.txt
- echo '{{.TMPL2_FOO2}}' > tmpl2_foo2.txt
- echo '{{.TMPL2_BAR2}}' > tmpl2_bar2.txt
- echo '{{.SHTMPL2_FOO}}' > shtmpl2_foo.txt
- echo '{{.SHTMPL2_FOO2}}' > shtmpl2_foo2.txt
- echo '{{.NESTEDTMPL2_FOO2}}' > nestedtmpl2_foo2.txt
- echo {{.OVERRIDE}} > override.txt
vars:
FOO: foo
BAR: $echo bar
BAZ:
sh: echo baz
TMPL_FOO: "{{.FOO}}"
TMPL_BAR: "{{.BAR}}"
TMPL_FOO2: "{{.FOO2}}"
TMPL_BAR2: "{{.BAR2}}"
SHTMPL_FOO:
sh: "echo '{{.FOO}}'"
SHTMPL_FOO2:
sh: "echo '{{.FOO2}}'"
NESTEDTMPL_FOO: "{{.TMPL_FOO}}"
NESTEDTMPL_FOO2: "{{.TMPL2_FOO2}}"
OVERRIDE: "bar"
invalid-var-tmpl:
vars:
CHARS: "abcd"
INVALID: "{{range .CHARS}}no end"

View File

@@ -1,12 +0,0 @@
FOO2: foo2
BAR2: $echo bar2
BAZ2:
sh: echo baz2
TMPL2_FOO: "{{.FOO}}"
TMPL2_BAR: "{{.BAR}}"
TMPL2_FOO2: "{{.FOO2}}"
TMPL2_BAR2: "{{.BAR2}}"
SHTMPL2_FOO2:
sh: "echo '{{.FOO2}}'"
NESTEDTMPL2_FOO2: "{{.TMPL2_FOO2}}"
OVERRIDE: "foo"

View File

@@ -1,43 +0,0 @@
default:
vars:
MULTILINE: "\n\nfoo\n bar\nfoobar\n\nbaz\n\n"
cmds:
- task: file
vars:
CONTENT:
sh: "echo 'foo\nbar'"
FILE: "echo_foobar.txt"
- task: file
vars:
CONTENT:
sh: "echo -n 'foo\nbar'"
FILE: "echo_n_foobar.txt"
- task: file
vars:
CONTENT:
sh: echo -n "{{.MULTILINE}}"
FILE: "echo_n_multiline.txt"
- task: file
vars:
CONTENT: "{{.MULTILINE}}"
FILE: "var_multiline.txt"
- task: file
vars:
CONTENT: "{{.MULTILINE | catLines}}"
FILE: "var_catlines.txt"
- task: enumfile
vars:
LINES: "{{.MULTILINE}}"
FILE: "var_enumfile.txt"
file:
cmds:
- |
cat << EOF > '{{.FILE}}'
{{.CONTENT}}
EOF
enumfile:
cmds:
- |
cat << EOF > '{{.FILE}}'
{{range $i, $line := .LINES| splitLines}}{{$i}}:{{$line}}
{{end}}EOF

View File

@@ -1,9 +1,9 @@
version: 2 version: '2'
tasks: tasks:
foo: foo:
cmds: cmds:
- echo "Foo" - echo "Foo"
bar: bar:
cmds: cmds:
- echo "Bar" - echo "Bar"

View File

@@ -2,8 +2,10 @@ package task
import ( import (
"path/filepath" "path/filepath"
"strings"
"github.com/go-task/task/v2/internal/execext" "github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/status"
"github.com/go-task/task/v2/internal/taskfile" "github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v2/internal/templater" "github.com/go-task/task/v2/internal/templater"
) )
@@ -20,6 +22,7 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
r := templater.Templater{Vars: vars} r := templater.Templater{Vars: vars}
new := taskfile.Task{ new := taskfile.Task{
@@ -27,7 +30,6 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
Desc: r.Replace(origTask.Desc), Desc: r.Replace(origTask.Desc),
Sources: r.ReplaceSlice(origTask.Sources), Sources: r.ReplaceSlice(origTask.Sources),
Generates: r.ReplaceSlice(origTask.Generates), Generates: r.ReplaceSlice(origTask.Generates),
Status: r.ReplaceSlice(origTask.Status),
Dir: r.Replace(origTask.Dir), Dir: r.Replace(origTask.Dir),
Vars: nil, Vars: nil,
Env: nil, Env: nil,
@@ -94,5 +96,21 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
} }
} }
if len(origTask.Status) > 0 {
for _, checker := range []status.Checker{e.timestampChecker(&new), e.checksumChecker(&new)} {
value, err := checker.Value()
if err != nil {
return nil, err
}
vars[strings.ToUpper(checker.Kind())] = taskfile.Var{Live: value}
}
// Adding new variables, requires us to refresh the templaters
// cache of the the values manually
r.ResetCache()
new.Status = r.ReplaceSlice(origTask.Status)
}
return &new, r.Err() return &new, r.Err()
} }

View File

@@ -8,6 +8,7 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/taskfile" "github.com/go-task/task/v2/internal/taskfile"
"github.com/mattn/go-zglob" "github.com/mattn/go-zglob"
"github.com/radovskyb/watcher" "github.com/radovskyb/watcher"
@@ -24,14 +25,14 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error {
for i, c := range calls { for i, c := range calls {
tasks[i] = c.Task tasks[i] = c.Task
} }
e.Logger.Errf("task: Started watching for tasks: %s", strings.Join(tasks, ", ")) e.Logger.Errf(logger.Green, "task: Started watching for tasks: %s", strings.Join(tasks, ", "))
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
for _, c := range calls { for _, c := range calls {
c := c c := c
go func() { go func() {
if err := e.RunTask(ctx, c); err != nil && !isContextError(err) { if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
e.Logger.Errf("%v", err) e.Logger.Errf(logger.Red, "%v", err)
} }
}() }()
} }
@@ -49,7 +50,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error {
for { for {
select { select {
case event := <-w.Event: case event := <-w.Event:
e.Logger.VerboseErrf("task: received watch event: %v", event) e.Logger.VerboseErrf(logger.Magenta, "task: received watch event: %v", event)
cancel() cancel()
ctx, cancel = context.WithCancel(context.Background()) ctx, cancel = context.WithCancel(context.Background())
@@ -57,7 +58,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error {
c := c c := c
go func() { go func() {
if err := e.RunTask(ctx, c); err != nil && !isContextError(err) { if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
e.Logger.Errf("%v", err) e.Logger.Errf(logger.Red, "%v", err)
} }
}() }()
} }
@@ -68,7 +69,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error {
w.TriggerEvent(watcher.Remove, nil) w.TriggerEvent(watcher.Remove, nil)
}() }()
default: default:
e.Logger.Errf("%v", err) e.Logger.Errf(logger.Red, "%v", err)
} }
case <-w.Closed: case <-w.Closed:
cancel() cancel()
@@ -81,7 +82,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error {
// re-register each second because we can have new files // re-register each second because we can have new files
for { for {
if err := e.registerWatchedFiles(w, calls...); err != nil { if err := e.registerWatchedFiles(w, calls...); err != nil {
e.Logger.Errf("%v", err) e.Logger.Errf(logger.Red, "%v", err)
} }
time.Sleep(time.Second) time.Sleep(time.Second)
} }