Compare commits

..

229 Commits

Author SHA1 Message Date
Andrey Nering
d8555e5a5d v3.11.0 2022-02-19 19:40:29 -03:00
Andrey Nering
b323531dd5 Improvements and CHANGELOG for #651 2022-02-19 19:31:27 -03:00
Andrey Nering
cfb665310e Merge branch 'group-begin-message' of https://github.com/janslow/task into janslow-group-begin-message 2022-02-19 18:42:34 -03:00
Andrey Nering
51c6ebcd4d Add tests, documentation and changelog for #666 2022-02-19 18:24:43 -03:00
Andrey Nering
e94d1b6b9f Merge branch 'task-498-taskfile-dist' of https://github.com/tylermmorton/task into tylermmorton-task-498-taskfile-dist 2022-02-19 18:01:00 -03:00
Andrey Nering
ca7b32105d Merge pull request #674 from go-task/dependabot/go_modules/mvdan.cc/sh/v3-3.4.3
Bump mvdan.cc/sh/v3 from 3.4.2 to 3.4.3
2022-02-19 17:55:30 -03:00
dependabot[bot]
264db2737b Bump mvdan.cc/sh/v3 from 3.4.2 to 3.4.3
Bumps [mvdan.cc/sh/v3](https://github.com/mvdan/sh) from 3.4.2 to 3.4.3.
- [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.2...v3.4.3)

---
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>
2022-02-19 20:51:22 +00:00
Andrey Nering
5f2c9a6e45 Merge pull request #671 from JessThrysoee/bashcompletion
bash-completion refactoring
2022-02-13 19:51:40 -03:00
Jess Thrysoee
19be1f1bf0 bash-completion refactoring
1. 'compgen -c' lists _all_ command names on the system, which is not
   appropriate for this script, furthermore echo does not read from stdin
   so the output is lost.

2. use _get_comp_words_by_ref and __ltrim_colon_completions to handle task
   names with colons.

   "...modifying COMP_WORDBREAKS in your completion script is not safe
   (as it is a global variable and it has the side effect of affecting
   the behavior of other completion scripts"
   Ref.: https://stackoverflow.com/a/12495727/7044304

3. Add options completion

4. Use task --list-all
2022-02-13 23:44:04 +01:00
tylermmorton
248952bc8f Add dist fallbacks to defaultTaskfiles 2022-01-29 11:53:36 -05:00
Andrey Nering
2373743eac Merge pull request #652 from jcwillox/master
Add completions to nfpms packages
2022-01-17 10:18:22 -03:00
Andrey Nering
f119596be6 Docs: Small change to title 2022-01-17 09:38:32 -03:00
Andrey Nering
b7cb41b388 Merge pull request #657 from lechuckroh/intellij-plugin
Update 'community.md' to add IntelliJ plugin link
2022-01-17 09:37:06 -03:00
Lechuck Roh
a65ee26446 Update 'community.md' to add IntelliJ plugin link 2022-01-16 22:24:12 +09:00
Josh Willox
7ed3cea40b Add completions to nfpms packages 2022-01-14 20:48:28 +11:00
Jay Anslow
74f5cf8f29 Add support for begin/end messages with grouped output
Fixes #647

This allows CI systems that support grouping (such as with [GitHub Actions's `::group::` command](https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#grouping-log-lines) and [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/logging-commands?view=azure-devops&tabs=bash#formatting-commands)) to collapse all of the logs for a single task, to improve readability of logs

## Example

The following Taskfile

```
# Taskfile.yml
version: 3
output:
  group:
    begin: "::group::{{ .TASK }}"
    end: "::endgroup::"
tasks:
  default:
    cmds:
      - "echo 'Hello, World!'"
```

Results in the following output
```bash
$ task
task: [default] echo 'Hello, World!'
::group::default
Hello, World!
::endgroup::
```

See [this GitHub Actions job](https://github.com/janslow/task/runs/4811059609?check_suite_focus=true) for a full example

<img width="771" alt="image" src="https://user-images.githubusercontent.com/1253367/149429832-6cb0c1b5-0758-442e-9375-c4daa65771bc.png">
<img width="394" alt="image" src="https://user-images.githubusercontent.com/1253367/149429851-1d5d2ab5-9095-4795-9b57-f91750720d40.png">
2022-01-14 00:22:14 +00:00
Andrey Nering
086d13ca2f Docs: Remove line reference from file link 2022-01-13 10:39:38 -03:00
Andrey Nering
2780e96179 Merge pull request #648 from sagikazarmark/docs-nix-install
docs: add nix as an installation method
2022-01-13 10:38:49 -03:00
Mark Sagi-Kazar
191678f9d6 docs: add nix as an installation method 2022-01-13 00:15:04 +01:00
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
Andrey Nering
45dbbcd179 v3.2.0 2021-01-07 13:08:07 -03:00
Andrey Nering
83d25bfa00 Refactor: Fix import order
It should be: stdlib > libs > app
2021-01-07 11:48:33 -03:00
Andrey Nering
299e27af15 Fix build 2021-01-07 11:39:36 -03:00
Andrey Nering
ec4cd5ed48 Fix .task directory location
Closes #247
2021-01-07 11:36:09 -03:00
Andrey Nering
59d2733b88 Make dynamic variables run on the right directory
It was always running in the main Taskfile dir, even when the variable was
declared in an included taskfile in another directory or when task had a
custom dir.

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

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

Closes #403
2020-12-27 17:15:12 -03:00
Andrey Nering
16fad60833 execext: Small refactor 2020-12-27 16:51:00 -03:00
Victor Nogueira
f5eb80759b Fix duplicate task name on "Short task syntax" example
To make it a valid YAML file and avoid the error:

```
yaml: unmarshal errors:
  line 6: mapping key "build" already defined at line 4
```
2020-12-07 13:00:25 +02:00
Bryce Schober
bf88bd5da5 Use {{default}} for an overriding usage
Fixes documentation confusion raised in #376.
2020-11-18 17:09:44 -08: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
115 changed files with 2693 additions and 706 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
@@ -27,4 +27,4 @@ jobs:
run: go build -o ./bin/task -v ./cmd/task
- name: Test
run: ./bin/task test
run: ./bin/task test --output=group --output-group-begin='::group::{{.TASK}}' --output-group-end='::endgroup::'

4
.gitignore vendored
View File

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

View File

@@ -1,6 +1,6 @@
build:
binary: task
main: cmd/task/task.go
main: ./cmd/task
goos:
- windows
- darwin
@@ -8,14 +8,27 @@ build:
goarch:
- 386
- amd64
- arm
- arm64
goarm:
- 6
ignore:
- goos: darwin
goarch: 386
env:
- CGO_ENABLED=0
ldflags:
- -s -w # Don't set main.version.
gomod:
proxy: true
archives:
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
files:
- README.md
- LICENSE
- completion/**/*
format_overrides:
- goos: windows
format: zip
@@ -41,3 +54,27 @@ nfpms:
- deb
- rpm
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
contents:
- src: completion/bash/task.bash
dst: /etc/bash_completion.d/task
- src: completion/fish/task.fish
dst: /usr/share/fish/completions/task.fish
- src: completion/zsh/_task
dst: /usr/local/share/zsh/site-functions/_task
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,13 +1,209 @@
# Changelog
## v3.0.1
## v3.11.0 - 2022-01-19
- Task now supports printing begin and end messages when using the `group`
output mode, useful for grouping tasks in CI systems.
[Check out the documentation](http://taskfile.dev/#/usage?id=output-syntax) for more information
([#647](https://github.com/go-task/task/issues/647), [#651](https://github.com/go-task/task/pull/651)).
- Add `Taskfile.dist.yml` and `Taskfile.dist.yaml` to the supported file
name list. [Check out the documentation](https://taskfile.dev/#/usage?id=supported-file-names) for more information
([#498](https://github.com/go-task/task/issues/498), [#666](https://github.com/go-task/task/pull/666)).
## 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
([#247](https://github.com/go-task/task/issues/247)).
- Fix a bug where dynamic variables (those declared with `sh:`) were not
running in the task directory when the task has a custom dir or it was
in an included Taskfile
([#384](https://github.com/go-task/task/issues/384)).
- The watch feature (via the `--watch` flag) got a few different bug fixes and
should be more stable now
([#423](https://github.com/go-task/task/pull/423), [#365](https://github.com/go-task/task/issues/365)).
## v3.1.0 - 2021-01-03
- Fix a bug when the checksum up-to-date resolution is used by a task
with a custom `label:` attribute
([#412](https://github.com/go-task/task/issues/412)).
- Starting from this release, we're releasing official ARMv6 and ARM64 binaries
for Linux
([#375](https://github.com/go-task/task/issues/375), [#418](https://github.com/go-task/task/issues/418)).
- Task now respects the order of declaration of included Taskfiles when
evaluating variables declaring by them
([#393](https://github.com/go-task/task/issues/393)).
- `set -e` is now automatically set on every command. This was done to fix an
issue where multiline string commands wouldn't really fail unless the
sentence was in the last line
([#403](https://github.com/go-task/task/issues/403)).
## v3.0.1 - 2020-12-26
- Allow use as a library by moving the required packages out of the `internal`
directory
([#358](https://github.com/go-task/task/pull/358)).
- Do not error if a specified dotenv file does not exist
([#378](https://github.com/go-task/task/issues/378), [#385](https://github.com/go-task/task/pull/385)).
- Fix panic when you have empty tasks in your Taskfile
([#338](https://github.com/go-task/task/issues/338), [#362](https://github.com/go-task/task/pull/362)).
## v3.0.0
## 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))
@@ -50,7 +246,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

@@ -1,10 +1,15 @@
![Test](https://github.com/go-task/task/workflows/Test/badge.svg)
![GoReleaser](https://github.com/go-task/task/workflows/goreleaser/badge.svg)
![GitHub All Releases](https://img.shields.io/github/downloads/go-task/task/total)
<div align="center">
<a href="https://taskfile.dev">
<img src="docs/Logo.png" width="200px" height="200px" />
</a>
# Task
<h1>Task</h1>
Task is a task runner / build tool that aims to be simpler and easier to use
than, for example, [GNU Make](https://www.gnu.org/software/make/).
<p>
Task is a task runner / build tool that aims to be simpler and easier to use than, for example, <a href="https://www.gnu.org/software/make/">GNU Make<a>.
</p>
See [taskfile.dev](https://taskfile.dev) for the documentation.
<p>
See <a href="https://taskfile.dev">taskfile.dev</a> for the documentation.
</p>
</div>

View File

@@ -12,6 +12,9 @@ vars:
GO_PACKAGES:
sh: go list ./...
env:
CGO_ENABLED: '0'
tasks:
default:
cmds:
@@ -21,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

@@ -4,10 +4,10 @@ import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3/args"
"github.com/go-task/task/v3/taskfile"
"github.com/stretchr/testify/assert"
)
func TestArgsV3(t *testing.T) {
@@ -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,18 +7,21 @@ 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"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/taskfile"
"github.com/spf13/pflag"
)
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,16 +69,18 @@ func main() {
dry bool
summary bool
parallel bool
concurrency int
dir string
entrypoint string
output string
output taskfile.Output
color bool
)
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")
@@ -85,12 +91,15 @@ func main() {
pflag.BoolVar(&summary, "summary", false, "show summary about a task")
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.StringVarP(&output.Name, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
pflag.StringVar(&output.Group.Begin, "output-group-begin", "", "message template to print before a task's grouped output")
pflag.StringVar(&output.Group.End, "output-group-end", "", "message template to print after a task's grouped 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 +126,31 @@ func main() {
if entrypoint != "" {
dir = filepath.Dir(entrypoint)
entrypoint = filepath.Base(entrypoint)
} else {
entrypoint = "Taskfile.yml"
}
if output.Name != "group" {
if output.Group.Begin != "" {
log.Fatal("task: You can't set --output-group-begin without --output=group")
return
}
if output.Group.End != "" {
log.Fatal("task: You can't set --output-group-end without --output=group")
return
}
}
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 +168,12 @@ func main() {
}
if list {
e.PrintTasksHelp()
e.ListTasksWithDesc()
return
}
if listAll {
e.ListAllTasks()
return
}
@@ -157,11 +181,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 +214,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 +246,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

@@ -1,21 +1,22 @@
# /bin/bash
_task_completion()
{
local scripts;
local curr_arg;
local cur
_get_comp_words_by_ref -n : cur
# Remove colon from word breaks
COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
case "$cur" in
--*)
local options="$(_parse_help task)"
COMPREPLY=( $(compgen -W "$options" -- "$cur") )
;;
*)
local tasks="$(task --list-all | awk 'NR>1 { sub(/:$/,"",$2); print $2 }')"
COMPREPLY=( $(compgen -W "$tasks" -- "$cur") )
;;
esac
scripts=$(task -l | sed '1d' | awk '{ print $2 }' | sed 's/:$//');
curr_arg="${COMP_WORDS[COMP_CWORD]:-"."}"
# Do not accept more than 1 argument
if [ "${#COMP_WORDS[@]}" != "2" ]; then
return
fi
COMPREPLY=($(compgen -c | echo "$scripts" | grep $curr_arg));
__ltrim_colon_completions "$cur"
}
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() {}

BIN
docs/Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,5 +1,9 @@
# Task
<div align="center">
<img id="logo" src="/Logo.png" height="250px" width="250px" />
</div>
Task is a task runner / build tool that aims to be simpler and easier to use
than, for example, [GNU Make][make].

View File

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

View File

@@ -2,7 +2,7 @@
- [Usage](usage.md)
- [Styleguide](styleguide.md)
- [Taskfile Versions](taskfile_versions.md)
- [Examples](examples.md)
- [Community](community.md)
- [Releasing Task](releasing_task.md)
- [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)

56
docs/community.md Normal file
View File

@@ -0,0 +1,56 @@
# 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).
### IntelliJ plugin
There's a JetBrains IntelliJ plugin done by
[@lechuckroh](https://github.com/lechuckroh), which has its code [here](https://github.com/lechuckroh/task-intellij-plugin)
and is published [here](https://plugins.jetbrains.com/plugin/17058-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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 170 KiB

View File

@@ -6,8 +6,25 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="A task runner / simpler Make alternative written in Go">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//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 {
transition: all 0.7s ease;
}
#logo:hover {
-webkit-transform: rotateZ(360deg);
-ms-transform: rotateZ(360deg);
transform: rotateZ(360deg);
}
.app-name-link img {
width: 125px;
}
:root {
--base-font-size: 14px;
--theme-color: #29beb0;
}
</style>
</head>
<body>
<div id="app"></div>
@@ -15,7 +32,8 @@
window.$docsify = {
name: 'Task',
repo: 'go-task/task',
themeColor: '#00add8',
logo: 'Logo.png',
themeColor: '#29beb0',
loadSidebar: true,
auto2top: true,
maxLevel: 3,
@@ -25,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

@@ -51,6 +51,19 @@ yay -S taskfile-git
This installation method is community owned, but since it's `-git` version of
the package, it's always latest available version based on the Git repository.
#### **Nix**
If you're on NixOS or have Nix installed
you can install Task from [nixpkgs](https://github.com/NixOS/nixpkgs):
```cmd
nix-env -iA nixpkgs.go-task
```
This installation method is community owned. After a new release of Task, it
may take some time until it's available in [nixpkgs](https://github.com/NixOS/nixpkgs).
<!-- tabs:end -->
## Get The Binary
@@ -68,12 +81,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 +100,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 +120,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

@@ -31,9 +31,16 @@ of updating versions there by editing
[this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json).
If you think its Task version is outdated, open an issue to let us know.
# Nix
Nix is a community owned installation method. Nix package maintainers usually take care
of updating versions there by editing
[this file](https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/development/tools/go-task/default.nix).
If you think its Task version is outdated, open an issue to let us know.
[goreleaser]: https://goreleaser.com/#continuous_integration
[homebrewtap]: https://github.com/go-task/homebrew-tap
[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

@@ -33,6 +33,20 @@ executable called must be available by the OS or in PATH.
If you omit a task name, "default" will be assumed.
## Supported file names
Task will look for the following file names, in order of priority:
- Taskfile.yml
- Taskfile.yaml
- Taskfile.dist.yml
- Taskfile.dist.yaml
The intention of having the `.dist` variants is to allow projects to have one
commited version (`.dist`) while still allowing individual users to override
the Taskfile by adding an additional `Taskfile.yml` (which would be on
`.gitignore`).
## Environment variables
### Task
@@ -78,18 +92,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 +168,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
@@ -269,16 +309,16 @@ attribute:
version: '3'
tasks:
main-task:
greet:
vars:
RECIPIENT: '{{default "World" .RECIPIENT}}'
cmds:
- task: write-file
vars: {FILE: "hello.txt", CONTENT: "Hello!"}
- task: write-file
vars: {FILE: "world.txt", CONTENT: "World!"}
- echo "Hello, {{.RECIPIENT}}!"
write-file:
greet-pessimistically:
cmds:
- echo "{{.CONTENT}}" > {{.FILE}}
- task: greet
vars: {RECIPIENT: "Cruel World"}
```
The above syntax is also supported in `deps`.
@@ -446,6 +486,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 +601,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 +689,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 +756,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.
@@ -827,6 +973,34 @@ tasks:
finishes, so you won't have live feedback for commands that take a long time
to run.
When using the `group` output, you can optionally provide a templated message
to print at the start and end of the group. This can be useful for instructing
CI systems to group all of the output for a given task, such as with
[GitHub Actions' `::group::` command](https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#grouping-log-lines)
or [Azure Pipelines](https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/logging-commands?expand=1&view=azure-devops&tabs=bash#formatting-commands).
```yaml
version: '3'
output:
group:
begin: '::begin::{{.TASK}}'
end: '::endgroup::'
tasks:
default:
cmds:
- echo 'Hello, World!'
silent: true
```
```bash
$ task default
::begin::default
Hello, World!
::endgroup::
```
The `prefix` output will prefix every line printed by a command with
`[task-name] ` as the prefix, but you can customize the prefix for a command
with the `prefix:` attribute:
@@ -862,10 +1036,32 @@ $ 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
have the default settings (e.g. no custom `env:`, `vars:`, `silent:` , etc):
have the default settings (e.g. no custom `env:`, `vars:`, `desc:`, `silent:` , etc):
```yaml
version: '3'
@@ -873,7 +1069,7 @@ version: '3'
tasks:
build: go build -v -o ./app{{exeExt}} .
build:
run:
- task: build
- ./app{{exeExt}} -h localhost -p 8080
```

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.3
)
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.3 h1:zbuKH7YH9cqU6PGajhFFXZY7dhPXcDr55iN/cUAqpuw=
mvdan.cc/sh/v3 v3.4.3/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,6 +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)
HandleDynamicVar(v taskfile.Var) (string, error)
FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
HandleDynamicVar(v taskfile.Var, dir string) (string, error)
ResetCache()
}

View File

@@ -30,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
@@ -37,8 +46,12 @@ type CompilerV2 struct {
// 4. Taskvars file variables
// 5. Environment variables
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
vr := varResolver{c: c, vars: compiler.GetEnviron()}
vr := varResolver{
c: c,
vars: compiler.GetEnviron(),
}
vr.vars.Set("TASK", taskfile.Var{Static: t.Task})
for _, vars := range []*taskfile.Vars{c.Taskvars, c.TaskfileVars, call.Vars, t.Vars} {
for i := 0; i < c.Expansions; i++ {
vr.merge(vars)
@@ -63,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)
static, err := vr.c.HandleDynamicVar(v, "")
if err != nil {
vr.err = err
return err
@@ -74,7 +87,7 @@ func (vr *varResolver) merge(vars *taskfile.Vars) {
vr.err = tr.Err()
}
func (c *CompilerV2) HandleDynamicVar(v taskfile.Var) (string, error) {
func (c *CompilerV2) HandleDynamicVar(v taskfile.Var, _ string) (string, error) {
if v.Static != "" || v.Sh == "" {
return v.Static, nil
}
@@ -92,12 +105,11 @@ func (c *CompilerV2) HandleDynamicVar(v taskfile.Var) (string, error) {
var stdout bytes.Buffer
opts := &execext.RunCommandOptions{
Command: v.Sh,
Dir: c.Dir,
Stdout: &stdout,
Stderr: c.Logger.Stderr,
}
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
@@ -109,3 +121,11 @@ func (c *CompilerV2) HandleDynamicVar(v taskfile.Var) (string, error) {
return result, nil
}
// ResetCache clear the dymanic variables cache
func (c *CompilerV2) ResetCache() {
c.muDynamicCache.Lock()
defer c.muDynamicCache.Unlock()
c.dynamicCache = nil
}

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"path/filepath"
"strings"
"sync"
@@ -19,6 +20,7 @@ var _ compiler.Compiler = &CompilerV3{}
type CompilerV3 struct {
Dir string
TaskfileEnv *taskfile.Vars
TaskfileVars *taskfile.Vars
Logger *logger.Logger
@@ -27,41 +29,86 @@ type CompilerV3 struct {
muDynamicCache sync.Mutex
}
func (c *CompilerV3) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
result := compiler.GetEnviron()
result.Set("TASK", taskfile.Var{Static: t.Task})
func (c *CompilerV3) GetTaskfileVariables() (*taskfile.Vars, error) {
return c.getVariables(nil, nil, true)
}
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)
if err != nil {
return err
}
result.Set(k, taskfile.Var{Static: static})
return nil
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()
if t != nil {
result.Set("TASK", taskfile.Var{Static: t.Task})
}
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
return func(k string, v taskfile.Var) error {
tr := templater.Templater{Vars: result, RemoveNoValue: true}
if !evaluateShVars {
result.Set(k, taskfile.Var{Static: tr.Replace(v.Static)})
return nil
}
v = taskfile.Var{
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),
Dir: v.Dir,
}
if err := tr.Err(); err != nil {
return err
}
static, err := c.HandleDynamicVar(v, dir)
if err != nil {
return err
}
result.Set(k, taskfile.Var{Static: static})
return nil
}
}
rangeFunc := getRangeFunc(c.Dir)
if err := c.TaskfileEnv.Range(rangeFunc); err != nil {
return nil, err
}
if err := c.TaskfileVars.Range(rangeFunc); err != nil {
return nil, err
}
if 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
}
return result, nil
}
func (c *CompilerV3) HandleDynamicVar(v taskfile.Var) (string, error) {
func (c *CompilerV3) HandleDynamicVar(v taskfile.Var, dir string) (string, error) {
if v.Static != "" || v.Sh == "" {
return v.Static, nil
}
@@ -76,15 +123,20 @@ func (c *CompilerV3) HandleDynamicVar(v taskfile.Var) (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,
Dir: c.Dir,
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
@@ -96,3 +148,11 @@ func (c *CompilerV3) HandleDynamicVar(v taskfile.Var) (string, error) {
return result, nil
}
// ResetCache clear the dymanic variables cache
func (c *CompilerV3) ResetCache() {
c.muDynamicCache.Lock()
defer c.muDynamicCache.Unlock()
c.dynamicCache = nil
}

View File

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

@@ -5,15 +5,25 @@ import (
"io"
)
type Group struct{}
type Group struct{
Begin, End string
}
func (Group) WrapWriter(w io.Writer, _ string) io.Writer {
return &groupWriter{writer: w}
func (g Group) WrapWriter(w io.Writer, _ string, tmpl Templater) io.Writer {
gw := &groupWriter{writer: w}
if g.Begin != "" {
gw.begin = tmpl.Replace(g.Begin) + "\n"
}
if g.End != "" {
gw.end = tmpl.Replace(g.End) + "\n"
}
return gw
}
type groupWriter struct {
writer io.Writer
buff bytes.Buffer
begin, end string
}
func (gw *groupWriter) Write(p []byte) (int, error) {
@@ -21,6 +31,14 @@ func (gw *groupWriter) Write(p []byte) (int, error) {
}
func (gw *groupWriter) Close() error {
if gw.buff.Len() == 0 {
// don't print begin/end messages if there's no buffered entries
return nil
}
if _, err := io.WriteString(gw.writer, gw.begin); err != nil {
return err
}
gw.buff.WriteString(gw.end)
_, err := io.Copy(gw.writer, &gw.buff)
return err
}

View File

@@ -6,6 +6,6 @@ import (
type Interleaved struct{}
func (Interleaved) WrapWriter(w io.Writer, _ string) io.Writer {
func (Interleaved) WrapWriter(w io.Writer, _ string, _ Templater) io.Writer {
return w
}

View File

@@ -1,9 +1,49 @@
package output
import (
"fmt"
"io"
"github.com/go-task/task/v3/taskfile"
)
type Output interface {
WrapWriter(w io.Writer, prefix string) io.Writer
// Templater executes a template engine.
// It is provided by the templater.Templater package.
type Templater interface {
// Replace replaces the provided template string with a rendered string.
Replace(tmpl string) string
}
type Output interface {
WrapWriter(w io.Writer, prefix string, tmpl Templater) io.Writer
}
// Build the Output for the requested taskfile.Output.
func BuildFor(o *taskfile.Output) (Output, error) {
switch o.Name {
case "interleaved", "":
if err := checkOutputGroupUnset(o); err != nil {
return nil, err
}
return Interleaved{}, nil
case "group":
return Group{
Begin: o.Group.Begin,
End: o.Group.End,
}, nil
case "prefixed":
if err := checkOutputGroupUnset(o); err != nil {
return nil, err
}
return Prefixed{}, nil
default:
return nil, fmt.Errorf(`task: output style %q not recognized`, o.Name)
}
}
func checkOutputGroupUnset(o *taskfile.Output) error {
if o.Group.IsSet() {
return fmt.Errorf("task: output style %q does not support the group begin/end parameter", o.Name)
}
return nil
}

View File

@@ -6,15 +6,17 @@ import (
"io"
"testing"
"github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3/internal/output"
)
func TestInterleaved(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Interleaved{}
var w = o.WrapWriter(&b, "")
var w = o.WrapWriter(&b, "", nil)
fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, "foo\nbar\n", b.String())
@@ -25,7 +27,7 @@ func TestInterleaved(t *testing.T) {
func TestGroup(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Group{}
var w = o.WrapWriter(&b, "").(io.WriteCloser)
var w = o.WrapWriter(&b, "", nil).(io.WriteCloser)
fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, "", b.String())
@@ -35,10 +37,43 @@ func TestGroup(t *testing.T) {
assert.Equal(t, "foo\nbar\nbaz\n", b.String())
}
func TestGroupWithBeginEnd(t *testing.T) {
tmpl := templater.Templater{
Vars: &taskfile.Vars{
Keys: []string{"VAR1"},
Mapping: map[string]taskfile.Var{
"VAR1": {Static: "example-value"},
},
},
}
var o output.Output = output.Group{
Begin: "::group::{{ .VAR1 }}",
End: "::endgroup::",
}
t.Run("simple", func(t *testing.T) {
var b bytes.Buffer
var w = o.WrapWriter(&b, "", &tmpl).(io.WriteCloser)
fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, "", b.String())
fmt.Fprintln(w, "baz")
assert.Equal(t, "", b.String())
assert.NoError(t, w.Close())
assert.Equal(t, "::group::example-value\nfoo\nbar\nbaz\n::endgroup::\n", b.String())
})
t.Run("no output", func(t *testing.T) {
var b bytes.Buffer
var w = o.WrapWriter(&b, "", &tmpl).(io.WriteCloser)
assert.NoError(t, w.Close())
assert.Equal(t, "", b.String())
})
}
func TestPrefixed(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Prefixed{}
var w = o.WrapWriter(&b, "prefix").(io.WriteCloser)
var w = o.WrapWriter(&b, "prefix", nil).(io.WriteCloser)
t.Run("simple use cases", func(t *testing.T) {
b.Reset()

View File

@@ -9,7 +9,7 @@ import (
type Prefixed struct{}
func (Prefixed) WrapWriter(w io.Writer, prefix string) io.Writer {
func (Prefixed) WrapWriter(w io.Writer, prefix string, _ Templater) io.Writer {
return &prefixWriter{writer: w, prefix: prefix}
}

View File

@@ -4,7 +4,6 @@ import (
"crypto/md5"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
@@ -14,7 +13,8 @@ import (
// Checksum validades if a task is up to date by calculating its source
// files checksum
type Checksum struct {
Dir string
BaseDir string
TaskDir string
Task string
Sources []string
Generates []string
@@ -29,10 +29,10 @@ 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.Dir, c.Sources)
sources, err := globs(c.TaskDir, c.Sources)
if err != nil {
return false, err
}
@@ -43,8 +43,8 @@ func (c *Checksum) IsUpToDate() (bool, error) {
}
if !c.Dry {
_ = os.MkdirAll(filepath.Join(c.Dir, ".task", "checksum"), 0755)
if err = ioutil.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
_ = os.MkdirAll(filepath.Join(c.BaseDir, ".task", "checksum"), 0755)
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
return false, err
}
}
@@ -52,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.Dir, g)
generates, err := Glob(c.TaskDir, g)
if os.IsNotExist(err) {
return false, nil
}
@@ -107,7 +107,7 @@ func (*Checksum) Kind() string {
}
func (c *Checksum) checksumFilePath() string {
return filepath.Join(c.Dir, ".task", "checksum", c.normalizeFilename(c.Task))
return filepath.Join(c.BaseDir, ".task", "checksum", c.normalizeFilename(c.Task))
}
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")

View File

@@ -5,15 +5,15 @@ import (
"path/filepath"
"sort"
"github.com/go-task/task/v3/internal/execext"
"github.com/mattn/go-zglob"
"github.com/go-task/task/v3/internal/execext"
)
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

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

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 {
@@ -76,8 +95,9 @@ func (e *Executor) timestampChecker(t *taskfile.Task) status.Checker {
func (e *Executor) checksumChecker(t *taskfile.Task) status.Checker {
return &status.Checksum{
Dir: t.Dir,
Task: t.Task,
BaseDir: e.Dir,
TaskDir: t.Dir,
Task: t.Name(),
Sources: t.Sources,
Generates: t.Generates,
Dry: e.Dry,

274
task.go
View File

@@ -16,6 +16,7 @@ import (
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/internal/summary"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
"github.com/go-task/task/v3/taskfile/read"
@@ -32,16 +33,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
@@ -50,12 +52,15 @@ type Executor struct {
Logger *logger.Logger
Compiler compiler.Compiler
Output output.Output
OutputStyle string
OutputStyle taskfile.Output
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 +69,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 +106,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 +148,12 @@ func (e *Executor) Setup() error {
if v == 2.0 {
v = 2.6
}
if v == 3.0 {
v = 3.8
}
if v > 3.0 {
return fmt.Errorf(`task: Taskfile versions greater than v3.0 not implemented in the version of Task`)
if v > 3.8 {
return fmt.Errorf(`task: Taskfile versions greater than v3.8 not implemented in the version of Task`)
}
// Color available only on v3
@@ -168,33 +172,48 @@ 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 < 2.1 && e.Taskfile.Output != "" {
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.IsSet() {
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
}
if v < 2.2 && len(e.Taskfile.Includes) > 0 {
if v < 2.2 && e.Taskfile.Includes.Len() > 0 {
return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`)
}
if v >= 3.0 && e.Taskfile.Expansions > 2 {
return fmt.Errorf(`task: The "expansions" setting is not available anymore on v3.0`)
}
if e.OutputStyle != "" {
e.Taskfile.Output = e.OutputStyle
if v < 3.8 && e.Taskfile.Output.Group.IsSet() {
return fmt.Errorf(`task: Taskfile option "output.group" is only available starting on Taskfile version v3.8`)
}
switch e.Taskfile.Output {
case "", "interleaved":
e.Output = output.Interleaved{}
case "group":
e.Output = output.Group{}
case "prefixed":
e.Output = output.Prefixed{}
default:
return fmt.Errorf(`task: output option "%s" not recognized`, e.Taskfile.Output)
if !e.OutputStyle.IsSet() {
e.OutputStyle = e.Taskfile.Output
}
e.Output, err = output.BuildFor(&e.OutputStyle)
if err != nil {
return err
}
if e.Taskfile.Method == "" {
@@ -229,19 +248,45 @@ func (e *Executor) Setup() error {
}
if v < 3 {
for _, taskfile := range e.Taskfile.Includes {
err := e.Taskfile.Includes.Range(func(_ string, taskfile taskfile.IncludedTaskfile) error {
if taskfile.AdvancedImport {
return errors.New(`task: Import with additional parameters is only available starting on Taskfile version v3`)
}
return nil
})
if err != nil {
return err
}
}
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
}
@@ -255,48 +300,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 {
@@ -319,6 +380,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
@@ -334,11 +398,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
@@ -346,15 +422,25 @@ 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{}
}
vars, err := e.Compiler.FastGetVariables(t, call)
outputTemplater := &templater.Templater{Vars: vars, RemoveNoValue: true}
if err != nil {
return fmt.Errorf("task: failed to get variables: %w", err)
}
stdOut := outputWrapper.WrapWriter(e.Stdout, t.Prefix, outputTemplater)
stdErr := outputWrapper.WrapWriter(e.Stderr, t.Prefix, outputTemplater)
defer func() {
if _, ok := stdOut.(*os.File); !ok {
if closer, ok := stdOut.(io.Closer); ok {
@@ -368,7 +454,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
}
}()
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
err = execext.RunCommand(ctx, &execext.RunCommandOptions{
Command: cmd.Cmd,
Dir: t.Dir,
Env: getEnviron(t),
@@ -377,7 +463,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
@@ -392,10 +478,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,26 +4,31 @@ import (
"bytes"
"context"
"fmt"
"io/ioutil"
"io"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3"
"github.com/go-task/task/v3/taskfile"
"github.com/stretchr/testify/assert"
)
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) {
@@ -303,6 +348,8 @@ func TestPrecondition(t *testing.T) {
}
func TestGenerates(t *testing.T) {
const dir = "testdata/generates"
const (
srcTask = "sub/src.txt"
relTask = "rel.txt"
@@ -310,9 +357,6 @@ func TestGenerates(t *testing.T) {
fileWithSpaces = "my text file.txt"
)
// This test does not work with a relative dir.
dir, err := filepath.Abs("testdata/generates")
assert.NoError(t, err)
var srcFile = filepath.Join(dir, srcTask)
for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} {
@@ -474,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"
@@ -508,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) {
@@ -529,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"}))
@@ -548,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)
@@ -563,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())
@@ -610,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)
}
@@ -626,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())
@@ -713,6 +806,78 @@ 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 TestSupportedFileNames(t *testing.T) {
fileNames := []string{
"Taskfile.yml",
"Taskfile.yaml",
"Taskfile.dist.yml",
"Taskfile.dist.yaml",
}
for _, fileName := range fileNames {
t.Run(fileName, func(t *testing.T) {
tt := fileContentTest{
Dir: fmt.Sprintf("testdata/file_names/%s", fileName),
Target: "default",
TrimSpace: true,
Files: map[string]string{
"output.txt": "hello",
},
}
tt.Run(t)
})
}
}
func TestSummary(t *testing.T) {
const dir = "testdata/summary"
@@ -727,7 +892,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)
@@ -800,11 +965,53 @@ 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/from_root_taskfile.txt": "subdirectory\n",
"subdirectory/from_included_taskfile.txt": "subdirectory\n",
"subdirectory/from_included_taskfile_task.txt": "subdirectory\n",
"subdirectory/from_interpolated_dir.txt": "subdirectory\n",
},
}
tt.Run(t)
}
func TestDisplaysErrorOnUnsupportedVersion(t *testing.T) {
e := task.Executor{
Dir: "testdata/version/v1",
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Stdout: io.Discard,
Stderr: io.Discard,
}
err := e.Setup()
assert.Error(t, err)
@@ -868,3 +1075,146 @@ 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"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: true,
}
assert.NoError(t, e.Setup())
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
assert.Contains(t, buff.String(), `"this_should_fail": executable file not found in $PATH`)
}
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())
})
}
}
func TestOutputGroup(t *testing.T) {
const dir = "testdata/output_group"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
expectedOutputOrder := strings.TrimSpace(`
task: [hello] echo 'Hello!'
::group::hello
Hello!
::endgroup::
task: [bye] echo 'Bye!'
::group::bye
Bye!
::endgroup::
`)
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "bye"}))
t.Log(buff.String())
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
}

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

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

@@ -17,16 +17,14 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
if t2.Expansions != 0 && t2.Expansions != 2 {
t1.Expansions = t2.Expansions
}
if t2.Output != "" {
if t2.Output.IsSet() {
t1.Output = t2.Output
}
if t1.Includes == nil {
t1.Includes = make(IncludedTaskfiles)
}
for k, v := range t2.Includes {
t1.Includes[k] = v
t1.Includes = &IncludedTaskfiles{}
}
t1.Includes.Merge(t2.Includes)
if t1.Vars == nil {
t1.Vars = &Vars{}
@@ -51,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...)
}
}

55
taskfile/output.go Normal file
View File

@@ -0,0 +1,55 @@
package taskfile
import (
"fmt"
)
// Output of the Task output
type Output struct {
// Name of the Output.
Name string `yaml:"-"`
// Group specific style
Group OutputGroup
}
// IsSet returns true if and only if a custom output style is set.
func (s *Output) IsSet() bool {
return s.Name != ""
}
// UnmarshalYAML implements yaml.Unmarshaler
// It accepts a scalar node representing the Output.Name or a mapping node representing the OutputGroup.
func (s *Output) UnmarshalYAML(unmarshal func(interface{}) error) error {
var name string
if err := unmarshal(&name); err == nil {
s.Name = name
return nil
}
var tmp struct {
Group *OutputGroup
}
if err := unmarshal(&tmp); err != nil {
return fmt.Errorf("task: output style must be a string or mapping with a \"group\" key: %w", err)
}
if tmp.Group == nil {
return fmt.Errorf("task: output style must have the \"group\" key when in mapping form")
}
*s = Output{
Name: "group",
Group: *tmp.Group,
}
return nil
}
// OutputGroup is the style options specific to the Group style.
type OutputGroup struct {
Begin, End string
}
// IsSet returns true if and only if a custom output style is set.
func (g *OutputGroup) IsSet() bool {
if g == nil {
return false
}
return g.Begin != "" || g.End != ""
}

View File

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

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,11 +7,11 @@ import (
"path/filepath"
"runtime"
"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"
"github.com/joho/godotenv"
"gopkg.in/yaml.v3"
)
var (
@@ -19,14 +19,31 @@ 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.dist.yml",
"Taskfile.dist.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,66 +54,60 @@ 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})
}
}
}
}
for namespace, includedTask := range t.Includes {
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 {
return nil, err
return err
}
}
if filepath.IsAbs(includedTask.Taskfile) {
path = includedTask.Taskfile
} else {
path = filepath.Join(dir, includedTask.Taskfile)
path, err := execext.Expand(includedTask.Taskfile)
if err != nil {
return err
}
if !filepath.IsAbs(path) {
path = filepath.Join(dir, path)
}
info, err := os.Stat(path)
path, err = exists(path)
if err != nil {
return nil, err
}
if info.IsDir() {
path = filepath.Join(path, "Taskfile.yml")
if includedTask.Optional {
return nil
}
return err
}
includedTaskfile, err := readTaskfile(path)
if err != nil {
return nil, err
return err
}
if len(includedTaskfile.Includes) > 0 {
return nil, ErrIncludedTaskfilesCantHaveIncludes
if includedTaskfile.Includes.Len() > 0 {
return ErrIncludedTaskfilesCantHaveIncludes
}
if v >= 3.0 && len(includedTaskfile.Dotenv) > 0 {
return nil, ErrIncludedTaskfilesCantHaveDotenvs
return ErrIncludedTaskfilesCantHaveDotenvs
}
if includedTask.AdvancedImport {
for k, v := range includedTaskfile.Vars.Mapping {
o := v
o.Dir = filepath.Join(dir, includedTask.Dir)
includedTaskfile.Vars.Mapping[k] = o
}
for k, v := range includedTaskfile.Env.Mapping {
o := v
o.Dir = filepath.Join(dir, includedTask.Dir)
includedTaskfile.Env.Mapping[k] = o
}
for _, task := range includedTaskfile.Tasks {
if !filepath.IsAbs(task.Dir) {
task.Dir = filepath.Join(includedTask.Dir, task.Dir)
@@ -105,8 +116,12 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
}
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
return nil, err
return err
}
return nil
})
if err != nil {
return nil, err
}
if v < 3.0 {
@@ -141,3 +156,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

@@ -6,9 +6,9 @@ import (
"path/filepath"
"runtime"
"github.com/go-task/task/v3/taskfile"
"gopkg.in/yaml.v3"
"github.com/go-task/task/v3/taskfile"
)
// Taskvars reads a Taskvars for a given directory

10
taskfile/slice.go Normal file
View File

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

View File

@@ -1,9 +1,5 @@
package taskfile
import (
"errors"
)
// Tasks represents a group of tasks
type Tasks map[string]*Task
@@ -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

@@ -9,14 +9,15 @@ import (
type Taskfile struct {
Version string
Expansions int
Output string
Output Output
Method string
Includes IncludedTaskfiles
Includes *IncludedTaskfiles
Vars *Vars
Env *Vars
Tasks Tasks
Silent bool
Dotenv []string
Run string
}
// UnmarshalYAML implements yaml.Unmarshaler interface
@@ -24,14 +25,15 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
var taskfile struct {
Version string
Expansions int
Output string
Output Output
Method string
Includes IncludedTaskfiles
Includes *IncludedTaskfiles
Vars *Vars
Env *Vars
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

@@ -3,10 +3,10 @@ package taskfile_test
import (
"testing"
"github.com/go-task/task/v3/taskfile"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"github.com/go-task/task/v3/taskfile"
)
func TestCmdParse(t *testing.T) {
@@ -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
@@ -53,21 +47,12 @@ func (vs *Vars) Set(key string, value Var) {
if vs.Mapping == nil {
vs.Mapping = make(map[string]Var, 1)
}
if !strSliceContains(vs.Keys, key) {
if !stringSliceContains(vs.Keys, key) {
vs.Keys = append(vs.Keys, key)
}
vs.Mapping[key] = value
}
func strSliceContains(s []string, str string) bool {
for _, v := range s {
if v == str {
return true
}
}
return false
}
// Range allows you to loop into the vars in its right order
func (vs *Vars) Range(yield func(key string, value Var) error) error {
if vs == nil {
@@ -84,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
@@ -102,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

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

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

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

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

View File

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

View File

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

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

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

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

View File

@@ -0,0 +1,4 @@
version: '3'
tasks:
default: echo "hello" > output.txt

View File

@@ -0,0 +1,4 @@
version: '3'
tasks:
default: echo "hello" > output.txt

View File

@@ -0,0 +1,4 @@
version: '3'
tasks:
default: echo "hello" > output.txt

View File

@@ -0,0 +1,4 @@
version: '3'
tasks:
default: echo "hello" > output.txt

View File

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

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

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!"

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