Compare commits

...

185 Commits

Author SHA1 Message Date
Andrey Nering
79f595d8d1 v3.10.0 2022-01-04 18:19:48 -03:00
Andrey Nering
db2865fb17 Add CHANGELOG entry for #579 2022-01-04 18:10:24 -03:00
Andrey Nering
f945fa60d9 Merge branch 'bugfix/issue-481-dynamic-vars-broken' of https://github.com/masaushi/task into masaushi-bugfix/issue-481-dynamic-vars-broken 2022-01-04 17:39:14 -03:00
Andrey Nering
454988f657 Fix typo 🤦 2022-01-04 17:19:38 -03:00
Andrey Nering
7e0346d6eb Add CHANGELOG, documentation and small improvements to #401 2022-01-04 17:16:21 -03:00
Andrey Nering
00a90d1fe6 Merge branch 'f/list-all' of https://github.com/therealkevinard/task into therealkevinard-f/list-all 2022-01-04 17:03:12 -03:00
Andrey Nering
d6c185580a Add CHANGELOG, documentation and small improvements to #626 2022-01-04 16:56:13 -03:00
Jacob McCollum
fd9132c15d remove extra file 2022-01-03 13:22:06 -05:00
Kevin Ard
42702e81b3 refactor: wrap PrintTasksHelp with arg-less signatures
provide exported methods for accessing PrintTasksHelp variants.
2022-01-03 12:12:18 -05:00
Jacob McCollum
09c9d55695 Changes from PR Review:
- Remove ^task syntax from `defer`
- Support task call syntax in defer
2022-01-02 16:38:06 -05:00
Jacob McCollum
69e9effc88 initial pass at deferred commands 2022-01-02 15:55:43 -05:00
Andrey Nering
1c782c599f Remove deprecated "$" and "^" prefixes
`$` was a variable prefix that make it being evaluated as shell. It was
replaced with `sh:`.

`^` is a command prefix that make it run another task. It was replaced
with `task:`.

These were added long ago when we were experimenting with stuff and kept for
some time for backward compatibility reasons, but sometimes causes confusion
and I think the time to remove the code came.

Closes #644
Closes #645
Ref #642

Co-authored-by: Trite <60318513+Trite8Q1@users.noreply.github.com>
2022-01-02 15:26:42 -03:00
Andrey Nering
ed37071fd6 Merge pull request #621 from kerma/feat/support-yaml
Add support for yaml extension
2022-01-02 15:08:50 -03:00
Margus Kerma
d73cf106b1 Merge branch 'master' into feat/support-yaml 2022-01-02 15:30:23 +02:00
Margus Kerma
1d7982e80a fix(#584): Add support to yaml extension
- init creates Taskfile.yaml
- add changelog entry
- add zsh completion support for Taskfile.yaml
2022-01-02 15:23:10 +02:00
Andrey Nering
d5d1984116 Pin released vetsion of mvdan/sh v3.4.2 2021-12-30 16:45:08 -03:00
Andrey Nering
9eda1629bb Merge pull request #636 from nichady/replace-ioutil
replace usages of ioutil with io and os
2021-12-20 23:58:23 -03:00
nichady
9a5d49774e replace usages of ioutil with io and os 2021-12-20 20:00:34 -06:00
Andrey Nering
b2efebce96 Merge pull request #634 from go-task/upgrade-github-actions
Upgrade GitHub actions
2021-12-19 22:14:23 -03:00
Andrey Nering
85232bd704 Upgrade GitHub actions
Closes #633
2021-12-19 22:06:51 -03:00
Andrey Nering
df4e3aea79 v3.9.2 2021-12-02 09:48:30 -03:00
Andrey Nering
290d45fd05 Upgrade mvdan.cc/sh 2021-12-02 09:45:34 -03:00
Andrey Nering
168e8c925c CHANGELOG: Fix typo 2021-11-29 12:55:43 -03:00
Andrey Nering
d9859b18fe v3.9.1 2021-11-28 16:19:30 -03:00
Andrey Nering
784847f35b CHANGELOG: Add entry for #533 and #588 2021-11-28 16:16:12 -03:00
Andrey Nering
97287377d1 CHANGELOG: Add entries for #597 and #598 2021-11-28 16:16:12 -03:00
Andrey Nering
a15b66e003 Merge pull request #588 from jespino/issue-533
Adding task started and task finished in the verbose otput
2021-11-28 16:14:56 -03:00
Andrey Nering
a441b4b90d Merge pull request #598 from sylv-io/fix/false_fail
task: Check context error
2021-11-28 16:10:32 -03:00
Andrey Nering
0dcc1390a6 GitHub: Remove automatic "bug" or "feature" lables in issues 2021-11-28 15:58:41 -03:00
Andrey Nering
01c86636e9 Add CHANGELOG and minor changes to #613 2021-11-28 15:54:49 -03:00
Andrey Nering
846c27d579 Merge pull request #613 from Peter554/fix-quoting-cli-args
Fix quoting of CLI_ARGS
2021-11-28 15:51:03 -03:00
Andrey Nering
db05059b42 CHANGELOG: Add entry for #614 and #612 2021-11-28 15:43:38 -03:00
Andrey Nering
b824328850 Merge pull request #614 from kerma/fix/panic-on-empty-included-cmd
Add nil check for included cmd
2021-11-28 15:41:38 -03:00
Andrey Nering
a8767a2b1a CHANGELOG: Mention mvdan/sh upgrade 2021-11-28 15:38:47 -03:00
Andrey Nering
5e14e7fb70 Merge pull request #615 from go-task/dependabot/go_modules/mvdan.cc/sh/v3-3.4.1
Bump mvdan.cc/sh/v3 from 3.4.0 to 3.4.1
2021-11-27 17:32:22 -03:00
dependabot[bot]
fbaa7be52e Bump mvdan.cc/sh/v3 from 3.4.0 to 3.4.1
Bumps [mvdan.cc/sh/v3](https://github.com/mvdan/sh) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/mvdan/sh/releases)
- [Changelog](https://github.com/mvdan/sh/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mvdan/sh/compare/v3.4.0...v3.4.1)

---
updated-dependencies:
- dependency-name: mvdan.cc/sh/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-27 11:03:48 +00:00
Margus Kerma
b6016b244e fix(#612): Add nil check for included cmd 2021-11-26 11:20:05 +02:00
Peter Byfield
e339a64261 Fix quoting of CLI_ARGS
Consider a task:

test:
  cmds:
    - pytest {{.CLI_ARGS}}

Running `task test -- -m "not foo"` should be equivalent
to running `pytest -m "not foo"`. However, with the current
implementation the quoting of CLI_ARGS is lost and the task
executes `python -m not foo`, which results in an error.
2021-11-25 14:13:45 +01:00
Andrey Nering
17e18442ab Merge pull request #600 from OmriSteiner/typofix
Fix typo in usage.md
2021-11-09 12:15:26 -03:00
Omri Steiner
e8aa3a17a6 Fix typo in usage.md 2021-11-09 16:18:42 +02:00
Marcello Sylvester Bauer
bdb97eab86 task: Check context error
Check context error after running dependencies, to prevent false
negative precondition errors due to "context canceled".

Signed-off-by: Marcello Sylvester Bauer <sylv@sylv.io>
2021-11-05 15:06:51 +01:00
Jesús Espino
690000254c Apply suggestions from code review
Co-authored-by: Andrey Nering <andrey@nering.com.br>
2021-10-31 09:37:23 +01:00
Andrey Nering
6a0b778978 Add CHANGELOG for #592 2021-10-30 11:15:56 -03:00
Andrey Nering
549d141053 Merge pull request #592 from caarlos0/brew
fix: goreleaser brew completions
2021-10-30 11:07:13 -03:00
Carlos A Becker
c31ecdb8de fix: goreleaser brew completions 2021-10-27 14:48:00 -03:00
Jesús Espino
8a09d044c7 Adding task started and task finished in the verbose otput 2021-10-16 21:12:26 +02:00
Andrey Nering
a3b5b89930 Fix typo in CHANGELOG 2021-10-02 18:51:52 -03:00
Andrey Nering
ad6f100f6a v3.9.0 2021-10-02 18:47:30 -03:00
Andrey Nering
3cfe21af58 Add shellQuote template function 2021-10-02 18:29:07 -03:00
Andrey Nering
b70a660975 Add CHANGELOG for #580 2021-10-02 18:07:54 -03:00
Andrey Nering
04c1d1389f go mod tidy 2021-10-02 17:52:02 -03:00
Andrey Nering
f12156bf81 Merge pull request #580 from system-transparency/update/sh
upgrade mvdan.cc/sh/v3 v3.3.1 => v3.4.0
2021-10-02 17:50:37 -03:00
Marcello Sylvester Bauer
0177ac660b upgrade mvdan.cc/sh/v3 v3.3.1 => v3.4.0
Signed-off-by: Marcello Sylvester Bauer <sylv@sylv.io>
2021-10-02 12:33:07 +02:00
Andrey Nering
361b9b4ce4 v3.8.0 2021-09-26 22:30:26 -03:00
Andrey Nering
78792bd11c Add CHANGELOG + Small improvement for #563 2021-09-26 21:55:31 -03:00
Andrey Nering
8b38ddfcd9 Merge pull request #563 from system-transparency/feature/interactive
Task: Add interactive parameter
2021-09-26 21:40:50 -03:00
Andrey Nering
78ddf50d2d Documentation: Remove reference to deprecated "expansions" keyword
Closes #575
2021-09-26 21:28:40 -03:00
masaushi
93dcb20e12 fix error in evaluating dynamic variables with newly created directory 2021-09-26 22:30:32 +09:00
Andrey Nering
41a71e1dee Add CHANGELOG for #573 2021-09-25 09:48:49 -03:00
Andrey Nering
a5ed8ad58c Merge pull request #573 from masaushi/bugfix/issue-534-seg-fault-on-empty-command
Fix segmentation fault on nil slice element for issue #534
2021-09-25 09:46:47 -03:00
Andrey Nering
e45ed85b55 Add CHANGELOG + Small nits for #552 2021-09-25 09:40:03 -03:00
Andrey Nering
52474f9103 Merge pull request #552 from justafish/justafish/519/optional-includes
Allow includes to be optional
2021-09-25 09:36:44 -03:00
Andrey Nering
c2587da27d Merge pull request #577 from go-task/dependabot/go_modules/github.com/fatih/color-1.13.0
Bump github.com/fatih/color from 1.12.0 to 1.13.0
2021-09-25 09:32:28 -03:00
Andrey Nering
26036877b2 Merge pull request #576 from go-task/dependabot/go_modules/github.com/joho/godotenv-1.4.0
Bump github.com/joho/godotenv from 1.3.0 to 1.4.0
2021-09-25 09:31:54 -03:00
dependabot[bot]
906cdd9050 Bump github.com/fatih/color from 1.12.0 to 1.13.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/fatih/color
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-25 11:04:26 +00:00
dependabot[bot]
762662d056 Bump github.com/joho/godotenv from 1.3.0 to 1.4.0
Bumps [github.com/joho/godotenv](https://github.com/joho/godotenv) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/joho/godotenv/releases)
- [Commits](https://github.com/joho/godotenv/compare/v1.3.0...v1.4.0)

---
updated-dependencies:
- dependency-name: github.com/joho/godotenv
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-25 11:04:15 +00:00
masaushi
1de4b38766 fix segmentation fault on nil slice element 2021-09-15 00:01:33 +09:00
Andrey Nering
6c73ab823b Add CHANGELOG + gofmt for #557 2021-09-06 10:30:45 -03:00
Andrey Nering
5ef1651151 Merge branch 'include-homedir' of https://github.com/amancevice/task into amancevice-include-homedir 2021-09-06 10:24:23 -03:00
Alexander Mancevice
8d695bc8d7 simplify logic to expand included paths 2021-09-05 10:57:49 -04:00
Alexander Mancevice
c892d055ed Use internal execext.Expand to expand ~ in includes 2021-09-05 08:18:47 -04:00
Andrey Nering
b327e54be1 Give up on trying to make Gorelease release Homebrew automatically
It refuses to do it and I don't know why. Gonna publish the manifest manually as before
2021-09-04 22:17:41 -03:00
Andrey Nering
989045489c v3.7.3
🙄
2021-09-04 22:06:45 -03:00
Andrey Nering
888338c60e v3.7.2
Attempt to fix Homebrew releasing by Goreleaser
2021-09-04 22:03:22 -03:00
Andrey Nering
e6c6cc7811 v3.7.1 2021-09-04 21:52:46 -03:00
Andrey Nering
fa0e72bd69 Documentation typo
Fixes #555
2021-09-04 21:30:28 -03:00
Andrey Nering
18decac44d Homebrew: Automate with Goreleaser and start publishing M1 binaries 2021-09-04 21:26:17 -03:00
Andrey Nering
1012a0cf2b Upgrade to Go 1.17. Set 1.16 as the minimal version 2021-09-04 21:04:40 -03:00
Andrey Nering
7e4de945cf Merge pull request #564 from JulienBreux/feat/upgrade-goreleaser-action
Upgrade Goreleaser action to v2 - To ensure mac/m1 mac/arm release
2021-09-04 20:56:16 -03:00
Andrey Nering
a468272726 Merge pull request #567 from LukeCarrier/go-1.16
Build with Go 1.16
2021-09-04 20:52:24 -03:00
Luke Carrier
039d8f000d Build with Go 1.16
This enables darwin-arm64 builds to support Apple Silicon-based Macs.
2021-09-04 22:56:39 +01:00
Marcello Sylvester Bauer
2dc181c75e Task: Add interactive parameter
Add the task parameter "interactive" to force interleaved output in order
to make interactive CLI apps work.

Feature request in #217

TODO:
* Update documentation

Signed-off-by: Marcello Sylvester Bauer <sylv@sylv.io>
2021-09-03 10:31:24 +02:00
Julien Breux
d35f960a8a Upgrade Goreleaser action to v2 2021-09-02 17:45:01 +02:00
Andrey Nering
634f8ed574 Merge pull request #562 from notnmeyer/fix-fish-shell-completions
Fix fish completions
2021-09-01 13:34:10 -03:00
Nate Meyer
d369451308 keep the sed, we do want to skip the first line 2021-09-01 08:54:48 -07:00
Nate Meyer
8f1202424d properly format each line 2021-09-01 08:33:08 -07:00
Alexander Mancevice
0a6833e9d8 Allow included Taskfiles to use ~/* paths 2021-08-21 07:20:33 -04:00
Sally Young
8f80fc4e2c Issue #519: Allow includes to be optional 2021-08-11 17:28:44 +01:00
Andrey Nering
50e5813222 Merge pull request #544 from AlexanderBeyn/fix-bash-completion
Fix bash completions when the current argument begins with a dash
2021-08-07 13:54:39 -03:00
Andrey Nering
7bc268aeaa Merge pull request #545 from go-task/dependabot/go_modules/mvdan.cc/sh/v3-3.3.1
Bump mvdan.cc/sh/v3 from 3.3.0 to 3.3.1
2021-08-07 13:53:22 -03:00
dependabot[bot]
537b5b1e25 Bump mvdan.cc/sh/v3 from 3.3.0 to 3.3.1
Bumps [mvdan.cc/sh/v3](https://github.com/mvdan/sh) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/mvdan/sh/releases)
- [Changelog](https://github.com/mvdan/sh/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mvdan/sh/compare/v3.3.0...v3.3.1)

---
updated-dependencies:
- dependency-name: mvdan.cc/sh/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-07 11:03:11 +00:00
Alexander Beyn
aae38f8ce7 Don't let grep treat the search pattern as an option 2021-08-07 02:28:52 -07:00
Andrey Nering
ad05432bcf v3.7.0 2021-07-31 21:36:03 -03:00
Andrey Nering
8aa983257d Small adjustments and CHANGELOG for #359 2021-07-31 20:29:59 -03:00
Andrey Nering
046a97d1e5 go.mod: Upgrade minimal Go version to v1.15 2021-07-31 20:29:19 -03:00
Ross Hammermeister
d28649b13d Refactoring startExecution 2021-07-31 19:36:40 -03:00
Ross Hammermeister
3e16ca37bc Updating version checking 2021-07-31 19:36:40 -03:00
Ross Hammermeister
2da38a5bdc Cleaning up the run code 2021-07-31 19:36:40 -03:00
Ross Hammermeister
bbe1d8b52e Adding proper version checking for taskfile when using run 2021-07-31 19:36:40 -03:00
Ross Hammermeister
97c85e39c3 Only run task once for #53 2021-07-31 19:36:40 -03:00
Andrey Nering
a7b59e5b12 community.md: Update title level 2021-07-23 18:32:13 -03:00
Andrey Nering
9eb1252ce9 Merge pull request #531 from biozz/patch-2
Update community.md about Sublime Text 4 package
2021-07-23 18:30:11 -03:00
Ivan Elfimov
0e01e13670 Update community.md about Sublime Text 4 package
Added a simple abstract about Sublime Text 4 package with the links to the source code and package control.
2021-07-23 23:45:22 +03:00
Andrey Nering
239e61e718 v3.6.0 2021-07-10 23:13:03 -03:00
Andrey Nering
22549e9fd8 CHANGELOG: Fix typo 2021-07-10 22:04:15 -03:00
Andrey Nering
1f9fd24064 Small improvement and CHANGELOG for #477 2021-07-10 21:58:03 -03:00
Andrey Nering
a7594740e3 Merge pull request #477 from mrngsht/sources_evaluation
Evaluate sources also if status is up-to-date
2021-07-10 21:54:47 -03:00
Andrey Nering
945c72cf6c Small optimization and bug fix: don't compute variables if not needed for dotenv:
Closes #517
2021-07-10 19:46:53 -03:00
Andrey Nering
824b0c0132 Improve error message 2021-07-10 19:33:36 -03:00
Andrey Nering
51e9f2f579 Site: Enable search 2021-07-10 19:13:04 -03:00
Andrey Nering
e8ec33d9d0 Site: Return to light theme 2021-07-10 19:07:17 -03:00
Andrey Nering
3bbbaf12fd v3.5.0 2021-07-04 21:53:22 -03:00
Andrey Nering
30ffacd879 Update currency 2021-07-04 21:47:36 -03:00
Andrey Nering
75e9b7791c Remove Patreon
Also, do a few more updates.
2021-07-04 21:45:07 -03:00
Andrey Nering
4b665ab19a Add CHANGELOG entry for 08265ed1d7 2021-06-05 16:04:01 -03:00
Nicolas Fouché
08265ed1d7 Allow vars in dotenv paths, including environment variables
Closes #453
Closes #434
Ref #433

Co-authored-by: Andrey Nering <andrey@nering.com.br>
2021-06-05 16:00:19 -03:00
Andrey Nering
cded9af90f v3.4.3 2021-05-30 23:03:10 -03:00
Andrey Nering
4e1f2ad017 Enforce NO_COLOR=1 on tests to make sure it passes
Closes #459
Fixes #480
Ref #343
Ref fatih/color#137
2021-05-30 22:48:48 -03:00
Andrey Nering
7f92b7072d Merge pull request #496 from go-task/dependabot/go_modules/github.com/fatih/color-1.12.0
Bump github.com/fatih/color from 1.11.0 to 1.12.0
2021-05-30 22:19:21 -03:00
dependabot[bot]
4a589ba6a4 Bump github.com/fatih/color from 1.11.0 to 1.12.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.11.0 to 1.12.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.11.0...v1.12.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-29 11:04:31 +00:00
Andrey Nering
7f16325fcc Merge pull request #491 from go-task/dependabot/go_modules/mvdan.cc/sh/v3-3.3.0
Bump mvdan.cc/sh/v3 from 3.2.4 to 3.3.0
2021-05-22 09:53:20 -03:00
dependabot[bot]
b62e5bf34c Bump mvdan.cc/sh/v3 from 3.2.4 to 3.3.0
Bumps [mvdan.cc/sh/v3](https://github.com/mvdan/sh) from 3.2.4 to 3.3.0.
- [Release notes](https://github.com/mvdan/sh/releases)
- [Changelog](https://github.com/mvdan/sh/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mvdan/sh/compare/v3.2.4...v3.3.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-22 11:03:11 +00:00
Andrey Nering
bd6b348cc7 Update CHANGELOG 2021-05-20 21:30:13 -03:00
Andrey Nering
62f35fe8c8 Merge pull request #485 from lpdyck/bug/watch-repects-task-dir
Changes --watch to respect task directory
2021-05-20 21:26:43 -03:00
Andrey Nering
cb2cb4659c Merge pull request #490 from per1234/update-setup-task-action
Update docs for new arduino/setup-task action repository
2021-05-20 21:23:42 -03:00
per1234
a2c58415cf Update docs for new arduino/setup-task action repository
The GitHub Actions action for installing Task has graduated from its original home in the experimental `arduino/action`
repository with a move to a dedicated permanent repository at `arduino/setup-task`. This move was accompanied by
significant upgrades to the project infrastructure to facilitate its maintenance and quality assurance.

A 1.0.0 release has been made and a `v1` ref that will track all releases in the major version 1 series. I have updated
the example snippet to use this `v1 ref because this will cause the user's workflows to use stable release versions of
the action while also benefiting from ongoing development to the action at each patch or minor release up until such time
as a new major release is made. At this time the user will be given the opportunity to evaluate whether any changes to
the workflow are required by the breaking change to the action that triggered the major release before manually updating
the major ref in the workflows (e.g., `uses: arduino/setup-task@v2`).
2021-05-20 02:40:09 -07:00
Andrey Nering
2cb9987c99 Merge pull request #489 from go-task/dependabot/go_modules/github.com/fatih/color-1.11.0
Bump github.com/fatih/color from 1.10.0 to 1.11.0
2021-05-17 21:54:18 -03:00
dependabot[bot]
36584cfb7c Bump github.com/fatih/color from 1.10.0 to 1.11.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.10.0 to 1.11.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.10.0...v1.11.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-15 11:03:43 +00:00
Landon Dyck
b825ad6a12 use status.Glob for watch globbing 2021-05-08 15:02:08 -05:00
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
mrngsht
58c69e36a1 Evaluate sources also if status is up-to-date 2021-04-21 21:56:25 +09: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
Kevin Ard
347c796662 add tests to previous 2020-11-13 16:24:34 -05:00
Kevin Ard
9bed7f7a9b feat (help): allow cli option to list tasks with no desc
added an add'l cli option that lists all tasks, with or without description.
orig. behavior: task -l lists tasks with desc field
new behaviour: task -la or task -a will list all tasks. if task has desc, it will be included.

BREAKING CHANGES: none, that I know of.
NOTES/Concerns:
- This is wip.
- Haven't checked how it interacts with bash completion.
- The new Executor.TaskNames func does not use e.CompiledTask(taskfile.Call{Task: task.Task})
2020-11-13 15:27:03 -05:00
93 changed files with 2001 additions and 576 deletions

3
.github/FUNDING.yml vendored
View File

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

View File

@@ -1,7 +1,6 @@
---
name: Bug Report
about: Use the template to report bugs and issues
labels: bug
---
- Task version:

View File

@@ -1,7 +1,6 @@
---
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.

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

@@ -10,17 +10,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v1
uses: actions/setup-go@v2
with:
go-version: 1.15.x
go-version: 1.17.x
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
GITHUB_TOKEN: ${{secrets.GH_PAT}}

View File

@@ -5,18 +5,18 @@ jobs:
name: Test
strategy:
matrix:
go-version: [1.14.x, 1.15.x]
go-version: [1.16.x, 1.17.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{matrix.platform}}
steps:
- name: Set up Go ${{matrix.go-version}}
uses: actions/setup-go@v1
uses: actions/setup-go@v2
with:
go-version: ${{matrix.go-version}}
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
uses: actions/checkout@v2
- name: Download Go modules
run: go mod download

3
.gitignore vendored
View File

@@ -19,8 +19,9 @@ dist/
.DS_Store
# intellij idea/goland
# editors
.idea/
.vscode/
# exuberant ctags
tags

View File

@@ -1,6 +1,6 @@
build:
binary: task
main: cmd/task/task.go
main: ./cmd/task
goos:
- windows
- darwin
@@ -17,9 +17,18 @@ build:
goarch: 386
env:
- CGO_ENABLED=0
ldflags:
- -s -w # Don't set main.version.
gomod:
proxy: true
archives:
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
files:
- README.md
- LICENSE
- completion/**/*
format_overrides:
- goos: windows
format: zip
@@ -45,3 +54,20 @@ nfpms:
- deb
- rpm
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
brews:
- name: go-task
description: Task runner / simpler Make alternative written in Go
license: MIT
homepage: https://taskfile.dev
folder: Formula
tap:
owner: go-task
name: homebrew-tap
test:
system "#{bin}/task", "--help"
install: |-
bin.install "task"
bash_completion.install "completion/bash/task.bash" => "task"
zsh_completion.install "completion/zsh/_task" => "_task"
fish_completion.install "completion/fish/task.fish"

View File

@@ -1,6 +1,160 @@
# Changelog
## v3.2.0
## v3.10.0 - 2022-01-04
- A new `--list-all` (alias `-a`) flag is now available. It's similar to the
exiting `--list` (`-l`) but prints all tasks, even those without a
description
([#383](https://github.com/go-task/task/issues/383), [#401](https://github.com/go-task/task/pull/401)).
- It's now possible to schedule cleanup commands to run once a task finishes
with the `defer:` keyword
([Documentation](https://taskfile.dev/#/usage?id=doing-task-cleanup-with-defer), [#475](https://github.com/go-task/task/issues/475), [#626](https://github.com/go-task/task/pull/626)).
- Remove long deprecated and undocumented `$` variable prefix and `^` command
prefix
([#642](https://github.com/go-task/task/issues/642), [#644](https://github.com/go-task/task/issues/644), [#645](https://github.com/go-task/task/pull/645)).
- Add support for `.yaml` extension (as an alternative to `.yml`).
This was requested multiple times throughout the years. Enjoy!
([#183](https://github.com/go-task/task/issues/183), [#184](https://github.com/go-task/task/pull/184), [#369](https://github.com/go-task/task/issues/369), [#584](https://github.com/go-task/task/issues/584), [#621](https://github.com/go-task/task/pull/621)).
- Fixed error when computing a variable when the task directory do not exist
yet
([#481](https://github.com/go-task/task/issues/481), [#579](https://github.com/go-task/task/pull/579)).
## v3.9.2 - 2021-12-02
- Upgrade [mvdan/sh](https://github.com/mvdan/sh) which contains a fix a for
a important regression on Windows
([#619](https://github.com/go-task/task/issues/619), [mvdan/sh#768](https://github.com/mvdan/sh/issues/768), [mvdan/sh#769](https://github.com/mvdan/sh/pull/769)).
## v3.9.1 - 2021-11-28
- Add logging in verbose mode for when a task starts and finishes
([#533](https://github.com/go-task/task/issues/533), [#588](https://github.com/go-task/task/pull/588)).
- Fix an issue with preconditions and context errors
([#597](https://github.com/go-task/task/issues/597), [#598](https://github.com/go-task/task/pull/598)).
- Quote each `{{.CLI_ARGS}}` argument to prevent one with spaces to become many
([#613](https://github.com/go-task/task/pull/613)).
- Fix nil pointer when `cmd:` was left empty
([#612](https://github.com/go-task/task/issues/612), [#614](https://github.com/go-task/task/pull/614)).
- Upgrade [mvdan/sh](https://github.com/mvdan/sh) which contains two
relevant fixes:
- Fix quote of empty strings in `shellQuote`
([#609](https://github.com/go-task/task/issues/609), [mvdan/sh#763](https://github.com/mvdan/sh/issues/763)).
- Fix issue of wrong environment variable being picked when there's another
very similar one
([#586](https://github.com/go-task/task/issues/586), [mvdan/sh#745](https://github.com/mvdan/sh/pull/745)).
- Install shell completions automatically when installing via Homebrew
([#264](https://github.com/go-task/task/issues/264), [#592](https://github.com/go-task/task/pull/592), [go-task/homebrew-tap#2](https://github.com/go-task/homebrew-tap/pull/2)).
## v3.9.0 - 2021-10-02
- A new `shellQuote` function was added to the template system
(`{{shellQuote "a string"}}`) to ensure a string is safe for use in shell
([mvdan/sh#727](https://github.com/mvdan/sh/pull/727), [mvdan/sh#737](https://github.com/mvdan/sh/pull/737), [Documentation](https://pkg.go.dev/mvdan.cc/sh/v3@v3.4.0/syntax#Quote))
- In this version [mvdan.cc/sh](https://github.com/mvdan/sh) was upgraded
with some small fixes and features
- The `read -p` flag is now supported
([#314](https://github.com/go-task/task/issues/314), [mvdan/sh#551](https://github.com/mvdan/sh/issues/551), [mvdan/sh#772](https://github.com/mvdan/sh/pull/722))
- The `pwd -P` and `pwd -L` flags are now supported
([#553](https://github.com/go-task/task/issues/553), [mvdan/sh#724](https://github.com/mvdan/sh/issues/724), [mvdan/sh#728](https://github.com/mvdan/sh/pull/728))
- The `$GID` environment variable is now correctly being set
([#561](https://github.com/go-task/task/issues/561), [mvdan/sh#723](https://github.com/mvdan/sh/pull/723))
## v3.8.0 - 2021-09-26
- Add `interactive: true` setting to improve support for interactive CLI apps
([#217](https://github.com/go-task/task/issues/217), [#563](https://github.com/go-task/task/pull/563)).
- Fix some `nil` errors
([#534](https://github.com/go-task/task/issues/534), [#573](https://github.com/go-task/task/pull/573)).
- Add ability to declare an included Taskfile as optional
([#519](https://github.com/go-task/task/issues/519), [#552](https://github.com/go-task/task/pull/552)).
- Add support for including Taskfiles in the home directory by using `~`
([#539](https://github.com/go-task/task/issues/539), [#557](https://github.com/go-task/task/pull/557)).
## v3.7.3 - 2021-09-04
- Add official support to Apple M1 ([#564](https://github.com/go-task/task/pull/564), [#567](https://github.com/go-task/task/pull/567)).
- Our [official Homebrew tap](https://github.com/go-task/homebrew-tap) will
support more platforms, including Apple M1
## v3.7.0 - 2021-07-31
- Add `run:` setting to control if tasks should run multiple times or not.
Available options are `always` (the default), `when_changed` (if a variable
modified the task) and `once` (run only once no matter what).
This is a long time requested feature. Enjoy!
([#53](https://github.com/go-task/task/issues/53), [#359](https://github.com/go-task/task/pull/359)).
## v3.6.0 - 2021-07-10
- Allow using both `sources:` and `status:` in the same task
([#411](https://github.com/go-task/task/issues/411), [#427](https://github.com/go-task/task/issues/427), [#477](https://github.com/go-task/task/pull/477)).
- Small optimization and bug fix: don't compute variables if not needed for
`dotenv:` ([#517](https://github.com/go-task/task/issues/517)).
## v3.5.0 - 2021-07-04
- Add support for interpolation in `dotenv:`
([#433](https://github.com/go-task/task/discussions/433), [#434](https://github.com/go-task/task/issues/434), [#453](https://github.com/go-task/task/pull/453)).
## v3.4.3 - 2021-05-30
- Add support for the `NO_COLOR` environment variable.
([#459](https://github.com/go-task/task/issues/459), [fatih/color#137](https://github.com/fatih/color/pull/137)).
- Fix bug where sources were not considering the right directory
in `--watch` mode
([#484](https://github.com/go-task/task/issues/484), [#485](https://github.com/go-task/task/pull/485)).
## 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
@@ -13,7 +167,7 @@
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
## 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
@@ -29,7 +183,7 @@
sentence was in the last line
([#403](https://github.com/go-task/task/issues/403)).
## v3.0.1
## v3.0.1 - 2020-12-26
- Allow use as a library by moving the required packages out of the `internal`
directory
@@ -39,7 +193,7 @@
- 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
## 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))
@@ -82,7 +236,7 @@
commands are green, errors are red, etc
([#207](https://github.com/go-task/task/pull/207)).
## v2.8.1 - 2019-05-20
## 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)).

View File

@@ -12,6 +12,9 @@ vars:
GO_PACKAGES:
sh: go list ./...
env:
CGO_ENABLED: '0'
tasks:
default:
cmds:
@@ -21,8 +24,6 @@ tasks:
desc: Installs Task
cmds:
- go install -v -ldflags="-w -s -X main.version={{.GIT_COMMIT}}" ./cmd/task
env:
CGO_ENABLED: '0'
mod:
desc: Downloads and tidy Go modules

View File

@@ -9,7 +9,7 @@ import (
// 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
var globals = &taskfile.Vars{}
for _, arg := range args {
if !strings.Contains(arg, "=") {
@@ -17,9 +17,6 @@ func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) {
continue
}
if globals == nil {
globals = &taskfile.Vars{}
}
name, value := splitVar(arg)
globals.Set(name, taskfile.Var{Static: value})
}
@@ -34,7 +31,7 @@ func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) {
// 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
var globals = &taskfile.Vars{}
for _, arg := range args {
if !strings.Contains(arg, "=") {
@@ -43,9 +40,6 @@ func ParseV2(args ...string) ([]taskfile.Call, *taskfile.Vars) {
}
if len(calls) < 1 {
if globals == nil {
globals = &taskfile.Vars{}
}
name, value := splitVar(arg)
globals.Set(name, taskfile.Var{Static: value})
} else {

View File

@@ -96,7 +96,9 @@ func TestArgsV3(t *testing.T) {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, globals := args.ParseV3(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls)
assert.Equal(t, test.ExpectedGlobals, globals)
if test.ExpectedGlobals.Len() > 0 || globals.Len() > 0 {
assert.Equal(t, test.ExpectedGlobals, globals)
}
})
}
}
@@ -198,7 +200,10 @@ func TestArgsV2(t *testing.T) {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, globals := args.ParseV2(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls)
assert.Equal(t, test.ExpectedGlobals, globals)
if test.ExpectedGlobals.Len() > 0 || globals.Len() > 0 {
assert.Equal(t, test.ExpectedGlobals, globals)
}
})
}
}

View File

@@ -7,9 +7,12 @@ import (
"os"
"os/signal"
"path/filepath"
"runtime/debug"
"strings"
"syscall"
"github.com/spf13/pflag"
"mvdan.cc/sh/v3/syntax"
"github.com/go-task/task/v3"
"github.com/go-task/task/v3/args"
@@ -18,7 +21,7 @@ import (
)
var (
version = "master"
version = ""
)
const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--taskfile] [--dry] [--summary] [task...]
@@ -57,6 +60,7 @@ func main() {
helpFlag bool
init bool
list bool
listAll bool
status bool
force bool
watch bool
@@ -65,6 +69,7 @@ func main() {
dry bool
summary bool
parallel bool
concurrency int
dir string
entrypoint string
output string
@@ -73,8 +78,9 @@ 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(&init, "init", "i", false, "creates a new Taskfile.yaml in the current folder")
pflag.BoolVarP(&list, "list", "l", false, "lists tasks with description of current Taskfile")
pflag.BoolVarP(&listAll, "list-all", "a", false, "lists tasks with or without a description")
pflag.BoolVar(&status, "status", false, "exits with non-zero exit code if any of the given tasks is not up-to-date")
pflag.BoolVarP(&force, "force", "f", false, "forces execution even when the task is up-to-date")
pflag.BoolVarP(&watch, "watch", "w", false, "enables watch of the given task")
@@ -86,11 +92,12 @@ func main() {
pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
pflag.StringVarP(&entrypoint, "taskfile", "t", "", `choose which Taskfile to run. Defaults to "Taskfile.yml"`)
pflag.StringVarP(&output, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
pflag.BoolVarP(&color, "color", "c", true, "colored output")
pflag.BoolVarP(&color, "color", "c", true, "colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable")
pflag.IntVarP(&concurrency, "concurrency", "C", 0, "limit number tasks to run concurrently")
pflag.Parse()
if versionFlag {
fmt.Printf("Task version: %s\n", version)
fmt.Printf("Task version: %s\n", getVersion())
return
}
@@ -117,21 +124,20 @@ func main() {
if entrypoint != "" {
dir = filepath.Dir(entrypoint)
entrypoint = filepath.Base(entrypoint)
} else {
entrypoint = "Taskfile.yml"
}
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,
@@ -149,7 +155,12 @@ func main() {
}
if list {
e.PrintTasksHelp()
e.ListTasksWithDesc()
return
}
if listAll {
e.ListAllTasks()
return
}
@@ -157,11 +168,19 @@ func main() {
calls []taskfile.Call
globals *taskfile.Vars
)
if v >= 3.0 {
calls, globals = args.ParseV3(pflag.Args()...)
} else {
calls, globals = args.ParseV2(pflag.Args()...)
tasksAndVars, cliArgs, err := getArgs()
if err != nil {
log.Fatal(err)
}
if v >= 3.0 {
calls, globals = args.ParseV3(tasksAndVars...)
} else {
calls, globals = args.ParseV2(tasksAndVars...)
}
globals.Set("CLI_ARGS", taskfile.Var{Static: cliArgs})
e.Taskfile.Vars.Merge(globals)
ctx := context.Background()
@@ -182,9 +201,30 @@ func main() {
}
}
func getArgs() ([]string, string, error) {
var (
args = pflag.Args()
doubleDashPos = pflag.CommandLine.ArgsLenAtDash()
)
if doubleDashPos == -1 {
return args, "", nil
}
var quotedCliArgs []string
for _, arg := range args[doubleDashPos:] {
quotedCliArg, err := syntax.Quote(arg, syntax.LangBash)
if err != nil {
return nil, "", err
}
quotedCliArgs = append(quotedCliArgs, quotedCliArg)
}
return args[:doubleDashPos], strings.Join(quotedCliArgs, " "), nil
}
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
@@ -193,3 +233,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
}

View File

@@ -15,7 +15,7 @@ _task_completion()
return
fi
COMPREPLY=($(compgen -c | echo "$scripts" | grep $curr_arg));
COMPREPLY=($(compgen -c | echo "$scripts" | grep -- $curr_arg));
}
complete -F _task_completion task

View File

@@ -1,5 +1,5 @@
function __task_get_tasks --description "Prints all available tasks with their description"
task -l | sed '1d' | awk '{ $1=""; print $0 }' | tr ': ', '\t' | string trim
task -l | sed '1d' | awk '{ $1=""; print $0 }' | sed 's/:\ /\t/g' | 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

View File

@@ -4,7 +4,7 @@
function __list() {
local -a scripts
if [ -f Taskfile.yml ]; then
if [ -f Taskfile.yml ] || [ -f Taskfile.yaml ]; then
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() {}

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)
- [Donate](donate.md)
- [![GitHub](https://icongram.jgog.in/simple/github.svg?color=808080&size=16)GitHub](https://github.com/go-task/task)
- [GitHub](https://github.com/go-task/task)

50
docs/community.md Normal file
View File

@@ -0,0 +1,50 @@
# 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).
### Sublime Text 4 package
There is a convenience wrapper for initializing and running tasks from Sublime Text's command palette. The package is
developed by [@biozz](https://github.com/biozz), the source code is available [here](https://github.com/biozz/sublime-taskfile)
and it is published on Package Control [here](https://packagecontrol.io/packages/Taskfile).
## Installation methods
Some installation methods are maintained by third party:
- [GitHub Actions](https://github.com/arduino/setup-task)
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.

View File

@@ -14,22 +14,14 @@ 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)
- [$50 per month](https://opencollective.com/task/contribute/sponsor-28775/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)
- [Any value - One-time donation](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A&currency_code=USD&source=url)
## PIX (Brazil only)
If you're Brazilian, you can donate any value by
<a href="/pix.png" target="_blank">using this QR Code</a>.

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

View File

@@ -6,7 +6,7 @@
<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="//cdn.jsdelivr.net/npm/docsify-themeable/dist/css/theme-simple.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple.css">
<meta name="google-site-verification" content="VGAYkbdmuaciIDGkBe-eAg9yfZg0C6ostgonbGxxOa0" />
<style>
#logo {
@@ -20,6 +20,10 @@
.app-name-link img {
width: 125px;
}
:root {
--base-font-size: 14px;
--theme-color: #29beb0;
}
</style>
</head>
<body>
@@ -39,6 +43,7 @@
<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/docsify/lib/plugins/search.min.js"></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>

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

@@ -68,12 +68,12 @@ The `task_checksums.txt` file contains the SHA-256 checksum for each file.
#### **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.
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
# For Default Installion to ./bin with debug logging
# For Default Installation to ./bin with debug logging
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
# For Installation To /usr/local/bin for userwide access with debug logging
@@ -87,12 +87,12 @@ sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/b
#### **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)
[this action](https://github.com/arduino/setup-task)
by the Arduino team:
```yaml
- name: Install Task
uses: Arduino/actions/setup-taskfile@master
uses: arduino/setup-task@v1
```
This installation method is community owned.
@@ -107,29 +107,27 @@ This installation method is community owned.
First, make sure you have [Go][go] properly installed and setup.
You can easily install it globally by running:
You can easily install the latest release globally by running:
```bash
go get -u github.com/go-task/task/v3/cmd/task
go install github.com/go-task/task/v3/cmd/task@latest
```
Or you can install into another directory:
```bash
git clone https://github.com/go-task/task
cd task
env GOBIN=/bin go install github.com/go-task/task/v3/cmd/task@latest
```
# Compiling binary to $GOPATH/bin
go install -v ./cmd/task
If using Go 1.15 or earlier, instead use:
# Compiling it to another location.
# Use -o ./task.exe on Windows.
go build -v -o ./task ./cmd/task
```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, instead of compiling the edge (master branch) version.
> released binary.
<!-- tabs:end -->

BIN
docs/pix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -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

@@ -28,7 +28,7 @@ officially supported. On Linux, only `Taskfile.yml` will work, though.
- `version:`
- `includes:`
- Configuration ones, like `output:`, `expansions:` or `silent:`
- Configuration ones, like `output:` or `silent:`
- `vars:`
- `env:`
- `tasks:`
@@ -60,7 +60,6 @@ version: '3'
includes:
docker: ./docker/Taskfile.yml
output: prefixed
expansions: 3
vars:
FOO: bar
env:
@@ -76,7 +75,6 @@ includes:
docker: ./docker/Taskfile.yml
output: prefixed
expansions: 3
vars:
FOO: bar

View File

@@ -78,18 +78,25 @@ setting:
KEYNAME=VALUE
```
```
# testing/.env
ENDPOINT=testing.com
```
```yaml
# Taskfile.yml
version: '3'
dotenv: ['.env']
env:
ENV: testing
dotenv: ['.env', '{{.ENV}}/.env.', '{{.HOME}}/.env']
tasks:
greet:
cmds:
- echo "Using $KEYNAME"
- echo "Using $KEYNAME and endpoint $ENDPOINT"
```
## Including other Taskfiles
@@ -147,6 +154,25 @@ includes:
> This was a deliberate decision to keep use and implementation simple.
> If you disagree, open an GitHub issue and explain your use case. =)
### Optional includes
Includes marked as optional will allow Task to continue execution as normal if
the included file is missing.
```yaml
version: '3'
includes:
tests:
taskfile: ./tests/Taskfile.yml
optional: true
tasks:
greet:
cmds:
- echo "This command can still be successfully executed if ./tests/Taskfile.yml does not exist"
```
## Task directory
By default, tasks will be executed in the directory where the Taskfile is
@@ -446,6 +472,47 @@ tasks:
- echo "I will not run"
```
### Limiting when tasks run
If a task executed by multiple `cmds` or multiple `deps` you can control
when it is executed using `run`. `run` can also be set at the root
of the Taskfile to change the behavior of all the tasks unless explicitly
overridden.
Supported values for `run`:
* `always` (default) always attempt to invoke the task regardless of the
number of previous executions
* `once` only invoke this task once regardless of the number of references
* `when_changed` only invokes the task once for each unique set of variables
passed into the task
```yaml
version: '3'
tasks:
default:
cmds:
- task: generate-file
vars: { CONTENT: '1' }
- task: generate-file
vars: { CONTENT: '2' }
- task: generate-file
vars: { CONTENT: '2' }
generate-file:
run: when_changed
deps:
- install-deps
cmds:
- echo {{.CONTENT}}
install-deps:
run: once
cmds:
- sleep 5 # long operation like installing packages
```
## Variables
When doing interpolation of variables, Task will look for the below.
@@ -520,6 +587,66 @@ tasks:
This works for all types of variables.
## Forwarding CLI arguments to commands
If `--` is given in the CLI, all following parameters 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}}
```
## Doing task cleanup with `defer`
With the `defer` keyword, it's possible to schedule cleanup to be run once
the task finishes. The difference with just putting it as the last command is
that this command will run even when the task fails.
In the example below `rm -rf tmpdir/` will run even if the third command fails:
```yaml
version: '3'
tasks:
default:
cmds:
- mkdir -p tmpdir/
- defer: rm -rf tmpdir/
- echo 'Do work on tmpdir/'
```
If you want to move the cleanup command into another task, that's possible as
well:
```yaml
version: '3'
tasks:
default:
cmds:
- mkdir -p tmpdir/
- defer: { task: cleanup }
- echo 'Do work on tmpdir/'
cleanup: rm -rf tmpdir/
```
> NOTE: Due to the nature of how the
[Go's own `defer` work](https://go.dev/tour/flowcontrol/13), the deferred
commands are executed in the reverse order if you schedule multiple of them.
## Go's template engine
Task parse commands as [Go's template engine][gotemplate] before executing
@@ -548,9 +675,12 @@ Task also adds the following functions:
- `toSlash`: Does nothing on Unix, but on Windows converts a string from `\`
path format to `/`.
- `fromSlash`: Opposite of `toSlash`. Does nothing on Unix, but on Windows
converts a string from `\` path format to `/`.
converts a string from `/` path format to `\`.
- `exeExt`: Returns the right executable extension for the current OS
(`".exe"` for Windows, `""` for others).
- `shellQuote`: Quotes a string to make it safe for use in shell scripts.
Task uses [this Go function](https://pkg.go.dev/mvdan.cc/sh/v3@v3.4.0/syntax#Quote)
for this. The Bash dialect is assumed.
Example:
@@ -612,6 +742,8 @@ would print the following output:
* test: Run all the go tests.
```
If you want to see all tasks, there's a `--list-all` (alias `-a`) flag as well.
## Display summary of task
Running `task --summary task-name` will show a summary of a task.
@@ -862,6 +994,28 @@ $ task default
> The `output` option can also be specified by the `--output` or `-o` flags.
## Interactive CLI application
When running interactive CLI applications inside Task they can sometimes behave
weirdly, specially when the [output mode](#output-syntax) is set to something
other than `interleaved` (the default), or when interactive apps are ran in
parallel with other tasks.
The `interactive: true` tells Task this is an interactive application, and Task
will try to optimize for it:
```yaml
version: '3'
tasks:
cmds:
- vim my-file.txt
interactive: true
```
If you still have problem running an interactive app through Task, please open
an issue about it.
## Short task syntax
Starting on Task v3, you can now write tasks with a shorter syntax if they

22
go.mod
View File

@@ -1,17 +1,17 @@
module github.com/go-task/task/v3
require (
github.com/fatih/color v1.7.0
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb
github.com/joho/godotenv v1.3.0
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.5.1
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
github.com/fatih/color v1.13.0
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
github.com/joho/godotenv v1.4.0
github.com/mattn/go-zglob v0.0.3
github.com/mitchellh/hashstructure/v2 v2.0.2
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-20210220032951-036812b2e83c
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
mvdan.cc/sh/v3 v3.2.1
mvdan.cc/sh/v3 v3.4.2
)
go 1.13
go 1.16

94
go.sum
View File

@@ -1,62 +1,72 @@
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/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-20200516131648-f9bac4e523eb h1:/qbv1F67s6ehqX9mG23cJOeca3FWpOVKgtPfPUMAi0k=
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/frankban/quicktest v1.13.1 h1:xVm/f9seEhZFL9+n5kv5XLrGwy6elc4V9v/XFY2vmd8=
github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
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/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
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/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
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-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
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/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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.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/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.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
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-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665 h1:QOQNt6vCjMpXE7JSK5VvAzJC1byuN3FgTNSBwf+CJgI=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/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=
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.1 h1:uQBpiGM+rEuHse3Q+W7ajuJUeOtFVJUN/6GeX4/dUWE=
mvdan.cc/sh/v3 v3.2.1/go.mod h1:fPQmabBpREM/XQ9YXSU5ZFZ/Sm+PmKP9/vkFHgYKJEI=
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
mvdan.cc/sh/v3 v3.4.2 h1:d3TKODXfZ1bjWU/StENN+GDg5xOzNu5+C8AEu405E5U=
mvdan.cc/sh/v3 v3.4.2/go.mod h1:p/tqPPI4Epfk2rICAe2RoaNd8HBSJ8t9Y2DA9yQlbzY=

28
hash.go Normal file
View File

@@ -0,0 +1,28 @@
package task
import (
"fmt"
"github.com/go-task/task/v3/internal/hash"
"github.com/go-task/task/v3/taskfile"
)
func (e *Executor) GetHash(t *taskfile.Task) (string, error) {
r := t.Run
if r == "" {
r = e.Taskfile.Run
}
var h hash.HashFunc
switch r {
case "always":
h = hash.Empty
case "once":
h = hash.Name
case "when_changed":
h = hash.Hash
default:
return "", fmt.Errorf(`task: invalid run "%s"`, r)
}
return h(t)
}

40
help.go
View File

@@ -9,11 +9,32 @@ import (
"github.com/go-task/task/v3/taskfile"
)
// PrintTasksHelp prints help os tasks that have a description
func (e *Executor) PrintTasksHelp() {
tasks := e.tasksWithDesc()
// ListTasksWithDesc reports tasks that have a description spec.
func (e *Executor) ListTasksWithDesc() {
e.printTasks(false)
return
}
// ListAllTasks reports all tasks, with or without a description spec.
func (e *Executor) ListAllTasks() {
e.printTasks(true)
return
}
func (e *Executor) printTasks(listAll bool) {
var tasks []*taskfile.Task
if listAll {
tasks = e.allTaskNames()
} else {
tasks = e.tasksWithDesc()
}
if len(tasks) == 0 {
e.Logger.Outf(logger.Yellow, "task: No tasks with description available")
if listAll {
e.Logger.Outf(logger.Yellow, "task: No tasks available")
} else {
e.Logger.Outf(logger.Yellow, "task: No tasks with description available. Try --list-all to list all tasks")
}
return
}
e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
@@ -26,11 +47,20 @@ func (e *Executor) PrintTasksHelp() {
w.Flush()
}
func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
for _, task := range e.Taskfile.Tasks {
tasks = append(tasks, task)
}
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
return
}
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

@@ -3,7 +3,6 @@ package task
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
)
@@ -24,15 +23,15 @@ tasks:
// InitTaskfile Taskfile creates a new Taskfile
func InitTaskfile(w io.Writer, dir string) error {
f := filepath.Join(dir, "Taskfile.yml")
f := filepath.Join(dir, "Taskfile.yaml")
if _, err := os.Stat(f); err == nil {
return ErrTaskfileAlreadyExists
}
if err := ioutil.WriteFile(f, []byte(defaultTaskfile), 0644); err != nil {
if err := os.WriteFile(f, []byte(defaultTaskfile), 0644); err != nil {
return err
}
fmt.Fprintf(w, "Taskfile.yml created in the current directory\n")
fmt.Fprintf(w, "Taskfile.yaml created in the current directory\n")
return nil
}

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

@@ -7,7 +7,9 @@ import (
// Compiler handles compilation of a task before its execution.
// E.g. variable merger, template processing, etc.
type Compiler interface {
GetTaskfileVariables() (*taskfile.Vars, 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,7 +4,6 @@ import (
"bytes"
"context"
"fmt"
"path/filepath"
"strings"
"sync"
@@ -31,6 +30,15 @@ type CompilerV2 struct {
muDynamicCache sync.Mutex
}
func (c *CompilerV2) GetTaskfileVariables() (*taskfile.Vars, error) {
return &taskfile.Vars{}, nil
}
// 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
@@ -38,16 +46,8 @@ type CompilerV2 struct {
// 4. Taskvars file variables
// 5. Environment variables
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
// NOTE(@andreynering): We're manually joining these paths here because
// this is the raw task, not the compiled one.
dir := t.Dir
if !filepath.IsAbs(dir) {
dir = filepath.Join(c.Dir, dir)
}
vr := varResolver{
c: c,
dir: dir,
vars: compiler.GetEnviron(),
}
vr.vars.Set("TASK", taskfile.Var{Static: t.Task})
@@ -62,7 +62,6 @@ func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfi
type varResolver struct {
c *CompilerV2
dir string
vars *taskfile.Vars
err error
}
@@ -77,7 +76,7 @@ func (vr *varResolver) merge(vars *taskfile.Vars) {
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),
}
static, err := vr.c.HandleDynamicVar(v, vr.dir)
static, err := vr.c.HandleDynamicVar(v, "")
if err != nil {
vr.err = err
return err
@@ -88,7 +87,7 @@ func (vr *varResolver) merge(vars *taskfile.Vars) {
vr.err = tr.Err()
}
func (c *CompilerV2) HandleDynamicVar(v taskfile.Var, dir string) (string, error) {
func (c *CompilerV2) HandleDynamicVar(v taskfile.Var, _ string) (string, error) {
if v.Static != "" || v.Sh == "" {
return v.Static, nil
}
@@ -106,12 +105,11 @@ func (c *CompilerV2) HandleDynamicVar(v taskfile.Var, dir string) (string, error
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)
return "", fmt.Errorf(`task: Command "%s" failed: %s`, opts.Command, err)
}
// Trim a single trailing newline from the result to make most command

View File

@@ -20,6 +20,7 @@ var _ compiler.Compiler = &CompilerV3{}
type CompilerV3 struct {
Dir string
TaskfileEnv *taskfile.Vars
TaskfileVars *taskfile.Vars
Logger *logger.Logger
@@ -28,41 +29,79 @@ type CompilerV3 struct {
muDynamicCache sync.Mutex
}
func (c *CompilerV3) GetTaskfileVariables() (*taskfile.Vars, error) {
return c.getVariables(nil, nil, true)
}
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})
// NOTE(@andreynering): We're manually joining these paths here because
// this is the raw task, not the compiled one.
dir := t.Dir
if !filepath.IsAbs(dir) {
dir = filepath.Join(c.Dir, dir)
if t != nil {
result.Set("TASK", taskfile.Var{Static: t.Task})
}
rangeFunc := func(k string, v taskfile.Var) error {
tr := templater.Templater{Vars: result, RemoveNoValue: true}
v = taskfile.Var{
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),
}
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
}
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 t == nil || call == nil {
return result, nil
}
if err := call.Vars.Range(rangeFunc); err != nil {
return nil, err
}
if err := t.Vars.Range(rangeFunc); err != nil {
// 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
}
@@ -84,6 +123,11 @@ func (c *CompilerV3) HandleDynamicVar(v taskfile.Var, dir string) (string, error
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,
@@ -92,7 +136,7 @@ func (c *CompilerV3) HandleDynamicVar(v taskfile.Var, dir string) (string, error
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)
return "", fmt.Errorf(`task: Command "%s" failed: %s`, opts.Command, err)
}
// Trim a single trailing newline from the result to make most command

View File

@@ -47,10 +47,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(openHandler),
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
dirOption(opts.Dir),
)
if err != nil {
return err
@@ -87,3 +87,24 @@ func openHandler(ctx context.Context, path string, flag int, perm os.FileMode) (
}
return interp.DefaultOpenHandler()(ctx, path, flag, perm)
}
func dirOption(path string) interp.RunnerOption {
return func(r *interp.Runner) error {
err := interp.Dir(path)(r)
if err == nil {
return nil
}
// If the specified directory doesn't exist, it will be created later.
// Therefore, even if `interp.Dir` method returns an error, the
// directory path should be set only when the directory cannot be found.
if absPath, _ := filepath.Abs(path); absPath != "" {
if _, err := os.Stat(absPath); os.IsNotExist(err) {
r.Dir = absPath
return nil
}
}
return err
}
}

24
internal/hash/hash.go Normal file
View File

@@ -0,0 +1,24 @@
package hash
import (
"fmt"
"github.com/mitchellh/hashstructure/v2"
"github.com/go-task/task/v3/taskfile"
)
type HashFunc func(*taskfile.Task) (string, error)
func Empty(*taskfile.Task) (string, error) {
return "", nil
}
func Name(t *taskfile.Task) (string, error) {
return t.Task, nil
}
func Hash(t *taskfile.Task) (string, error) {
h, err := hashstructure.Hash(t, hashstructure.FormatV2, nil)
return fmt.Sprintf("%s:%d", t.Task, h), err
}

View File

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

View File

@@ -4,7 +4,6 @@ import (
"crypto/md5"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
@@ -30,7 +29,7 @@ func (c *Checksum) IsUpToDate() (bool, error) {
checksumFile := c.checksumFilePath()
data, _ := ioutil.ReadFile(checksumFile)
data, _ := os.ReadFile(checksumFile)
oldMd5 := strings.TrimSpace(string(data))
sources, err := globs(c.TaskDir, c.Sources)
@@ -45,7 +44,7 @@ func (c *Checksum) IsUpToDate() (bool, error) {
if !c.Dry {
_ = os.MkdirAll(filepath.Join(c.BaseDir, ".task", "checksum"), 0755)
if err = ioutil.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
return false, err
}
}
@@ -53,7 +52,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.TaskDir, g)
generates, err := Glob(c.TaskDir, g)
if os.IsNotExist(err) {
return false, nil
}

View File

@@ -13,7 +13,7 @@ import (
func globs(dir string, globs []string) ([]string, error) {
files := make([]string, 0)
for _, g := range globs {
f, err := glob(dir, g)
f, err := Glob(dir, g)
if err != nil {
continue
}
@@ -23,7 +23,7 @@ func globs(dir string, globs []string) ([]string, error) {
return files, nil
}
func glob(dir string, g string) ([]string, error) {
func Glob(dir string, g string) ([]string, error) {
files := make([]string, 0)
if !filepath.IsAbs(g) {
g = filepath.Join(dir, g)

View File

@@ -6,7 +6,8 @@ import (
"strings"
"text/template"
"github.com/go-task/slim-sprig"
sprig "github.com/go-task/slim-sprig"
"mvdan.cc/sh/v3/syntax"
)
var (
@@ -37,6 +38,9 @@ func init() {
}
return ""
},
"shellQuote": func(str string) (string, error) {
return syntax.Quote(str, syntax.LangBash)
},
// IsSH is deprecated.
"IsSH": func() bool { return true },
}

View File

@@ -63,7 +63,7 @@ func (r *Templater) ReplaceSlice(strs []string) []string {
}
func (r *Templater) ReplaceVars(vars *taskfile.Vars) *taskfile.Vars {
if r.err != nil || vars == nil || len(vars.Keys) == 0 {
if r.err != nil || vars.Len() == 0 {
return nil
}

View File

@@ -29,16 +29,35 @@ func (e *Executor) Status(ctx context.Context, calls ...taskfile.Call) error {
}
func (e *Executor) isTaskUpToDate(ctx context.Context, t *taskfile.Task) (bool, error) {
if len(t.Status) == 0 && len(t.Sources) == 0 {
return false, nil
}
if len(t.Status) > 0 {
return e.isTaskUpToDateStatus(ctx, t)
isUpToDate, err := e.isTaskUpToDateStatus(ctx, t)
if err != nil {
return false, err
}
if !isUpToDate {
return false, nil
}
}
checker, err := e.getStatusChecker(t)
if err != nil {
return false, err
if len(t.Sources) > 0 {
checker, err := e.getStatusChecker(t)
if err != nil {
return false, err
}
isUpToDate, err := checker.IsUpToDate()
if err != nil {
return false, err
}
if !isUpToDate {
return false, nil
}
}
return checker.IsUpToDate()
return true, nil
}
func (e *Executor) statusOnError(t *taskfile.Task) error {

233
task.go
View File

@@ -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
@@ -54,8 +55,11 @@ type Executor struct {
taskvars *taskfile.Vars
taskCallCount map[string]*int32
mkdirMutexMap map[string]*sync.Mutex
concurrencySemaphore chan struct{}
taskCallCount map[string]*int32
mkdirMutexMap map[string]*sync.Mutex
executionHashes map[string]context.Context
executionHashesMutex sync.Mutex
}
// Run runs Task
@@ -64,14 +68,14 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
for _, c := range calls {
if _, ok := e.Taskfile.Tasks[c.Task]; !ok {
// FIXME: move to the main package
e.PrintTasksHelp()
e.ListTasksWithDesc()
return &taskNotFoundError{taskName: c.Task}
}
}
if e.Summary {
for i, c := range calls {
compiledTask, err := e.CompiledTask(c)
compiledTask, err := e.FastCompiledTask(c)
if err != nil {
return nil
}
@@ -101,10 +105,6 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
// Setup setups Executor's internal state
func (e *Executor) Setup() error {
if e.Entrypoint == "" {
e.Entrypoint = "Taskfile.yml"
}
var err error
e.Taskfile, err = read.Taskfile(e.Dir, e.Entrypoint)
if err != nil {
@@ -147,9 +147,12 @@ func (e *Executor) Setup() error {
if v == 2.0 {
v = 2.6
}
if v == 3.0 {
v = 3.7
}
if v > 3.0 {
return fmt.Errorf(`task: Taskfile versions greater than v3.0 not implemented in the version of Task`)
if v > 3.7 {
return fmt.Errorf(`task: Taskfile versions greater than v3.7 not implemented in the version of Task`)
}
// Color available only on v3
@@ -168,11 +171,29 @@ func (e *Executor) Setup() error {
} else {
e.Compiler = &compilerv3.CompilerV3{
Dir: e.Dir,
TaskfileEnv: e.Taskfile.Env,
TaskfileVars: e.Taskfile.Vars,
Logger: e.Logger,
}
}
if v >= 3.0 {
env, err := read.Dotenv(e.Compiler, e.Taskfile, e.Dir)
if err != nil {
return err
}
err = env.Range(func(key string, value taskfile.Var) error {
if _, ok := e.Taskfile.Env.Mapping[key]; !ok {
e.Taskfile.Env.Set(key, value)
}
return nil
})
if err != nil {
return err
}
}
if v < 2.1 && e.Taskfile.Output != "" {
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
}
@@ -240,12 +261,34 @@ func (e *Executor) Setup() error {
}
}
if v < 3.7 {
if e.Taskfile.Run != "" {
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
}
for _, task := range e.Taskfile.Tasks {
if task.Run != "" {
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
}
}
}
if e.Taskfile.Run == "" {
e.Taskfile.Run = "always"
}
e.executionHashes = make(map[string]context.Context)
e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks))
e.mkdirMutexMap = make(map[string]*sync.Mutex, len(e.Taskfile.Tasks))
for k := range e.Taskfile.Tasks {
e.taskCallCount[k] = new(int32)
e.mkdirMutexMap[k] = &sync.Mutex{}
}
if e.Concurrency > 0 {
e.concurrencySemaphore = make(chan struct{}, e.Concurrency)
}
return nil
}
@@ -259,48 +302,64 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
return &MaximumTaskCallExceededError{task: call.Task}
}
if err := e.runDeps(ctx, t); err != nil {
return err
}
release := e.acquireConcurrencyLimit()
defer release()
if !e.Force {
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
if err != nil {
return e.startExecution(ctx, t, func(ctx context.Context) error {
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" started`, call.Task)
if err := e.runDeps(ctx, t); err != nil {
return err
}
upToDate, err := e.isTaskUpToDate(ctx, t)
if err != nil {
return err
}
if upToDate && preCondMet {
if !e.Silent {
e.Logger.Errf(logger.Magenta, `task: Task "%s" is up to date`, t.Name())
}
return nil
}
}
if err := e.mkdir(t); err != nil {
e.Logger.Errf(logger.Red, "task: cannot make directory %q: %v", t.Dir, err)
}
for i := range t.Cmds {
if err := e.runCommand(ctx, t, call, i); err != nil {
if err2 := e.statusOnError(t); err2 != nil {
e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v", err2)
if !e.Force {
if err := ctx.Err(); err != nil {
return err
}
if execext.IsExitError(err) && t.IgnoreError {
e.Logger.VerboseErrf(logger.Yellow, "task: task error ignored: %v", err)
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
if err != nil {
return err
}
upToDate, err := e.isTaskUpToDate(ctx, t)
if err != nil {
return err
}
if upToDate && preCondMet {
if !e.Silent {
e.Logger.Errf(logger.Magenta, `task: Task "%s" is up to date`, t.Name())
}
return nil
}
}
if err := e.mkdir(t); err != nil {
e.Logger.Errf(logger.Red, "task: cannot make directory %q: %v", t.Dir, err)
}
for i := range t.Cmds {
if t.Cmds[i].Defer {
defer e.runDeferred(t, call, i)
continue
}
return &taskRunError{t.Task, err}
if err := e.runCommand(ctx, t, call, i); err != nil {
if err2 := e.statusOnError(t); err2 != nil {
e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v", err2)
}
if execext.IsExitError(err) && t.IgnoreError {
e.Logger.VerboseErrf(logger.Yellow, "task: task error ignored: %v", err)
continue
}
return &taskRunError{t.Task, err}
}
}
}
return nil
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" finished`, call.Task)
return nil
})
}
func (e *Executor) mkdir(t *taskfile.Task) error {
@@ -323,6 +382,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
@@ -338,11 +400,23 @@ func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error {
return g.Wait()
}
func (e *Executor) runDeferred(t *taskfile.Task, call taskfile.Call, i int) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := e.runCommand(ctx, t, call, i); err != nil {
e.Logger.VerboseErrf(logger.Yellow, `task: ignored error in deferred cmd: %s`, err.Error())
}
}
func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfile.Call, i int) error {
cmd := t.Cmds[i]
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
@@ -350,15 +424,20 @@ 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 {
return nil
}
stdOut := e.Output.WrapWriter(e.Stdout, t.Prefix)
stdErr := e.Output.WrapWriter(e.Stderr, t.Prefix)
outputWrapper := e.Output
if t.Interactive {
outputWrapper = output.Interleaved{}
}
stdOut := outputWrapper.WrapWriter(e.Stdout, t.Prefix)
stdErr := outputWrapper.WrapWriter(e.Stderr, t.Prefix)
defer func() {
if _, ok := stdOut.(*os.File); !ok {
if closer, ok := stdOut.(io.Closer); ok {
@@ -381,7 +460,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
@@ -396,10 +475,48 @@ 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
}
func (e *Executor) startExecution(ctx context.Context, t *taskfile.Task, execute func(ctx context.Context) error) error {
h, err := e.GetHash(t)
if err != nil {
return err
}
if h == "" {
return execute(ctx)
}
e.executionHashesMutex.Lock()
otherExecutionCtx, ok := e.executionHashes[h]
if ok {
e.executionHashesMutex.Unlock()
e.Logger.VerboseErrf(logger.Magenta, "task: skipping execution of task: %s", h)
<-otherExecutionCtx.Done()
return nil
}
ctx, cancel := context.WithCancel(ctx)
defer cancel()
e.executionHashes[h] = ctx
e.executionHashesMutex.Unlock()
return execute(ctx)
}

View File

@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"fmt"
"io/ioutil"
"io"
"os"
"path/filepath"
"runtime"
@@ -17,13 +17,18 @@ import (
"github.com/go-task/task/v3/taskfile"
)
func init() {
_ = os.Setenv("NO_COLOR", "1")
}
// fileContentTest provides a basic reusable test-case for running a Taskfile
// and inspect generated files.
type fileContentTest struct {
Dir string
Target string
TrimSpace bool
Files map[string]string
Dir string
Entrypoint string
Target string
TrimSpace bool
Files map[string]string
}
func (fct fileContentTest) name(file string) string {
@@ -36,16 +41,17 @@ func (fct fileContentTest) Run(t *testing.T) {
}
e := &task.Executor{
Dir: fct.Dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Dir: fct.Dir,
Entrypoint: fct.Entrypoint,
Stdout: io.Discard,
Stderr: io.Discard,
}
assert.NoError(t, e.Setup(), "e.Setup()")
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: fct.Target}), "e.Run(target)")
for name, expectContent := range fct.Files {
t.Run(fct.name(name), func(t *testing.T) {
b, err := ioutil.ReadFile(filepath.Join(fct.Dir, name))
b, err := os.ReadFile(filepath.Join(fct.Dir, name))
assert.NoError(t, err, "Error reading file")
s := string(b)
if fct.TrimSpace {
@@ -59,8 +65,8 @@ 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,
Stdout: io.Discard,
Stderr: io.Discard,
}
assert.NoError(t, e.Setup(), "e.Setup()")
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
@@ -126,6 +132,7 @@ func TestVarsV3(t *testing.T) {
"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)
@@ -163,13 +170,29 @@ func TestVarsInvalidTmpl(t *testing.T) {
e := &task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
}
assert.NoError(t, e.Setup(), "e.Setup()")
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: io.Discard,
Stderr: io.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",
@@ -215,8 +238,8 @@ func TestDeps(t *testing.T) {
e := &task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
@@ -231,12 +254,18 @@ func TestDeps(t *testing.T) {
func TestStatus(t *testing.T) {
const dir = "testdata/status"
var file = filepath.Join(dir, "foo.txt")
_ = os.Remove(file)
files := []string{
"foo.txt",
"bar.txt",
}
if _, err := os.Stat(file); err == nil {
t.Errorf("File should not exist: %v", err)
for _, f := range files {
path := filepath.Join(dir, f)
_ = os.Remove(path)
if _, err := os.Stat(path); err == nil {
t.Errorf("File should not exist: %v", err)
}
}
var buff bytes.Buffer
@@ -248,17 +277,33 @@ func TestStatus(t *testing.T) {
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
if _, err := os.Stat(file); err != nil {
t.Errorf("File should exist: %v", err)
for _, f := range files {
if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
t.Errorf("File should exist: %v", err)
}
}
e.Silent = false
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
if buff.String() != `task: Task "gen-foo" is up to date`+"\n" {
t.Errorf("Wrong output message: %s", buff.String())
}
// all: not up-to-date
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
assert.Equal(t, "task: [gen-foo] touch foo.txt", strings.TrimSpace(buff.String()))
buff.Reset()
// status: not up-to-date
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
assert.Equal(t, "task: [gen-foo] touch foo.txt", strings.TrimSpace(buff.String()))
buff.Reset()
// sources: not up-to-date
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
assert.Equal(t, "task: [gen-bar] touch bar.txt", strings.TrimSpace(buff.String()))
buff.Reset()
// all: up-to-date
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
assert.Equal(t, `task: Task "gen-bar" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
}
func TestPrecondition(t *testing.T) {
@@ -308,7 +353,7 @@ func TestGenerates(t *testing.T) {
const (
srcTask = "sub/src.txt"
relTask = "rel.txt"
absTask = "sub/abs.txt"
absTask = "abs.txt"
fileWithSpaces = "my text file.txt"
)
@@ -473,10 +518,58 @@ func TestLabelInList(t *testing.T) {
Stderr: &buff,
}
assert.NoError(t, e.Setup())
e.PrintTasksHelp()
e.ListTasksWithDesc()
assert.Contains(t, buff.String(), "foobar")
}
// task -al case 1: listAll list all tasks
func TestListAllShowsNoDesc(t *testing.T) {
const dir = "testdata/list_mixed_desc"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
var title string
e.ListAllTasks()
for _, title = range []string{
"foo",
"voo",
"doo",
} {
assert.Contains(t, buff.String(), title)
}
}
// task -al case 2: !listAll list some tasks (only those with desc)
func TestListCanListDescOnly(t *testing.T) {
const dir = "testdata/list_mixed_desc"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
e.ListTasksWithDesc()
var title string
assert.Contains(t, buff.String(), "foo")
for _, title = range []string{
"voo",
"doo",
} {
assert.NotContains(t, buff.String(), title)
}
}
func TestStatusVariables(t *testing.T) {
const dir = "testdata/status_vars"
@@ -507,20 +600,21 @@ func TestStatusVariables(t *testing.T) {
func TestInit(t *testing.T) {
const dir = "testdata/init"
var file = filepath.Join(dir, "Taskfile.yml")
var file = filepath.Join(dir, "Taskfile.yaml")
_ = os.Remove(file)
if _, err := os.Stat(file); err == nil {
t.Errorf("Taskfile.yml should not exist")
t.Errorf("Taskfile.yaml should not exist")
}
if err := task.InitTaskfile(ioutil.Discard, dir); err != nil {
if err := task.InitTaskfile(io.Discard, dir); err != nil {
t.Error(err)
}
if _, err := os.Stat(file); err != nil {
t.Errorf("Taskfile.yml should exist")
t.Errorf("Taskfile.yaml should exist")
}
_ = os.Remove(file)
}
func TestCyclicDep(t *testing.T) {
@@ -528,8 +622,8 @@ func TestCyclicDep(t *testing.T) {
e := task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
}
assert.NoError(t, e.Setup())
assert.IsType(t, &task.MaximumTaskCallExceededError{}, e.Run(context.Background(), taskfile.Call{Task: "task-1"}))
@@ -547,8 +641,8 @@ func TestTaskVersion(t *testing.T) {
t.Run(test.Dir, func(t *testing.T) {
e := task.Executor{
Dir: test.Dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
}
assert.NoError(t, e.Setup())
assert.Equal(t, test.Version, e.Taskfile.Version)
@@ -562,8 +656,8 @@ func TestTaskIgnoreErrors(t *testing.T) {
e := task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
}
assert.NoError(t, e.Setup())
@@ -609,7 +703,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)
}
@@ -625,8 +719,8 @@ func TestDryChecksum(t *testing.T) {
e := task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
Dry: true,
}
assert.NoError(t, e.Setup())
@@ -712,6 +806,56 @@ func TestIncludesCallingRoot(t *testing.T) {
tt.Run(t)
}
func TestIncludesOptional(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/includes_optional",
Target: "default",
TrimSpace: true,
Files: map[string]string{
"called_dep.txt": "called_dep",
}}
tt.Run(t)
}
func TestIncludesOptionalImplicitFalse(t *testing.T) {
e := task.Executor{
Dir: "testdata/includes_optional_implicit_false",
Stdout: io.Discard,
Stderr: io.Discard,
}
err := e.Setup()
assert.Error(t, err)
assert.Equal(t, "stat testdata/includes_optional_implicit_false/TaskfileOptional.yml: no such file or directory", err.Error())
}
func TestIncludesOptionalExplicitFalse(t *testing.T) {
e := task.Executor{
Dir: "testdata/includes_optional_explicit_false",
Stdout: io.Discard,
Stderr: io.Discard,
}
err := e.Setup()
assert.Error(t, err)
assert.Equal(t, "stat testdata/includes_optional_explicit_false/TaskfileOptional.yml: no such file or directory", err.Error())
}
func TestIncludesFromCustomTaskfile(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/includes_yaml",
Entrypoint: "Custom.ext",
Target: "default",
TrimSpace: true,
Files: map[string]string{
"main.txt": "main",
"included_with_yaml_extension.txt": "included_with_yaml_extension",
"included_with_custom_file.txt": "included_with_custom_file",
},
}
tt.Run(t)
}
func TestSummary(t *testing.T) {
const dir = "testdata/summary"
@@ -726,7 +870,7 @@ func TestSummary(t *testing.T) {
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-summary"}, taskfile.Call{Task: "other-task-with-summary"}))
data, err := ioutil.ReadFile(filepath.Join(dir, "task-with-summary.txt"))
data, err := os.ReadFile(filepath.Join(dir, "task-with-summary.txt"))
assert.NoError(t, err)
expectedOutput := string(data)
@@ -799,13 +943,43 @@ func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) {
_ = os.RemoveAll(toBeCreated)
}
func TestDynamicVariablesRunOnTheNewCreatedDir(t *testing.T) {
const expected = "created"
const dir = "testdata/dir/dynamic_var_on_created_dir/"
const toBeCreated = dir + expected
const target = "default"
var out bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &out,
Stderr: &out,
}
// Ensure that the directory to be created doesn't actually exist.
_ = os.RemoveAll(toBeCreated)
if _, err := os.Stat(toBeCreated); err == nil {
t.Errorf("Directory should not exist: %v", err)
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: target}))
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
assert.Equal(t, expected, got, "Mismatch in the working directory")
// Clean-up after ourselves only if no error.
_ = os.RemoveAll(toBeCreated)
}
func TestDynamicVariablesShouldRunOnTheTaskDir(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/dir/dynamic_var",
Target: "default",
TrimSpace: false,
Files: map[string]string{
"subdirectory/dir.txt": "subdirectory\n",
"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)
@@ -814,8 +988,8 @@ func TestDynamicVariablesShouldRunOnTheTaskDir(t *testing.T) {
func TestDisplaysErrorOnUnsupportedVersion(t *testing.T) {
e := task.Executor{
Dir: "testdata/version/v1",
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
}
err := e.Setup()
assert.Error(t, err)
@@ -880,6 +1054,44 @@ func TestDotenvShouldAllowMissingEnv(t *testing.T) {
tt.Run(t)
}
func TestDotenvHasLocalEnvInPath(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/dotenv/local_env_in_path",
Target: "default",
TrimSpace: false,
Files: map[string]string{
"var.txt": "VAR='var_in_dot_env_1'\n",
},
}
tt.Run(t)
}
func TestDotenvHasLocalVarInPath(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/dotenv/local_var_in_path",
Target: "default",
TrimSpace: false,
Files: map[string]string{
"var.txt": "VAR='var_in_dot_env_3'\n",
},
}
tt.Run(t)
}
func TestDotenvHasEnvVarInPath(t *testing.T) {
os.Setenv("ENV_VAR", "testing")
tt := fileContentTest{
Dir: "testdata/dotenv/env_var_in_path",
Target: "default",
TrimSpace: false,
Files: map[string]string{
"var.txt": "VAR='var_in_dot_env_2'\n",
},
}
tt.Run(t)
}
func TestExitImmediately(t *testing.T) {
const dir = "testdata/exit_immediately"
@@ -895,3 +1107,67 @@ func TestExitImmediately(t *testing.T) {
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`)
}
func TestRunOnlyRunsJobsHashOnce(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/run",
Target: "generate-hash",
Files: map[string]string{
"hash.txt": "starting 1\n1\n2\n",
},
}
tt.Run(t)
}
func TestDeferredCmds(t *testing.T) {
const dir = "testdata/deferred"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
expectedOutputOrder := strings.TrimSpace(`
task: [task-2] echo 'cmd ran'
cmd ran
task: [task-2] exit 1
task: [task-2] echo 'failing' && exit 2
failing
task: [task-2] echo 'echo ran'
echo ran
task: [task-1] echo 'task-1 ran successfully'
task-1 ran successfully
`)
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "task-2"}))
fmt.Println(buff.String())
assert.Contains(t, buff.String(), expectedOutputOrder)
}
func TestIgnoreNilElements(t *testing.T) {
tests := []struct {
name string
dir string
}{
{"nil cmd", "testdata/ignore_nil_elements/cmds"},
{"nil dep", "testdata/ignore_nil_elements/deps"},
{"nil include", "testdata/ignore_nil_elements/includes"},
{"nil precondition", "testdata/ignore_nil_elements/preconditions"},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var buff bytes.Buffer
e := task.Executor{
Dir: test.dir,
Stdout: &buff,
Stderr: &buff,
Silent: true,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
assert.Equal(t, "string-slice-1\n", buff.String())
})
}
}

View File

@@ -1,10 +1,5 @@
package taskfile
import (
"errors"
"strings"
)
// Cmd is a task command
type Cmd struct {
Cmd string
@@ -12,6 +7,7 @@ type Cmd struct {
Task string
Vars *Vars
IgnoreError bool
Defer bool
}
// Dep is a task dependency
@@ -20,22 +16,11 @@ type Dep struct {
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
if err := unmarshal(&cmd); err == nil {
if strings.HasPrefix(cmd, "^") {
c.Task = strings.TrimPrefix(cmd, "^")
} else {
c.Cmd = cmd
}
c.Cmd = cmd
return nil
}
var cmdStruct struct {
@@ -49,16 +34,33 @@ func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
c.IgnoreError = cmdStruct.IgnoreError
return nil
}
var deferredCmd struct {
Defer string
}
if err := unmarshal(&deferredCmd); err == nil && deferredCmd.Defer != "" {
c.Defer = true
c.Cmd = deferredCmd.Defer
return nil
}
var deferredCall struct {
Defer Call
}
if err := unmarshal(&deferredCall); err == nil && deferredCall.Defer.Task != "" {
c.Defer = true
c.Task = deferredCall.Defer.Task
c.Vars = deferredCall.Defer.Vars
return nil
}
var taskCall struct {
Task string
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
@@ -72,10 +74,10 @@ func (d *Dep) UnmarshalYAML(unmarshal func(interface{}) error) error {
Task string
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

@@ -6,15 +6,11 @@ import (
"gopkg.in/yaml.v3"
)
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
Optional bool
AdvancedImport bool
}
@@ -97,13 +93,14 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
var includedTaskfile struct {
Taskfile string
Dir string
Optional bool
}
if err := unmarshal(&includedTaskfile); err == nil {
it.Dir = includedTaskfile.Dir
it.Taskfile = includedTaskfile.Taskfile
it.AdvancedImport = true
return nil
if err := unmarshal(&includedTaskfile); err != nil {
return err
}
return ErrCantUnmarshalIncludedTaskfile
it.Taskfile = includedTaskfile.Taskfile
it.Dir = includedTaskfile.Dir
it.Optional = includedTaskfile.Optional
it.AdvancedImport = true
return nil
}

View File

@@ -49,7 +49,7 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
dep.Task = taskNameWithNamespace(dep.Task, namespaces...)
}
for _, cmd := range v.Cmds {
if cmd.Task != "" {
if cmd != nil && cmd.Task != "" {
cmd.Task = taskNameWithNamespace(cmd.Task, namespaces...)
}
}

50
taskfile/read/dotenv.go Normal file
View File

@@ -0,0 +1,50 @@
package read
import (
"os"
"path/filepath"
"github.com/joho/godotenv"
"github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
)
func Dotenv(c compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.Vars, error) {
if len(tf.Dotenv) == 0 {
return nil, nil
}
vars, err := c.GetTaskfileVariables()
if err != nil {
return nil, err
}
env := &taskfile.Vars{}
tr := templater.Templater{Vars: vars, RemoveNoValue: true}
for _, dotEnvPath := range tf.Dotenv {
dotEnvPath = tr.Replace(dotEnvPath)
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 := env.Mapping[key]; !ok {
env.Set(key, taskfile.Var{Static: value})
}
}
}
return env, nil
}

View File

@@ -7,9 +7,9 @@ import (
"path/filepath"
"runtime"
"github.com/joho/godotenv"
"gopkg.in/yaml.v3"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
)
@@ -19,14 +19,26 @@ var (
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")
defaultTaskfiles = []string{"Taskfile.yml", "Taskfile.yaml"}
)
// Taskfile reads a Taskfile for a given directory
// Uses current dir when dir is left empty. Uses Taskfile.yml
// or Taskfile.yaml when entrypoint is left empty
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)
if dir == "" {
d, err := os.Getwd()
if err != nil {
return nil, err
}
dir = d
}
path, err := exists(filepath.Join(dir, entrypoint))
if err != nil {
return nil, err
}
t, err := readTaskfile(path)
if err != nil {
return nil, err
@@ -37,33 +49,13 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
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),
Optional: includedTask.Optional,
AdvancedImport: includedTask.AdvancedImport,
}
if err := tr.Err(); err != nil {
@@ -71,19 +63,22 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
}
}
if filepath.IsAbs(includedTask.Taskfile) {
path = includedTask.Taskfile
} else {
path = filepath.Join(dir, includedTask.Taskfile)
}
info, err := os.Stat(path)
path, err := execext.Expand(includedTask.Taskfile)
if err != nil {
return err
}
if info.IsDir() {
path = filepath.Join(path, "Taskfile.yml")
if !filepath.IsAbs(path) {
path = filepath.Join(dir, path)
}
path, err = exists(path)
if err != nil {
if includedTask.Optional {
return nil
}
return err
}
includedTaskfile, err := readTaskfile(path)
if err != nil {
return err
@@ -97,6 +92,17 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
}
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)
@@ -145,3 +151,22 @@ func readTaskfile(file string) (*taskfile.Taskfile, error) {
var t taskfile.Taskfile
return &t, yaml.NewDecoder(f).Decode(&t)
}
func exists(path string) (string, error) {
fi, err := os.Stat(path)
if err != nil {
return "", err
}
if fi.Mode().IsRegular() {
return path, nil
}
for _, n := range defaultTaskfiles {
fpath := filepath.Join(path, n)
if _, err := os.Stat(fpath); err == nil {
return fpath, nil
}
}
return "", fmt.Errorf(`task: No Taskfile found in "%s". Use "task --init" to create a new one`, path)
}

View File

@@ -1,9 +1,5 @@
package taskfile
import (
"errors"
)
// Tasks represents a group of tasks
type Tasks map[string]*Task
@@ -23,16 +19,13 @@ type Task struct {
Vars *Vars
Env *Vars
Silent bool
Interactive bool
Method string
Prefix string
IgnoreError bool
Run string
}
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
@@ -67,30 +60,32 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
Vars *Vars
Env *Vars
Silent bool
Interactive bool
Method string
Prefix string
IgnoreError bool `yaml:"ignore_error"`
Run string
}
if err := unmarshal(&task); err == nil {
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
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.Interactive = task.Interactive
t.Method = task.Method
t.Prefix = task.Prefix
t.IgnoreError = task.IgnoreError
t.Run = task.Run
return nil
}

View File

@@ -17,6 +17,7 @@ type Taskfile struct {
Tasks Tasks
Silent bool
Dotenv []string
Run string
}
// UnmarshalYAML implements yaml.Unmarshaler interface
@@ -32,6 +33,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
Tasks Tasks
Silent bool
Dotenv []string
Run string
}
if err := unmarshal(&taskfile); err != nil {
return err
@@ -46,6 +48,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
tf.Tasks = taskfile.Tasks
tf.Silent = taskfile.Silent
tf.Dotenv = taskfile.Dotenv
tf.Run = taskfile.Run
if tf.Expansions <= 0 {
tf.Expansions = 2
}

View File

@@ -19,6 +19,8 @@ vars:
PARAM1: VALUE1
PARAM2: VALUE2
`
yamlDeferredCall = `defer: { task: some_task, vars: { PARAM1: "var" } }`
yamlDeferredCmd = `defer: echo 'test'`
)
tests := []struct {
content string
@@ -41,6 +43,21 @@ vars:
},
}},
},
{
yamlDeferredCmd,
&taskfile.Cmd{},
&taskfile.Cmd{Cmd: "echo 'test'", Defer: true},
},
{
yamlDeferredCall,
&taskfile.Cmd{},
&taskfile.Cmd{Task: "some_task", Vars: &taskfile.Vars{
Keys: []string{"PARAM1"},
Mapping: map[string]taskfile.Var{
"PARAM1": taskfile.Var{Static: "var"},
},
}, Defer: true},
},
{
yamlDep,
&taskfile.Dep{},

View File

@@ -2,16 +2,10 @@ package taskfile
import (
"errors"
"strings"
"gopkg.in/yaml.v3"
)
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 struct {
Keys []string
@@ -75,7 +69,7 @@ func (vs *Vars) Range(yield func(key string, value Var) error) error {
// 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.Keys))
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
@@ -93,32 +87,36 @@ func (vs *Vars) ToCacheMap() (m map[string]interface{}) {
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
}
v.Static = str
return nil
}
var sh struct {
Sh string
}
if err := unmarshal(&sh); err == nil {
v.Sh = sh.Sh
return nil
if err := unmarshal(&sh); err != nil {
return err
}
return ErrCantUnmarshalVar
v.Sh = sh.Sh
return nil
}

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

12
testdata/deferred/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
version: "3"
tasks:
task-1:
- echo 'task-1 ran {{.PARAM}}'
task-2:
- defer: { task: "task-1", vars: { PARAM: "successfully" } }
- defer: echo 'echo ran'
- defer: echo 'failing' && exit 2
- echo 'cmd ran'
- exit 1

View File

@@ -1 +1 @@
subdirectory/dir.txt
*.txt

View File

@@ -1,11 +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 '{{.FOLDER}}' > dir.txt
- echo '{{.TASK_DIR}}' > from_root_taskfile.txt
dir: subdirectory
vars:
FOLDER:
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 +0,0 @@
subdirectory

View File

@@ -0,0 +1,10 @@
version: '3'
tasks:
default:
dir: created
cmds:
- echo {{.TASK_DIR}}
vars:
TASK_DIR:
sh: echo $(pwd)

View File

@@ -0,0 +1 @@
VAR_IN_DOTENV=var_in_dot_env_2

View File

@@ -0,0 +1,8 @@
version: "3"
dotenv: [".env.{{.ENV_VAR}}"]
tasks:
default:
cmds:
- echo "VAR='$VAR_IN_DOTENV'" > var.txt

View File

@@ -0,0 +1 @@
VAR_IN_DOTENV=var_in_dot_env_1

View File

@@ -0,0 +1,11 @@
version: "3"
env:
LOCAL_ENV: testing
dotenv: [".env.{{.LOCAL_ENV}}"]
tasks:
default:
cmds:
- echo "VAR='$VAR_IN_DOTENV'" > var.txt

View File

@@ -0,0 +1 @@
VAR_IN_DOTENV=var_in_dot_env_3

View File

@@ -0,0 +1,13 @@
version: "3"
vars:
PART_1: test
PART_2: ing
LOCAL_VAR: "{{.PART_1}}{{.PART_2}}"
dotenv: [".env.{{.LOCAL_VAR}}"]
tasks:
default:
cmds:
- echo "VAR='$VAR_IN_DOTENV'" > var.txt

View File

@@ -1,10 +1,11 @@
version: '3'
vars:
BUILD_DIR: $pwd
BUILD_DIR:
sh: pwd
tasks:
sub/abs.txt:
abs.txt:
desc: generates dest file based on absolute paths
deps:
- sub/src.txt

View File

@@ -0,0 +1,7 @@
version: '3'
tasks:
default:
cmds:
- echo "string-slice-1"
-

View File

@@ -0,0 +1,11 @@
version: '3'
tasks:
default:
deps:
-
- task: dep
dep:
cmds:
- echo "string-slice-1"

View File

@@ -0,0 +1,9 @@
version: '3'
includes:
inc: inc.yml
tasks:
default:
cmds:
- task: inc:default

View File

@@ -0,0 +1,7 @@
version: '3'
tasks:
default:
cmds:
-
- echo "string-slice-1"

View File

@@ -0,0 +1,9 @@
version: '3'
tasks:
default:
preconditions:
-
- sh: "[ 1 = 1 ]"
cmds:
- echo "string-slice-1"

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

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

11
testdata/includes_optional/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
version: '3'
includes:
included:
taskfile: TaskfileOptional.yml
optional: true
tasks:
default:
cmds:
- echo "called_dep" > called_dep.txt

View File

@@ -0,0 +1,11 @@
version: '3'
includes:
included:
taskfile: TaskfileOptional.yml
optional: false
tasks:
default:
cmds:
- echo "Hello, world!"

View File

@@ -0,0 +1,9 @@
version: '3'
includes:
included: TaskfileOptional.yml
tasks:
default:
cmds:
- echo "Hello, world!"

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

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

16
testdata/includes_yaml/Custom.ext vendored Normal file
View File

@@ -0,0 +1,16 @@
version: '3'
includes:
included: ./included
custom: ./included/custom.yaml
tasks:
default:
cmds:
- task: gen
- task: included:gen
- task: custom:gen
gen:
cmds:
- echo main > main.txt

View File

@@ -0,0 +1,6 @@
version: '3'
tasks:
gen:
cmds:
- echo included_with_yaml_extension > included_with_yaml_extension.txt

View File

@@ -0,0 +1,6 @@
version: '3'
tasks:
gen:
cmds:
- echo included_with_custom_file > included_with_custom_file.txt

12
testdata/list_mixed_desc/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
version: '3'
tasks:
foo:
label: "foobar"
desc: "foo has desc and label"
voo:
label: "voo has no desc"
doo:
label: "doo has desc, no label"

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

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

24
testdata/run/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
version: '3.7'
run: when_changed
tasks:
generate-hash:
- rm -f hash.txt
- task: input-content
vars: { CONTENT: '1' }
- task: input-content
vars: { CONTENT: '2' }
- task: input-content
vars: { CONTENT: '2' }
input-content:
deps:
- task: create-output
vars: { CONTENT: '1' }
cmds:
- echo {{.CONTENT}} >> hash.txt
create-output:
run: once
cmds:
- echo starting {{.CONTENT}} >> hash.txt

View File

@@ -4,5 +4,15 @@ tasks:
gen-foo:
cmds:
- touch foo.txt
sources:
- ./foo.txt
status:
- test -f foo.txt
- test 1 = 0
gen-bar:
cmds:
- touch bar.txt
sources:
- ./bar.txt
status:
- test 1 = 1

View File

@@ -35,7 +35,8 @@ tasks:
- echo '{{.TASK}}' > task_name.txt
vars:
FOO: foo
BAR: $echo bar
BAR:
sh: echo bar
BAZ:
sh: echo baz
TMPL_FOO: "{{.FOO}}"

View File

@@ -1,5 +1,6 @@
FOO2: foo2
BAR2: $echo bar2
BAR2:
sh: echo bar2
BAZ2:
sh: echo baz2
TMPL2_FOO: "{{.FOO}}"

1
testdata/vars/v3/.env vendored Normal file
View File

@@ -0,0 +1 @@
DOT_ENV_VAR=From .env file

View File

@@ -1,5 +1,7 @@
version: '3'
dotenv: [.env]
vars:
VAR_A: A
VAR_B: '{{.VAR_A}}B'
@@ -15,6 +17,7 @@ tasks:
- task: var-order
- task: dependent-sh
- task: with-call
- task: from-dot-env
missing-var: echo '{{.NON_EXISTING_VAR}}' > missing-var.txt
@@ -44,3 +47,5 @@ tasks:
MESSAGE: Hi, {{.ABC123}}!
cmds:
- echo "{{.MESSAGE}}" > with-call.txt
from-dot-env: echo '{{.DOT_ENV_VAR}}' > from-dot-env.txt

View File

@@ -13,12 +13,27 @@ import (
// CompiledTask returns a copy of a task, but replacing variables in almost all
// properties using the Go template package.
func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
return e.compiledTask(call, true)
}
// FastCompiledTask is like CompiledTask, but it skippes dynamic variables.
func (e *Executor) FastCompiledTask(call taskfile.Call) (*taskfile.Task, error) {
return e.compiledTask(call, false)
}
func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskfile.Task, error) {
origTask, ok := e.Taskfile.Tasks[call.Task]
if !ok {
return nil, &taskNotFoundError{call.Task}
}
vars, err := e.Compiler.GetVariables(origTask, call)
var vars *taskfile.Vars
var err error
if evaluateShVars {
vars, err = e.Compiler.GetVariables(origTask, call)
} else {
vars, err = e.Compiler.FastGetVariables(origTask, call)
}
if err != nil {
return nil, err
}
@@ -41,9 +56,11 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
Vars: nil,
Env: nil,
Silent: origTask.Silent,
Interactive: origTask.Interactive,
Method: r.Replace(origTask.Method),
Prefix: r.Replace(origTask.Prefix),
IgnoreError: origTask.IgnoreError,
Run: r.Replace(origTask.Run),
}
new.Dir, err = execext.Expand(new.Dir)
if err != nil {
@@ -59,47 +76,59 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
new.Env = &taskfile.Vars{}
new.Env.Merge(r.ReplaceVars(e.Taskfile.Env))
new.Env.Merge(r.ReplaceVars(origTask.Env))
err = new.Env.Range(func(k string, v taskfile.Var) error {
static, err := e.Compiler.HandleDynamicVar(v, new.Dir)
if evaluateShVars {
err = new.Env.Range(func(k string, v taskfile.Var) error {
static, err := e.Compiler.HandleDynamicVar(v, new.Dir)
if err != nil {
return err
}
new.Env.Set(k, taskfile.Var{Static: static})
return nil
})
if err != nil {
return err
return nil, err
}
new.Env.Set(k, taskfile.Var{Static: static})
return nil
})
if err != nil {
return nil, err
}
if len(origTask.Cmds) > 0 {
new.Cmds = make([]*taskfile.Cmd, len(origTask.Cmds))
for i, cmd := range origTask.Cmds {
new.Cmds[i] = &taskfile.Cmd{
new.Cmds = make([]*taskfile.Cmd, 0, len(origTask.Cmds))
for _, cmd := range origTask.Cmds {
if cmd == nil {
continue
}
new.Cmds = append(new.Cmds, &taskfile.Cmd{
Task: r.Replace(cmd.Task),
Silent: cmd.Silent,
Cmd: r.Replace(cmd.Cmd),
Vars: r.ReplaceVars(cmd.Vars),
IgnoreError: cmd.IgnoreError,
}
Defer: cmd.Defer,
})
}
}
if len(origTask.Deps) > 0 {
new.Deps = make([]*taskfile.Dep, len(origTask.Deps))
for i, dep := range origTask.Deps {
new.Deps[i] = &taskfile.Dep{
new.Deps = make([]*taskfile.Dep, 0, len(origTask.Deps))
for _, dep := range origTask.Deps {
if dep == nil {
continue
}
new.Deps = append(new.Deps, &taskfile.Dep{
Task: r.Replace(dep.Task),
Vars: r.ReplaceVars(dep.Vars),
}
})
}
}
if len(origTask.Preconditions) > 0 {
new.Preconditions = make([]*taskfile.Precondition, len(origTask.Preconditions))
for i, precond := range origTask.Preconditions {
new.Preconditions[i] = &taskfile.Precondition{
new.Preconditions = make([]*taskfile.Precondition, 0, len(origTask.Preconditions))
for _, precond := range origTask.Preconditions {
if precond == nil {
continue
}
new.Preconditions = append(new.Preconditions, &taskfile.Precondition{
Sh: r.Replace(precond.Sh),
Msg: r.Replace(precond.Msg),
}
})
}
}

View File

@@ -2,6 +2,7 @@ package task
import (
"context"
"fmt"
"os"
"os/signal"
"path/filepath"
@@ -10,8 +11,8 @@ import (
"time"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/status"
"github.com/go-task/task/v3/taskfile"
"github.com/mattn/go-zglob"
"github.com/radovskyb/watcher"
)
@@ -127,9 +128,9 @@ func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...taskfile.Ca
}
for _, s := range task.Sources {
files, err := zglob.Glob(s)
files, err := status.Glob(task.Dir, s)
if err != nil {
return err
return fmt.Errorf("task: %s: %w", s, err)
}
for _, f := range files {
absFile, err := filepath.Abs(f)