Compare commits

..

167 Commits

Author SHA1 Message Date
Andrey Nering
f8545d4c61 v3.4.2 2021-04-23 18:11:31 -03:00
Oleg Butuzov
9b42ef5d46 "file does not exist" improving for watchers
Adds additional (initial missing) context to the go generic
`os.ErrNotExist` error, in addition to other errors that (possibly)
can be returned by `zglob.Glob`

Closes #472
2021-04-23 18:03:49 -03:00
Andrey Nering
05ddfc0495 Merge pull request #476 from Pix4D/fix-os-kill
signals: do not try to catch uncatchable signals
2021-04-23 17:45:56 -03:00
Andrey Nering
53b2cebb66 Updated the version output to use Go module build information if avalable. Enabled GoReleaser module proxying for verifiable builds.
Co-authored-by: Jamie Edge <JamieEdge@users.noreply.github.com>
Co-authored-by: Carlos Alexandro Becker <caarlos0@gmail.com>
2021-04-23 17:35:15 -03:00
Marco Molteni
837fb71a24 signals: do not try to catch uncatchable signals
os.Kill is SIGKILL (kill -9), cannot be intercepted.
(see https://github.com/golang/go/issues/13080)
2021-04-20 14:57:23 +02:00
Marco Molteni
2e13cf5f74 gitignore more editors 2021-04-20 14:54:24 +02:00
Andrey Nering
0ae1681d9c CHANGELOG: Fix typos 2021-04-17 17:57:08 -03:00
Andrey Nering
ebb66ba8fb v3.4.1 2021-04-17 17:48:36 -03:00
Andrey Nering
e79354a039 Revert "Updated the version output to use Go module build information if available. Enabled GoReleaser module proxying for verifiable builds."
This reverts commit 2a3f049336.
2021-04-17 17:47:24 -03:00
Andrey Nering
a57beb1de4 Website: Remove GitHub logo
The website that hosted the SVG file is offline for a while.
2021-04-17 17:34:00 -03:00
Andrey Nering
1648c44ee2 Website: Use dark theme 2021-04-17 17:34:00 -03:00
Andrey Nering
efe47a149e Website: Remove "Examples" page 2021-04-17 17:33:52 -03:00
Andrey Nering
2d66a2f0f3 Improve YAML parse error reporting
Fixes #467
2021-04-17 17:12:39 -03:00
Andrey Nering
43a1f1314e Website: Add a "Community" page 2021-04-17 11:59:24 -03:00
Andrey Nering
4f4b282d7c Merge pull request #471 from go-task/dependabot/go_modules/github.com/stretchr/testify-1.7.0
Bump github.com/stretchr/testify from 1.5.1 to 1.7.0
2021-04-17 10:53:03 -03:00
dependabot[bot]
d3d4da18e5 Bump github.com/stretchr/testify from 1.5.1 to 1.7.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.5.1 to 1.7.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.5.1...v1.7.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-17 13:51:37 +00:00
Andrey Nering
b8da583986 Merge pull request #470 from go-task/dependabot/go_modules/github.com/fatih/color-1.10.0
Bump github.com/fatih/color from 1.7.0 to 1.10.0
2021-04-17 10:51:01 -03:00
Andrey Nering
73f6b42715 Merge pull request #469 from go-task/dependabot/go_modules/github.com/radovskyb/watcher-1.0.7
Bump github.com/radovskyb/watcher from 1.0.5 to 1.0.7
2021-04-17 10:50:21 -03:00
dependabot[bot]
0e2a4efdaa Bump github.com/radovskyb/watcher from 1.0.5 to 1.0.7
Bumps [github.com/radovskyb/watcher](https://github.com/radovskyb/watcher) from 1.0.5 to 1.0.7.
- [Release notes](https://github.com/radovskyb/watcher/releases)
- [Commits](https://github.com/radovskyb/watcher/compare/1.0.5...v1.0.7)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-17 13:47:39 +00:00
dependabot[bot]
6798e16aaf Bump github.com/fatih/color from 1.7.0 to 1.10.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.7.0 to 1.10.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.7.0...v1.10.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-17 13:47:38 +00:00
Andrey Nering
c9cc64ecfc Merge pull request #468 from go-task/dependabot/go_modules/github.com/mattn/go-zglob-0.0.3
Bump github.com/mattn/go-zglob from 0.0.1 to 0.0.3
2021-04-17 10:46:42 -03:00
dependabot[bot]
761f9045ac Bump github.com/mattn/go-zglob from 0.0.1 to 0.0.3
Bumps [github.com/mattn/go-zglob](https://github.com/mattn/go-zglob) from 0.0.1 to 0.0.3.
- [Release notes](https://github.com/mattn/go-zglob/releases)
- [Commits](https://github.com/mattn/go-zglob/compare/v0.0.1...v0.0.3)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-17 13:43:00 +00:00
Andrey Nering
dfae979287 Add .github/dependabot.yml 2021-04-17 10:42:18 -03:00
George Pollard
fe917affd2 Include task name in log output 2021-04-04 16:16:22 -03:00
Andrey Nering
d44207dd7f Merge pull request #463 from JamieEdge/update-install-docs
Update Go modules installation documentation for Go 1.16
2021-04-04 15:48:19 -03:00
Andrey Nering
ec8b1403bd Use early return and add CHANGELOG for #462 2021-04-04 15:40:34 -03:00
Jamie Edge
6f3d108c1e Updated the install script documentation. 2021-04-04 17:31:41 +01:00
Jamie Edge
c34ee9c1f9 Updated the Go modules installation documentation for Go 1.16. 2021-04-04 17:30:42 +01:00
Jamie Edge
2a3f049336 Updated the version output to use Go module build information if available. Enabled GoReleaser module proxying for verifiable builds. 2021-04-04 12:57:58 +01:00
Andrey Nering
0c91011e88 Merge pull request #460 from patrick-mota/patch-1
Fix typo default installation
2021-03-28 11:10:38 -03:00
Andrey Nering
8bcd8719aa Docs: Add GO111MODULE=on to go get command 2021-03-28 10:45:36 -03:00
Ganon
29a8af509b Fix typo default installation 2021-03-26 13:19:45 +01:00
Andrey Nering
d3cd9f17f9 Documentation: Update link 2021-03-20 13:30:38 -03:00
Andrey Nering
b9aea8c5ec v3.3.0 2021-03-20 13:21:00 -03:00
Andrey Nering
897619a961 Upgrade github.com/spf13/pflag to v1.0.5 2021-03-20 12:00:39 -03:00
Andrey Nering
e6c4706b73 Add support for delegating CLI arguments with "--" and a special CLI_ARGS variable
Closes #327
2021-03-20 11:58:45 -03:00
Andrey Nering
8994c50d34 Upgrade mvdan.cc/sh to v3.2.4 2021-03-20 10:38:13 -03:00
Andrey Nering
55b62e47eb Upgrade mvdan.cc/sh to v3.2.2 2021-03-07 15:32:22 -03:00
Ross Hammermeister
c6ecf70377 Adding a --concurrency (-C) flag 2021-03-07 09:49:57 -03:00
Andrey Nering
f0cd7d27fb Taskfile: Set CGO_ENABLED=0 globally
We want that also for running tests, and not only for building it.
2021-03-07 09:30:33 -03:00
Andrey Nering
f923bb499b CHANGELOG: Fix wrong year in release date 2021-02-16 17:53:28 -03:00
Andrey Nering
aa3a29fed2 CHANGELOG: Add missing release dates 2021-02-16 17:52:32 -03:00
Andrey Nering
47d3011c85 v3.2.2 2021-01-12 13:21:36 -03:00
Andrey Nering
cec713a47a Update CHANGELOG 2021-01-12 12:09:03 -03:00
Andrey Nering
bf6d0c0a74 Improve performance of --list and --summary flags
Closes #332
2021-01-12 12:03:04 -03:00
Andrey Nering
c11672fca3 Envs should be overridable
System-wide environment variable should have priority. That's how it
works for .env files, so this is consistent.

Closes #425
2021-01-12 11:32:49 -03:00
Andrey Nering
e086b654aa Environment from .env file should be available as variables
Fixes #379
2021-01-12 11:11:40 -03:00
Andrey Nering
1107f691ea Update install script
Closes #428

Co-authored-by: odidev <odidev@puresoftware.com>
2021-01-12 10:43:45 -03:00
Andrey Nering
b095ca5756 v3.2.1 2021-01-09 13:57:20 -03:00
Andrey Nering
4afc0e8ed0 Fixed some bugs and regressions regarding dynamic variables and directories
Closes #426
2021-01-09 13:51:06 -03:00
Andrey Nering
141b377b4e Partly revert 59d2733b88
Keep the old behavior on v2
2021-01-09 10:55:18 -03:00
Andrey Nering
402a478785 Update CHANGELOG 2021-01-09 10:46:53 -03:00
Andrey Nering
73680584f3 Upgrade github.com/go-task/slim-sprig 2021-01-07 13:56:07 -03:00
Andrey Nering
45dbbcd179 v3.2.0 2021-01-07 13:08:07 -03:00
Andrey Nering
83d25bfa00 Refactor: Fix import order
It should be: stdlib > libs > app
2021-01-07 11:48:33 -03:00
Andrey Nering
299e27af15 Fix build 2021-01-07 11:39:36 -03:00
Andrey Nering
ec4cd5ed48 Fix .task directory location
Closes #247
2021-01-07 11:36:09 -03:00
Andrey Nering
59d2733b88 Make dynamic variables run on the right directory
It was always running in the main Taskfile dir, even when the variable was
declared in an included taskfile in another directory or when task had a
custom dir.

Closes #384
2021-01-07 11:26:11 -03:00
Andrey Nering
cbdd088188 Remove manual event trigger 2021-01-05 11:36:30 -03:00
Andrey Nering
2d52485d7b Watch: Clear vars cache between runs
Closes #365
2021-01-05 11:19:34 -03:00
Andrey Nering
d830178ef8 Do more watch fixes
This improves the work done on #423
2021-01-05 10:48:04 -03:00
James Wendel
049984b4cc Update watch.go 2021-01-03 21:26:09 -07:00
James Wendel
d261a986ab Update watch.go
Watch: Stop removing and addings files all the time.
2021-01-03 17:08:16 -07:00
Andrey Nering
9b2e25735b v3.1.0 2021-01-03 19:37:09 -03:00
Andrey Nering
e09e75b0ba Fix a bug when the checksum up-to-date resolution is used by a task with a custom label: attribute
Closes #412

Co-authored-by: Adam Wasila <adam.wasila@gmail.com>
2021-01-03 19:22:38 -03:00
Andrey Nering
6630113fef Release ARMv6 and ARM64 binaries on Linux
Closes #375
Closes #418
2021-01-03 19:12:38 -03:00
Andrey Nering
b2f08c9c20 Merge pull request #415 from felladrin/patch-1
Fix duplicate task name on "Short task syntax" example
2021-01-02 11:02:49 -03:00
Andrey Nering
6a4315b7e7 Merge pull request #407 from bryceschober/patch-1
Use {{default}} for an overriding usage
2021-01-01 19:21:50 -03:00
Andrey Nering
8b3e62ff6d Add forgotten CHANGELOG entry for #358
Ref #121
2021-01-01 19:06:00 -03:00
Andrey Nering
f1d3f6740d Update CHANGELOG.md 2021-01-01 18:57:28 -03:00
Andrey Nering
9ccd1d920c .gitignore: Add /tmp 2021-01-01 18:30:14 -03:00
Andrey Nering
9674d75ff6 Evaluate included taskfiles in order of declaration
Closes #393
2021-01-01 18:27:50 -03:00
Andrey Nering
22fd74846d Use interp.Params("-e") intead of running "set -e" manually
This is an improvement for ac8e344173
2021-01-01 17:32:42 -03:00
Andrey Nering
777645888a New logo and color 2020-12-27 19:07:38 -03:00
Andrey Nering
ac8e344173 Run "set -e" automatically for every command
Without this, multiline command strings won't always exit when they fail.

Closes #403
2020-12-27 17:15:12 -03:00
Andrey Nering
16fad60833 execext: Small refactor 2020-12-27 16:51:00 -03:00
Andrey Nering
cb96a39b46 v3.0.1 2020-12-26 15:34:17 -03:00
Andrey Nering
a540634b5b Add about: to issue templates 2020-12-26 15:27:15 -03:00
Andrey Nering
e15576bc47 Update issue templates 2020-12-26 15:24:46 -03:00
Andrey Nering
95359760ae Documentation: go get is actually supported
Closes #395
2020-12-26 15:06:39 -03:00
Andrey Nering
be209cb7b6 Merge pull request #406 from ezhukov/patch-2
Update curl arguments in installation.md
2020-12-26 15:00:04 -03:00
Victor Nogueira
f5eb80759b Fix duplicate task name on "Short task syntax" example
To make it a valid YAML file and avoid the error:

```
yaml: unmarshal errors:
  line 6: mapping key "build" already defined at line 4
```
2020-12-07 13:00:25 +02:00
Andrey Nering
9f125502f8 Update Taskfile.yml
We now use modules. Dep is history
2020-12-05 19:10:49 -03:00
Andrey Nering
3f856c4b1c Merge pull request #414 from go-task/mvdan/sh-3.2.1
Upgrade mvdan/sh to v3.2.1
2020-12-05 19:04:48 -03:00
Andrey Nering
f55fb1e3a5 Upgrade mvdan/sh to v3.2.1 2020-12-05 18:59:34 -03:00
Bryce Schober
bf88bd5da5 Use {{default}} for an overriding usage
Fixes documentation confusion raised in #376.
2020-11-18 17:09:44 -08:00
Eugene Zhukov
b7112e02db Update curl arguments in installation.md
Double -s argument does not make sense according to curl manual page.
-s stands for silent, while -S (capital S) stands for showing the error.
When used in combination, curl shows an error message if it fails, but disables progress meter.

Finally, in the end of sh command there is -d, which stands for debug and contradicts -sS curl arguments.
I suggest to remove curl silencers all together, because more debug is better in CIs. I also suggest to use --location instead of -L for clarity.
2020-11-17 10:02:06 +02:00
Andrey Nering
b136166fc9 Merge pull request #387 from sheldonhull/patch-1
feat: improve installation script documentation for shell installs
2020-11-08 11:46:58 -03:00
sheldonhull
75727c3d68 fix: improve curl command install
- works on macOS
- works in Linux (tested in docker container)
2020-10-16 18:12:26 -05:00
sheldonhull
6c625b3359 fix: typo on curl install comment
fix from url to curl
2020-10-14 22:30:19 -05:00
Andrey Nering
60759a4e3b Post-fixes and CHANGELOG for #385 2020-10-12 21:16:09 -03:00
Andrey Nering
582a66bb2f Merge pull request #385 from chris-garrett/dev/378-missing-env
Resolves #378 - allow for missing env files as they may be bootstrapped.
2020-10-12 21:12:08 -03:00
Andrey Nering
d78f78bb5c Fix panic for empty tasks
Closes #338
Closes #362

Co-authored-by: Bharath Kumar <shettybharath4@gmail.com>
2020-10-12 21:03:13 -03:00
sheldonhull
71b7d062d5 feat: improve installation script documentation for shell installs
- The default didn't work well for me out of the gate.
- This is the modified version to support passing in the arguments easily as well as an example for installing to `/usr/local/bin` for using in Codespaces or equivalent development workflow.
2020-10-07 14:57:25 -05:00
Chris Garrett
c6138a0660 #378 - allow for missing env files as they may be bootstrapped. 2020-10-03 16:39:58 -06:00
Andrey Nering
ce4ac97269 Documentation updates 2020-09-06 14:33:28 -03:00
Andrey Nering
2088a86512 Merge pull request #372 from go-task/go-1-15
CI: Update Go version to 1.15.x
2020-09-05 17:59:55 -03:00
Andrey Nering
e296fe2b98 Merge pull request #371 from onedr0p/patch-1
Make fish completions compatible with MacOS
2020-09-05 17:59:24 -03:00
Andrey Nering
96b8890ecc CI: Update Go version to 1.15.x 2020-09-05 17:57:24 -03:00
Devin Buhl
db6fae2f5b Make completion compatible with MacOS 2020-09-03 18:52:36 -04:00
Andrey Nering
6743cdbb65 README: Add downloads badge 2020-08-25 18:15:51 -03:00
Andrey Nering
71466c9a27 Merge pull request #358 from damian-szulc/feature/allow-to-use-as-external-lib
Allow usage as an external library
2020-08-24 22:14:57 -03:00
Andrey Nering
1bdf7e3192 v3: Post-release improvements to the documentation
Closes #367
2020-08-24 21:43:08 -03:00
Andrey Nering
7285f3c844 Merge pull request #364 from shilangyu/fish-completions
Added fish completions
2020-08-24 21:20:04 -03:00
Andrey Nering
eb257d3aa7 Merge pull request #366 from jonasagx/patch-1
typo and grammar fixes
2020-08-24 21:17:36 -03:00
Jonas Galvão Xavier
87f11491d9 typo and grammar fixes 2020-08-22 13:33:04 -07:00
shilangyu
5735a02473 added fish completions 2020-08-22 11:59:17 +00:00
Damian Szulc
47dd9b5a03 Move args and taskfile packages to root directory 2020-08-19 10:59:58 +02:00
Andrey Nering
7652d7889b Some documentation improvements after the release of v3
Thanks @marco-m
2020-08-17 09:46:55 -03:00
Andrey Nering
dd2116c897 v3.0.0 2020-08-16 22:07:14 -03:00
Andrey Nering
c5566b3e94 Update version in the home page 2020-08-16 22:01:49 -03:00
Andrey Nering
30cbf02bff Update documentation to reflect the new default "method" 2020-08-16 21:59:42 -03:00
Andrey Nering
9e4e9b4f1a Doc: Update "Taskfile versions" documentation 2020-08-16 21:56:21 -03:00
Andrey Nering
6f290f28b6 On documentation: version: '2' -> version: '3' 2020-08-16 21:34:33 -03:00
Andrey Nering
6ff3c9015b On v3, treat all CLI variables as global variables
Closes #336
Ref #341

Co-authored-by: Egor Kovetskiy <e.kovetskiy@gmail.com>
2020-08-16 21:27:11 -03:00
Andrey Nering
e28b82b2b7 Upgrade mvdan.cc/sh to v3.1.2 2020-08-16 21:05:52 -03:00
Andrey Nering
3edf124f96 Merge pull request #220 from go-task/v3
v3 base branch
2020-08-16 15:54:20 -03:00
Andrey Nering
fb72b46a3c github.com/go-task/task/v2 -> github.com/go-task/task/v3 2020-08-16 15:48:19 -03:00
Andrey Nering
49bf395f61 Remove the vendor/ directory 2020-08-16 15:46:22 -03:00
Andrey Nering
eab14b6c49 Some improvements to #356 2020-08-15 19:13:30 -03:00
Chris Garrett
8b962fb8e8 #324 implement dotenv 2020-08-03 16:18:38 -06:00
Chris Garrett
e5a3c861cb Merge remote-tracking branch 'upstream/v3' into v3 2020-08-03 12:04:36 -06:00
Andrey Nering
c7bb3d63b0 Merge pull request #349 from central182/fix-typo
Fix some typos in the documentation
2020-07-19 16:02:58 -03:00
Arnold
e6b543c15e Fix some typos in the documentation 2020-07-07 16:25:27 +09:00
Andrey Nering
8137517d93 Merge branch 'master' into v3 2020-06-14 17:13:46 -03:00
Andrey Nering
572f6a7fab CHANGELOG, docs and nits for #321 and #337 2020-06-14 17:12:20 -03:00
Andrey Nering
c6d9201680 Merge pull request #337 from adamwasila/subtask-alias
Allow overriding the task name in the logger output
2020-06-14 17:01:28 -03:00
Andrey Nering
7dcb3af944 Merge pull request #347 from ronvanderheijden/master
Completion file honors includes
2020-06-14 16:38:13 -03:00
Adam Wasila
4bc183a8a1 Add basic unit tests for label attribute 2020-06-14 15:12:48 +02:00
Adam Wasila
9f83311931 Add label field to task definition
Label is an alternative name for task that replace it when printed in following context eg.:

- log: when given task is up to date and is skipped from execution
- log: when given task is NOT up to date (`--status` command)
- in `--summary` and `--list` commands output
2020-06-14 13:42:20 +02:00
Ron van der Heijden
10986d3a7c Accept : multiple times 2020-06-13 17:01:14 +02:00
Andrey Nering
f4f6efa547 Skip cleanup if task doesn't have any sources listed
Ref #333
2020-05-31 15:48:23 -03:00
Andrey Nering
bf64259af3 taskfile.dev: Add note about parallel deps
Reference: #331
2020-05-23 14:13:15 -03:00
Andrey Nering
6141ba84ce taskfile.dev: Switch completely from unpkg.com to jsdelivr.net 2020-05-23 14:00:38 -03:00
Andrey Nering
329902f0db taskfile.dev: Improve the installation page by using tabs 2020-05-23 13:46:03 -03:00
Andrey Nering
bfcaa7a443 taskfile.dev: Install Docsify Tabs plugin 2020-05-23 13:45:57 -03:00
Andrey Nering
45915bf0ed taskfile.dev: Remove tracking stuff 2020-05-23 13:45:50 -03:00
Andrey Nering
ee7f2a541f v3.0.0-preview4 2020-05-20 22:32:30 -03:00
Andrey Nering
59a00eae98 Merge branch 'master' into v3 2020-05-20 22:26:45 -03:00
Andrey Nering
df8293bee6 v2.8.1 2020-05-20 19:26:23 -03:00
Andrey Nering
935216f179 Upgrade mvdan.cc/sh to v3.1.1 2020-05-20 19:22:27 -03:00
Andrey Nering
f56bbd46fd Fix typo 2020-05-18 13:16:50 -03:00
Andrey Nering
9f0f18c5c4 v3: Allow interpolation on "includes"
The idea is to allow manual inclusion of a OS-dependant Taskfile, since it's
not automatically included anymore.
2020-05-17 16:03:03 -03:00
Andrey Nering
191c34c9c4 v3: Do not include Taskfile_{{OS}}.yml automatically 2020-05-17 15:42:27 -03:00
Andrey Nering
6a604b3002 v3: Taskvars is no more 2020-05-17 15:34:32 -03:00
Andrey Nering
5a435b533e v3: Disallow the "expansions" setting on Taskfiles in v3 2020-05-17 15:28:25 -03:00
Andrey Nering
4b027722b1 Merge pull request #311 from go-task/vars-refactor-for-v3
v3: Variables refactoring
2020-05-17 14:56:11 -03:00
Andrey Nering
68ce8642b1 Create v3 compiler which respects declaration order of variables
Also, fix "<no value>" been printed when a non-existing variable is printed.
2020-05-16 15:46:07 -03:00
Andrey Nering
4913b6a0f1 Merge branch 'v3' into vars-refactor-for-v3 2020-05-16 11:18:28 -03:00
Andrey Nering
aee0ab05f4 Merge branch 'master' into v3 2020-05-16 10:28:00 -03:00
Andrey Nering
b44432f24a Upgrade slim-sprig package version 2020-05-16 10:21:54 -03:00
Andrey Nering
86be13ff1f Merge pull request #330 from danquah/print-usage
Have the --help/-h flag print usage.
2020-05-14 21:15:00 -03:00
Andrey Nering
ee95df0e57 Merge pull request #329 from danquah/version-print-std-out
Switch to printing the version string to std out
2020-05-14 21:12:43 -03:00
Andrey Nering
442c29f020 Merge pull request #328 from danquah/fix-goreleaser-name-template
Fix name_template in Go Releaser
2020-05-14 21:11:22 -03:00
Mads H. Danquah
739037fc37 Have the --help/-h flag print usage.
pflag will output a "pflag: help requested" message after displaying the usage
if we don't explicitly  handle the flag.
2020-05-14 21:58:46 +02:00
Mads H. Danquah
f8252020aa Switch to printing the version string to std out 2020-05-14 21:40:49 +02:00
Mads H. Danquah
38b87439fe name_template is now file_name_template
See https://goreleaser.com/deprecations/#nfpmsname_template
2020-05-14 20:48:33 +02:00
Chris Garrett
116879f7ea Update docs to reflect change from Travis to GitHub 2020-05-03 14:58:02 -03:00
Andrey Nering
2eafb2f067 Update CHANGELOG 2020-05-01 15:50:32 -03:00
Andrey Nering
c36f0f6f7f Merge pull request #317 from hatappi/master
suppress context errors when use watch option
2020-05-01 15:38:16 -03:00
hatappi
bfc033959b suppress context errors when use watch option 2020-04-29 11:56:02 +09:00
Andrey Nering
814f350b6d go mod vendor 2020-04-25 15:56:41 -03:00
Andrey Nering
05db8ce582 Merge pull request #315 from go-task/mvdan-sh-3.1.0
Upgrade mvdan.cc/sh to v3.1.0
2020-04-25 15:55:32 -03:00
Andrey Nering
3fd36a0c72 Upgrade mvdan.cc/sh to v3.1.0
Closes #270
2020-04-25 15:51:47 -03:00
Andrey Nering
cbb12b29bd v3: Fix bug where global vars were not being considered 2020-04-05 11:16:27 -03:00
Andrey Nering
6ed30f1add Refactor variables: Keep order of declaration
This shouldn't have any behavior changes for now. This is a code
refactor that should allow us to do further improvements on how
variables are handled, specially regarding respecting the declaration
order in Taskfiles, which should make it easier for the users.

Initial work on #218
2020-04-05 11:16:14 -03:00
Andrey Nering
a044c41c66 Upgrade github.com/go-yaml/yaml to v3 2020-03-28 11:27:49 -03:00
668 changed files with 2515 additions and 291796 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1,2 +1,3 @@
open_collective: task
patreon: andreynering
custom: 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A&currency_code=BRL&source=url'

View File

@@ -1,6 +0,0 @@
<!--
If relevant, include the following information:
- Task version
- OS
- Example Taskfile showing the issue
-->

14
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,14 @@
---
name: Bug Report
about: Use the template to report bugs and issues
labels: bug
---
- Task version:
- Operating System:
### Example Taskfile showing the issue
```yaml
```

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions, Ideas and General Discussions
url: https://github.com/go-task/task/discussions
about: Ask questions and discuss general ideas with the community

View File

@@ -0,0 +1,12 @@
---
name: Feature Request
about: Use the template to make feature requests
labels: feature
---
Describe in detail what feature do you want to see in Task.
Give examples if possible.
Please, search if this wasn't proposed before, and if this is more like an idea
than a strong feature request, consider opening a
[discussion](https://github.com/go-task/task/discussions) instead.

10
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
day: saturday
time: '08:00'
timezone: America/Sao_Paulo

View File

@@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.14.x
go-version: 1.15.x
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1

View File

@@ -5,7 +5,7 @@ jobs:
name: Test
strategy:
matrix:
go-version: [1.13.x, 1.14.x]
go-version: [1.14.x, 1.15.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{matrix.platform}}
steps:

4
.gitignore vendored
View File

@@ -19,11 +19,13 @@ dist/
.DS_Store
# intellij idea/goland
# editors
.idea/
.vscode/
# exuberant ctags
tags
/bin
/testdata/vars/v1
/tmp

View File

@@ -1,6 +1,6 @@
build:
binary: task
main: cmd/task/task.go
main: cmd/task
goos:
- windows
- darwin
@@ -8,11 +8,20 @@ build:
goarch:
- 386
- amd64
- arm
- arm64
goarm:
- 6
ignore:
- goos: darwin
goarch: 386
env:
- CGO_ENABLED=0
ldflags:
- -s -w # Don't set main.version.
gomod:
proxy: true
archives:
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
@@ -40,4 +49,4 @@ nfpms:
formats:
- deb
- rpm
name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"

View File

@@ -1,16 +1,115 @@
# Changelog
# v3.0.0 - Preview 3
## v3.4.2 - 2021-04-23
- On watch, report which file failed to read
([#472](https://github.com/go-task/task/pull/472)).
- Do not try to catch SIGKILL signal, which are not actually possible
([#476](https://github.com/go-task/task/pull/476)).
- Improve version reporting when building Task from source using Go Modules
([#462](https://github.com/go-task/task/pull/462), [#473](https://github.com/go-task/task/pull/473)).
## v3.4.1 - 2021-04-17
- Improve error reporting when parsing YAML: in some situations where you
would just see an generic error, you'll now see the actual error with
more detail: the YAML line the failed to parse, for example
([#467](https://github.com/go-task/task/issues/467)).
- A JSON Schema was published [here](https://json.schemastore.org/taskfile.json)
and is automatically being used by some editors like Visual Studio Code
([#135](https://github.com/go-task/task/issues/135)).
- Print task name before the command in the log output
([#398](https://github.com/go-task/task/pull/398)).
## v3.3.0 - 2021-03-20
- Add support for delegating CLI arguments to commands with `--` and a
special `CLI_ARGS` variable
([#327](https://github.com/go-task/task/issues/327)).
- Add a `--concurrency` (alias `-C`) flag, to limit the number of tasks that
run concurrently. This is useful for heavy workloads.
([#345](https://github.com/go-task/task/pull/345)).
## v3.2.2 - 2021-01-12
- Improve performance of `--list` and `--summary` by skipping running shell
variables for these flags
([#332](https://github.com/go-task/task/issues/332)).
- Fixed a bug where an environment in a Taskfile was not always overridable
by the system environment
([#425](https://github.com/go-task/task/issues/425)).
- Fixed environment from .env files not being available as variables
([#379](https://github.com/go-task/task/issues/379)).
- The install script is now working for ARM platforms
([#428](https://github.com/go-task/task/pull/428)).
## v3.2.1 - 2021-01-09
- Fixed some bugs and regressions regarding dynamic variables and directories
([#426](https://github.com/go-task/task/issues/426)).
- The [slim-sprig](https://github.com/go-task/slim-sprig) package was updated
with the upstream [sprig](https://github.com/Masterminds/sprig).
## v3.2.0 - 2021-01-07
- Fix the `.task` directory being created in the task directory instead of the
Taskfile directory
([#247](https://github.com/go-task/task/issues/247)).
- Fix a bug where dynamic variables (those declared with `sh:`) were not
running in the task directory when the task has a custom dir or it was
in an included Taskfile
([#384](https://github.com/go-task/task/issues/384)).
- The watch feature (via the `--watch` flag) got a few different bug fixes and
should be more stable now
([#423](https://github.com/go-task/task/pull/423), [#365](https://github.com/go-task/task/issues/365)).
## v3.1.0 - 2021-01-03
- Fix a bug when the checksum up-to-date resolution is used by a task
with a custom `label:` attribute
([#412](https://github.com/go-task/task/issues/412)).
- Starting from this release, we're releasing official ARMv6 and ARM64 binaries
for Linux
([#375](https://github.com/go-task/task/issues/375), [#418](https://github.com/go-task/task/issues/418)).
- Task now respects the order of declaration of included Taskfiles when
evaluating variables declaring by them
([#393](https://github.com/go-task/task/issues/393)).
- `set -e` is now automatically set on every command. This was done to fix an
issue where multiline string commands wouldn't really fail unless the
sentence was in the last line
([#403](https://github.com/go-task/task/issues/403)).
## v3.0.1 - 2020-12-26
- Allow use as a library by moving the required packages out of the `internal`
directory
([#358](https://github.com/go-task/task/pull/358)).
- Do not error if a specified dotenv file does not exist
([#378](https://github.com/go-task/task/issues/378), [#385](https://github.com/go-task/task/pull/385)).
- Fix panic when you have empty tasks in your Taskfile
([#338](https://github.com/go-task/task/issues/338), [#362](https://github.com/go-task/task/pull/362)).
## v3.0.0 - 2020-08-16
- On `v3`, all CLI variables will be considered global variables
([#336](https://github.com/go-task/task/issues/336), [#341](https://github.com/go-task/task/pull/341))
- Add support to `.env` like files
([#324](https://github.com/go-task/task/issues/324), [#356](https://github.com/go-task/task/pull/356)).
- Add `label:` to task so you can override the task name in the logs
([#321](https://github.com/go-task/task/issues/321]), [#337](https://github.com/go-task/task/pull/337)).
- Refactor how variables work on version 3
([#311](https://github.com/go-task/task/pull/311)).
- Disallow `expansions` on v3 since it has no effect.
- `Taskvars.yml` is not automatically included anymore.
- `Taskfile_{{OS}}.yml` is not automatically included anymore.
- Allow interpolation on `includes`, so you can manually include a Taskfile
based on operation system, for example.
- Expose `.TASK` variable in templates with the task name
([#252](https://github.com/go-task/task/issues/252)).
- Implement short task syntax
([#194](https://github.com/go-task/task/issues/194), [#240](https://github.com/go-task/task/pull/240)).
- Added option to make included Taskfile run commands on its own directory
([#260](https://github.com/go-task/task/issues/260), [#144](https://github.com/go-task/task/issues/144))
# 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
@@ -25,9 +124,6 @@
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%
@@ -36,6 +132,17 @@
commands are green, errors are red, etc
([#207](https://github.com/go-task/task/pull/207)).
## v2.8.1 - 2020-05-20
- Fix error code for the `--help` flag
([#300](https://github.com/go-task/task/issues/300), [#330](https://github.com/go-task/task/pull/330)).
- Print version to stdout instead of stderr
([#299](https://github.com/go-task/task/issues/299), [#329](https://github.com/go-task/task/pull/329)).
- Supress `context` errors when using the `--watch` flag
([#313](https://github.com/go-task/task/issues/313), [#317](https://github.com/go-task/task/pull/317)).
- Support templating on description
([#276](https://github.com/go-task/task/issues/276), [#283](https://github.com/go-task/task/pull/283)).
## v2.8.0 - 2019-12-07
- Add `--parallel` flag (alias `-p`) to run tasks given by the command line in

View File

@@ -1,22 +1,15 @@
[![Build Status](https://travis-ci.org/go-task/task.svg?branch=master)](https://travis-ci.org/go-task/task)
<div align="center">
<a href="https://taskfile.dev">
<img src="docs/Logo.png" width="200px" height="200px" />
</a>
# Task
<h1>Task</h1>
Task is a task runner / build tool that aims to be simpler and easier to use
than, for example, [GNU Make](https://www.gnu.org/software/make/).
<p>
Task is a task runner / build tool that aims to be simpler and easier to use than, for example, <a href="https://www.gnu.org/software/make/">GNU Make<a>.
</p>
See [taskfile.dev](https://taskfile.dev) for documentation.
---
## Sponsors
[![Sponsors](https://opencollective.com/task/sponsors.svg?width=890)](https://opencollective.com/task)
## Backers
[![Backers](https://opencollective.com/task/backers.svg?width=890)](https://opencollective.com/task)
## Contributors
[![Contributors](https://opencollective.com/task/contributors.svg?width=890)](https://github.com/go-task/task/graphs/contributors)
<p>
See <a href="https://taskfile.dev">taskfile.dev</a> for the documentation.
</p>
</div>

View File

@@ -12,6 +12,9 @@ vars:
GO_PACKAGES:
sh: go list ./...
env:
CGO_ENABLED: '0'
tasks:
default:
cmds:
@@ -21,11 +24,15 @@ tasks:
desc: Installs Task
cmds:
- go install -v -ldflags="-w -s -X main.version={{.GIT_COMMIT}}" ./cmd/task
env:
CGO_ENABLED: '0'
dl-deps:
desc: Downloads cli dependencies
mod:
desc: Downloads and tidy Go modules
cmds:
- go mod download
- go mod tidy
cli-deps:
desc: Downloads CLI dependencies
cmds:
- task: go-get
vars: {REPO: golang.org/x/lint/golint}
@@ -34,17 +41,6 @@ tasks:
- task: go-get
vars: {REPO: github.com/goreleaser/godownloader}
vendor:
desc: Sync vendor/ directory according to go.mod file
cmds:
- go mod vendor
update-deps:
desc: Updates dependencies
cmds:
- dep ensure
- dep ensure -update
clean:
desc: Cleans temp files and folders
cmds:
@@ -67,7 +63,7 @@ tasks:
cmds:
- goreleaser --snapshot --rm-dist
generate-install-script:
gen-install-script:
desc: Generate install script using https://github.com/goreleaser/godownloader
cmds:
- godownloader --repo go-task/task -o install-task.sh

64
args/args.go Normal file
View File

@@ -0,0 +1,64 @@
package args
import (
"strings"
"github.com/go-task/task/v3/taskfile"
)
// ParseV3 parses command line argument: tasks and global variables
func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) {
var calls []taskfile.Call
var globals = &taskfile.Vars{}
for _, arg := range args {
if !strings.Contains(arg, "=") {
calls = append(calls, taskfile.Call{Task: arg})
continue
}
name, value := splitVar(arg)
globals.Set(name, taskfile.Var{Static: value})
}
if len(calls) == 0 {
calls = append(calls, taskfile.Call{Task: "default"})
}
return calls, globals
}
// ParseV2 parses command line argument: tasks and vars of each task
func ParseV2(args ...string) ([]taskfile.Call, *taskfile.Vars) {
var calls []taskfile.Call
var globals = &taskfile.Vars{}
for _, arg := range args {
if !strings.Contains(arg, "=") {
calls = append(calls, taskfile.Call{Task: arg})
continue
}
if len(calls) < 1 {
name, value := splitVar(arg)
globals.Set(name, taskfile.Var{Static: value})
} else {
if calls[len(calls)-1].Vars == nil {
calls[len(calls)-1].Vars = &taskfile.Vars{}
}
name, value := splitVar(arg)
calls[len(calls)-1].Vars.Set(name, taskfile.Var{Static: value})
}
}
if len(calls) == 0 {
calls = append(calls, taskfile.Call{Task: "default"})
}
return calls, globals
}
func splitVar(s string) (string, string) {
pair := strings.SplitN(s, "=", 2)
return pair[0], pair[1]
}

209
args/args_test.go Normal file
View File

@@ -0,0 +1,209 @@
package args_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3/args"
"github.com/go-task/task/v3/taskfile"
)
func TestArgsV3(t *testing.T) {
tests := []struct {
Args []string
ExpectedCalls []taskfile.Call
ExpectedGlobals *taskfile.Vars
}{
{
Args: []string{"task-a", "task-b", "task-c"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
},
},
{
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO", "BAR", "BAZ"},
Mapping: map[string]taskfile.Var{
"FOO": taskfile.Var{Static: "bar"},
"BAR": taskfile.Var{Static: "baz"},
"BAZ": taskfile.Var{Static: "foo"},
},
},
},
{
Args: []string{"task-a", "CONTENT=with some spaces"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"CONTENT"},
Mapping: map[string]taskfile.Var{
"CONTENT": taskfile.Var{Static: "with some spaces"},
},
},
},
{
Args: []string{"FOO=bar", "task-a", "task-b"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO"},
Mapping: map[string]taskfile.Var{
"FOO": {Static: "bar"},
},
},
},
{
Args: nil,
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{"FOO=bar", "BAR=baz"},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO", "BAR"},
Mapping: map[string]taskfile.Var{
"FOO": {Static: "bar"},
"BAR": {Static: "baz"},
},
},
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, globals := args.ParseV3(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls)
if test.ExpectedGlobals.Len() > 0 || globals.Len() > 0 {
assert.Equal(t, test.ExpectedGlobals, globals)
}
})
}
}
func TestArgsV2(t *testing.T) {
tests := []struct {
Args []string
ExpectedCalls []taskfile.Call
ExpectedGlobals *taskfile.Vars
}{
{
Args: []string{"task-a", "task-b", "task-c"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
},
},
{
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
ExpectedCalls: []taskfile.Call{
{
Task: "task-a",
Vars: &taskfile.Vars{
Keys: []string{"FOO"},
Mapping: map[string]taskfile.Var{
"FOO": taskfile.Var{Static: "bar"},
},
},
},
{Task: "task-b"},
{
Task: "task-c",
Vars: &taskfile.Vars{
Keys: []string{"BAR", "BAZ"},
Mapping: map[string]taskfile.Var{
"BAR": taskfile.Var{Static: "baz"},
"BAZ": taskfile.Var{Static: "foo"},
},
},
},
},
},
{
Args: []string{"task-a", "CONTENT=with some spaces"},
ExpectedCalls: []taskfile.Call{
{
Task: "task-a",
Vars: &taskfile.Vars{
Keys: []string{"CONTENT"},
Mapping: map[string]taskfile.Var{
"CONTENT": taskfile.Var{Static: "with some spaces"},
},
},
},
},
},
{
Args: []string{"FOO=bar", "task-a", "task-b"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO"},
Mapping: map[string]taskfile.Var{
"FOO": {Static: "bar"},
},
},
},
{
Args: nil,
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{"FOO=bar", "BAR=baz"},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO", "BAR"},
Mapping: map[string]taskfile.Var{
"FOO": {Static: "bar"},
"BAR": {Static: "baz"},
},
},
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, globals := args.ParseV2(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls)
if test.ExpectedGlobals.Len() > 0 || globals.Len() > 0 {
assert.Equal(t, test.ExpectedGlobals, globals)
}
})
}
}

View File

@@ -2,21 +2,25 @@ package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"runtime/debug"
"strings"
"syscall"
"github.com/go-task/task/v2"
"github.com/go-task/task/v2/internal/args"
"github.com/go-task/task/v2/internal/logger"
"github.com/spf13/pflag"
"github.com/go-task/task/v3"
"github.com/go-task/task/v3/args"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/taskfile"
)
var (
version = "master"
version = ""
)
const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--taskfile] [--dry] [--summary] [task...]
@@ -52,6 +56,7 @@ func main() {
var (
versionFlag bool
helpFlag bool
init bool
list bool
status bool
@@ -62,6 +67,7 @@ func main() {
dry bool
summary bool
parallel bool
concurrency int
dir string
entrypoint string
output string
@@ -69,6 +75,7 @@ func main() {
)
pflag.BoolVar(&versionFlag, "version", false, "show Task version")
pflag.BoolVarP(&helpFlag, "help", "h", false, "shows Task usage")
pflag.BoolVarP(&init, "init", "i", false, "creates a new Taskfile.yml in the current folder")
pflag.BoolVarP(&list, "list", "l", false, "lists tasks with description of current Taskfile")
pflag.BoolVar(&status, "status", false, "exits with non-zero exit code if any of the given tasks is not up-to-date")
@@ -83,10 +90,16 @@ func main() {
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.BoolVarP(&color, "color", "c", true, "colored output")
pflag.IntVarP(&concurrency, "concurrency", "C", 0, "limit number tasks to run concurrently")
pflag.Parse()
if versionFlag {
log.Printf("Task version: %s\n", version)
fmt.Printf("Task version: %s\n", getVersion())
return
}
if helpFlag {
pflag.Usage()
return
}
@@ -113,16 +126,17 @@ func main() {
}
e := task.Executor{
Force: force,
Watch: watch,
Verbose: verbose,
Silent: silent,
Dir: dir,
Dry: dry,
Entrypoint: entrypoint,
Summary: summary,
Parallel: parallel,
Color: color,
Force: force,
Watch: watch,
Verbose: verbose,
Silent: silent,
Dir: dir,
Dry: dry,
Entrypoint: entrypoint,
Summary: summary,
Parallel: parallel,
Color: color,
Concurrency: concurrency,
Stdin: os.Stdin,
Stdout: os.Stdout,
@@ -133,17 +147,32 @@ func main() {
if err := e.Setup(); err != nil {
log.Fatal(err)
}
v, err := e.Taskfile.ParsedVersion()
if err != nil {
log.Fatal(err)
return
}
if list {
e.PrintTasksHelp()
return
}
calls, globals := args.Parse(pflag.Args()...)
for name, value := range globals {
e.Taskfile.Vars[name] = value
var (
calls []taskfile.Call
globals *taskfile.Vars
tasksAndVars, cliArgs = getArgs()
)
if v >= 3.0 {
calls, globals = args.ParseV3(tasksAndVars...)
} else {
calls, globals = args.ParseV2(tasksAndVars...)
}
globals.Set("CLI_ARGS", taskfile.Var{Static: strings.Join(cliArgs, " ")})
e.Taskfile.Vars.Merge(globals)
ctx := context.Background()
if !watch {
ctx = getSignalContext()
@@ -162,9 +191,25 @@ func main() {
}
}
func getArgs() (tasksAndVars, cliArgs []string) {
var (
args = pflag.Args()
doubleDashPos = pflag.CommandLine.ArgsLenAtDash()
)
if doubleDashPos != -1 {
tasksAndVars = args[:doubleDashPos]
cliArgs = args[doubleDashPos:]
} else {
tasksAndVars = args
}
return
}
func getSignalContext() context.Context {
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, os.Kill, syscall.SIGTERM)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background())
go func() {
sig := <-ch
@@ -173,3 +218,21 @@ func getSignalContext() context.Context {
}()
return ctx
}
func getVersion() string {
if version != "" {
return version
}
info, ok := debug.ReadBuildInfo()
if !ok || info.Main.Version == "" {
return "unknown"
}
version = info.Main.Version
if info.Main.Sum != "" {
version += fmt.Sprintf(" (%s)", info.Main.Sum)
}
return version
}

24
completion/fish/task.fish Normal file
View File

@@ -0,0 +1,24 @@
function __task_get_tasks --description "Prints all available tasks with their description"
task -l | sed '1d' | awk '{ $1=""; print $0 }' | tr ': ', '\t' | string trim
end
complete -c task -d '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.' -xa "(__task_get_tasks)"
complete -c task -s c -l color -d 'colored output (default true)'
complete -c task -s d -l dir -d 'sets directory of execution'
complete -c task -l dry -d 'compiles and prints tasks in the order that they would be run, without executing them'
complete -c task -s f -l force -d 'forces execution even when the task is up-to-date'
complete -c task -s h -l help -d 'shows Task usage'
complete -c task -s i -l init -d 'creates a new Taskfile.yml in the current folder'
complete -c task -s l -l list -d 'lists tasks with description of current Taskfile'
complete -c task -s o -l output -d 'sets output style: [interleaved|group|prefixed]' -xa "interleaved group prefixed"
complete -c task -s p -l parallel -d 'executes tasks provided on command line in parallel'
complete -c task -s s -l silent -d 'disables echoing'
complete -c task -l status -d 'exits with non-zero exit code if any of the given tasks is not up-to-date'
complete -c task -l summary -d 'show summary about a task'
complete -c task -s t -l taskfile -d 'choose which Taskfile to run. Defaults to "Taskfile.yml"'
complete -c task -s v -l verbose -d 'enables verbose mode'
complete -c task -l version -d 'show Task version'
complete -c task -s w -l watch -d 'enables watch of the given task'

View File

@@ -5,7 +5,7 @@ function __list() {
local -a scripts
if [ -f Taskfile.yml ]; then
scripts=($(task -l | sed '1d' | sed 's/^\* //' | awk '{ print $1 }' | sed 's/:$//' | sed 's/:/\\:/'))
scripts=($(task -l | sed '1d' | sed 's/^\* //' | awk '{ print $1 }' | sed 's/:$//' | sed 's/:/\\:/g'))
_describe 'script' scripts
fi
}

25
concurrency.go Normal file
View File

@@ -0,0 +1,25 @@
package task
func (e *Executor) acquireConcurrencyLimit() func() {
if e.concurrencySemaphore == nil {
return emptyFunc
}
e.concurrencySemaphore <- struct{}{}
return func() {
<-e.concurrencySemaphore
}
}
func (e *Executor) releaseConcurrencyLimit() func() {
if e.concurrencySemaphore == nil {
return emptyFunc
}
<-e.concurrencySemaphore
return func() {
e.concurrencySemaphore <- struct{}{}
}
}
func emptyFunc() {}

BIN
docs/Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,5 +1,9 @@
# Task
<div align="center">
<img id="logo" src="/Logo.png" height="250px" width="250px" />
</div>
Task is a task runner / build tool that aims to be simpler and easier to use
than, for example, [GNU Make][make].
@@ -11,7 +15,7 @@ Once [installed](installation.md), you just need to describe your build tasks
using a simple [YAML][yaml] schema in a file called `Taskfile.yml`:
```yaml
version: '2'
version: '3'
tasks:
hello:
@@ -38,18 +42,6 @@ guide to check the full schema documentation and Task features.
if a given set of files haven't changed since last run (based either on its
timestamp or content).
## Sponsors
[![Sponsors](https://opencollective.com/task/sponsors.svg?width=890)](https://opencollective.com/task)
## Backers
[![Backers](https://opencollective.com/task/backers.svg?width=890)](https://opencollective.com/task)
## Contributors
[![Contributors](https://opencollective.com/task/contributors.svg?width=890)](https://github.com/go-task/task/graphs/contributors)
[make]: https://www.gnu.org/software/make/
[go]: https://golang.org/
[yaml]: http://yaml.org/

View File

@@ -10,3 +10,8 @@ tasks:
desc: Serves the documentation site locally
cmds:
- docsify serve .
ico:
desc: Generate favicon.ico from Logo.png
cmds:
- convert -background transparent "Logo.png" -define icon:auto-resize=16,24,32,48,64,72,96,128,256 "favicon.ico"

View File

@@ -2,7 +2,7 @@
- [Usage](usage.md)
- [Styleguide](styleguide.md)
- [Taskfile Versions](taskfile_versions.md)
- [Examples](examples.md)
- [Community](community.md)
- [Releasing Task](releasing_task.md)
- [Alternative Task Runners](alternative_task_runners.md)
- [![Github](https://icongram.jgog.in/simple/github.svg?color=808080&size=16)Github](https://github.com/go-task/task)
- [Donate](donate.md)
- [GitHub](https://github.com/go-task/task)

View File

@@ -1,17 +0,0 @@
# Alternative task runners
## YAML based
- [rliebz/tusk][tusk]
## Go based
- [magefile/mage][mage]
## Make similar
- [casey/just][just]
[tusk]: https://github.com/rliebz/tusk
[mage]: https://github.com/magefile/mage
[just]: https://github.com/casey/just

44
docs/community.md Normal file
View File

@@ -0,0 +1,44 @@
# Community
Some of the work to improve the Task ecosystem is done by the community, be
it installation methods or integrations with code editor. I (the author) am
thankful for everyone that helps me to improve the overall experience.
## Editor Integrations
### JSON Schema
[@KROSF](https://github.com/KROSF) worked on a JSON Schema [into this Gist](https://gist.github.com/KROSF/c5435acf590acd632f71bb720f685895),
which later was made officially available by [@Crandel](https://github.com/Crandel)
at [https://json.schemastore.org/taskfile.json](https://json.schemastore.org/taskfile.json).
Further improvements are possible by opening pull requests changing
[this file](https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/taskfile.json).
Some code editors, like Visual Studio Code, make use of Schema Store
automatically.
### Visual Studio Code extension
Additionally, there's also some work done by
[@paulvarache](https://github.com/paulvarache) in making an Visual Studio Code
extension, which has its code [here](https://github.com/paulvarache/vscode-taskfile)
and is published [here](https://marketplace.visualstudio.com/items?itemName=paulvarache.vscode-taskfile).
## Installation methods
Some installation methods are maintained by third party:
- [GitHub Actions](https://github.com/arduino/actions/tree/master/setup-taskfile)
by [@arduino](https://github.com/arduino)
- [AUR](https://aur.archlinux.org/packages/taskfile-git)
by [@kovetskiy](https://github.com/kovetskiy)
- [Scoop](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json)
## More
Also, thanks for all the [code contributors](https://github.com/go-task/task/graphs/contributors),
[financial contributors](https://opencollective.com/task), all those who
[reported bugs](https://github.com/go-task/task/issues?q=is%3Aissue) and
[answered questions](https://github.com/go-task/task/discussions).
If you know something that is missing in this document, please submit a
pull request.

35
docs/donate.md Normal file
View File

@@ -0,0 +1,35 @@
# Donate
If you find this project useful, you can consider donating by using one of the
channels listed below.
This is just a way of saying "thank you", it won't give you any benefits like
higher priority on issues or something similar.
## Open Collective
Task is on [Open Collective](https://opencollective.com/task) and you have
these options to donate:
- [$2 per month](https://opencollective.com/task/contribute/backer-4034/checkout)
- [$5 per month](https://opencollective.com/task/contribute/supporter-8404/checkout)
- [$20 per month](https://opencollective.com/task/contribute/sponsor-4035/checkout)
- [Custom value - One-time donation option supported](https://opencollective.com/task/donate)
## Patreon
I'm also on [Patreon](https://www.patreon.com/andreynering) if
you prefer:
- [$5 per month](https://www.patreon.com/join/andreynering/checkout?rid=4229277)
- [$10 per month](https://www.patreon.com/join/andreynering/checkout?rid=4229276)
- [$15 per month](https://www.patreon.com/join/andreynering/checkout?rid=4229275)
You can choose a custom value on any of the links above.
Patreon does not support one-time donation. As a workaround you can fire a
subscription and cancel it once the donation was succeded.
## PayPal
- [Any value - One-time donation](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A&currency_code=BRL&source=url)

View File

@@ -1,7 +0,0 @@
# Examples
The [go-task/examples][examples] intends to be a collection of Taskfiles for
various use cases.
(It still lacks many examples, though. Contributions are welcome).
[examples]: https://github.com/go-task/examples

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 170 KiB

View File

@@ -6,25 +6,25 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="A task runner / simpler Make alternative written in Go">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//unpkg.com/docsify-themeable/dist/css/theme-simple.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple-dark.css">
<meta name="google-site-verification" content="VGAYkbdmuaciIDGkBe-eAg9yfZg0C6ostgonbGxxOa0" />
<script>
var SeedAndDewConfig = {};
(function() {
SeedAndDewConfig['adClass'] = "snd-ad";
/* * * DON'T EDIT BELOW THIS LINE * * */
SeedAndDewConfig['projectId'] = '16e0aed0-b265-48c9-9eae-0aad56147553';
SeedAndDewConfig['loadStartTime'] = performance.now();
SeedAndDewConfig['apiVersion'] = '2018-05-28'
SeedAndDewConfig['sessionId'] = Math.random().toString(36).substring(2, 15);
var snd = document.createElement('script');
snd.type = 'text/javascript';
snd.async = true;
snd.src = 'https://www.seedanddew.com/static/embed.min.js';
(document.getElementsByTagName('head')[0] ||
document.getElementsByTagName('body')[0]).appendChild(snd);
})();
</script>
<style>
#logo {
transition: all 0.7s ease;
}
#logo:hover {
-webkit-transform: rotateZ(360deg);
-ms-transform: rotateZ(360deg);
transform: rotateZ(360deg);
}
.app-name-link img {
width: 125px;
}
:root {
--base-font-size: 14px;
--theme-color: #29beb0;
}
</style>
</head>
<body>
<div id="app"></div>
@@ -32,18 +32,18 @@
window.$docsify = {
name: 'Task',
repo: 'go-task/task',
ga: 'UA-126286662-1',
themeColor: '#00add8',
logo: 'Logo.png',
themeColor: '#29beb0',
loadSidebar: true,
auto2top: true,
maxLevel: 3,
subMaxLevel: 3
}
</script>
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
<script src="//unpkg.com/docsify/lib/plugins/ga.min.js"></script>
<script src="//unpkg.com/docsify-themeable"></script>
<script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-yaml.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify-themeable/dist/js/docsify-themeable.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify-tabs"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-bash.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-yaml.min.js"></script>
</body>
</html>

View File

@@ -1,6 +1,6 @@
#!/bin/sh
set -e
# Code generated by godownloader on 2018-04-07T17:47:38Z. DO NOT EDIT.
# Code generated by godownloader on 2021-01-12T13:40:40Z. DO NOT EDIT.
#
usage() {
@@ -27,11 +27,12 @@ parse_args() {
# over-ridden by flag below
BINDIR=${BINDIR:-./bin}
while getopts "b:dh?" arg; do
while getopts "b:dh?x" arg; do
case "$arg" in
b) BINDIR="$OPTARG" ;;
d) log_set_priority 10 ;;
h | \?) usage "$0" ;;
x) set -x ;;
esac
done
shift $((OPTIND - 1))
@@ -42,46 +43,41 @@ parse_args() {
# network, either nothing will happen or will syntax error
# out preventing half-done work
execute() {
tmpdir=$(mktmpdir)
tmpdir=$(mktemp -d)
log_debug "downloading files into ${tmpdir}"
http_download "${tmpdir}/${TARBALL}" "${TARBALL_URL}"
http_download "${tmpdir}/${CHECKSUM}" "${CHECKSUM_URL}"
hash_sha256_verify "${tmpdir}/${TARBALL}" "${tmpdir}/${CHECKSUM}"
srcdir="${tmpdir}"
(cd "${tmpdir}" && untar "${TARBALL}")
install -d "${BINDIR}"
for binexe in "task" ; do
test ! -d "${BINDIR}" && install -d "${BINDIR}"
for binexe in $BINARIES; do
if [ "$OS" = "windows" ]; then
binexe="${binexe}.exe"
fi
install "${srcdir}/${binexe}" "${BINDIR}/"
log_info "installed ${BINDIR}/${binexe}"
done
rm -rf "${tmpdir}"
}
is_supported_platform() {
platform=$1
found=1
case "$platform" in
windows/386) found=0 ;;
windows/amd64) found=0 ;;
darwin/386) found=0 ;;
darwin/amd64) found=0 ;;
linux/386) found=0 ;;
linux/amd64) found=0 ;;
get_binaries() {
case "$PLATFORM" in
darwin/amd64) BINARIES="task" ;;
darwin/arm64) BINARIES="task" ;;
darwin/armv6) BINARIES="task" ;;
linux/386) BINARIES="task" ;;
linux/amd64) BINARIES="task" ;;
linux/arm64) BINARIES="task" ;;
linux/armv6) BINARIES="task" ;;
windows/386) BINARIES="task" ;;
windows/amd64) BINARIES="task" ;;
windows/arm64) BINARIES="task" ;;
windows/armv6) BINARIES="task" ;;
*)
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
exit 1
;;
esac
case "$platform" in
darwin/386) found=1 ;;
esac
return $found
}
check_platform() {
if is_supported_platform "$PLATFORM"; then
# optional logging goes here
true
else
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
exit 1
fi
}
tag_to_version() {
if [ -z "${TAG}" ]; then
@@ -99,8 +95,8 @@ tag_to_version() {
VERSION=${TAG#v}
}
adjust_format() {
# change format (tar.gz or zip) based on ARCH
case ${ARCH} in
# change format (tar.gz or zip) based on OS
case ${OS} in
windows) FORMAT=zip ;;
esac
true
@@ -174,7 +170,9 @@ log_crit() {
uname_os() {
os=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$os" in
msys_nt) os="windows" ;;
cygwin_nt*) os="windows" ;;
mingw*) os="windows" ;;
msys_nt*) os="windows" ;;
esac
echo "$os"
}
@@ -186,9 +184,9 @@ uname_arch() {
i686) arch="386" ;;
i386) arch="386" ;;
aarch64) arch="arm64" ;;
armv5*) arch="arm5" ;;
armv6*) arch="arm6" ;;
armv7*) arch="arm7" ;;
armv5*) arch="armv5" ;;
armv6*) arch="armv6" ;;
armv7*) arch="armv7" ;;
esac
echo ${arch}
}
@@ -234,8 +232,8 @@ uname_arch_check() {
untar() {
tarball=$1
case "${tarball}" in
*.tar.gz | *.tgz) tar -xzf "${tarball}" ;;
*.tar) tar -xf "${tarball}" ;;
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;;
*.tar) tar --no-same-owner -xf "${tarball}" ;;
*.zip) unzip "${tarball}" ;;
*)
log_err "untar unknown archive format for ${tarball}"
@@ -243,11 +241,6 @@ untar() {
;;
esac
}
mktmpdir() {
test -z "$TMPDIR" && TMPDIR="$(mktemp -d)"
mkdir -p "${TMPDIR}"
echo "${TMPDIR}"
}
http_download_curl() {
local_file=$1
source_url=$2
@@ -368,7 +361,7 @@ uname_arch_check "$ARCH"
parse_args "$@"
check_platform
get_binaries
tag_to_version

View File

@@ -1,12 +1,12 @@
# Installation
## Binary
Task offers many installation methods. Check out the available methods below.
Or you can download the binary from the [releases][releases] page and add to
your $PATH. DEB and RPM packages are also available.
The `task_checksums.txt` file contains the sha256 checksum for each file.
## Package Managers
## Homebrew
<!-- tabs:start -->
#### **Homebrew**
If you're on macOS or Linux and have [Homebrew][homebrew] installed, getting
Task is as simple as running:
@@ -15,19 +15,17 @@ Task is as simple as running:
brew install go-task/tap/go-task
```
> This installation method is only currently supported on amd64 architectures.
#### **Snap**
## Snap
Task is available for [Snapcraft][snapcraft], but keep in mind that your
Task is available in [Snapcraft][snapcraft], but keep in mind that your
Linux distribution should allow classic confinement for Snaps to Task work
right:
```bash
sudo snap install task
sudo snap install task --classic
```
## Scoop
#### **Scoop**
If you're on Windows and have [Scoop][scoop] installed, use `extras` bucket
to install Task like:
@@ -40,7 +38,7 @@ scoop install task
This installation method is community owned. After a new release of Task, it
may take some time until it's available on Scoop.
## Arch Linux
#### **AUR**
If you're on Arch Linux you can install Task from
[AUR](https://aur.archlinux.org/packages/taskfile-git) using your favorite
@@ -53,45 +51,40 @@ yay -S taskfile-git
This installation method is community owned, but since it's `-git` version of
the package, it's always latest available version based on the Git repository.
## Go
<!-- tabs:end -->
Task now uses [Go Modules](https://github.com/golang/go/wiki/Modules), which
means you may have trouble compiling it on older Go versions or using
`$GOPATH`.
## Get The Binary
For CI environments we recommend using the [Install Script](#install-script)
instead, which is faster and more stable, since it'll just download the latest
released binary, instead of compiling the edge (master branch) version.
<!-- tabs:start -->
Installing in another directory:
#### **Binary**
You can download the binary from the [releases page on GitHub][releases] and
add to your `$PATH`.
DEB and RPM packages are also available.
The `task_checksums.txt` file contains the SHA-256 checksum for each file.
#### **Install Script**
We also have an [install script][installscript] which is very useful in
scenarios like CI. Many thanks to [GoDownloader][godownloader] for enabling the
easy generation of this script.
```bash
git clone https://github.com/go-task/task
cd task
# For Default Installation to ./bin with debug logging
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
# compiling binary to $GOPATH/bin
go install -v ./cmd/task
# For Installation To /usr/local/bin for userwide access with debug logging
# May require sudo sh
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
# compiling it to another location
# use -o ./task.exe on Windows
go build -v -o ./task ./cmd/task
```
Both methods requires having the [Go][go] environment properly setup locally.
## Install script
We also have a [install script][installscript], which is very useful on
scenarios like CIs. Many thanks to [godownloader][godownloader] for allowing
easily generating this script.
```bash
curl -sL https://taskfile.dev/install.sh | sh
```
> This method will download the binary on the local `./bin` directory by default.
## GitHub Actions
#### **GitHub Actions**
If you want to install Task in GitHub Actions you can try using
[this action](https://github.com/arduino/actions/tree/master/setup-taskfile)
@@ -104,8 +97,42 @@ by the Arduino team:
This installation method is community owned.
<!-- tabs:end -->
## Build From Source
<!-- tabs:start -->
#### **Go Modules**
First, make sure you have [Go][go] properly installed and setup.
You can easily install the latest release globally by running:
```bash
go install github.com/go-task/task/v3/cmd/task@latest
```
Or you can install into another directory:
```bash
env GOBIN=/bin go install github.com/go-task/task/v3/cmd/task@latest
```
If using Go 1.15 or earlier, instead use:
```bash
env GO111MODULE=on go get -u github.com/go-task/task/v3/cmd/task@latest
```
> For CI environments we recommend using the [Install Script](#get-the-binary)
> instead, which is faster and more stable, since it'll just download the latest
> released binary.
<!-- tabs:end -->
[go]: https://golang.org/
[snapcraft]: https://snapcraft.io/
[snapcraft]: https://snapcraft.io/task
[homebrew]: https://brew.sh/
[installscript]: https://github.com/go-task/task/blob/master/install-task.sh
[releases]: https://github.com/go-task/task/releases

View File

@@ -4,9 +4,9 @@ The release process of Task is done with the help of
[GoReleaser][goreleaser]. You can test the release process locally by calling
the `test-release` task of the Taskfile.
The Travis CI should release automatically when a new
Git tag is pushed to master, either for the artifact uploading (raw executables
and DEB and RPM packages)
[GitHub Actions](https://github.com/go-task/task/actions) should release
artifacts automatically when a new Git tag is pushed to master
(raw executables and DEB and RPM packages).
# Homebrew
@@ -36,4 +36,4 @@ If you think its Task version is outdated, open an issue to let us know.
[gotaskrb]: https://github.com/go-task/homebrew-tap/blob/master/Formula/go-task.rb
[snappackage]: https://github.com/go-task/snap
[snapcraftyaml]: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml#L2
[snapcraftdashboard]: https://dashboard.snapcraft.io/
[snapcraftdashboard]: https://snapcraft.io/task/releases

View File

@@ -56,7 +56,7 @@ tasks:
```yaml
# bad
version: 2
version: '3'
includes:
docker: ./docker/Taskfile.yml
output: prefixed
@@ -70,7 +70,7 @@ tasks:
# good
version: 2
version: '3'
includes:
docker: ./docker/Taskfile.yml
@@ -92,7 +92,7 @@ tasks:
```yaml
# bad
version: 2
version: '3'
tasks:
foo:
@@ -107,7 +107,7 @@ tasks:
# good
version: 2
version: '3'
tasks:
foo:
@@ -127,7 +127,7 @@ tasks:
```yaml
# bad
version: 2
version: '3'
vars:
binary_name: myapp
@@ -139,7 +139,7 @@ tasks:
# good
version: 2
version: '3'
vars:
BINARY_NAME: myapp
@@ -154,7 +154,7 @@ tasks:
```yaml
# bad
version: 2
version: '3'
tasks:
greet:
@@ -163,7 +163,7 @@ tasks:
# good
version: 2
version: '3'
tasks:
greet:
@@ -177,7 +177,7 @@ This convention is also used by most people for any Go templating.
```yaml
# bad
version: 2
version: '3'
tasks:
do_something_fancy:
@@ -186,7 +186,7 @@ tasks:
# good
version: 2
version: '3'
tasks:
do-something-fancy:
@@ -198,7 +198,7 @@ tasks:
```yaml
# good
version: 2
version: '3'
tasks:
docker:build:

View File

@@ -9,8 +9,8 @@ The Taskfile version follows the Task version. E.g. the change to Taskfile
version `2` means that Task `v2.0.0` should be release to support it.
The `version:` key on Taskfile accepts a semver string, so either `2`, `2.0` or
`2.0.0` is accepted. You you choose to use `2.0` Task will not enable future
`2.1` features, but if you choose to use `2`, than any `2.x.x` features will be
`2.0.0` is accepted. If you choose to use `2.0` Task will not enable future
`2.1` features, but if you choose to use `2`, then any `2.x.x` features will be
available, but not `3.0.0+`.
## Version 1
@@ -35,7 +35,7 @@ The variable priority order was also different:
## Version 2.0
At version 2, we introduced the `version:` key, to allow us to envolve Task
At version 2, we introduced the `version:` key, to allow us to evolve Task
with new features without breaking existing Taskfiles. The new syntax is as
follows:
@@ -110,7 +110,7 @@ tasks:
prefix: server
```
From this version it's not also possible to ignore errors of a command or task
From this version it's also possible to ignore errors of a command or task
(check documentation [here][ignore_errors]):
```yaml
@@ -163,3 +163,63 @@ Please check the [documentation][includes]
[output]: usage.md#output-syntax
[ignore_errors]: usage.md#ignore-errors
[includes]: usage.md#including-other-taskfiles
## Version 3
These are some major changes done on `v3`:
- Task's output will now be colored
- Added support for `.env` like files
- Added `label:` setting to task so one can override how the task name
appear in the logs
- A global `method:` was added to allow setting the default method,
and Task's default changed to `checksum`
- Two magic variables were added when using `status:`: `CHECKSUM` and
`TIMESTAMP` which contains, respectively, the md5 checksum and greatest
modification timestamp of the files listed on `sources:`
- Also, the `TASK` variable is always available with the current task name
- CLI variables are always treated as global variables
- Added `dir:` option to `includes` to allow choosing on which directory an
included Taskfile will run:
```yaml
includes:
docs:
taskfile: ./docs
dir: ./docs
```
- Implemented short task syntax. All below syntaxes are equivalent:
```yaml
version: '3'
tasks:
print:
cmds:
- echo "Hello, World!"
```
```yaml
version: '3'
tasks:
print:
- echo "Hello, World!"
```
```yaml
version: '3'
tasks:
print: echo "Hello, World!"
```
- There was a major refactor on how variables are handled. They're now easier
to understand. The `expansions:` setting was removed as it became unncessary.
This is the order in which Task will process variables, each level can see
the variables set by the previous one and override those.
- Environment variables
- Global + CLI variables
- Call variables
- Task variables

View File

@@ -8,7 +8,7 @@ The example below allows compiling a Go app and uses [Minify][minify] to concat
and minify multiple CSS files into a single one.
```yaml
version: '2'
version: '3'
tasks:
build:
@@ -33,12 +33,14 @@ executable called must be available by the OS or in PATH.
If you omit a task name, "default" will be assumed.
## Environment
## Environment variables
### Task
You can use `env` to set custom environment variables for a specific task:
```yaml
version: '2'
version: '3'
tasks:
greet:
@@ -52,7 +54,7 @@ Additionally, you can set globally environment variables, that'll be available
to all tasks:
```yaml
version: '2'
version: '3'
env:
GREETING: Hey, there!
@@ -66,53 +68,37 @@ tasks:
> NOTE: `env` supports expansion and retrieving output from a shell command
> just like variables, as you can see on the [Variables](#variables) section.
## Operating System specific tasks
### .env files
If you add a `Taskfile_{{GOOS}}.yml` you can override or amend your Taskfile
based on the operating system.
You can also ask Task to include `.env` like files by using the `dotenv:`
setting:
Example:
Taskfile.yml:
```yaml
version: '2'
tasks:
build:
cmds:
- echo "default"
```
# .env
KEYNAME=VALUE
```
Taskfile_linux.yml:
```yaml
version: '2'
# Taskfile.yml
version: '3'
dotenv: ['.env']
tasks:
build:
greet:
cmds:
- echo "linux"
- echo "Using $KEYNAME"
```
Will print out `linux` and not `default`.
Keep in mind that the version of the files should match. Also, when redefining
a task the whole task is replaced, properties of the task are not merged.
It's also possible to have an OS specific `Taskvars.yml` file, like
`Taskvars_windows.yml`, `Taskvars_linux.yml`, or `Taskvars_darwin.yml`. See the
[variables section](#variables) below.
## Including other Taskfiles
> This feature is still experimental and may have bugs.
If you want to share tasks between different projects (Taskfiles), you can use
the importing mechanism to include other Taskfiles using the `includes` keyword:
```yaml
version: '2'
version: '3'
includes:
docs: ./documentation # will look for ./documentation/Taskfile.yml
@@ -124,6 +110,21 @@ namespace. So, you'd call `task docs:serve` to run the `serve` task from
`documentation/Taskfile.yml` or `task docker:build` to run the `build` task
from the `DockerTasks.yml` file.
### OS-specific Taskfiles
With `version: '2'`, task automatically includes any `Taskfile_{{OS}}.yml`
if it exists (for example: `Taskfile_windows.yml`, `Taskfile_linux.yml` or
`Taskfile_darwin.yml`). Since this behavior was a bit too implicit, it
was removed on version 3, but you still can have a similar behavior by
explicitly importing these files:
```yaml
version: '3'
includes:
build: ./Taskfile_{{OS}}.yml
```
### Directory of included Taskfile
By default, included Taskfile's tasks are ran in the current directory, even
@@ -153,7 +154,7 @@ located. But you can easily make the task run in another folder informing
`dir`:
```yaml
version: '2'
version: '3'
tasks:
serve:
@@ -167,11 +168,15 @@ If the directory doesn't exist, `task` creates it.
## Task dependencies
> Dependencies run in parallel, so dependencies of a task shouldn't depend one
> another. If you want to force tasks to run serially take a look at the
> [Calling Another Task](#calling-another-task) section below.
You may have tasks that depend on others. Just pointing them on `deps` will
make them run automatically before running the parent task:
```yaml
version: '2'
version: '3'
tasks:
build:
@@ -190,7 +195,7 @@ In the above example, `assets` will always run right before `build` if you run
A task can have only dependencies and no commands to group tasks together:
```yaml
version: '2'
version: '3'
tasks:
assets:
@@ -215,7 +220,7 @@ If you want to pass information to dependencies, you can do that the same
manner as you would to [call another task](#calling-another-task):
```yaml
version: '2'
version: '3'
tasks:
default:
@@ -239,7 +244,7 @@ often result in a faster build pipeline. But in some situations you may need
to call other tasks serially. In this case, just use the following syntax:
```yaml
version: '2'
version: '3'
tasks:
main-task:
@@ -261,19 +266,19 @@ Overriding variables in the called task is as simple as informing `vars`
attribute:
```yaml
version: '2'
version: '3'
tasks:
main-task:
greet:
vars:
RECIPIENT: '{{default "World" .RECIPIENT}}'
cmds:
- task: write-file
vars: {FILE: "hello.txt", CONTENT: "Hello!"}
- task: write-file
vars: {FILE: "world.txt", CONTENT: "World!"}
- echo "Hello, {{.RECIPIENT}}!"
write-file:
greet-pessimistically:
cmds:
- echo "{{.CONTENT}}" > {{.FILE}}
- task: greet
vars: {RECIPIENT: "Cruel World"}
```
The above syntax is also supported in `deps`.
@@ -290,7 +295,7 @@ If a task generates something, you can inform Task the source and generated
files, so Task will prevent to run them if not necessary.
```yaml
version: '2'
version: '3'
tasks:
build:
@@ -315,18 +320,18 @@ tasks:
- public/style.css
```
`sources` and `generates` can be files or file patterns. When both are given,
Task will compare the modification date/time of the files to determine if it's
`sources` and `generates` can be files or file patterns. When given,
Task will compare the checksum of the source files to determine if it's
necessary to run the task. If not, it will just print a message like
`Task "js" is up to date`.
If you prefer this check to be made by the content of the files, instead of
its timestamp, just set the `method` property to `checksum`.
You will probably want to ignore the `.task` folder in your `.gitignore` file
(It's there that Task stores the last checksum).
If you prefer this check to be made by the modification timestamp of the files,
instead of its checksum (content), just set the `method` property to `timestamp`.
```yaml
version: '2'
version: '3'
tasks:
build:
@@ -341,13 +346,17 @@ tasks:
> TIP: method `none` skips any validation and always run the task.
> NOTE: for the `checksum` (default) method to work, it's only necessary to
> inform the source files, but if you want to use the `timestamp` method, you
> also need to inform the generated files with `generates`.
### 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:
```yaml
version: '2'
version: '3'
tasks:
generate-files:
@@ -392,7 +401,7 @@ conditions to be _true_ you can use the `preconditions` stanza.
expansion and they SHOULD all return 0.
```yaml
version: '2'
version: '3'
tasks:
generate-files:
@@ -420,19 +429,20 @@ executing tasks that depend on it, a `precondition` will fail a task, along
with any other tasks that depend on it.
```yaml
version: '2'
version: '3'
tasks:
task_will_fail:
task-will-fail:
preconditions:
- sh: "exit 1"
task_will_also_fail:
task-will-also-fail:
deps:
- task_will_fail
- task-will-fail
task_will_still_fail:
task-will-still-fail:
cmds:
- task: task_will_fail
- task: task-will-fail
- echo "I will not run"
```
@@ -442,10 +452,9 @@ When doing interpolation of variables, Task will look for the below.
They are listed below in order of importance (e.g. most important first):
- Variables declared in the task definition
- Variables given while calling a task from another.
- Variables given while calling a task from another
(See [Calling another task](#calling-another-task) above)
- Variables declared in the `vars:` option in the `Taskfile`
- Variables available in the `Taskvars.yml` file
- Global variables (those declared in the `vars:` option in the Taskfile)
- Environment variables
Example of sending parameters with environment variables:
@@ -454,27 +463,20 @@ Example of sending parameters with environment variables:
$ TASK_VARIABLE=a-value task do-something
```
> TIP: A special variable `.TASK` is always available containg the task name.
> TIP: A special variable `.TASK` is always available containing the task name.
Since some shells don't support above syntax to set environment variables
(Windows) tasks also accepts a similar style when not in the beginning of
the command. Variables given in this form are only visible to the task called
right before.
the command.
```bash
$ task write-file FILE=file.txt "CONTENT=Hello, World!" print "MESSAGE=All done!"
```
If you want to set global variables using this syntax, give it before any task:
```bash
$ task OUTPUT=file.txt generate-file
```
Example of locally declared vars:
```yaml
version: '2'
version: '3'
tasks:
print-var:
@@ -487,7 +489,7 @@ tasks:
Example of global vars in a `Taskfile.yml`:
```yaml
version: '2'
version: '3'
vars:
GREETING: Hello from Taskfile!
@@ -498,38 +500,6 @@ tasks:
- echo "{{.GREETING}}"
```
Example of `Taskvars.yml` file:
```yaml
PROJECT_NAME: My Project
DEV_MODE: production
GIT_COMMIT: {sh: git log -n 1 --format=%h}
```
### Variables expansion
Variables are expanded 2 times by default. You can change that by setting the
`expansions:` option. Change that will be necessary if you compose many
variables together:
```yaml
version: '2'
expansions: 3
vars:
FOO: foo
BAR: bar
BAZ: baz
FOOBAR: "{{.FOO}}{{.BAR}}"
FOOBARBAZ: "{{.FOOBAR}}{{.BAZ}}"
tasks:
default:
cmds:
- echo "{{.FOOBARBAZ}}"
```
### Dynamic variables
The below syntax (`sh:` prop in a variable) is considered a dynamic variable.
@@ -537,7 +507,7 @@ The value will be treated as a command and the output assigned. If there is one
or more trailing newlines, the last newline will be trimmed.
```yaml
version: '2'
version: '3'
tasks:
build:
@@ -550,6 +520,27 @@ tasks:
This works for all types of variables.
## Forwarding CLI arguments to commands
If `--` is given in the CLI, all following paramaters are added to a
special `.CLI_ARGS` variable. This is useful to forward arguments to another
command.
The below example will run `yarn install`.
```bash
$ task yarn -- install
```
```yaml
version: '3'
tasks:
yarn:
cmds:
- yarn {{.CLI_ARGS}}
```
## Go's template engine
Task parse commands as [Go's template engine][gotemplate] before executing
@@ -559,7 +550,7 @@ 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:
```yaml
version: '2'
version: '3'
tasks:
print-date:
@@ -585,7 +576,7 @@ Task also adds the following functions:
Example:
```yaml
version: '2'
version: '3'
tasks:
print-os:
@@ -613,7 +604,7 @@ Running `task --list` (or `task -l`) lists all tasks with a description.
The following Taskfile:
```yaml
version: '2'
version: '3'
tasks:
build:
@@ -644,11 +635,11 @@ would print the following output:
## Display summary of task
Running `task --summary task-name` will show a summary of a task
Running `task --summary task-name` will show a summary of a task.
The following Taskfile:
```yaml
version: '2'
version: '3'
tasks:
release:
@@ -656,7 +647,7 @@ tasks:
summary: |
Release your project to github
It will build your project before starting the release it.
It will build your project before starting the release.
Please make sure that you have set GITHUB_TOKEN before starting.
cmds:
- your-release-tool
@@ -673,7 +664,7 @@ task: release
Release your project to github
It will build your project before starting the release it.
It will build your project before starting the release.
Please make sure that you have set GITHUB_TOKEN before starting.
dependencies:
@@ -687,13 +678,37 @@ If the task does not have a summary or a description, a warning is printed.
Please note: *showing the summary will not execute the command*.
## Overriding task name
Sometimes you may want to override the task name print on summary, up-to-date
messages to STDOUT, etc. In this case you can just set `label:`, which can also
be interpolated with variables:
```yaml
version: '3'
tasks:
default:
- task: print
vars:
MESSAGE: hello
- task: print
vars:
MESSAGE: world
print:
label: 'print-{{.MESSAGE}}'
cmds:
- echo "{{.MESSAGE}}"
```
## Silent mode
Silent mode disables echoing of commands before Task runs it.
For the following Taskfile:
```yaml
version: '2'
version: '3'
tasks:
echo:
@@ -719,7 +734,7 @@ There are four ways to enable silent mode:
* At command level:
```yaml
version: '2'
version: '3'
tasks:
echo:
@@ -731,7 +746,7 @@ tasks:
* At task level:
```yaml
version: '2'
version: '3'
tasks:
echo:
@@ -743,7 +758,7 @@ tasks:
* Globally at Taskfile level:
```yaml
version: '2'
version: '3'
silent: true
@@ -758,7 +773,7 @@ tasks:
If you want to suppress STDOUT instead, just redirect a command to `/dev/null`:
```yaml
version: '2'
version: '3'
tasks:
echo:
@@ -777,7 +792,7 @@ You have the option to ignore errors during command execution.
Given the following Taskfile:
```yaml
version: '2'
version: '3'
tasks:
echo:
@@ -790,7 +805,7 @@ Task will abort the execution after running `exit 1` because the status code `1`
However it is possible to continue with execution using `ignore_error`:
```yaml
version: '2'
version: '3'
tasks:
echo:
@@ -821,7 +836,7 @@ options you can choose:
To choose another one, just set it to root in the Taskfile:
```yaml
version: '2'
version: '3'
output: 'group'
@@ -838,7 +853,7 @@ tasks:
with the `prefix:` attribute:
```yaml
version: '2'
version: '3'
output: prefixed
@@ -871,7 +886,7 @@ $ task default
## Short task syntax
Starting on Task v3, you can now write tasks with a shorter syntax if they
have the default settings (e.g. no custom `env:`, `vars:`, `silent:` , etc):
have the default settings (e.g. no custom `env:`, `vars:`, `desc:`, `silent:` , etc):
```yaml
version: '3'
@@ -879,16 +894,16 @@ version: '3'
tasks:
build: go build -v -o ./app{{exeExt}} .
build:
run:
- task: build
- ./app{{exeExt}} -h localhost -p 8080
```
## Watch tasks
If you give a `--watch` or `-w` argument, task will watch for file changes
With the flags `--watch` or `-w` task will watch for file changes
and run the task again. This requires the `sources` attribute to be given,
so task know which files to watch.
so task knows which files to watch.
[gotemplate]: https://golang.org/pkg/text/template/
[minify]: https://github.com/tdewolff/minify/tree/master/cmd/minify

22
go.mod
View File

@@ -1,16 +1,16 @@
module github.com/go-task/task/v2
module github.com/go-task/task/v3
require (
github.com/fatih/color v1.7.0
github.com/go-task/slim-sprig v0.0.0-20191103011349-ba7d6a531428
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-zglob v0.0.1
github.com/radovskyb/watcher v1.0.5
github.com/spf13/pflag v1.0.3
github.com/stretchr/testify v1.4.0
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
gopkg.in/yaml.v2 v2.2.2
mvdan.cc/sh/v3 v3.0.2
github.com/fatih/color v1.10.0
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
github.com/joho/godotenv v1.3.0
github.com/mattn/go-zglob v0.0.3
github.com/radovskyb/watcher v1.0.7
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
mvdan.cc/sh/v3 v3.2.4
)
go 1.13

83
go.sum
View File

@@ -1,57 +1,60 @@
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/go-task/slim-sprig v0.0.0-20191103011349-ba7d6a531428 h1:62JQa/NJVTX0I2G7GlVBbDl/8f6kmUQCoVf8R3fHvqE=
github.com/go-task/slim-sprig v0.0.0-20191103011349-ba7d6a531428/go.mod h1:BU9jUCGL9r2yojTEkwli1J2X4GmnOJPAsUXFtPud7Mg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
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/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/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/pkg/diff v0.0.0-20190930165518-531926345625/go.mod h1:kFj35MyHn14a6pIgWhm46KVjJr5CHys3eEYxkuKD1EI=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To=
github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/radovskyb/watcher v1.0.5 h1:wqt7gb+HjGacvFoLTKeT44C+XVPxu7bvHvKT1IvZ7rw=
github.com/radovskyb/watcher v1.0.5/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
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.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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-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-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM=
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407 h1:5zh5atpUEdIc478E/ebrIaHLKcfVvG6dL/fGv7BcMoM=
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
mvdan.cc/editorconfig v0.1.1-0.20191109213504-890940e3f00e/go.mod h1:Ge4atmRUYqueGppvJ7JNrtqpqokoJEFxYbP0Z+WeKS8=
mvdan.cc/sh/v3 v3.0.2 h1:NU+UpTZHRdIZ9igRo8zi/7+5/NpetYlhlyDhz1/AdMM=
mvdan.cc/sh/v3 v3.0.2/go.mod h1:rBIndNJFYPp8xSppiZcGIk6B5d1g3OEARxEaXjPxwVI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mvdan.cc/editorconfig v0.1.1-0.20200121172147-e40951bde157/go.mod h1:Ge4atmRUYqueGppvJ7JNrtqpqokoJEFxYbP0Z+WeKS8=
mvdan.cc/sh/v3 v3.2.4 h1:+fZaWcXWRjYAvqzEKoDhDM3DkxdDUykU2iw0VMKFe9s=
mvdan.cc/sh/v3 v3.2.4/go.mod h1:fPQmabBpREM/XQ9YXSU5ZFZ/Sm+PmKP9/vkFHgYKJEI=

View File

@@ -5,8 +5,8 @@ import (
"sort"
"text/tabwriter"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/taskfile"
)
// PrintTasksHelp prints help os tasks that have a description
@@ -21,7 +21,7 @@ func (e *Executor) PrintTasksHelp() {
// Format in tab-separated columns with a tab stop of 8.
w := tabwriter.NewWriter(e.Stdout, 0, 8, 0, '\t', 0)
for _, task := range tasks {
fmt.Fprintf(w, "* %s: \t%s\n", task.Task, task.Desc)
fmt.Fprintf(w, "* %s: \t%s\n", task.Name(), task.Desc)
}
w.Flush()
}
@@ -30,7 +30,7 @@ func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
for _, task := range e.Taskfile.Tasks {
if task.Desc != "" {
compiledTask, err := e.CompiledTask(taskfile.Call{Task: task.Task})
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
if err == nil {
task = compiledTask
}

View File

@@ -1,6 +1,6 @@
#!/bin/sh
set -e
# Code generated by godownloader on 2018-04-07T17:47:38Z. DO NOT EDIT.
# Code generated by godownloader on 2021-01-12T13:40:40Z. DO NOT EDIT.
#
usage() {
@@ -27,11 +27,12 @@ parse_args() {
# over-ridden by flag below
BINDIR=${BINDIR:-./bin}
while getopts "b:dh?" arg; do
while getopts "b:dh?x" arg; do
case "$arg" in
b) BINDIR="$OPTARG" ;;
d) log_set_priority 10 ;;
h | \?) usage "$0" ;;
x) set -x ;;
esac
done
shift $((OPTIND - 1))
@@ -42,46 +43,41 @@ parse_args() {
# network, either nothing will happen or will syntax error
# out preventing half-done work
execute() {
tmpdir=$(mktmpdir)
tmpdir=$(mktemp -d)
log_debug "downloading files into ${tmpdir}"
http_download "${tmpdir}/${TARBALL}" "${TARBALL_URL}"
http_download "${tmpdir}/${CHECKSUM}" "${CHECKSUM_URL}"
hash_sha256_verify "${tmpdir}/${TARBALL}" "${tmpdir}/${CHECKSUM}"
srcdir="${tmpdir}"
(cd "${tmpdir}" && untar "${TARBALL}")
install -d "${BINDIR}"
for binexe in "task" ; do
test ! -d "${BINDIR}" && install -d "${BINDIR}"
for binexe in $BINARIES; do
if [ "$OS" = "windows" ]; then
binexe="${binexe}.exe"
fi
install "${srcdir}/${binexe}" "${BINDIR}/"
log_info "installed ${BINDIR}/${binexe}"
done
rm -rf "${tmpdir}"
}
is_supported_platform() {
platform=$1
found=1
case "$platform" in
windows/386) found=0 ;;
windows/amd64) found=0 ;;
darwin/386) found=0 ;;
darwin/amd64) found=0 ;;
linux/386) found=0 ;;
linux/amd64) found=0 ;;
get_binaries() {
case "$PLATFORM" in
darwin/amd64) BINARIES="task" ;;
darwin/arm64) BINARIES="task" ;;
darwin/armv6) BINARIES="task" ;;
linux/386) BINARIES="task" ;;
linux/amd64) BINARIES="task" ;;
linux/arm64) BINARIES="task" ;;
linux/armv6) BINARIES="task" ;;
windows/386) BINARIES="task" ;;
windows/amd64) BINARIES="task" ;;
windows/arm64) BINARIES="task" ;;
windows/armv6) BINARIES="task" ;;
*)
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
exit 1
;;
esac
case "$platform" in
darwin/386) found=1 ;;
esac
return $found
}
check_platform() {
if is_supported_platform "$PLATFORM"; then
# optional logging goes here
true
else
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
exit 1
fi
}
tag_to_version() {
if [ -z "${TAG}" ]; then
@@ -99,8 +95,8 @@ tag_to_version() {
VERSION=${TAG#v}
}
adjust_format() {
# change format (tar.gz or zip) based on ARCH
case ${ARCH} in
# change format (tar.gz or zip) based on OS
case ${OS} in
windows) FORMAT=zip ;;
esac
true
@@ -174,7 +170,9 @@ log_crit() {
uname_os() {
os=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$os" in
msys_nt) os="windows" ;;
cygwin_nt*) os="windows" ;;
mingw*) os="windows" ;;
msys_nt*) os="windows" ;;
esac
echo "$os"
}
@@ -186,9 +184,9 @@ uname_arch() {
i686) arch="386" ;;
i386) arch="386" ;;
aarch64) arch="arm64" ;;
armv5*) arch="arm5" ;;
armv6*) arch="arm6" ;;
armv7*) arch="arm7" ;;
armv5*) arch="armv5" ;;
armv6*) arch="armv6" ;;
armv7*) arch="armv7" ;;
esac
echo ${arch}
}
@@ -234,8 +232,8 @@ uname_arch_check() {
untar() {
tarball=$1
case "${tarball}" in
*.tar.gz | *.tgz) tar -xzf "${tarball}" ;;
*.tar) tar -xf "${tarball}" ;;
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;;
*.tar) tar --no-same-owner -xf "${tarball}" ;;
*.zip) unzip "${tarball}" ;;
*)
log_err "untar unknown archive format for ${tarball}"
@@ -243,11 +241,6 @@ untar() {
;;
esac
}
mktmpdir() {
test -z "$TMPDIR" && TMPDIR="$(mktemp -d)"
mkdir -p "${TMPDIR}"
echo "${TMPDIR}"
}
http_download_curl() {
local_file=$1
source_url=$2
@@ -368,7 +361,7 @@ uname_arch_check "$ARCH"
parse_args "$@"
check_platform
get_binaries
tag_to_version

View File

@@ -1,47 +0,0 @@
package args
import (
"strings"
"github.com/go-task/task/v2/internal/taskfile"
)
// Parse parses command line argument: tasks and vars of each task
func Parse(args ...string) ([]taskfile.Call, taskfile.Vars) {
var calls []taskfile.Call
var globals taskfile.Vars
for _, arg := range args {
if !strings.Contains(arg, "=") {
calls = append(calls, taskfile.Call{Task: arg})
continue
}
if len(calls) < 1 {
if globals == nil {
globals = taskfile.Vars{}
}
name, value := splitVar(arg)
globals[name] = taskfile.Var{Static: value}
} else {
if calls[len(calls)-1].Vars == nil {
calls[len(calls)-1].Vars = make(taskfile.Vars)
}
name, value := splitVar((arg))
calls[len(calls)-1].Vars[name] = taskfile.Var{Static: value}
}
}
if len(calls) == 0 {
calls = append(calls, taskfile.Call{Task: "default"})
}
return calls, globals
}
func splitVar(s string) (string, string) {
pair := strings.SplitN(s, "=", 2)
return pair[0], pair[1]
}

View File

@@ -1,98 +0,0 @@
package args_test
import (
"fmt"
"testing"
"github.com/go-task/task/v2/internal/args"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/stretchr/testify/assert"
)
func TestArgs(t *testing.T) {
tests := []struct {
Args []string
ExpectedCalls []taskfile.Call
ExpectedGlobals taskfile.Vars
}{
{
Args: []string{"task-a", "task-b", "task-c"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
},
},
{
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
ExpectedCalls: []taskfile.Call{
{
Task: "task-a",
Vars: taskfile.Vars{
"FOO": taskfile.Var{Static: "bar"},
},
},
{Task: "task-b"},
{
Task: "task-c",
Vars: taskfile.Vars{
"BAR": taskfile.Var{Static: "baz"},
"BAZ": taskfile.Var{Static: "foo"},
},
},
},
},
{
Args: []string{"task-a", "CONTENT=with some spaces"},
ExpectedCalls: []taskfile.Call{
{
Task: "task-a",
Vars: taskfile.Vars{
"CONTENT": taskfile.Var{Static: "with some spaces"},
},
},
},
},
{
Args: []string{"FOO=bar", "task-a", "task-b"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
},
ExpectedGlobals: taskfile.Vars{
"FOO": {Static: "bar"},
},
},
{
Args: nil,
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{"FOO=bar", "BAR=baz"},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
ExpectedGlobals: taskfile.Vars{
"FOO": {Static: "bar"},
"BAR": {Static: "baz"},
},
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, globals := args.Parse(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls)
assert.Equal(t, test.ExpectedGlobals, globals)
})
}
}

View File

@@ -1,12 +1,14 @@
package compiler
import (
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v3/taskfile"
)
// Compiler handles compilation of a task before its execution.
// E.g. variable merger, template processing, etc.
type Compiler interface {
GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error)
HandleDynamicVar(v taskfile.Var) (string, error)
GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
HandleDynamicVar(v taskfile.Var, dir string) (string, error)
ResetCache()
}

View File

@@ -4,21 +4,17 @@ import (
"os"
"strings"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v3/taskfile"
)
// GetEnviron the all return all environment variables encapsulated on a
// taskfile.Vars
func GetEnviron() taskfile.Vars {
var (
env = os.Environ()
m = make(taskfile.Vars, len(env))
)
for _, e := range env {
func GetEnviron() *taskfile.Vars {
m := &taskfile.Vars{}
for _, e := range os.Environ() {
keyVal := strings.SplitN(e, "=", 2)
key, val := keyVal[0], keyVal[1]
m[key] = taskfile.Var{Static: val}
m.Set(key, taskfile.Var{Static: val})
}
return m
}

View File

@@ -7,11 +7,11 @@ import (
"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"
"github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
)
var _ compiler.Compiler = &CompilerV2{}
@@ -19,8 +19,8 @@ var _ compiler.Compiler = &CompilerV2{}
type CompilerV2 struct {
Dir string
Taskvars taskfile.Vars
TaskfileVars taskfile.Vars
Taskvars *taskfile.Vars
TaskfileVars *taskfile.Vars
Expansions int
@@ -30,16 +30,25 @@ type CompilerV2 struct {
muDynamicCache sync.Mutex
}
// FastGetVariables is a no-op on v2
func (c *CompilerV2) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
return c.GetVariables(t, call)
}
// GetVariables returns fully resolved variables following the priority order:
// 1. Task variables
// 2. Call variables
// 3. Taskfile variables
// 4. Taskvars file variables
// 5. Environment variables
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error) {
vr := varResolver{c: c, vars: compiler.GetEnviron()}
vr.vars["TASK"] = taskfile.Var{Static: t.Task}
for _, vars := range []taskfile.Vars{c.Taskvars, c.TaskfileVars, call.Vars, t.Vars} {
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
vr := varResolver{
c: c,
vars: compiler.GetEnviron(),
}
vr.vars.Set("TASK", taskfile.Var{Static: t.Task})
for _, vars := range []*taskfile.Vars{c.Taskvars, c.TaskfileVars, call.Vars, t.Vars} {
for i := 0; i < c.Expansions; i++ {
vr.merge(vars)
}
@@ -49,31 +58,32 @@ func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (taskfil
type varResolver struct {
c *CompilerV2
vars taskfile.Vars
vars *taskfile.Vars
err error
}
func (vr *varResolver) merge(vars taskfile.Vars) {
func (vr *varResolver) merge(vars *taskfile.Vars) {
if vr.err != nil {
return
}
tr := templater.Templater{Vars: vr.vars}
for k, v := range vars {
vars.Range(func(k string, v taskfile.Var) error {
v = taskfile.Var{
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),
}
static, err := vr.c.HandleDynamicVar(v)
static, err := vr.c.HandleDynamicVar(v, "")
if err != nil {
vr.err = err
return
return err
}
vr.vars[k] = taskfile.Var{Static: static}
}
vr.vars.Set(k, taskfile.Var{Static: static})
return nil
})
vr.err = tr.Err()
}
func (c *CompilerV2) HandleDynamicVar(v taskfile.Var) (string, error) {
func (c *CompilerV2) HandleDynamicVar(v taskfile.Var, _ string) (string, error) {
if v.Static != "" || v.Sh == "" {
return v.Static, nil
}
@@ -91,7 +101,6 @@ func (c *CompilerV2) HandleDynamicVar(v taskfile.Var) (string, error) {
var stdout bytes.Buffer
opts := &execext.RunCommandOptions{
Command: v.Sh,
Dir: c.Dir,
Stdout: &stdout,
Stderr: c.Logger.Stderr,
}
@@ -108,3 +117,11 @@ func (c *CompilerV2) HandleDynamicVar(v taskfile.Var) (string, error) {
return result, nil
}
// ResetCache clear the dymanic variables cache
func (c *CompilerV2) ResetCache() {
c.muDynamicCache.Lock()
defer c.muDynamicCache.Unlock()
c.dynamicCache = nil
}

View File

@@ -0,0 +1,147 @@
package v3
import (
"bytes"
"context"
"fmt"
"path/filepath"
"strings"
"sync"
"github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
)
var _ compiler.Compiler = &CompilerV3{}
type CompilerV3 struct {
Dir string
TaskfileEnv *taskfile.Vars
TaskfileVars *taskfile.Vars
Logger *logger.Logger
dynamicCache map[string]string
muDynamicCache sync.Mutex
}
func (c *CompilerV3) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
return c.getVariables(t, call, true)
}
func (c *CompilerV3) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
return c.getVariables(t, call, false)
}
func (c *CompilerV3) getVariables(t *taskfile.Task, call taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
result := compiler.GetEnviron()
result.Set("TASK", taskfile.Var{Static: t.Task})
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
return func(k string, v taskfile.Var) error {
tr := templater.Templater{Vars: result, RemoveNoValue: true}
if !evaluateShVars {
result.Set(k, taskfile.Var{Static: tr.Replace(v.Static)})
return nil
}
v = taskfile.Var{
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),
Dir: v.Dir,
}
if err := tr.Err(); err != nil {
return err
}
static, err := c.HandleDynamicVar(v, dir)
if err != nil {
return err
}
result.Set(k, taskfile.Var{Static: static})
return nil
}
}
rangeFunc := getRangeFunc(c.Dir)
if err := c.TaskfileEnv.Range(rangeFunc); err != nil {
return nil, err
}
if err := c.TaskfileVars.Range(rangeFunc); err != nil {
return nil, err
}
if err := call.Vars.Range(rangeFunc); err != nil {
return nil, err
}
// NOTE(@andreynering): We're manually joining these paths here because
// this is the raw task, not the compiled one.
tr := templater.Templater{Vars: result, RemoveNoValue: true}
dir := tr.Replace(t.Dir)
if err := tr.Err(); err != nil {
return nil, err
}
if !filepath.IsAbs(dir) {
dir = filepath.Join(c.Dir, dir)
}
taskRangeFunc := getRangeFunc(dir)
if err := t.Vars.Range(taskRangeFunc); err != nil {
return nil, err
}
return result, nil
}
func (c *CompilerV3) HandleDynamicVar(v taskfile.Var, dir string) (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
}
// NOTE(@andreynering): If a var have a specific dir, use this instead
if v.Dir != "" {
dir = v.Dir
}
var stdout bytes.Buffer
opts := &execext.RunCommandOptions{
Command: v.Sh,
Dir: 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(logger.Magenta, `task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
return result, nil
}
// ResetCache clear the dymanic variables cache
func (c *CompilerV3) ResetCache() {
c.muDynamicCache.Lock()
defer c.muDynamicCache.Unlock()
c.dynamicCache = nil
}

View File

@@ -46,16 +46,10 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
}
r, err := interp.New(
interp.Params("-e"),
interp.Dir(opts.Dir),
interp.Env(expand.ListEnviron(environ...)),
interp.OpenHandler(func(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
if path == "/dev/null" {
return devNull{}, nil
}
return interp.DefaultOpenHandler()(ctx, path, flag, perm)
}),
interp.OpenHandler(openHandler),
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
)
if err != nil {
@@ -86,3 +80,10 @@ func Expand(s string) (string, error) {
}
return "", nil
}
func openHandler(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
if path == "/dev/null" {
return devNull{}, nil
}
return interp.DefaultOpenHandler()(ctx, path, flag, perm)
}

View File

@@ -6,9 +6,9 @@ import (
"io"
"testing"
"github.com/go-task/task/v2/internal/output"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3/internal/output"
)
func TestInterleaved(t *testing.T) {

View File

@@ -14,7 +14,8 @@ import (
// Checksum validades if a task is up to date by calculating its source
// files checksum
type Checksum struct {
Dir string
BaseDir string
TaskDir string
Task string
Sources []string
Generates []string
@@ -32,7 +33,7 @@ func (c *Checksum) IsUpToDate() (bool, error) {
data, _ := ioutil.ReadFile(checksumFile)
oldMd5 := strings.TrimSpace(string(data))
sources, err := globs(c.Dir, c.Sources)
sources, err := globs(c.TaskDir, c.Sources)
if err != nil {
return false, err
}
@@ -43,7 +44,7 @@ func (c *Checksum) IsUpToDate() (bool, error) {
}
if !c.Dry {
_ = os.MkdirAll(filepath.Join(c.Dir, ".task", "checksum"), 0755)
_ = os.MkdirAll(filepath.Join(c.BaseDir, ".task", "checksum"), 0755)
if err = ioutil.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
return false, err
}
@@ -52,7 +53,7 @@ func (c *Checksum) IsUpToDate() (bool, error) {
if len(c.Generates) > 0 {
// For each specified 'generates' field, check whether the files actually exist
for _, g := range c.Generates {
generates, err := glob(c.Dir, g)
generates, err := glob(c.TaskDir, g)
if os.IsNotExist(err) {
return false, nil
}
@@ -95,6 +96,9 @@ func (c *Checksum) Value() (interface{}, error) {
// OnError implements the Checker interface
func (c *Checksum) OnError() error {
if len(c.Sources) == 0 {
return nil
}
return os.Remove(c.checksumFilePath())
}
@@ -104,7 +108,7 @@ func (*Checksum) Kind() string {
}
func (c *Checksum) checksumFilePath() string {
return filepath.Join(c.Dir, ".task", "checksum", c.normalizeFilename(c.Task))
return filepath.Join(c.BaseDir, ".task", "checksum", c.normalizeFilename(c.Task))
}
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")

View File

@@ -5,9 +5,9 @@ import (
"path/filepath"
"sort"
"github.com/go-task/task/v2/internal/execext"
"github.com/mattn/go-zglob"
"github.com/go-task/task/v3/internal/execext"
)
func globs(dir string, globs []string) ([]string, error) {

View File

@@ -3,8 +3,8 @@ package summary
import (
"strings"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/taskfile"
)
func PrintTasks(l *logger.Logger, t *taskfile.Taskfile, c []taskfile.Call) {
@@ -56,7 +56,7 @@ func printTaskSummary(l *logger.Logger, t *taskfile.Task) {
}
func printTaskName(l *logger.Logger, t *taskfile.Task) {
l.Outf(logger.Default, "task: %s", t.Task)
l.Outf(logger.Default, "task: %s", t.Name())
l.Outf(logger.Default, "")
}

View File

@@ -5,11 +5,11 @@ import (
"strings"
"testing"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/summary"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/summary"
"github.com/go-task/task/v3/taskfile"
)
func TestPrintsDependenciesIfPresent(t *testing.T) {

View File

@@ -1,40 +0,0 @@
package taskfile
import "errors"
var (
// ErrCantUnmarshalIncludedTaskfile is returned for invalid var YAML.
ErrCantUnmarshalIncludedTaskfile = errors.New("task: can't unmarshal included value")
)
// IncludedTaskfile represents information about included tasksfile
type IncludedTaskfile struct {
Taskfile string
Dir string
AdvancedImport bool
}
// IncludedTaskfiles represents information about included tasksfiles
type IncludedTaskfiles = map[string]IncludedTaskfile
// UnmarshalYAML implements yaml.Unmarshaler interface
func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
var str string
if err := unmarshal(&str); err == nil {
it.Taskfile = str
return nil
}
var includedTaskfile struct {
Taskfile string
Dir string
}
if err := unmarshal(&includedTaskfile); err == nil {
it.Dir = includedTaskfile.Dir
it.Taskfile = includedTaskfile.Taskfile
it.AdvancedImport = true
return nil
}
return ErrCantUnmarshalIncludedTaskfile
}

View File

@@ -1,91 +0,0 @@
package read
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"github.com/go-task/task/v2/internal/taskfile"
"gopkg.in/yaml.v2"
)
var (
// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
)
// Taskfile reads a Taskfile for a given directory
func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
path := filepath.Join(dir, entrypoint)
if _, err := os.Stat(path); err != nil {
return nil, fmt.Errorf(`task: No Taskfile found on "%s". Use "task --init" to create a new one`, path)
}
t, err := readTaskfile(path)
if err != nil {
return nil, err
}
for namespace, includedTask := range t.Includes {
if filepath.IsAbs(includedTask.Taskfile) {
path = includedTask.Taskfile
} else {
path = filepath.Join(dir, includedTask.Taskfile)
}
info, err := os.Stat(path)
if err != nil {
return nil, err
}
if info.IsDir() {
path = filepath.Join(path, "Taskfile.yml")
}
includedTaskfile, err := readTaskfile(path)
if err != nil {
return nil, err
}
if len(includedTaskfile.Includes) > 0 {
return nil, ErrIncludedTaskfilesCantHaveIncludes
}
if includedTask.AdvancedImport {
for _, task := range includedTaskfile.Tasks {
if !filepath.IsAbs(task.Dir) {
task.Dir = filepath.Join(includedTask.Dir, task.Dir)
}
}
}
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
return nil, err
}
}
path = filepath.Join(dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
if _, err = os.Stat(path); err == nil {
osTaskfile, err := readTaskfile(path)
if err != nil {
return nil, err
}
if err = taskfile.Merge(t, osTaskfile); err != nil {
return nil, err
}
}
for name, task := range t.Tasks {
task.Task = name
}
return t, nil
}
func readTaskfile(file string) (*taskfile.Taskfile, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
var t taskfile.Taskfile
return &t, yaml.NewDecoder(f).Decode(&t)
}

View File

@@ -1,64 +0,0 @@
package taskfile
import (
"errors"
"strings"
)
var (
// ErrCantUnmarshalVar is returned for invalid var YAML.
ErrCantUnmarshalVar = errors.New("task: can't unmarshal var value")
)
// Vars is a string[string] variables map.
type Vars map[string]Var
// ToCacheMap converts Vars to a map containing only the static
// variables
func (vs Vars) ToCacheMap() (m map[string]interface{}) {
m = make(map[string]interface{}, len(vs))
for k, v := range vs {
if v.Sh != "" {
// Dynamic variable is not yet resolved; trigger
// <no value> to be used in templates.
continue
}
if v.Live != nil {
m[k] = v.Live
} else {
m[k] = v.Static
}
}
return
}
// Var represents either a static or dynamic variable.
type Var struct {
Static string
Live interface{}
Sh string
}
// UnmarshalYAML implements yaml.Unmarshaler interface.
func (v *Var) UnmarshalYAML(unmarshal func(interface{}) error) error {
var str string
if err := unmarshal(&str); err == nil {
if strings.HasPrefix(str, "$") {
v.Sh = strings.TrimPrefix(str, "$")
} else {
v.Static = str
}
return nil
}
var sh struct {
Sh string
}
if err := unmarshal(&sh); err == nil {
v.Sh = sh.Sh
return nil
}
return ErrCantUnmarshalVar
}

View File

@@ -2,9 +2,10 @@ package templater
import (
"bytes"
"strings"
"text/template"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v3/taskfile"
)
// Templater is a help struct that allow us to call "replaceX" funcs multiple
@@ -12,7 +13,8 @@ import (
// happen will be assigned to r.err, and consecutive calls to funcs will just
// return the zero value.
type Templater struct {
Vars taskfile.Vars
Vars *taskfile.Vars
RemoveNoValue bool
cacheMap map[string]interface{}
err error
@@ -42,6 +44,9 @@ func (r *Templater) Replace(str string) string {
r.err = err
return ""
}
if r.RemoveNoValue {
return strings.ReplaceAll(b.String(), "<no value>", "")
}
return b.String()
}
@@ -57,20 +62,22 @@ func (r *Templater) ReplaceSlice(strs []string) []string {
return new
}
func (r *Templater) ReplaceVars(vars taskfile.Vars) taskfile.Vars {
if r.err != nil || len(vars) == 0 {
func (r *Templater) ReplaceVars(vars *taskfile.Vars) *taskfile.Vars {
if r.err != nil || vars.Len() == 0 {
return nil
}
new := make(taskfile.Vars, len(vars))
for k, v := range vars {
new[k] = taskfile.Var{
var new taskfile.Vars
vars.Range(func(k string, v taskfile.Var) error {
new.Set(k, taskfile.Var{
Static: r.Replace(v.Static),
Live: v.Live,
Sh: r.Replace(v.Sh),
}
}
return new
})
return nil
})
return &new
}
func (r *Templater) Err() error {

View File

@@ -4,9 +4,9 @@ import (
"context"
"errors"
"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/v3/internal/execext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/taskfile"
)
var (

View File

@@ -4,10 +4,10 @@ import (
"context"
"fmt"
"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/taskfile"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/status"
"github.com/go-task/task/v3/taskfile"
)
// Status returns an error if any the of given tasks is not up-to-date
@@ -22,7 +22,7 @@ func (e *Executor) Status(ctx context.Context, calls ...taskfile.Call) error {
return err
}
if !isUpToDate {
return fmt.Errorf(`task: Task "%s" is not up-to-date`, t.Task)
return fmt.Errorf(`task: Task "%s" is not up-to-date`, t.Name())
}
}
return nil
@@ -76,8 +76,9 @@ func (e *Executor) timestampChecker(t *taskfile.Task) status.Checker {
func (e *Executor) checksumChecker(t *taskfile.Task) status.Checker {
return &status.Checksum{
Dir: t.Dir,
Task: t.Task,
BaseDir: e.Dir,
TaskDir: t.Dir,
Task: t.Name(),
Sources: t.Sources,
Generates: t.Generates,
Dry: e.Dry,

127
task.go
View File

@@ -6,18 +6,18 @@ import (
"fmt"
"io"
"os"
"strconv"
"sync"
"sync/atomic"
"github.com/go-task/task/v2/internal/compiler"
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/logger"
"github.com/go-task/task/v2/internal/output"
"github.com/go-task/task/v2/internal/summary"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v2/internal/taskfile/read"
"github.com/go-task/task/v3/internal/compiler"
compilerv2 "github.com/go-task/task/v3/internal/compiler/v2"
compilerv3 "github.com/go-task/task/v3/internal/compiler/v3"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/internal/summary"
"github.com/go-task/task/v3/taskfile"
"github.com/go-task/task/v3/taskfile/read"
"golang.org/x/sync/errgroup"
)
@@ -32,16 +32,17 @@ const (
type Executor struct {
Taskfile *taskfile.Taskfile
Dir string
Entrypoint string
Force bool
Watch bool
Verbose bool
Silent bool
Dry bool
Summary bool
Parallel bool
Color bool
Dir string
Entrypoint string
Force bool
Watch bool
Verbose bool
Silent bool
Dry bool
Summary bool
Parallel bool
Color bool
Concurrency int
Stdin io.Reader
Stdout io.Writer
@@ -52,10 +53,11 @@ type Executor struct {
Output output.Output
OutputStyle string
taskvars taskfile.Vars
taskvars *taskfile.Vars
taskCallCount map[string]*int32
mkdirMutexMap map[string]*sync.Mutex
concurrencySemaphore chan struct{}
taskCallCount map[string]*int32
mkdirMutexMap map[string]*sync.Mutex
}
// Run runs Task
@@ -71,7 +73,7 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
if e.Summary {
for i, c := range calls {
compiledTask, err := e.CompiledTask(c)
compiledTask, err := e.FastCompiledTask(c)
if err != nil {
return nil
}
@@ -110,11 +112,19 @@ func (e *Executor) Setup() error {
if err != nil {
return err
}
e.taskvars, err = read.Taskvars(e.Dir)
v, err := e.Taskfile.ParsedVersion()
if err != nil {
return err
}
if v < 3.0 {
e.taskvars, err = read.Taskvars(e.Dir)
if err != nil {
return err
}
}
if e.Stdin == nil {
e.Stdin = os.Stdin
}
@@ -131,11 +141,6 @@ func (e *Executor) Setup() error {
Color: e.Color,
}
v, err := strconv.ParseFloat(e.Taskfile.Version, 64)
if err != nil {
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`)
}
@@ -154,20 +159,32 @@ func (e *Executor) Setup() error {
e.Logger.Color = false
}
e.Compiler = &compilerv2.CompilerV2{
Dir: e.Dir,
Taskvars: e.taskvars,
TaskfileVars: e.Taskfile.Vars,
Expansions: e.Taskfile.Expansions,
Logger: e.Logger,
if v < 3 {
e.Compiler = &compilerv2.CompilerV2{
Dir: e.Dir,
Taskvars: e.taskvars,
TaskfileVars: e.Taskfile.Vars,
Expansions: e.Taskfile.Expansions,
Logger: e.Logger,
}
} else {
e.Compiler = &compilerv3.CompilerV3{
Dir: e.Dir,
TaskfileEnv: e.Taskfile.Env,
TaskfileVars: e.Taskfile.Vars,
Logger: e.Logger,
}
}
if v < 2.1 && e.Taskfile.Output != "" {
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
}
if v < 2.2 && len(e.Taskfile.Includes) > 0 {
if v < 2.2 && e.Taskfile.Includes.Len() > 0 {
return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`)
}
if v >= 3.0 && e.Taskfile.Expansions > 2 {
return fmt.Errorf(`task: The "expansions" setting is not available anymore on v3.0`)
}
if e.OutputStyle != "" {
e.Taskfile.Output = e.OutputStyle
@@ -215,10 +232,14 @@ func (e *Executor) Setup() error {
}
if v < 3 {
for _, taskfile := range e.Taskfile.Includes {
err := e.Taskfile.Includes.Range(func(_ string, taskfile taskfile.IncludedTaskfile) error {
if taskfile.AdvancedImport {
return errors.New(`task: Import with additional parameters is only available starting on Taskfile version v3`)
}
return nil
})
if err != nil {
return err
}
}
@@ -228,6 +249,10 @@ func (e *Executor) Setup() error {
e.taskCallCount[k] = new(int32)
e.mkdirMutexMap[k] = &sync.Mutex{}
}
if e.Concurrency > 0 {
e.concurrencySemaphore = make(chan struct{}, e.Concurrency)
}
return nil
}
@@ -241,6 +266,9 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
return &MaximumTaskCallExceededError{task: call.Task}
}
release := e.acquireConcurrencyLimit()
defer release()
if err := e.runDeps(ctx, t); err != nil {
return err
}
@@ -258,7 +286,7 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
if upToDate && preCondMet {
if !e.Silent {
e.Logger.Errf(logger.Magenta, `task: Task "%s" is up to date`, t.Task)
e.Logger.Errf(logger.Magenta, `task: Task "%s" is up to date`, t.Name())
}
return nil
}
@@ -305,6 +333,9 @@ func (e *Executor) mkdir(t *taskfile.Task) error {
func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error {
g, ctx := errgroup.WithContext(ctx)
reacquire := e.releaseConcurrencyLimit()
defer reacquire()
for _, d := range t.Deps {
d := d
@@ -325,6 +356,9 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
switch {
case cmd.Task != "":
reacquire := e.releaseConcurrencyLimit()
defer reacquire()
err := e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars})
if err != nil {
return err
@@ -332,7 +366,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
return nil
case cmd.Cmd != "":
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Taskfile.Silent && !e.Silent) {
e.Logger.Errf(logger.Green, "task: %s", cmd.Cmd)
e.Logger.Errf(logger.Green, "task: [%s] %s", t.Name(), cmd.Cmd)
}
if e.Dry {
@@ -363,7 +397,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
Stderr: stdErr,
})
if execext.IsExitError(err) && cmd.IgnoreError {
e.Logger.VerboseErrf(logger.Yellow, "task: command error ignored: %v", err)
e.Logger.VerboseErrf(logger.Yellow, "task: [%s] command error ignored: %v", t.Name(), err)
return nil
}
return err
@@ -378,10 +412,19 @@ func getEnviron(t *taskfile.Task) []string {
}
environ := os.Environ()
for k, v := range t.Env.ToCacheMap() {
if s, ok := v.(string); ok {
environ = append(environ, fmt.Sprintf("%s=%s", k, s))
str, isString := v.(string)
if !isString {
continue
}
if _, alreadySet := os.LookupEnv(k); alreadySet {
continue
}
environ = append(environ, fmt.Sprintf("%s=%s", k, str))
}
return environ
}

View File

@@ -11,10 +11,10 @@ import (
"strings"
"testing"
"github.com/go-task/task/v2"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3"
"github.com/go-task/task/v3/taskfile"
)
// fileContentTest provides a basic reusable test-case for running a Taskfile
@@ -56,6 +56,16 @@ func (fct fileContentTest) Run(t *testing.T) {
}
}
func TestEmptyTask(t *testing.T) {
e := &task.Executor{
Dir: "testdata/empty_task",
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
}
assert.NoError(t, e.Setup(), "e.Setup()")
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
}
func TestEnv(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/env",
@@ -107,6 +117,21 @@ func TestVarsV2(t *testing.T) {
tt.Run(t)
}
func TestVarsV3(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/vars/v3",
Target: "default",
Files: map[string]string{
"missing-var.txt": "\n",
"var-order.txt": "ABCDEF\n",
"dependent-sh.txt": "123456\n",
"with-call.txt": "Hi, ABC123!\n",
"from-dot-env.txt": "From .env file\n",
},
}
tt.Run(t)
}
func TestMultilineVars(t *testing.T) {
for _, dir := range []string{"testdata/vars/v2/multiline"} {
tt := fileContentTest{
@@ -146,6 +171,22 @@ func TestVarsInvalidTmpl(t *testing.T) {
assert.EqualError(t, e.Run(context.Background(), taskfile.Call{Task: target}), expectError, "e.Run(target)")
}
func TestConcurrency(t *testing.T) {
const (
dir = "testdata/concurrency"
target = "default"
)
e := &task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Concurrency: 1,
}
assert.NoError(t, e.Setup(), "e.Setup()")
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: target}), "e.Run(target)")
}
func TestParams(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/params",
@@ -279,6 +320,8 @@ func TestPrecondition(t *testing.T) {
}
func TestGenerates(t *testing.T) {
const dir = "testdata/generates"
const (
srcTask = "sub/src.txt"
relTask = "rel.txt"
@@ -286,9 +329,6 @@ func TestGenerates(t *testing.T) {
fileWithSpaces = "my text file.txt"
)
// This test does not work with a relative dir.
dir, err := filepath.Abs("testdata/generates")
assert.NoError(t, err)
var srcFile = filepath.Join(dir, srcTask)
for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} {
@@ -370,6 +410,90 @@ func TestStatusChecksum(t *testing.T) {
assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String())
}
func TestLabelUpToDate(t *testing.T) {
const dir = "testdata/label_uptodate"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "foo"}))
assert.Contains(t, buff.String(), "foobar")
}
func TestLabelSummary(t *testing.T) {
const dir = "testdata/label_summary"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Summary: true,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "foo"}))
assert.Contains(t, buff.String(), "foobar")
}
func TestLabelInStatus(t *testing.T) {
const dir = "testdata/label_status"
e := task.Executor{
Dir: dir,
}
assert.NoError(t, e.Setup())
err := e.Status(context.Background(), taskfile.Call{Task: "foo"})
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "foobar")
}
}
func TestLabelWithVariableExpansion(t *testing.T) {
const dir = "testdata/label_var"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "foo"}))
assert.Contains(t, buff.String(), "foobaz")
}
func TestLabelInSummary(t *testing.T) {
const dir = "testdata/label_summary"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "foo"}))
assert.Contains(t, buff.String(), "foobar")
}
func TestLabelInList(t *testing.T) {
const dir = "testdata/label_list"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
e.PrintTasksHelp()
assert.Contains(t, buff.String(), "foobar")
}
func TestStatusVariables(t *testing.T) {
const dir = "testdata/status_vars"
@@ -502,7 +626,7 @@ func TestDry(t *testing.T) {
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
assert.Equal(t, "task: touch file.txt", strings.TrimSpace(buff.String()))
assert.Equal(t, "task: [build] touch file.txt", strings.TrimSpace(buff.String()))
if _, err := os.Stat(file); err == nil {
t.Errorf("File should not exist %s", file)
}
@@ -546,6 +670,7 @@ func TestIncludes(t *testing.T) {
"included_taskfile_without_dir.txt": "included_taskfile_without_dir",
"./module2/included_directory_with_dir.txt": "included_directory_with_dir",
"./module2/included_taskfile_with_dir.txt": "included_taskfile_with_dir",
"os_include.txt": "os",
},
}
tt.Run(t)
@@ -691,6 +816,21 @@ func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) {
_ = os.RemoveAll(toBeCreated)
}
func TestDynamicVariablesShouldRunOnTheTaskDir(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/dir/dynamic_var",
Target: "default",
TrimSpace: false,
Files: map[string]string{
"subdirectory/from_root_taskfile.txt": "subdirectory\n",
"subdirectory/from_included_taskfile.txt": "subdirectory\n",
"subdirectory/from_included_taskfile_task.txt": "subdirectory\n",
"subdirectory/from_interpolated_dir.txt": "subdirectory\n",
},
}
tt.Run(t)
}
func TestDisplaysErrorOnUnsupportedVersion(t *testing.T) {
e := task.Executor{
Dir: "testdata/version/v1",
@@ -717,3 +857,61 @@ func TestShortTaskNotation(t *testing.T) {
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
assert.Equal(t, "string-slice-1\nstring-slice-2\nstring\n", buff.String())
}
func TestDotenvShouldIncludeAllEnvFiles(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/dotenv/default",
Target: "default",
TrimSpace: false,
Files: map[string]string{
"include.txt": "INCLUDE1='from_include1' INCLUDE2='from_include2'\n",
},
}
tt.Run(t)
}
func TestDotenvShouldErrorWhenIncludingDependantDotenvs(t *testing.T) {
const dir = "testdata/dotenv/error_included_envs"
const entry = "Taskfile.yml"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Entrypoint: entry,
Summary: true,
Stdout: &buff,
Stderr: &buff,
}
err := e.Setup()
assert.Error(t, err)
assert.Contains(t, err.Error(), "move the dotenv")
}
func TestDotenvShouldAllowMissingEnv(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/dotenv/missing_env",
Target: "default",
TrimSpace: false,
Files: map[string]string{
"include.txt": "INCLUDE1='' INCLUDE2=''\n",
},
}
tt.Run(t)
}
func TestExitImmediately(t *testing.T) {
const dir = "testdata/exit_immediately"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: true,
}
assert.NoError(t, e.Setup())
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
assert.Contains(t, buff.String(), `"this_should_fail": executable file not found in $PATH`)
}

View File

@@ -3,5 +3,5 @@ package taskfile
// Call is the parameters to a task call
type Call struct {
Task string
Vars Vars
Vars *Vars
}

View File

@@ -1,7 +1,6 @@
package taskfile
import (
"errors"
"strings"
)
@@ -10,23 +9,16 @@ type Cmd struct {
Cmd string
Silent bool
Task string
Vars Vars
Vars *Vars
IgnoreError bool
}
// Dep is a task dependency
type Dep struct {
Task string
Vars Vars
Vars *Vars
}
var (
// ErrCantUnmarshalCmd is returned for invalid command YAML
ErrCantUnmarshalCmd = errors.New("task: can't unmarshal cmd value")
// ErrCantUnmarshalDep is returned for invalid dependency YAML
ErrCantUnmarshalDep = errors.New("task: can't unmarshal dep value")
)
// UnmarshalYAML implements yaml.Unmarshaler interface
func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
var cmd string
@@ -51,14 +43,14 @@ func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
var taskCall struct {
Task string
Vars Vars
Vars *Vars
}
if err := unmarshal(&taskCall); err == nil {
c.Task = taskCall.Task
c.Vars = taskCall.Vars
return nil
if err := unmarshal(&taskCall); err != nil {
return err
}
return ErrCantUnmarshalCmd
c.Task = taskCall.Task
c.Vars = taskCall.Vars
return nil
}
// UnmarshalYAML implements yaml.Unmarshaler interface
@@ -70,12 +62,12 @@ func (d *Dep) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
var taskCall struct {
Task string
Vars Vars
Vars *Vars
}
if err := unmarshal(&taskCall); err == nil {
d.Task = taskCall.Task
d.Vars = taskCall.Vars
return nil
if err := unmarshal(&taskCall); err != nil {
return err
}
return ErrCantUnmarshalDep
d.Task = taskCall.Task
d.Vars = taskCall.Vars
return nil
}

View File

@@ -0,0 +1,103 @@
package taskfile
import (
"errors"
"gopkg.in/yaml.v3"
)
// IncludedTaskfile represents information about included tasksfile
type IncludedTaskfile struct {
Taskfile string
Dir string
AdvancedImport bool
}
// IncludedTaskfiles represents information about included tasksfiles
type IncludedTaskfiles struct {
Keys []string
Mapping map[string]IncludedTaskfile
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (tfs *IncludedTaskfiles) UnmarshalYAML(node *yaml.Node) error {
if node.Kind != yaml.MappingNode {
return errors.New("task: includes is not a map")
}
// NOTE(@andreynering): on this style of custom unmarsheling,
// even number contains the keys, while odd numbers contains
// the values.
for i := 0; i < len(node.Content); i += 2 {
keyNode := node.Content[i]
valueNode := node.Content[i+1]
var v IncludedTaskfile
if err := valueNode.Decode(&v); err != nil {
return err
}
tfs.Set(keyNode.Value, v)
}
return nil
}
// Len returns the length of the map
func (tfs *IncludedTaskfiles) Len() int {
if tfs == nil {
return 0
}
return len(tfs.Keys)
}
// Merge merges the given IncludedTaskfiles into the caller one
func (tfs *IncludedTaskfiles) Merge(other *IncludedTaskfiles) {
other.Range(func(key string, value IncludedTaskfile) error {
tfs.Set(key, value)
return nil
})
}
// Set sets a value to a given key
func (tfs *IncludedTaskfiles) Set(key string, includedTaskfile IncludedTaskfile) {
if tfs.Mapping == nil {
tfs.Mapping = make(map[string]IncludedTaskfile, 1)
}
if !stringSliceContains(tfs.Keys, key) {
tfs.Keys = append(tfs.Keys, key)
}
tfs.Mapping[key] = includedTaskfile
}
// Range allows you to loop into the included taskfiles in its right order
func (tfs *IncludedTaskfiles) Range(yield func(key string, includedTaskfile IncludedTaskfile) error) error {
if tfs == nil {
return nil
}
for _, k := range tfs.Keys {
if err := yield(k, tfs.Mapping[k]); err != nil {
return err
}
}
return nil
}
// UnmarshalYAML implements yaml.Unmarshaler interface
func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
var str string
if err := unmarshal(&str); err == nil {
it.Taskfile = str
return nil
}
var includedTaskfile struct {
Taskfile string
Dir string
}
if err := unmarshal(&includedTaskfile); err != nil {
return err
}
it.Dir = includedTaskfile.Dir
it.Taskfile = includedTaskfile.Taskfile
it.AdvancedImport = true
return nil
}

View File

@@ -22,25 +22,18 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
}
if t1.Includes == nil {
t1.Includes = make(IncludedTaskfiles)
}
for k, v := range t2.Includes {
t1.Includes[k] = v
t1.Includes = &IncludedTaskfiles{}
}
t1.Includes.Merge(t2.Includes)
if t1.Vars == nil {
t1.Vars = make(Vars)
t1.Vars = &Vars{}
}
for k, v := range t2.Vars {
t1.Vars[k] = v
}
if t1.Env == nil {
t1.Env = make(Vars)
}
for k, v := range t2.Env {
t1.Env[k] = v
t1.Env = &Vars{}
}
t1.Vars.Merge(t2.Vars)
t1.Env.Merge(t2.Env)
if t1.Tasks == nil {
t1.Tasks = make(Tasks)

View File

@@ -3,10 +3,10 @@ package taskfile_test
import (
"testing"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
"github.com/go-task/task/v3/taskfile"
)
func TestPreconditionParse(t *testing.T) {

158
taskfile/read/taskfile.go Normal file
View File

@@ -0,0 +1,158 @@
package read
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"github.com/joho/godotenv"
"gopkg.in/yaml.v3"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
)
var (
// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
// ErrIncludedTaskfilesCantHaveDotenvs is returned when a included Taskfile contains dotenvs
ErrIncludedTaskfilesCantHaveDotenvs = errors.New("task: Included Taskfiles can't have dotenv declarations. Please, move the dotenv declaration to the main Taskfile")
)
// Taskfile reads a Taskfile for a given directory
func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
path := filepath.Join(dir, entrypoint)
if _, err := os.Stat(path); err != nil {
return nil, fmt.Errorf(`task: No Taskfile found on "%s". Use "task --init" to create a new one`, path)
}
t, err := readTaskfile(path)
if err != nil {
return nil, err
}
v, err := t.ParsedVersion()
if err != nil {
return nil, err
}
if v >= 3.0 {
for _, dotEnvPath := range t.Dotenv {
if !filepath.IsAbs(dotEnvPath) {
dotEnvPath = filepath.Join(dir, dotEnvPath)
}
if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
continue
}
envs, err := godotenv.Read(dotEnvPath)
if err != nil {
return nil, err
}
for key, value := range envs {
if _, ok := t.Env.Mapping[key]; !ok {
t.Env.Set(key, taskfile.Var{Static: value})
}
}
}
}
err = t.Includes.Range(func(namespace string, includedTask taskfile.IncludedTaskfile) error {
if v >= 3.0 {
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
includedTask = taskfile.IncludedTaskfile{
Taskfile: tr.Replace(includedTask.Taskfile),
Dir: tr.Replace(includedTask.Dir),
AdvancedImport: includedTask.AdvancedImport,
}
if err := tr.Err(); err != nil {
return err
}
}
if filepath.IsAbs(includedTask.Taskfile) {
path = includedTask.Taskfile
} else {
path = filepath.Join(dir, includedTask.Taskfile)
}
info, err := os.Stat(path)
if err != nil {
return err
}
if info.IsDir() {
path = filepath.Join(path, "Taskfile.yml")
}
includedTaskfile, err := readTaskfile(path)
if err != nil {
return err
}
if includedTaskfile.Includes.Len() > 0 {
return ErrIncludedTaskfilesCantHaveIncludes
}
if v >= 3.0 && len(includedTaskfile.Dotenv) > 0 {
return ErrIncludedTaskfilesCantHaveDotenvs
}
if includedTask.AdvancedImport {
for k, v := range includedTaskfile.Vars.Mapping {
o := v
o.Dir = filepath.Join(dir, includedTask.Dir)
includedTaskfile.Vars.Mapping[k] = o
}
for k, v := range includedTaskfile.Env.Mapping {
o := v
o.Dir = filepath.Join(dir, includedTask.Dir)
includedTaskfile.Env.Mapping[k] = o
}
for _, task := range includedTaskfile.Tasks {
if !filepath.IsAbs(task.Dir) {
task.Dir = filepath.Join(includedTask.Dir, task.Dir)
}
}
}
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
if v < 3.0 {
path = filepath.Join(dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
if _, err = os.Stat(path); err == nil {
osTaskfile, err := readTaskfile(path)
if err != nil {
return nil, err
}
if err = taskfile.Merge(t, osTaskfile); err != nil {
return nil, err
}
}
}
for name, task := range t.Tasks {
if task == nil {
task = &taskfile.Task{}
t.Tasks[name] = task
}
task.Task = name
}
return t, nil
}
func readTaskfile(file string) (*taskfile.Taskfile, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
var t taskfile.Taskfile
return &t, yaml.NewDecoder(f).Decode(&t)
}

View File

@@ -6,14 +6,14 @@ import (
"path/filepath"
"runtime"
"github.com/go-task/task/v2/internal/taskfile"
"gopkg.in/yaml.v3"
"gopkg.in/yaml.v2"
"github.com/go-task/task/v3/taskfile"
)
// Taskvars reads a Taskvars for a given directory
func Taskvars(dir string) (taskfile.Vars, error) {
vars := make(taskfile.Vars)
func Taskvars(dir string) (*taskfile.Vars, error) {
vars := &taskfile.Vars{}
path := filepath.Join(dir, "Taskvars.yml")
if _, err := os.Stat(path); err == nil {
@@ -29,24 +29,17 @@ func Taskvars(dir string) (taskfile.Vars, error) {
if err != nil {
return nil, err
}
if vars == nil {
vars = osVars
} else {
for k, v := range osVars {
vars[k] = v
}
}
vars.Merge(osVars)
}
return vars, nil
}
func readTaskvars(file string) (taskfile.Vars, error) {
func readTaskvars(file string) (*taskfile.Vars, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
var vars taskfile.Vars
return vars, yaml.NewDecoder(f).Decode(&vars)
return &vars, yaml.NewDecoder(f).Decode(&vars)
}

10
taskfile/slice.go Normal file
View File

@@ -0,0 +1,10 @@
package taskfile
func stringSliceContains(s []string, str string) bool {
for _, v := range s {
if v == str {
return true
}
}
return false
}

View File

@@ -1,9 +1,5 @@
package taskfile
import (
"errors"
)
// Tasks represents a group of tasks
type Tasks map[string]*Task
@@ -12,6 +8,7 @@ type Task struct {
Task string
Cmds []*Cmd
Deps []*Dep
Label string
Desc string
Summary string
Sources []string
@@ -19,18 +16,20 @@ type Task struct {
Status []string
Preconditions []*Precondition
Dir string
Vars Vars
Env Vars
Vars *Vars
Env *Vars
Silent bool
Method string
Prefix string
IgnoreError bool
}
var (
// ErrCantUnmarshalTask is returned for invalid task YAML
ErrCantUnmarshalTask = errors.New("task: can't unmarshal task value")
)
func (t *Task) Name() string {
if t.Label != "" {
return t.Label
}
return t.Task
}
func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
var cmd Cmd
@@ -48,6 +47,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
var task struct {
Cmds []*Cmd
Deps []*Dep
Label string
Desc string
Summary string
Sources []string
@@ -55,32 +55,31 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
Status []string
Preconditions []*Precondition
Dir string
Vars Vars
Env Vars
Vars *Vars
Env *Vars
Silent bool
Method string
Prefix string
IgnoreError bool `yaml:"ignore_error"`
}
if err := unmarshal(&task); err == nil {
t.Cmds = task.Cmds
t.Deps = task.Deps
t.Desc = task.Desc
t.Summary = task.Summary
t.Sources = task.Sources
t.Generates = task.Generates
t.Status = task.Status
t.Preconditions = task.Preconditions
t.Dir = task.Dir
t.Vars = task.Vars
t.Env = task.Env
t.Silent = task.Silent
t.Method = task.Method
t.Prefix = task.Prefix
t.IgnoreError = task.IgnoreError
return nil
if err := unmarshal(&task); err != nil {
return err
}
return ErrCantUnmarshalTask
t.Cmds = task.Cmds
t.Deps = task.Deps
t.Label = task.Label
t.Desc = task.Desc
t.Summary = task.Summary
t.Sources = task.Sources
t.Generates = task.Generates
t.Status = task.Status
t.Preconditions = task.Preconditions
t.Dir = task.Dir
t.Vars = task.Vars
t.Env = task.Env
t.Silent = task.Silent
t.Method = task.Method
t.Prefix = task.Prefix
t.IgnoreError = task.IgnoreError
return nil
}

View File

@@ -1,16 +1,22 @@
package taskfile
import (
"fmt"
"strconv"
)
// Taskfile represents a Taskfile.yml
type Taskfile struct {
Version string
Expansions int
Output string
Method string
Includes IncludedTaskfiles
Vars Vars
Env Vars
Includes *IncludedTaskfiles
Vars *Vars
Env *Vars
Tasks Tasks
Silent bool
Dotenv []string
}
// UnmarshalYAML implements yaml.Unmarshaler interface
@@ -20,11 +26,12 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
Expansions int
Output string
Method string
Includes IncludedTaskfiles
Vars Vars
Env Vars
Includes *IncludedTaskfiles
Vars *Vars
Env *Vars
Tasks Tasks
Silent bool
Dotenv []string
}
if err := unmarshal(&taskfile); err != nil {
return err
@@ -38,11 +45,24 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
tf.Env = taskfile.Env
tf.Tasks = taskfile.Tasks
tf.Silent = taskfile.Silent
tf.Dotenv = taskfile.Dotenv
if tf.Expansions <= 0 {
tf.Expansions = 2
}
if tf.Vars == nil {
tf.Vars = make(Vars)
tf.Vars = &Vars{}
}
if tf.Env == nil {
tf.Env = &Vars{}
}
return nil
}
// ParsedVersion returns the version as a float64
func (tf *Taskfile) ParsedVersion() (float64, error) {
v, err := strconv.ParseFloat(tf.Version, 64)
if err != nil {
return 0, fmt.Errorf(`task: Could not parse taskfile version "%s": %v`, tf.Version, err)
}
return v, nil
}

View File

@@ -3,10 +3,10 @@ package taskfile_test
import (
"testing"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
"github.com/go-task/task/v3/taskfile"
)
func TestCmdParse(t *testing.T) {
@@ -33,9 +33,12 @@ vars:
{
yamlTaskCall,
&taskfile.Cmd{},
&taskfile.Cmd{Task: "another-task", Vars: taskfile.Vars{
"PARAM1": taskfile.Var{Static: "VALUE1"},
"PARAM2": taskfile.Var{Static: "VALUE2"},
&taskfile.Cmd{Task: "another-task", Vars: &taskfile.Vars{
Keys: []string{"PARAM1", "PARAM2"},
Mapping: map[string]taskfile.Var{
"PARAM1": taskfile.Var{Static: "VALUE1"},
"PARAM2": taskfile.Var{Static: "VALUE2"},
},
}},
},
{
@@ -46,9 +49,12 @@ vars:
{
yamlTaskCall,
&taskfile.Dep{},
&taskfile.Dep{Task: "another-task", Vars: taskfile.Vars{
"PARAM1": taskfile.Var{Static: "VALUE1"},
"PARAM2": taskfile.Var{Static: "VALUE2"},
&taskfile.Dep{Task: "another-task", Vars: &taskfile.Vars{
Keys: []string{"PARAM1", "PARAM2"},
Mapping: map[string]taskfile.Var{
"PARAM1": taskfile.Var{Static: "VALUE1"},
"PARAM2": taskfile.Var{Static: "VALUE2"},
},
}},
},
}

127
taskfile/var.go Normal file
View File

@@ -0,0 +1,127 @@
package taskfile
import (
"errors"
"strings"
"gopkg.in/yaml.v3"
)
// Vars is a string[string] variables map.
type Vars struct {
Keys []string
Mapping map[string]Var
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (vs *Vars) UnmarshalYAML(node *yaml.Node) error {
if node.Kind != yaml.MappingNode {
return errors.New("task: vars is not a map")
}
// NOTE(@andreynering): on this style of custom unmarsheling,
// even number contains the keys, while odd numbers contains
// the values.
for i := 0; i < len(node.Content); i += 2 {
keyNode := node.Content[i]
valueNode := node.Content[i+1]
var v Var
if err := valueNode.Decode(&v); err != nil {
return err
}
vs.Set(keyNode.Value, v)
}
return nil
}
// Merge merges the given Vars into the caller one
func (vs *Vars) Merge(other *Vars) {
other.Range(func(key string, value Var) error {
vs.Set(key, value)
return nil
})
}
// Set sets a value to a given key
func (vs *Vars) Set(key string, value Var) {
if vs.Mapping == nil {
vs.Mapping = make(map[string]Var, 1)
}
if !stringSliceContains(vs.Keys, key) {
vs.Keys = append(vs.Keys, key)
}
vs.Mapping[key] = value
}
// Range allows you to loop into the vars in its right order
func (vs *Vars) Range(yield func(key string, value Var) error) error {
if vs == nil {
return nil
}
for _, k := range vs.Keys {
if err := yield(k, vs.Mapping[k]); err != nil {
return err
}
}
return nil
}
// ToCacheMap converts Vars to a map containing only the static
// variables
func (vs *Vars) ToCacheMap() (m map[string]interface{}) {
m = make(map[string]interface{}, vs.Len())
vs.Range(func(k string, v Var) error {
if v.Sh != "" {
// Dynamic variable is not yet resolved; trigger
// <no value> to be used in templates.
return nil
}
if v.Live != nil {
m[k] = v.Live
} else {
m[k] = v.Static
}
return nil
})
return
}
// Len returns the size of the map
func (vs *Vars) Len() int {
if vs == nil {
return 0
}
return len(vs.Keys)
}
// Var represents either a static or dynamic variable.
type Var struct {
Static string
Live interface{}
Sh string
Dir string
}
// UnmarshalYAML implements yaml.Unmarshaler interface.
func (v *Var) UnmarshalYAML(unmarshal func(interface{}) error) error {
var str string
if err := unmarshal(&str); err == nil {
if strings.HasPrefix(str, "$") {
v.Sh = strings.TrimPrefix(str, "$")
} else {
v.Static = str
}
return nil
}
var sh struct {
Sh string
}
if err := unmarshal(&sh); err != nil {
return err
}
v.Sh = sh.Sh
return nil
}

View File

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

32
testdata/concurrency/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
version: '2'
tasks:
default:
deps:
- t1
t1:
deps:
- t3
- t4
cmds:
- task: t2
- echo done 1
t2:
deps:
- t5
- t6
cmds:
- echo done 2
t3:
cmds:
- echo done 3
t4:
cmds:
- echo done 4
t5:
cmds:
- echo done 5
t6:
cmds:
- echo done 6

View File

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

View File

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

View File

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

1
testdata/dir/dynamic_var/.gitignore vendored Normal file
View File

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

33
testdata/dir/dynamic_var/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
version: '3'
includes:
sub:
taskfile: subdirectory
dir: subdirectory
vars:
DIRECTORY: subdirectory
tasks:
default:
- task: from-root-taskfile
- task: sub:from-included-taskfile
- task: sub:from-included-taskfile-task
- task: from-interpolated-dir
from-root-taskfile:
cmds:
- echo '{{.TASK_DIR}}' > from_root_taskfile.txt
dir: subdirectory
vars:
TASK_DIR:
sh: basename $(pwd)
silent: true
from-interpolated-dir:
cmds:
- echo '{{.INTERPOLATED_DIR}}' > from_interpolated_dir.txt
dir: '{{.DIRECTORY}}'
vars:
INTERPOLATED_DIR:
sh: basename $(pwd)

View File

@@ -0,0 +1,19 @@
version: '3'
vars:
TASKFILE_DIR:
sh: basename $(pwd)
tasks:
from-included-taskfile:
cmds:
- echo '{{.TASKFILE_DIR}}' > from_included_taskfile.txt
silent: true
from-included-taskfile-task:
cmds:
- echo '{{.TASKFILE_TASK_DIR}}' > from_included_taskfile_task.txt
silent: true
vars:
TASKFILE_TASK_DIR:
sh: basename $(pwd)

View File

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

View File

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

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

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

8
testdata/dotenv/default/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
version: '3'
dotenv: ['../include1/.env', '../include1/envs/.env']
tasks:
default:
cmds:
- echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" > include.txt

View File

@@ -0,0 +1,9 @@
version: '3'
includes:
include1: '../include1'
tasks:
default:
cmds:
- echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" > include-errors2.txt

1
testdata/dotenv/include1/.env vendored Normal file
View File

@@ -0,0 +1 @@
INCLUDE1=from_include1

3
testdata/dotenv/include1/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
version: '3'
dotenv: ['.env']

1
testdata/dotenv/include1/envs/.env vendored Normal file
View File

@@ -0,0 +1 @@
INCLUDE2=from_include2

View File

@@ -0,0 +1,8 @@
version: '3'
dotenv: ['.env']
tasks:
default:
cmds:
- echo "INCLUDE1='$INCLUDE1' INCLUDE2='$INCLUDE2'" > include.txt

View File

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

View File

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

4
testdata/empty_task/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
version: '3'
tasks:
default:

View File

@@ -1,4 +1,4 @@
version: '2'
version: '3'
vars:
BAZ:

View File

@@ -0,0 +1,6 @@
version: '3'
tasks:
default: |
this_should_fail
echo "This shoudn't be print"

View File

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

View File

@@ -1,4 +1,8 @@
version: '2'
version: '3'
vars:
BUILD_DIR:
sh: pwd
tasks:
abs.txt:

View File

@@ -1 +0,0 @@
BUILD_DIR: $pwd

0
testdata/generates/sub/.keep vendored Normal file
View File

View File

@@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
task-should-pass:

View File

@@ -13,6 +13,7 @@ includes:
included_taskfile_with_dir:
taskfile: ./module2/Taskfile.yml
dir: ./module2
included_os: ./Taskfile_{{OS}}.yml
tasks:
default:
@@ -24,6 +25,7 @@ tasks:
- task: included_taskfile_without_dir:gen_dir
- task: included_with_dir:gen_file
- task: included_taskfile_with_dir:gen_dir
- task: included_os:gen
gen:
cmds:

Some files were not shown because too many files have changed in this diff Show More