Compare commits

...

324 Commits

Author SHA1 Message Date
Andrey Nering
ee7f2a541f v3.0.0-preview4 2020-05-20 22:32:30 -03:00
Andrey Nering
59a00eae98 Merge branch 'master' into v3 2020-05-20 22:26:45 -03:00
Andrey Nering
df8293bee6 v2.8.1 2020-05-20 19:26:23 -03:00
Andrey Nering
935216f179 Upgrade mvdan.cc/sh to v3.1.1 2020-05-20 19:22:27 -03:00
Andrey Nering
f56bbd46fd Fix typo 2020-05-18 13:16:50 -03:00
Andrey Nering
9f0f18c5c4 v3: Allow interpolation on "includes"
The idea is to allow manual inclusion of a OS-dependant Taskfile, since it's
not automatically included anymore.
2020-05-17 16:03:03 -03:00
Andrey Nering
191c34c9c4 v3: Do not include Taskfile_{{OS}}.yml automatically 2020-05-17 15:42:27 -03:00
Andrey Nering
6a604b3002 v3: Taskvars is no more 2020-05-17 15:34:32 -03:00
Andrey Nering
5a435b533e v3: Disallow the "expansions" setting on Taskfiles in v3 2020-05-17 15:28:25 -03:00
Andrey Nering
4b027722b1 Merge pull request #311 from go-task/vars-refactor-for-v3
v3: Variables refactoring
2020-05-17 14:56:11 -03:00
Andrey Nering
68ce8642b1 Create v3 compiler which respects declaration order of variables
Also, fix "<no value>" been printed when a non-existing variable is printed.
2020-05-16 15:46:07 -03:00
Andrey Nering
4913b6a0f1 Merge branch 'v3' into vars-refactor-for-v3 2020-05-16 11:18:28 -03:00
Andrey Nering
aee0ab05f4 Merge branch 'master' into v3 2020-05-16 10:28:00 -03:00
Andrey Nering
b44432f24a Upgrade slim-sprig package version 2020-05-16 10:21:54 -03:00
Andrey Nering
86be13ff1f Merge pull request #330 from danquah/print-usage
Have the --help/-h flag print usage.
2020-05-14 21:15:00 -03:00
Andrey Nering
ee95df0e57 Merge pull request #329 from danquah/version-print-std-out
Switch to printing the version string to std out
2020-05-14 21:12:43 -03:00
Andrey Nering
442c29f020 Merge pull request #328 from danquah/fix-goreleaser-name-template
Fix name_template in Go Releaser
2020-05-14 21:11:22 -03:00
Mads H. Danquah
739037fc37 Have the --help/-h flag print usage.
pflag will output a "pflag: help requested" message after displaying the usage
if we don't explicitly  handle the flag.
2020-05-14 21:58:46 +02:00
Mads H. Danquah
f8252020aa Switch to printing the version string to std out 2020-05-14 21:40:49 +02:00
Mads H. Danquah
38b87439fe name_template is now file_name_template
See https://goreleaser.com/deprecations/#nfpmsname_template
2020-05-14 20:48:33 +02:00
Chris Garrett
116879f7ea Update docs to reflect change from Travis to GitHub 2020-05-03 14:58:02 -03:00
Andrey Nering
2eafb2f067 Update CHANGELOG 2020-05-01 15:50:32 -03:00
Andrey Nering
c36f0f6f7f Merge pull request #317 from hatappi/master
suppress context errors when use watch option
2020-05-01 15:38:16 -03:00
hatappi
bfc033959b suppress context errors when use watch option 2020-04-29 11:56:02 +09:00
Andrey Nering
814f350b6d go mod vendor 2020-04-25 15:56:41 -03:00
Andrey Nering
05db8ce582 Merge pull request #315 from go-task/mvdan-sh-3.1.0
Upgrade mvdan.cc/sh to v3.1.0
2020-04-25 15:55:32 -03:00
Andrey Nering
3fd36a0c72 Upgrade mvdan.cc/sh to v3.1.0
Closes #270
2020-04-25 15:51:47 -03:00
Andrey Nering
cbb12b29bd v3: Fix bug where global vars were not being considered 2020-04-05 11:16:27 -03:00
Andrey Nering
6ed30f1add Refactor variables: Keep order of declaration
This shouldn't have any behavior changes for now. This is a code
refactor that should allow us to do further improvements on how
variables are handled, specially regarding respecting the declaration
order in Taskfiles, which should make it easier for the users.

Initial work on #218
2020-04-05 11:16:14 -03:00
Andrey Nering
a044c41c66 Upgrade github.com/go-yaml/yaml to v3 2020-03-28 11:27:49 -03:00
Andrey Nering
fb78e53a14 v3.0.0-preview3 2020-03-28 11:01:28 -03:00
Andrey Nering
acfbbaa549 Merge branch 'master' into v3 2020-03-28 10:48:49 -03:00
Andrey Nering
d52d74c64c go mod vendor 2020-03-22 21:42:45 -03:00
Andrey Nering
d36f73de01 Merge pull request #303 from go-task/ci-updates
Remove Travis CI and upgrade Go version on GitHub Actions
2020-03-01 15:49:25 -03:00
Andrey Nering
628c4a7b5f Upgrade Go version on CI 2020-03-01 15:46:56 -03:00
Andrey Nering
ca07a663e1 Remove Travis CI 2020-03-01 15:45:51 -03:00
Andrey Nering
66d008391e Merge pull request #302 from go-task/upgrade-mvdan-sh-to-v3.0.2
Upgrade mvdan.cc/sh to v3.0.2
2020-03-01 15:38:03 -03:00
Andrey Nering
eef84bda26 Upgrade mvdan.cc/sh to v3.0.2 2020-03-01 15:35:23 -03:00
Andrey Nering
e0defe71aa Merge pull request #298 from thejray/master
Remove trailing colon from task listing
2020-03-01 15:26:37 -03:00
thejray
069257151e Remove trailing colon from task listing 2020-02-19 07:22:37 -05:00
Andrey Nering
3f80a3b39e Improve documentation for included Taskfiles
Follow-up of #292
2020-02-16 11:21:06 -03:00
Andrey Nering
b2a56161bb Make ./docs/Taskfile.yml run on ./docs 2020-02-16 11:20:53 -03:00
Andrey Nering
5e75639244 Merge pull request #292 from evg4b/included_tasks
Added option to make included Taskfile run commands on its own directory
2020-02-16 10:50:35 -03:00
Evgeny Abramovich
cb2cd3e10f Updated CHANGELOG.md 2020-02-15 18:07:27 +03:00
Evgeny Abramovich
0acb911d6a Fixed absolute path resolving for included tasksfile 2020-02-15 18:07:09 +03:00
Evgeny Abramovich
17ad7060b3 Added version validation and updated tests 2020-02-15 17:24:06 +03:00
Evgeny Abramovich
f38ba7fcd3 Removed automatic inclusion of Taskfiles by OS and update tests 2020-02-15 17:19:09 +03:00
Evgeny Abramovich
a3464068bd Rename TaskFile to Taskfile 2020-02-12 10:42:00 +03:00
Andrey Nering
347ecc028f Merge pull request #286 from kovetskiy/master
Add note about Arch Linux package
2020-02-11 21:26:41 -03:00
Egor Kovetskiy
94ac60fa09 docs/installation.md: fix typo for Git
Co-Authored-By: Andrey Nering <andrey.nering@gmail.com>
2020-02-10 11:45:27 +03:00
Evgeny Abramovich
d567e23e50 Added tests for new inport taskfile logic 2020-01-29 11:25:11 +03:00
Evgeny Abramovich
8ff81562d2 Added os-related files for included taskfiles 2020-01-29 10:39:43 +03:00
Evgeny Abramovich
7a8142ed92 Added included taskfile directory resolving 2020-01-29 10:39:26 +03:00
Evgeny Abramovich
eaba1b9cc8 Added structure for storage information about included tasks 2020-01-29 10:02:22 +03:00
Andrey Nering
b08b3546d2 Merge pull request #283 from paulvarache/desc-tmpl
Compile tasks before printing help or summary
2020-01-26 18:47:15 -03:00
Paul Varache
7453e688fd Compile tasks before printing help or summary (Closes #276) 2020-01-26 21:32:20 +00:00
Andrey Nering
32b097b3f2 Merge pull request #281 from paulvarache/ps-complete
Add PowerShell completion script
2020-01-26 17:16:57 -03:00
Egor Kovetskiy
22394def78 Add note about Arch Linux package 2020-01-25 15:44:55 +03:00
Paul Varache
68ecb7fbdd Add PowerShell completion script 2020-01-20 11:26:15 +00:00
Andrey Nering
de98a53b43 Upgrade mvdan.cc/sh to v3.0.1 2020-01-11 21:46:57 -03:00
Andrey Nering
1c9fbf92c6 GitHub Actions: Add action to run GoReleaser on new tags 2020-01-11 21:29:04 -03:00
Andrey Nering
c068b05232 Delete taskfile.org -> taskfile.dev redirect code
This has been migrated to Netlify
2019-12-23 22:25:19 -03:00
Andrey Nering
15338ecb18 Merge branch 'master' into v3 2019-12-07 22:04:16 -03:00
Andrey Nering
01e9a8f720 v2.8.0 2019-12-07 21:54:10 -03:00
Andrey Nering
4bdfe64afb Add hability silent all tasks
By add `silent: true` at the root of the Taskfile.
2019-12-07 21:44:09 -03:00
Andrey Nering
b7b752b92f Allow shorter syntax for tasks with default configuration
Closes #194
Closes #240

Co-authored-by: Jaedle <dennis.jekubczyk@gmail.com>
2019-12-07 21:28:02 -03:00
Andrey Nering
b7bcd204b4 go fmt internal/taskfile/task.go 2019-12-07 20:09:16 -03:00
Andrey Nering
ec934ba3c0 .editorconfig: Remove "indent_size"
This way, everyone can configure use their preferred tab size.

Closes #269

Co-authored-by: Nick Klauer <klauer@gmail.com>
2019-12-07 19:49:55 -03:00
Andrey Nering
7373639f57 Expose .TASK variable with the task name
Closes #252
2019-12-07 19:43:10 -03:00
Andrey Nering
d718527a1f Merge branch 'master' into v3 2019-12-07 16:54:29 -03:00
Andrey Nering
48add0f293 Write more args tests 2019-12-07 16:48:23 -03:00
Andrey Nering
a4685229c9 Fix bug of Task not executing the "default" task
When global vars were informed using the CLI.

I took the oportunity to move this logic to the proper package and
write a test.
2019-12-07 16:20:36 -03:00
Andrey Nering
f0bc4d26a0 Update FUNDING.yml 2019-11-30 16:15:59 -03:00
Andrey Nering
1d3b93d88d Remove bold from colored text 2019-11-24 21:07:12 -03:00
Andrey Nering
62752ba7e1 Merge branch 'master' into v3 2019-11-24 21:02:33 -03:00
Andrey Nering
6a4f420187 go mod vendor 2019-11-24 21:00:02 -03:00
Andrey Nering
6640632683 Merge pull request #271 from go-task/upgrade-mvdan-sh-to-v3.0.0-beta1
Upgrade mvdan.cc/sh to v3.0.0-beta1
2019-11-24 19:20:59 -03:00
Andrey Nering
09d5d802d0 Upgrade mvdan.cc/sh to v3.0.0-beta1 2019-11-24 19:17:09 -03:00
Andrey Nering
fea23ed6d4 Add Changelog and Docs for --parallel 2019-11-15 23:31:18 -03:00
Andrey Nering
10a6c4dc7a Merge pull request #266 from RossHammer/master
Add flag to allow tasks provided on the command line to be run in parallel
2019-11-15 23:22:53 -03:00
Andrey Nering
4cdaa72224 Merge pull request #263 from tillhoff/patch-1
fixed grammar on styleguide
2019-11-15 22:45:07 -03:00
Ross Hammermeister
27bc1ca5d1 Add flag to allow tasks provided on the command line to be run in parallel 2019-11-13 13:50:04 -07:00
till.hoffmann
1ea49188c9 fixed grammar on styleguide 2019-11-13 12:34:13 +01:00
Andrey Nering
3084ef129c v2.7.1 2019-11-10 20:36:17 -03:00
Andrey Nering
c0d112f858 Merge pull request #261 from AlbyIanna/AlbyIanna/fix-typo
fix typo in usage.md
2019-11-10 19:58:06 -03:00
Andrey Nering
2265dda84c Merge pull request #249 from bfarayev/feature/bash-autocomplete
Bash autocompletion for task
2019-11-10 19:53:33 -03:00
Alberto Iannaccone
263b094cab fix typo in usage.md 2019-11-08 11:01:57 +01:00
Bakhtiyar Farayev
fbd13614a5 Fix typo and re-organize the folder structure for task completion 2019-11-05 10:30:01 +11:00
Andrey Nering
9eab74b595 Updating slim-sprig 2019-11-02 22:18:03 -03:00
Andrey Nering
5acdb041a9 Merge branch 'master' into v3 2019-11-02 22:16:44 -03:00
Andrey Nering
0494d7ebe3 Update changelog 2019-10-27 18:27:47 -03:00
Andrey Nering
9a8442c946 Update some tools and fix error and calling exit 0
Fixes #251
2019-10-27 18:14:22 -03:00
Andrey Nering
e1dcd0b441 Add a styleguide to documentation site 2019-10-27 17:28:12 -03:00
Andrey Nering
a152db7054 Docs: Mention GitHub Action by the Arduino team 2019-10-24 23:55:21 -03:00
Andrey Nering
b9e092674e Docs: Advice using Go Modules instead of GOPATH 2019-10-24 23:46:29 -03:00
Andrey Nering
4162b5f41d Merge pull request #248 from go-task/improve-github-actions
Improve GitHub Action and also test Go 1.12
2019-10-24 23:30:21 -03:00
Andrey Nering
67ae6f210f Improve GitHub Action and also test Go 1.12 2019-10-24 23:25:06 -03:00
Andrey Nering
f6c5a46626 Update vendor/ directory
Fixes #250
2019-09-26 19:04:13 -03:00
Andrey Nering
d6f7e01c53 Fix Travis 2019-09-22 18:57:42 -03:00
Andrey Nering
46463e4e24 Disabling broken Travis releasing for now
I plan to move this to GitHub Actions. For now, I'll do it manually.
2019-09-22 18:50:16 -03:00
Andrey Nering
bc99509395 Merge branch 'master' into v3 2019-09-22 18:43:45 -03:00
Andrey Nering
5c420f3a34 v2.7.0 2019-09-22 18:21:31 -03:00
Bakhtiyar Farayev
393712ead2 Add initial version of bash autocomplete 2019-09-15 09:06:00 +10:00
Andrey Nering
d3060b0060 Add CHANGELOG for #216 2019-09-14 18:09:34 -03:00
Andrey Nering
14d7f04a81 Always expode .TIMESTAMP and .STATUS when using status: 2019-09-14 18:04:41 -03:00
Andrey Nering
1a28e5e0d4 Few code improvements on #216 2019-09-14 17:54:41 -03:00
Andrey Nering
884cd0d636 Merge branch 'CypherpunkArmory-report-timestamp-to-status' into v3 2019-09-14 17:18:42 -03:00
Andrey Nering
6a7a3c0ae8 Merge pull request #246 from go-task/method-on-v3
Add global "method:" option to allow setting a default for all tasks. Change default from "timestamp" to "checksum"
2019-09-08 23:02:40 -03:00
Andrey Nering
948e6bd57c Update v3 CHANGELOG 2019-09-08 22:59:27 -03:00
Andrey Nering
78595fba0b Make "checksum" the default method in v3 2019-09-08 22:51:56 -03:00
Andrey Nering
8020284b12 Add global method: option to set default method 2019-09-08 22:51:14 -03:00
Andrey Nering
d6a49da870 Merge branch 'master' into v3 2019-09-08 22:12:02 -03:00
Andrey Nering
84da80356d Use stdlib instead of go-homedir 2019-09-08 22:07:48 -03:00
Andrey Nering
bcbb85eac3 Merge pull request #245 from go-task/upgrade-sh
Upgrade mvdan.cc/sh to use edge
2019-09-08 21:59:59 -03:00
Andrey Nering
0e1d8a72e6 Revert "Ensure the $HOME env is being set on Windows"
This reverts commit 52028fc3bc.
2019-09-08 21:56:23 -03:00
Andrey Nering
bbdd698869 Upgrade mvdan.cc/sh to use edge 2019-09-08 21:55:02 -03:00
Andrey Nering
bae1e1ee9f Update .gitignore 2019-09-07 14:46:46 -03:00
Andrey Nering
7138785500 Merge branch 'master' into v3 2019-09-07 14:44:21 -03:00
Andrey Nering
8f684ffa6d Merge pull request #239 from go-task/ci-using-github-actions
Add CI for Linux/Windows/MacOS powered by GitHub Actions
2019-09-07 14:43:00 -03:00
Andrey Nering
9be3666fe7 Trying to fix Travis 2019-09-07 14:40:39 -03:00
Andrey Nering
b7785678f4 CI: Upgrade to Go 1.13 2019-09-07 14:35:48 -03:00
Andrey Nering
d8005b4cf6 Fix typo 2019-09-07 14:32:02 -03:00
Andrey Nering
52028fc3bc Ensure the $HOME env is being set on Windows 2019-09-07 14:29:13 -03:00
Andrey Nering
5285ec23ae Fix summary test on Windows 2019-09-01 22:26:53 -03:00
Andrey Nering
3c882e5c57 Merge branch 'master' into ci-using-github-actions 2019-09-01 22:18:58 -03:00
Andrey Nering
1a33f9168b Merge branch 'report-timestamp-to-status' of https://github.com/CypherpunkArmory/task into CypherpunkArmory-report-timestamp-to-status 2019-09-01 21:44:23 -03:00
Andrey Nering
ccae3d7383 Update CHANGELOG 2019-08-25 18:17:33 -03:00
Andrey Nering
847651a90a Improve note wording a bit 2019-08-25 18:15:26 -03:00
Andrey Nering
1b8998e7a2 Merge pull request #237 from jaedle/v3
Remove all code related support of version 1
2019-08-25 18:11:47 -03:00
Andrey Nering
dc8fb79759 Merge branch 'master' into v3 2019-08-25 18:05:40 -03:00
Stephen Prater
6b0935d6cf Fix tests 2019-08-25 13:47:29 -07:00
Stephen Prater
d1183ce272 Merge branch 'master' into report-timestamp-to-status
* master:
  Update CHANGELOG
  Small improvements to #228
  Fix a typo
  Fix Checksum.IsUpToDate
  Remove directory check
  Update glob.go
  Separate error handlings for readability
  Re-run the task if generated files do not exist
2019-08-25 13:46:02 -07:00
Stephen Prater
a1aec8178a Export Time Struct to Template 2019-08-25 13:36:48 -07:00
Andrey Nering
ad569a8a36 Update CHANGELOG 2019-08-25 16:41:54 -03:00
Andrey Nering
0d9fdbaac1 Small improvements to #228 2019-08-25 16:32:25 -03:00
Andrey Nering
f5cd3eab9e Merge pull request #228 from topecongiro/checksum-look-for-generated
Re-run the task if generated files do not exist
2019-08-25 16:32:03 -03:00
Stephen Prater
cb6fe4bb59 Merge remote-tracking branch 'upstream/v3' into report-timestamp-to-status
* upstream/v3:
  v3.0.0-preview1
  Update v3 changelog
  Only have colored output on v3
  Add --color=false flag to disable colored output
  Update documentation about sprig
  Update CHANGELOG
  Migrate from sprig to slim-sprig
  Fix build after merging master
  Use colors for some output messages
2019-08-25 10:33:13 -07:00
Stephen Prater
db36bc67f1 Changes per feedback 2019-08-25 10:30:00 -07:00
Stephen Prater
e0f72a6193 Apply suggestions from code review
Co-Authored-By: Andrey Nering <andrey.nering@gmail.com>
2019-08-25 09:39:39 -07:00
Stephen Prater
1ee684b7c0 Expose timestamp and checksum to status 2019-08-25 09:39:39 -07:00
jaedle
93005512b4 cleanp taskfile reader 2019-08-24 06:28:12 +02:00
Seiichi Uchida
8987cd64a0 Fix a typo 2019-08-21 13:42:19 +09:00
Seiichi Uchida
fac51dcf03 Fix Checksum.IsUpToDate
- Check whether generates exist after the creation of checksum file
- Check whether generates exist if only the user specified generates fields
- Check for each generates field instead of taking it as a whole
2019-08-21 13:35:16 +09:00
Seiichi Uchida
01101a4c9b Remove directory check 2019-08-21 13:34:58 +09:00
Seiichi Uchida
d561e40817 Update glob.go
- Rename glob() to globs()
- Add glob() which handles a single glob pattern
- Change glob() and globs() so that they do not return directoreis
2019-08-21 13:33:12 +09:00
jaedle
b8094fd771 bump version of auto generated taskfile 2019-08-19 21:01:36 +02:00
jaedle
af5d9c952d assert error message 2019-08-19 21:01:01 +02:00
jaedle
ce4e187cbc show documentation of version 1, add deprecation notice 2019-08-19 20:59:02 +02:00
jaedle
821c80b61e Fix error message
Co-Authored-By: Andrey Nering <andrey.nering@gmail.com>
2019-08-19 20:53:35 +02:00
jaedle
5a6fb7c973 Bump version of taskfile
Co-Authored-By: Andrey Nering <andrey.nering@gmail.com>
2019-08-19 20:53:20 +02:00
Seiichi Uchida
0cb298ebdf Separate error handlings for readability 2019-08-19 13:00:10 +09:00
jaedle
0f385f9f4e remove v1 2019-08-18 17:37:21 +02:00
Andrey Nering
a149368725 Calling Go directly on Windows 2019-08-15 22:50:28 -03:00
Andrey Nering
afeefe8259 Build on the root directory 2019-08-15 22:45:46 -03:00
Andrey Nering
690d3c27a2 Do we need to use backslash here? 2019-08-15 22:42:06 -03:00
Andrey Nering
3d56ea5ce5 Fix binary calling on Windows 2019-08-15 22:38:36 -03:00
Andrey Nering
fdff7f80a3 Skip linting for now 2019-08-15 22:35:29 -03:00
Andrey Nering
fe6978b107 Fix CI 2019-08-15 22:32:45 -03:00
Andrey Nering
57db6865d2 Build binary first and test all packages 2019-08-15 22:28:55 -03:00
Andrey Nering
d235d5ab28 Add CI for Linux/Windows/MacOS powered by GitHub Actions 2019-08-15 22:24:31 -03:00
Andrey Nering
c47c15ee47 v3.0.0-preview1 2019-08-11 23:43:27 -03:00
Andrey Nering
613dfe06d3 Fix two Goreleaser deprecation messages
https://goreleaser.com/deprecations/#archive

https://goreleaser.com/deprecations/#nfpm
2019-08-11 23:15:08 -03:00
Andrey Nering
6803ad2e59 Update v3 changelog 2019-08-11 23:10:12 -03:00
Andrey Nering
d5a791b470 Merge branch 'master' into v3 2019-08-11 23:03:58 -03:00
Andrey Nering
a312d61d68 Update CHANGELOG.md 2019-08-11 23:01:25 -03:00
Andrey Nering
e414c1f7b0 Documentation: Improve wording on the Variables section
Thanks @crewjam for the suggestion

Closes #234
2019-08-10 19:50:47 -03:00
Andrey Nering
955359b073 Fix nil panic bug when assigning global var and no var is declared on the Taskfile
Closes #229
Ref #234
2019-08-10 19:38:57 -03:00
Seiichi Uchida
26e0c0887a Re-run the task if generated files do not exist 2019-08-01 13:08:53 +09:00
Andrey Nering
4c295b564a Update CHANGELOG.md 2019-07-21 11:23:49 -03:00
Andrey Nering
2eb52da0db v2.6.0 2019-07-21 11:11:30 -03:00
Andrey Nering
d8bfb3ab13 Add CHANGELOG and documentation for Linux support on Homebrew
Ref: https://github.com/go-task/homebrew-tap/pull/1

Thanks @dawidd6
2019-07-21 11:03:22 -03:00
Andrey Nering
d970e93507 Add --taskfile flag (alias -t) to allow running another Taskfile
Closes #221
2019-07-21 10:57:04 -03:00
Andrey Nering
e6255081a8 Merge pull request #207 from go-task/colored-output
Use colors for some output messages
2019-07-07 14:41:25 -03:00
Andrey Nering
623db0ed94 Only have colored output on v3 2019-07-07 14:18:02 -03:00
Andrey Nering
0e575e9c25 Add --color=false flag to disable colored output 2019-07-07 14:13:53 -03:00
Andrey Nering
fb23ba9878 Merge branch 'v3' into colored-output 2019-07-07 14:04:12 -03:00
Andrey Nering
4e09fc7f43 Update documentation about sprig 2019-06-22 22:48:53 -03:00
Andrey Nering
64cfdd815f Update CHANGELOG 2019-06-22 22:47:23 -03:00
Andrey Nering
f6f31e0a8d Merge pull request #219 from go-task/migrate-from-sprig-to-slim-sprig
Migrate from sprig to slim-sprig
2019-06-22 22:37:42 -03:00
Andrey Nering
bd5fb9be03 Migrate from sprig to slim-sprig 2019-06-22 22:17:24 -03:00
Andrey Nering
762714de68 Merge pull request #213 from marco-m/document-dir-creation
Document dir: creation (see PR #211)
2019-06-16 11:03:08 -03:00
Marco Molteni
82a3651a18 Document dir: creation (see PR #211) 2019-06-16 11:33:00 +02:00
Andrey Nering
dd9cdb0ec9 Fix build after merging master 2019-06-15 22:47:15 -03:00
Andrey Nering
7f082a821d Merge branch 'v3' into colored-output 2019-06-15 22:42:15 -03:00
Andrey Nering
abe0352de9 Fixed some bugs regarding minor version checks on version:
1. I have forgot to update it on recent releases. Seems that most people just
   use round versions since nobody complained.
2. It's too hard to understand how the github.com/Masterminds/semver package
   works, so I just got rid of it and we're now using plain float checks.
2019-06-15 22:39:35 -03:00
Andrey Nering
4cee4aa5a8 Fix typo 2019-06-15 21:58:37 -03:00
Andrey Nering
9c68c7c50b Add changelog for #205 2019-06-15 21:56:34 -03:00
Andrey Nering
0608782cfa Merge pull request #205 from CypherpunkArmory/add-precondition-to-task
Add Preconditions to Tasks
2019-06-15 21:55:20 -03:00
Andrey Nering
edeaf3794a Merge pull request #212 from ezhukov/patch-1
Add missing "-" in usage.md
2019-06-15 21:21:41 -03:00
Andrey Nering
fe2b8c8afa Post-fixes to #211 2019-06-15 21:12:54 -03:00
Andrey Nering
b66bf58064 Merge branch 'marco-m-209-create-dir' 2019-06-15 20:53:34 -03:00
Andrey Nering
957dfa9cdf Merge branch '209-create-dir' of https://github.com/marco-m/task into marco-m-209-create-dir 2019-06-15 20:52:08 -03:00
Stephen Prater
cc9264854e Change error output 2019-06-11 12:20:56 -07:00
Stephen Prater
d1463b3e24 Fix typos per review 2019-06-11 11:46:22 -07:00
Eugene Zhukov
f1082520e1 Add missing "-" in usage.md 2019-06-11 11:35:10 +03:00
Marco Molteni
733c563194 Protect creation of "dir:" with a mutex 2019-06-10 17:40:20 +02:00
Andrey Nering
0200d043c3 Add funding button via OpenCollective 2019-06-09 21:53:55 -03:00
Marco Molteni
9c475c36e7 Handle the common case when the task directory is not specified
Closes #209
2019-06-06 20:40:31 +02:00
Marco Molteni
c663c5c507 When "dir:" attribute points to a non-existing dir, create it
Closes #209
2019-06-04 18:58:22 +02:00
Marco Molteni
1e93c38307 Task directory: test when "dir:" attribute points to an existing dir 2019-06-04 18:36:35 +02:00
Marco Molteni
81baf808c9 Task directory: test default case (no "dir:" attribute) 2019-06-04 18:24:01 +02:00
Marco Molteni
74537689dc Fix spelling 2019-06-04 08:08:25 +02:00
Stephen Prater
12ab01d5e6 Clarify difference between status and precondition in docs 2019-05-28 13:18:06 -07:00
Stephen Prater
044d3a0ff9 Remove ignore_errors 2019-05-28 13:02:59 -07:00
Stephen Prater
659cae6a4c Apply suggestions from code review
Co-Authored-By: Andrey Nering <andrey.nering@gmail.com>
2019-05-28 12:28:29 -07:00
Andrey Nering
8efc38ad82 Use colors for some output messages 2019-05-26 18:36:39 -03:00
Stephen Prater
bd5882f0f0 Add Preconditions to Tasks 2019-05-17 13:51:15 -07:00
Andrey Nering
6ff9ba9df9 v2.5.2 2019-05-11 11:28:21 -03:00
Andrey Nering
b2df398a12 go mod vendor 2019-05-11 11:22:47 -03:00
Andrey Nering
83d618e1eb Revert "Upgrade to yaml/go-yaml v3"
This reverts commit 8001fb3915.
2019-05-11 11:22:13 -03:00
Andrey Nering
f0768b3af1 Allow setting global variables through the CLI
Closes #192
2019-05-11 11:06:47 -03:00
Andrey Nering
0233ce52ed v2.5.1 2019-04-27 17:56:30 -03:00
Andrey Nering
6e6f337509 Updated change log 2019-04-27 17:28:58 -03:00
Andrey Nering
1546415b8f Update CHANGELOG.md 2019-04-21 17:16:35 -03:00
Andrey Nering
20725c69bf Merge pull request #200 from go-task/fix-output-issues
Fixes some bugs relatated to commands output handling
2019-04-21 17:05:21 -03:00
Andrey Nering
90613220c6 Fixes some bugs relatated to commands output handling
This seems to fix some of the bugs reported by issues like #114 and #190.

Seems that the standard library's os/exec package has some black magic to
detect if a writer is an actual *os.File, and some stuff are handled
differently, then.

Fixes #114
Fixes #190
2019-04-21 16:55:47 -03:00
Andrey Nering
659fd2ae93 Update Go version on CI 2019-04-13 17:44:55 -03:00
Andrey Nering
29d899f7da Merge pull request #198 from go-task/yaml-v3
Upgrade to go-yaml/yaml v3
2019-04-13 17:29:36 -03:00
Andrey Nering
902a0a01a9 go vendor mod 2019-04-13 17:26:27 -03:00
Andrey Nering
8001fb3915 Upgrade to yaml/go-yaml v3 2019-04-13 17:25:28 -03:00
Andrey Nering
e81e2802f0 Small fix to redirector 2019-03-23 17:48:18 -03:00
Andrey Nering
1ee066ec42 Merge pull request #188 from sosiska/patch-1
Rewrite if-else chain to switch statement
2019-03-23 17:04:31 -03:00
Kirill Motkov
53d54d1c4a Rewrite if-else chain to switch statement 2019-03-19 14:19:21 +03:00
Andrey Nering
10082b60b8 v2.5.0 2019-03-16 10:46:22 -03:00
Andrey Nering
c5b9773922 go mod vendor 2019-03-16 10:42:54 -03:00
Andrey Nering
de11323d28 mvdan.cc/sh: Use v2.6.4 2019-03-16 10:42:23 -03:00
Andrey Nering
9f269e1a95 Migrating from taskfile.org to taskfile.dev 2019-03-04 23:23:30 -03:00
Andrey Nering
e4204168a0 Remove unnecessary extra empty line 2019-03-04 22:56:23 -03:00
Andrey Nering
9c350f8ef1 Update Change Log 2019-03-04 22:56:23 -03:00
Andrey Nering
db19fdac29 Update CNAME 2019-03-04 22:23:39 -03:00
Andrey Nering
d516b238b1 Merge pull request #180 from jaedle/master
Display task summary
2019-03-04 21:44:44 -03:00
Andrey Nering
f9330f6cd9 Merge pull request #182 from GuillaumeAmat/fix-completion
Fix the zsh completion with sub-tasks
2019-03-04 21:39:43 -03:00
jaedle
360da29e1f refactoring 2019-03-04 13:04:04 +01:00
jaedle
9cfac1642a rename method for summary/summaries 2019-03-04 13:03:13 +01:00
jaedle
db90e87d10 rearrange imports 2019-03-04 12:53:06 +01:00
jaedle
b7564080bc add space between tasks 2019-03-04 12:48:26 +01:00
jaedle
1d783bf6c7 refactoring 2019-03-04 12:47:01 +01:00
jaedle
1025c2e3a1 add unit test for spacing between summaries 2019-03-04 12:46:02 +01:00
jaedle
4fd82ab222 refactoring 2019-03-04 12:28:26 +01:00
jaedle
8eadfc1bf6 refactoring 2019-03-04 12:28:11 +01:00
jaedle
f66edbad50 refactoring 2019-03-04 12:27:10 +01:00
jaedle
c7f17b5319 refactoring 2019-03-04 12:25:42 +01:00
jaedle
23c4adcef6 add spacing for tasks 2019-03-04 12:15:40 +01:00
jaedle
808542bed0 remove unnecassry test for multiple summaries 2019-03-04 12:13:13 +01:00
jaedle
93bfd57856 print summary for multiple tasks 2019-03-04 12:09:58 +01:00
jaedle
7e7e1bccba rearrange imports 2019-03-04 12:04:31 +01:00
jaedle
34f6da86c3 rearrange imports 2019-03-04 12:03:28 +01:00
Guillaume AMAT
15c0381c3c Fix the indentation 2019-03-04 07:03:06 +01:00
Guillaume AMAT
c2f4a57e02 Remove \s for MacOS compatibility, use awk instead 2019-03-03 23:32:35 +01:00
Andrey Nering
f945cf2343 Update internal/summary/summary_test.go
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 19:45:00 +01:00
Andrey Nering
5bca3cfd71 Update testdata/summary/Taskfile.yml
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 19:44:27 +01:00
Andrey Nering
26ce4e6886 Update testdata/summary/Taskfile.yml
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 19:44:14 +01:00
Andrey Nering
f5f0e0c376 Update internal/summary/summary.go
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 19:43:57 +01:00
Andrey Nering
9dea1e7f3e Update docs/usage.md
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 19:43:23 +01:00
Andrey Nering
c2e0f8c81f Update docs/usage.md
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 18:56:42 +01:00
Andrey Nering
d341bc25ce Revert "Try out Windows builds in Travis"
This reverts commit fc34d6b56f.

Unfortunately, something seems wrong with Windows build on Travis.
And the output log is unhelpful to debug the problem.
2019-03-03 14:42:33 -03:00
Andrey Nering
0379e2b51b Merge pull request #175 from emirb/patch-1
Try out Windows builds in Travis
2019-03-02 11:18:03 -03:00
Guillaume AMAT
e79026b840 Fix the zsh completion with sub-tasks 2019-03-02 01:40:31 +01:00
Emir Beganović
fc34d6b56f Try out Windows builds in Travis 2019-02-25 09:59:25 +04:00
jaedle
2a1571a99e refactoring 2019-02-24 19:14:15 +01:00
jaedle
c158608255 fix error in documentation 2019-02-24 19:10:44 +01:00
jaedle
3ca590b185 display summary for tasks without summary/description 2019-02-24 19:02:44 +01:00
jaedle
3f8ee21849 print error messsage if no summary or description present 2019-02-24 18:26:16 +01:00
jaedle
845b88a193 print only task name if summary 2019-02-24 18:20:59 +01:00
jaedle
e252972c7f rename test 2019-02-24 17:29:03 +01:00
jaedle
a9012ebfc5 refactoring 2019-02-24 17:28:06 +01:00
jaedle
5cfd9bbbbd refactoring 2019-02-24 17:25:03 +01:00
jaedle
c82a7240bb print task in command section 2019-02-24 17:23:31 +01:00
jaedle
a4a20d92a4 add unit test for full output 2019-02-24 17:20:29 +01:00
jaedle
890996f595 hides commands keywoard if not present 2019-02-24 17:12:22 +01:00
jaedle
474f27c6d3 add unit test for displaying commands 2019-02-24 17:10:59 +01:00
jaedle
33f3894372 add unit tests for summary 2019-02-24 17:05:37 +01:00
jaedle
24436ac76e refactoring 2019-02-24 16:26:46 +01:00
jaedle
3ee66ef705 remove output to own package 2019-02-24 16:25:27 +01:00
jaedle
a1765e1d33 refactoring 2019-02-24 16:17:47 +01:00
jaedle
765e3dbf72 print only commands if present 2019-02-24 16:15:59 +01:00
jaedle
80f5cee599 refactoring 2019-02-24 16:10:43 +01:00
jaedle
4dcb124693 print commands on summary only if commands are present 2019-02-24 16:08:32 +01:00
jaedle
31ecf167cc rename to summary in test fixtures 2019-02-24 15:54:11 +01:00
jaedle
3999480d64 refactoring 2019-02-24 15:45:39 +01:00
Andrey Nering
9dbb503c23 Update vendor directory 2019-02-24 11:45:32 -03:00
Andrey Nering
a98f803d87 Upgrade mvdan.cc/sh 2019-02-24 11:44:53 -03:00
jaedle
9e9ffeb5d5 refactoring 2019-02-24 15:43:45 +01:00
jaedle
33d4ad4d84 rename to summary 2019-02-24 15:38:18 +01:00
jaedle
d05d418c4c renaming field in taskfile to summary 2019-02-24 15:37:02 +01:00
jaedle
06d0af7a1d rename details in Executor to summary 2019-02-24 15:33:09 +01:00
jaedle
9a3b726068 change help to summary 2019-02-24 15:32:24 +01:00
jaedle
2676ab9a59 renamed program flag to summary 2019-02-24 15:31:46 +01:00
jaedle
a1837d553e refactoring 2019-02-24 14:59:19 +01:00
jaedle
fdbc130d8d do not show empty dependencies 2019-02-24 14:55:04 +01:00
jaedle
4b3cea3812 display dependend tasks 2019-02-24 14:53:39 +01:00
jaedle
1c3082ffa6 rename test fixture 2019-02-24 14:48:48 +01:00
jaedle
0446cfdba0 display commands of task 2019-02-24 14:37:14 +01:00
jaedle
db1d3183b6 refatoring 2019-02-24 14:32:47 +01:00
jaedle
fb666394fc refatoring 2019-02-24 14:31:29 +01:00
jaedle
1054c89a9d add missing test fixture file 2019-02-24 14:24:55 +01:00
jaedle
8dd87dc482 refactoring 2019-02-24 14:23:44 +01:00
jaedle
b2edbf05a1 refactoring 2019-02-24 14:20:39 +01:00
jaedle
6fb53a406b remove unusued expectations 2019-02-24 14:18:51 +01:00
jaedle
b05fa0821d move expectations for output to testdata 2019-02-24 14:18:07 +01:00
jaedle
0a808b1212 fix swapped expected and actual parameter 2019-02-24 14:10:46 +01:00
jaedle
f1d83e92a7 print command stub on details 2019-02-24 14:08:27 +01:00
jaedle
31b60f7f60 display task name on details 2019-02-24 14:01:53 +01:00
jaedle
c0f9af5daa refactoring 2019-02-24 12:15:59 +01:00
jaedle
b25a9e8884 refactoring 2019-02-24 12:13:18 +01:00
jaedle
3c0cf3cd55 fix documentation 2019-02-24 12:00:45 +01:00
jaedle
1ac6f17e6a should not surpress empty lines expect on last line 2019-02-24 11:58:44 +01:00
jaedle
399a2b38f3 add documentation for details 2019-02-24 11:52:31 +01:00
jaedle
b97221cdb2 ignore empty lines on description 2019-02-24 11:31:25 +01:00
jaedle
0164bc21ea be more specific in tests about output 2019-02-24 11:28:15 +01:00
jaedle
5a23250d32 simplified tests 2019-02-24 11:25:26 +01:00
jaedle
80d88d9789 refactoring 2019-02-24 11:22:14 +01:00
jaedle
31ead854c7 fix test expectation 2019-02-24 11:19:08 +01:00
jaedle
4b64fcb8a4 add more tests 2019-02-24 11:09:55 +01:00
jaedle
a951f2403d add more tests for details 2019-02-24 11:01:48 +01:00
jaedle
f9adeba7f1 add basic test for details 2019-02-24 09:53:49 +01:00
jaedle
5c823d51d0 revert changes for taskfile 2019-02-24 09:29:19 +01:00
jaedle
9be7521b83 refactoring 2019-02-24 09:28:25 +01:00
jaedle
c73ddc3552 refactoring 2019-02-24 09:27:26 +01:00
jaedle
4b7f058f41 refacotring 2019-02-24 09:25:39 +01:00
jaedle
07221a1b20 output detailed task description 2019-02-24 09:24:57 +01:00
jaedle
13614fb3c4 add details flag for cli 2019-02-24 08:51:20 +01:00
jaedle
4fa983bde7 ignore ide configuration 2019-02-24 08:24:09 +01:00
Andrey Nering
9cb1db8c0a Docs: Fix wrong URL 2019-02-21 21:57:21 -03:00
499 changed files with 122227 additions and 45838 deletions

View File

@@ -7,7 +7,6 @@ insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
indent_style = tab
indent_size = 8
[*.{md,yml,yaml,json,toml,htm,html}]
indent_style = space

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
open_collective: task
patreon: andreynering

26
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: goreleaser
on:
push:
tags:
- '*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.14.x
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

30
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Test
on: [push, pull_request]
jobs:
test:
name: Test
strategy:
matrix:
go-version: [1.13.x, 1.14.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
with:
go-version: ${{matrix.go-version}}
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Download Go modules
run: go mod download
env:
GOPROXY: https://proxy.golang.org
- name: Build
run: go build -o ./bin/task -v ./cmd/task
- name: Test
run: ./bin/task test

9
.gitignore vendored
View File

@@ -18,3 +18,12 @@
dist/
.DS_Store
# intellij idea/goland
.idea/
# exuberant ctags
tags
/bin
/testdata/vars/v1

View File

@@ -14,12 +14,11 @@ build:
env:
- CGO_ENABLED=0
archive:
name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
format_overrides:
- goos: windows
format: zip
archives:
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
format_overrides:
- goos: windows
format: zip
release:
draft: true
@@ -30,15 +29,15 @@ snapshot:
checksum:
name_template: "task_checksums.txt"
nfpm:
vendor: Task
homepage: https://github.com/go-task/task
maintainer: Andrey Nering <andrey.nering@gmail.com>
description: Simple task runner written in Go
license: MIT
conflicts:
- taskwarrior
formats:
- deb
- rpm
name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
nfpms:
- vendor: Task
homepage: https://github.com/go-task/task
maintainer: Andrey Nering <andrey.nering@gmail.com>
description: Simple task runner written in Go
license: MIT
conflicts:
- taskwarrior
formats:
- deb
- rpm
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"

View File

@@ -1,22 +0,0 @@
language: go
go:
- 1.10.x
- 1.11.x
addons:
apt:
packages:
- rpm
script:
- go install github.com/go-task/task/cmd/task
- task ci
deploy:
- provider: script
skip_cleanup: true
script: curl -sL http://git.io/goreleaser | bash
on:
tags: true
condition: $TRAVIS_OS_NAME = linux

View File

@@ -1,5 +1,122 @@
# Changelog
# v3.0.0 - Preview 4
- Refactor how variables work on version 3
([#311](https://github.com/go-task/task/pull/311)).
- Disallow `expansions` on v3 since it has no effect.
- `Taskvars.yml` is not automatically included anymore.
- `Taskfile_{{OS}}.yml` is not automatically included anymore.
- Allow interpolation on `includes`, so you can manually include a Taskfile
based on operation system, for example.
# v3.0.0 - Preview 3
- Expose `.TASK` variable in templates with the task name
([#252](https://github.com/go-task/task/issues/252)).
- Implement short task syntax
([#194](https://github.com/go-task/task/issues/194), [#240](https://github.com/go-task/task/pull/240)).
- Added option to make included Taskfile run commands on its own directory
([#260](https://github.com/go-task/task/issues/260), [#144](https://github.com/go-task/task/issues/144))
# v3.0.0 - Preview 2
- Taskfiles in version 1 are not supported anymore
([#237](https://github.com/go-task/task/pull/237)).
- Added global `method:` option. With this option, you can set a default
method to all tasks in a Taskfile
([#246](https://github.com/go-task/task/issues/246)).
- Changed default method from `timestamp` to `checksum`
([#246](https://github.com/go-task/task/issues/246)).
- New magic variables are now available when using `status:`:
`.TIMESTAMP` which contains the greatest modification date
from the files listed in `sources:`, and `.CHECKSUM`, which
contains a checksum of all files listed in `status:`.
This is useful for manual checking when using external, or even remote,
artifacts when using `status:`
([#216](https://github.com/go-task/task/pull/216)).
## v3.0.0 - Preview 1
- We're now using [slim-sprig](https://github.com/go-task/slim-sprig) instead of
[sprig](https://github.com/Masterminds/sprig), which allowed a file size
reduction of about 22%
([#219](https://github.com/go-task/task/pull/219)).
- We now use some colors on Task output to better distinguish message types -
commands are green, errors are red, etc
([#207](https://github.com/go-task/task/pull/207)).
## v2.8.1 - 2019-05-20
- Fix error code for the `--help` flag
([#300](https://github.com/go-task/task/issues/300), [#330](https://github.com/go-task/task/pull/330)).
- Print version to stdout instead of stderr
([#299](https://github.com/go-task/task/issues/299), [#329](https://github.com/go-task/task/pull/329)).
- Supress `context` errors when using the `--watch` flag
([#313](https://github.com/go-task/task/issues/313), [#317](https://github.com/go-task/task/pull/317)).
- Support templating on description
([#276](https://github.com/go-task/task/issues/276), [#283](https://github.com/go-task/task/pull/283)).
## v2.8.0 - 2019-12-07
- Add `--parallel` flag (alias `-p`) to run tasks given by the command line in
parallel
([#266](https://github.com/go-task/task/pull/266)).
- Fixed bug where calling the `task` CLI only informing global vars would not
execute the `default` task.
- Add hability to silent all tasks by adding `silent: true` a the root of the
Taskfile.
## v2.7.1 - 2019-11-10
- Fix error being raised when `exit 0` was called
([#251](https://github.com/go-task/task/issues/251)).
## v2.7.0 - 2019-09-22
- Fixed panic bug when assigning a global variable
([#229](https://github.com/go-task/task/issues/229), [#243](https://github.com/go-task/task/issues/234)).
- A task with `method: checksum` will now re-run if generated files are deleted
([#228](https://github.com/go-task/task/pull/228), [#238](https://github.com/go-task/task/issues/238)).
## v2.6.0 - 2019-07-21
- Fixed some bugs regarding minor version checks on `version:`.
- Add `preconditions:` to task
([#205](https://github.com/go-task/task/pull/205)).
- Create directory informed on `dir:` if it doesn't exist
([#209](https://github.com/go-task/task/issues/209), [#211](https://github.com/go-task/task/pull/211)).
- We now have a `--taskfile` flag (alias `-t`), which can be used to run
another Taskfile (other than the default `Taskfile.yml`)
([#221](https://github.com/go-task/task/pull/221)).
- It's now possible to install Task using Homebrew on Linux
([go-task/homebrew-tap#1](https://github.com/go-task/homebrew-tap/pull/1)).
## v2.5.2 - 2019-05-11
- Reverted YAML upgrade due issues with CRLF on Windows
([#201](https://github.com/go-task/task/issues/201), [go-yaml/yaml#450](https://github.com/go-yaml/yaml/issues/450)).
- Allow setting global variables through the CLI
([#192](https://github.com/go-task/task/issues/192)).
## 2.5.1 - 2019-04-27
- Fixed some issues with interactive command line tools, where sometimes
the output were not being shown, and similar issues
([#114](https://github.com/go-task/task/issues/114), [#190](https://github.com/go-task/task/issues/190), [#200](https://github.com/go-task/task/pull/200)).
- Upgraded [go-yaml/yaml](https://github.com/go-yaml/yaml) from v2 to v3.
## v2.5.0 - 2019-03-16
- We moved from the taskfile.org domain to the new fancy taskfile.dev domain.
While stuff is being redirected, we strongly recommend to everyone that use
[this install script](https://taskfile.dev/#/installation?id=install-script)
to use the new taskfile.dev domain on scripts from now on.
- Fixed to the ZSH completion
([#182](https://github.com/go-task/task/pull/182)).
- Add [`--summary` flag along with `summary:` task attribute](https://taskfile.org/#/usage?id=display-summary-of-task)
([#180](https://github.com/go-task/task/pull/180)).
## v2.4.0 - 2019-02-21
- Allow calling a task of the root Taskfile from an included Taskfile

View File

@@ -1,11 +1,12 @@
[![Build Status](https://travis-ci.org/go-task/task.svg?branch=master)](https://travis-ci.org/go-task/task)
![Test](https://github.com/go-task/task/workflows/Test/badge.svg)
![GoReleaser](https://github.com/go-task/task/workflows/goreleaser/badge.svg)
# Task
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/).
See [taskfile.org](https://taskfile.org) for documentation.
See [taskfile.dev](https://taskfile.dev) for documentation.
---

View File

@@ -1,7 +1,9 @@
version: '2'
version: '3'
includes:
docs: ./docs
docs:
taskfile: ./docs
dir: ./docs
vars:
GIT_COMMIT:
@@ -72,15 +74,12 @@ tasks:
- cp ./install-task.sh ./docs/install.sh
ci:
cmds:
- task: go-get
vars: {REPO: golang.org/x/lint/golint}
- task: lint
- task: test
- task: go-get
vars: {REPO: golang.org/x/lint/golint}
- task: lint
- task: test
go-get:
cmds:
- go get -u {{.REPO}}
go-get: go get -u {{.REPO}}
packages:
cmds:

View File

@@ -2,13 +2,16 @@ package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"syscall"
"github.com/go-task/task/v2"
"github.com/go-task/task/v2/internal/args"
"github.com/go-task/task/v2/internal/logger"
"github.com/spf13/pflag"
)
@@ -17,7 +20,7 @@ var (
version = "master"
)
const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--dry] [task...]
const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--taskfile] [--dry] [--summary] [task...]
Runs the specified task(s). Falls back to the "default" task if no task name
was specified, or lists all tasks if an unknown task name was specified.
@@ -26,12 +29,14 @@ Example: 'task hello' with the following 'Taskfile.yml' file will generate an
'output.txt' file with the content "hello".
'''
hello:
cmds:
- echo "I am going to write a file named 'output.txt' now."
- echo "hello" > output.txt
generates:
- output.txt
version: '3'
tasks:
hello:
cmds:
- echo "I am going to write a file named 'output.txt' now."
- echo "hello" > output.txt
generates:
- output.txt
'''
Options:
@@ -48,6 +53,7 @@ func main() {
var (
versionFlag bool
helpFlag bool
init bool
list bool
status bool
@@ -56,11 +62,16 @@ func main() {
verbose bool
silent bool
dry bool
summary bool
parallel bool
dir string
entrypoint string
output string
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(&list, "list", "l", false, "lists tasks with description of current Taskfile")
pflag.BoolVar(&status, "status", false, "exits with non-zero exit code if any of the given tasks is not up-to-date")
@@ -68,13 +79,22 @@ func main() {
pflag.BoolVarP(&watch, "watch", "w", false, "enables watch of the given task")
pflag.BoolVarP(&verbose, "verbose", "v", false, "enables verbose mode")
pflag.BoolVarP(&silent, "silent", "s", false, "disables echoing")
pflag.BoolVarP(&parallel, "parallel", "p", false, "executes tasks provided on command line in parallel")
pflag.BoolVar(&dry, "dry", false, "compiles and prints tasks in the order that they would be run, without executing them")
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.Parse()
if versionFlag {
log.Printf("Task version: %s\n", version)
fmt.Printf("Task version: %s\n", version)
return
}
if helpFlag {
pflag.Usage()
return
}
@@ -89,13 +109,28 @@ func main() {
return
}
if dir != "" && entrypoint != "" {
log.Fatal("task: You can't set both --dir and --taskfile")
return
}
if entrypoint != "" {
dir = filepath.Dir(entrypoint)
entrypoint = filepath.Base(entrypoint)
} else {
entrypoint = "Taskfile.yml"
}
e := task.Executor{
Force: force,
Watch: watch,
Verbose: verbose,
Silent: silent,
Dir: dir,
Dry: dry,
Force: force,
Watch: watch,
Verbose: verbose,
Silent: silent,
Dir: dir,
Dry: dry,
Entrypoint: entrypoint,
Summary: summary,
Parallel: parallel,
Color: color,
Stdin: os.Stdin,
Stdout: os.Stdout,
@@ -112,16 +147,8 @@ func main() {
return
}
arguments := pflag.Args()
if len(arguments) == 0 {
log.Println("task: No argument given, trying default task")
arguments = []string{"default"}
}
calls, err := args.Parse(arguments...)
if err != nil {
log.Fatal(err)
}
calls, globals := args.Parse(pflag.Args()...)
e.Taskfile.Vars.Merge(globals)
ctx := context.Background()
if !watch {
@@ -129,14 +156,15 @@ func main() {
}
if status {
if err = e.Status(ctx, calls...); err != nil {
if err := e.Status(ctx, calls...); err != nil {
log.Fatal(err)
}
return
}
if err := e.Run(ctx, calls...); err != nil {
log.Fatal(err)
e.Logger.Errf(logger.Red, "%v", err)
os.Exit(1)
}
}

21
completion/bash/task.bash Normal file
View File

@@ -0,0 +1,21 @@
_task_completion()
{
local scripts;
local curr_arg;
# Remove colon from word breaks
COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
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));
}
complete -F _task_completion task

10
completion/ps/task.ps1 Normal file
View File

@@ -0,0 +1,10 @@
$scriptBlock = {
param($commandName, $wordToComplete, $cursorPosition)
$curReg = "task{.exe}? (.*?)$"
$startsWith = $wordToComplete | Select-String $curReg -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value }
$reg = "\* ($startsWith.+?):"
$listOutput = $(task -l)
$listOutput | Select-String $reg -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value + " " }
}
Register-ArgumentCompleter -Native -CommandName task -ScriptBlock $scriptBlock

2
completion/zsh/_task Normal file → Executable file
View File

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

View File

@@ -1 +1 @@
taskfile.org
taskfile.dev

View File

@@ -1,4 +1,4 @@
version: '2'
version: '3'
tasks:
install:
@@ -9,4 +9,4 @@ tasks:
serve:
desc: Serves the documentation site locally
cmds:
- docsify serve docs
- docsify serve .

View File

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

View File

@@ -8,13 +8,15 @@ The `task_checksums.txt` file contains the sha256 checksum for each file.
## Homebrew
If you're on macOS and have [Homebrew][homebrew] installed, getting Task is
as simple as running:
If you're on macOS or Linux and have [Homebrew][homebrew] installed, getting
Task is as simple as running:
```bash
brew install go-task/tap/go-task
```
> This installation method is only currently supported on amd64 architectures.
## Snap
Task is available for [Snapcraft][snapcraft], but keep in mind that your
@@ -38,21 +40,29 @@ scoop install task
This installation method is community owned. After a new release of Task, it
may take some time until it's available on Scoop.
## Arch Linux
If you're on Arch Linux you can install Task from
[AUR](https://aur.archlinux.org/packages/taskfile-git) using your favorite
package manager such as `yay`, `pacaur` or `yaourt`:
```cmd
yay -S taskfile-git
```
This installation method is community owned, but since it's `-git` version of
the package, it's always latest available version based on the Git repository.
## Go
Task now uses [Go Modules](https://github.com/golang/go/wiki/Modules), which
means you may have trouble compiling it on older Go versions.
means you may have trouble compiling it on older Go versions or using
`$GOPATH`.
For CI environments we recommend using the [Install Script](#install-script)
instead, which is faster and more stable, since it'll just download the latest
released binary, instead of compiling the edge (master branch) version.
Installing in your `$GOPATH`:
```bash
go get -u -v github.com/go-task/task/cmd/task
```
Installing in another directory:
```bash
@@ -60,7 +70,7 @@ git clone https://github.com/go-task/task
cd task
# compiling binary to $GOPATH/bin
go install -v
go install -v ./cmd/task
# compiling it to another location
# use -o ./task.exe on Windows
@@ -76,11 +86,24 @@ scenarios like CIs. Many thanks to [godownloader][godownloader] for allowing
easily generating this script.
```bash
curl -s https://taskfile.org/install.sh | sh
curl -sL https://taskfile.dev/install.sh | sh
```
> This method will download the binary on the local `./bin` directory by default.
## 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)
by the Arduino team:
```yaml
- name: Install Task
uses: Arduino/actions/setup-taskfile@master
```
This installation method is community owned.
[go]: https://golang.org/
[snapcraft]: https://snapcraft.io/
[homebrew]: https://brew.sh/

View File

@@ -4,9 +4,9 @@ The release process of Task is done with the help of
[GoReleaser][goreleaser]. You can test the release process locally by calling
the `test-release` task of the Taskfile.
The Travis CI should release automatically when a new
Git tag is pushed to master, either for the artifact uploading (raw executables
and DEB and RPM packages)
[GitHub Actions](https://github.com/go-task/task/actions) should release
artifacts automatically when a new Git tag is pushed to master
(raw executables and DEB and RPM packages).
# Homebrew
@@ -28,7 +28,7 @@ the [Snapcraft dashboard][snapcraftdashboard]
Scoop is a community owned installation method. Scoop owners usually take care
of updating versions there by editing
[this file](https://github.com/lukesampson/scoop-extras/blob/master/task.json).
[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.
[goreleaser]: https://goreleaser.com/#continuous_integration

213
docs/styleguide.md Normal file
View File

@@ -0,0 +1,213 @@
# Styleguide
This is the official Task styleguide for `Taskfile.yml` files. This guide
contains some basic instructions to keep your Taskfile clean and familiar to
other users.
This contains general guidelines, but don't necessarely need to be strictly
followed. Feel free to disagree and proceed differently in some point if you
need or want to. Also, feel free to open issues or pull requests with
improvements to this guide.
## Use `Taskfile.yml` and not `taskfile.yml`
```yaml
# bad
taskfile.yml
# good
Taskfile.yml
```
This is important especially for Linux users. Windows and macOS have case
insensitive filesystems, so `taskfile.yml` will end up working, even that not
officially supported. On Linux, only `Taskfile.yml` will work, though.
## Use the correct order of keywords
- `version:`
- `includes:`
- Configuration ones, like `output:`, `expansions:` or `silent:`
- `vars:`
- `env:`
- `tasks:`
## Use 2 spaces for indentation
This is the most common convention for YAML files, and Task follows it.
```yaml
# bad
tasks:
foo:
cmds:
- echo 'foo'
# good
tasks:
foo:
cmds:
- echo 'foo'
```
## Separate with spaces the mains sections
```yaml
# bad
version: 2
includes:
docker: ./docker/Taskfile.yml
output: prefixed
expansions: 3
vars:
FOO: bar
env:
BAR: baz
tasks:
# ...
# good
version: 2
includes:
docker: ./docker/Taskfile.yml
output: prefixed
expansions: 3
vars:
FOO: bar
env:
BAR: baz
tasks:
# ...
```
## Add spaces between tasks
```yaml
# bad
version: 2
tasks:
foo:
cmds:
- echo 'foo'
bar:
cmds:
- echo 'bar'
baz:
cmds:
- echo 'baz'
# good
version: 2
tasks:
foo:
cmds:
- echo 'foo'
bar:
cmds:
- echo 'bar'
baz:
cmds:
- echo 'baz'
```
## Use upper-case variable names
```yaml
# bad
version: 2
vars:
binary_name: myapp
tasks:
build:
cmds:
- go build -o {{.binary_name}} .
# good
version: 2
vars:
BINARY_NAME: myapp
tasks:
build:
cmds:
- go build -o {{.BINARY_NAME}} .
```
## Don't wrap vars in spaces when templating
```yaml
# bad
version: 2
tasks:
greet:
cmds:
- echo '{{ .MESSAGE }}'
# good
version: 2
tasks:
greet:
cmds:
- echo '{{.MESSAGE}}'
```
This convention is also used by most people for any Go templating.
## Separate task name words with a dash
```yaml
# bad
version: 2
tasks:
do_something_fancy:
cmds:
- echo 'Do something'
# good
version: 2
tasks:
do-something-fancy:
cmds:
- echo 'Do something'
```
## Use colon for task namespacing
```yaml
# good
version: 2
tasks:
docker:build:
cmds:
- docker ...
docker:run:
cmds:
- docker-compose ...
```
This is also done automatically when using included Taskfiles.

View File

@@ -15,6 +15,8 @@ available, but not `3.0.0+`.
## Version 1
> NOTE: Taskfiles in version 1 are not supported on Task >= v3.0.0 anymore.
In the first version of the `Taskfile`, the `version:` key was not available,
because the tasks was in the root of the YAML document. Like this:
@@ -141,6 +143,21 @@ includes:
docker: ./DockerTasks.yml
```
## Version 2.6
Version 2.6 comes with `preconditions` stanza in tasks.
```yaml
version: '2'
tasks:
upload_environment:
preconditions:
- test -f .env
cmds:
- aws s3 cp .env s3://myenvironment
```
Please check the [documentation][includes]
[output]: usage.md#output-syntax

View File

@@ -63,7 +63,7 @@ tasks:
- echo $GREETING
```
> NOTE: `env` supports expansion and and retrieving output from a shell command
> NOTE: `env` supports expansion and retrieving output from a shell command
> just like variables, as you can see on the [Variables](#variables) section.
## Operating System specific tasks
@@ -124,6 +124,21 @@ namespace. So, you'd call `task docs:serve` to run the `serve` task from
`documentation/Taskfile.yml` or `task docker:build` to run the `build` task
from the `DockerTasks.yml` file.
### Directory of included Taskfile
By default, included Taskfile's tasks are ran in the current directory, even
if the Taskfile is in another directory, but you can force its tasks to run
in another directory by using this alternative syntax:
```yaml
version: '3'
includes:
docs:
taskfile: ./docs/Taskfile.yml
dir: ./docs
```
> The included Taskfiles must be using the same schema version the main
> Taskfile uses.
@@ -148,6 +163,8 @@ tasks:
- caddy
```
If the directory doesn't exist, `task` creates it.
## Task dependencies
You may have tasks that depend on others. Just pointing them on `deps` will
@@ -191,6 +208,9 @@ tasks:
If there is more than one dependency, they always run in parallel for better
performance.
> You can also make the tasks given by the command line run in parallel by
> using the `--parallel` flag (alias `-p`). Example: `task --parallel js css`.
If you want to pass information to dependencies, you can do that the same
manner as you would to [call another task](#calling-another-task):
@@ -264,6 +284,8 @@ The above syntax is also supported in `deps`.
## Prevent unnecessary work
### By fingerprinting locally generated files and their sources
If a task generates something, you can inform Task the source and generated
files, so Task will prevent to run them if not necessary.
@@ -319,6 +341,8 @@ tasks:
> TIP: method `none` skips any validation and always run the task.
### Using programmatic checks to indicate a task is up to date.
Alternatively, you can inform a sequence of tests as `status`. If no error
is returned (exit status 0), the task is considered up-to-date:
@@ -338,18 +362,86 @@ tasks:
- test -f directory/file2.txt
```
Normally, you would use `sources` in combination with
`generates` - but for tasks that generate remote artifacts (Docker images,
deploys, CD releases) the checksum source and timestamps require either
access to the artifact or for an out-of-band refresh of the `.checksum`
fingerprint file.
Two special variables `{{.CHECKSUM}}` and `{{.TIMESTAMP}}` are available
for interpolation within `status` commands, depending on the method assigned
to fingerprint the sources. Only `source` globs are fingerprinted.
Note that the `{{.TIMESTAMP}}` variable is a "live" Go `time.Time` struct, and
can be formatted using any of the methods that `time.Time` responds to.
See [the Go Time documentation](https://golang.org/pkg/time/) for more information.
You can use `--force` or `-f` if you want to force a task to run even when
up-to-date.
Also, `task --status [tasks]...` will exit with a non-zero exit code if any of
the tasks are not up-to-date.
### Using programmatic checks to cancel execution of an task and it's dependencies
In addition to `status` checks, there are also `preconditions` checks, which are
the logical inverse of `status` checks. That is, if you need a certain set of
conditions to be _true_ you can use the `preconditions` stanza.
`preconditions` are similar to `status` lines except they support `sh`
expansion and they SHOULD all return 0.
```yaml
version: '2'
tasks:
generate-files:
cmds:
- mkdir directory
- touch directory/file1.txt
- touch directory/file2.txt
# test existence of files
preconditions:
- test -f .env
- sh: "[ 1 = 0 ]"
msg: "One doesn't equal Zero, Halting"
```
Preconditions can set specific failure messages that can tell
a user what steps to take using the `msg` field.
If a task has a dependency on a sub-task with a precondition, and that
precondition is not met - the calling task will fail. Note that a task
executed with a failing precondition will not run unless `--force` is
given.
Unlike `status` which will skip a task if it is up to date, and continue
executing tasks that depend on it, a `precondition` will fail a task, along
with any other tasks that depend on it.
```yaml
version: '2'
tasks:
task_will_fail:
preconditions:
- sh: "exit 1"
task_will_also_fail:
deps:
- task_will_fail
task_will_still_fail:
cmds:
- task: task_will_fail
- echo "I will not run"
```
## Variables
When doing interpolation of variables, Task will look for the below.
They are listed below in order of importance (e.g. most important first):
- Variables declared locally in the task
- Variables declared in the task definition
- Variables given while calling a task from another.
(See [Calling another task](#calling-another-task) above)
- Variables declared in the `vars:` option in the `Taskfile`
@@ -362,6 +454,8 @@ Example of sending parameters with environment variables:
$ TASK_VARIABLE=a-value task do-something
```
> TIP: A special variable `.TASK` is always available containg the task name.
Since some shells don't support above syntax to set environment variables
(Windows) tasks also accepts a similar style when not in the beginning of
the command. Variables given in this form are only visible to the task called
@@ -371,6 +465,12 @@ right before.
$ task write-file FILE=file.txt "CONTENT=Hello, World!" print "MESSAGE=All done!"
```
If you want to set global variables using this syntax, give it before any task:
```bash
$ task OUTPUT=file.txt generate-file
```
Example of locally declared vars:
```yaml
@@ -379,7 +479,7 @@ version: '2'
tasks:
print-var:
cmds:
echo "{{.VAR}}"
- echo "{{.VAR}}"
vars:
VAR: Hello!
```
@@ -455,7 +555,7 @@ This works for all types of variables.
Task parse commands as [Go's template engine][gotemplate] before executing
them. Variables are accessible through dot syntax (`.VARNAME`).
All functions by the Go's [sprig lib](http://masterminds.github.io/sprig/)
All functions by the Go's [slim-sprig lib](https://go-task.github.io/slim-sprig/)
are available. The following example gets the current date in a given format:
```yaml
@@ -542,6 +642,51 @@ would print the following output:
* test: Run all the go tests.
```
## Display summary of task
Running `task --summary task-name` will show a summary of a task
The following Taskfile:
```yaml
version: '2'
tasks:
release:
deps: [build]
summary: |
Release your project to github
It will build your project before starting the release it.
Please make sure that you have set GITHUB_TOKEN before starting.
cmds:
- your-release-tool
build:
cmds:
- your-build-tool
```
with running ``task --summary release`` would print the following output:
```
task: release
Release your project to github
It will build your project before starting the release it.
Please make sure that you have set GITHUB_TOKEN before starting.
dependencies:
- build
commands:
- your-release-tool
```
If a summary is missing, the description will be printed.
If the task does not have a summary or a description, a warning is printed.
Please note: *showing the summary will not execute the command*.
## Silent mode
Silent mode disables echoing of commands before Task runs it.
@@ -569,7 +714,7 @@ With silent mode on, the below will be print instead:
Print something
```
There's three ways to enable silent mode:
There are four ways to enable silent mode:
* At command level:
@@ -595,6 +740,19 @@ tasks:
silent: true
```
* Globally at Taskfile level:
```yaml
version: '2'
silent: true
tasks:
echo:
cmds:
- echo "Print something"
```
* Or globally with `--silent` or `-s` flag
If you want to suppress STDOUT instead, just redirect a command to `/dev/null`:
@@ -710,6 +868,22 @@ $ task default
> The `output` option can also be specified by the `--output` or `-o` flags.
## 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):
```yaml
version: '3'
tasks:
build: go build -v -o ./app{{exeExt}} .
build:
- task: build
- ./app{{exeExt}} -h localhost -p 8080
```
## Watch tasks
If you give a `--watch` or `-w` argument, task will watch for file changes

25
go.mod
View File

@@ -1,23 +1,16 @@
module github.com/go-task/task/v2
require (
github.com/Masterminds/semver v1.4.2
github.com/Masterminds/sprig v2.16.0+incompatible
github.com/aokoli/goutils v1.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.0.0 // indirect
github.com/huandu/xstrings v1.1.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/fatih/color v1.7.0
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-zglob v0.0.1
github.com/mitchellh/go-homedir v1.0.0
github.com/radovskyb/watcher v1.0.5
github.com/spf13/pflag v1.0.3
github.com/stretchr/testify v1.3.0
golang.org/x/crypto v0.0.0-20180830192347-182538f80094 // indirect
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d // indirect
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789 // indirect
gopkg.in/yaml.v2 v2.2.1
mvdan.cc/sh v2.6.3+incompatible
github.com/stretchr/testify v1.5.1
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
mvdan.cc/sh/v3 v3.1.1
)
go 1.13

75
go.sum
View File

@@ -1,47 +1,66 @@
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.16.0+incompatible h1:QZbMUPxRQ50EKAq3LFMnxddMu88/EUUG3qmxwtDmPsY=
github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg=
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/huandu/xstrings v1.1.0 h1:9oZY6Z/H3A1gytJxzuicbmV5QoR8M1TAPcn9WTg7vqg=
github.com/huandu/xstrings v1.1.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/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.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/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/pkg/diff v0.0.0-20190930165518-531926345625/go.mod h1:kFj35MyHn14a6pIgWhm46KVjJr5CHys3eEYxkuKD1EI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/radovskyb/watcher v1.0.5 h1:wqt7gb+HjGacvFoLTKeT44C+XVPxu7bvHvKT1IvZ7rw=
github.com/radovskyb/watcher v1.0.5/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
github.com/rogpeppe/go-internal v1.5.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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20180830192347-182538f80094 h1:rVTAlhYa4+lCfNxmAIEOGQRoD23UqP72M3+rSWVGDTg=
golang.org/x/crypto v0.0.0-20180830192347-182538f80094/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789 h1:T8D7l6WB3tLu+VpKvw06ieD/OhBi1XpJmG1U/FtttZg=
golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-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-20200217220822-9197077df867 h1:JoRuNIf+rpHl+VhScRQQvzbHed86tKkqwPMV34T8myw=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/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=
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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
mvdan.cc/sh v2.6.3+incompatible h1:uXnnFNSBQbKUwwh2iBSkVjG+GbwoMuI+UmBVPnNiWhA=
mvdan.cc/sh v2.6.3+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.1.1 h1:niuYC5Ug0KzLuN6CNX3ru37v4MkVD5Wm9T4Mk2eJr9A=
mvdan.cc/sh/v3 v3.1.1/go.mod h1:F+Vm4ZxPJxDKExMLhvjuI50oPnedVXpfjNSrusiTOno=

View File

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

View File

@@ -8,9 +8,9 @@ import (
"path/filepath"
)
const defaultTaskfile = `# https://taskfile.org
const defaultTaskfile = `# https://taskfile.dev
version: '2'
version: '3'
vars:
GREETING: Hello, World!

View File

@@ -1,36 +1,45 @@
package args
import (
"errors"
"strings"
"github.com/go-task/task/v2/internal/taskfile"
)
var (
// ErrVariableWithoutTask is returned when variables are given before any task
ErrVariableWithoutTask = errors.New("task: variable given before any task")
)
// Parse parses command line argument: tasks and vars of each task
func Parse(args ...string) ([]taskfile.Call, error) {
func Parse(args ...string) ([]taskfile.Call, *taskfile.Vars) {
var calls []taskfile.Call
var globals *taskfile.Vars
for _, arg := range args {
if !strings.Contains(arg, "=") {
calls = append(calls, taskfile.Call{Task: arg})
continue
}
if len(calls) < 1 {
return nil, ErrVariableWithoutTask
if globals == nil {
globals = &taskfile.Vars{}
}
name, value := splitVar(arg)
globals.Set(name, taskfile.Var{Static: value})
} else {
if calls[len(calls)-1].Vars == nil {
calls[len(calls)-1].Vars = &taskfile.Vars{}
}
name, value := splitVar(arg)
calls[len(calls)-1].Vars.Set(name, taskfile.Var{Static: value})
}
if calls[len(calls)-1].Vars == nil {
calls[len(calls)-1].Vars = make(taskfile.Vars)
}
pair := strings.SplitN(arg, "=", 2)
calls[len(calls)-1].Vars[pair[0]] = taskfile.Var{Static: pair[1]}
}
return calls, nil
if len(calls) == 0 {
calls = append(calls, taskfile.Call{Task: "default"})
}
return calls, globals
}
func splitVar(s string) (string, string) {
pair := strings.SplitN(s, "=", 2)
return pair[0], pair[1]
}

View File

@@ -12,13 +12,13 @@ import (
func TestArgs(t *testing.T) {
tests := []struct {
Args []string
Expected []taskfile.Call
Err error
Args []string
ExpectedCalls []taskfile.Call
ExpectedGlobals *taskfile.Vars
}{
{
Args: []string{"task-a", "task-b", "task-c"},
Expected: []taskfile.Call{
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
@@ -26,45 +26,88 @@ func TestArgs(t *testing.T) {
},
{
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
Expected: []taskfile.Call{
ExpectedCalls: []taskfile.Call{
{
Task: "task-a",
Vars: taskfile.Vars{
"FOO": taskfile.Var{Static: "bar"},
Vars: &taskfile.Vars{
Keys: []string{"FOO"},
Mapping: map[string]taskfile.Var{
"FOO": taskfile.Var{Static: "bar"},
},
},
},
{Task: "task-b"},
{
Task: "task-c",
Vars: taskfile.Vars{
"BAR": taskfile.Var{Static: "baz"},
"BAZ": taskfile.Var{Static: "foo"},
Vars: &taskfile.Vars{
Keys: []string{"BAR", "BAZ"},
Mapping: map[string]taskfile.Var{
"BAR": taskfile.Var{Static: "baz"},
"BAZ": taskfile.Var{Static: "foo"},
},
},
},
},
},
{
Args: []string{"task-a", "CONTENT=with some spaces"},
Expected: []taskfile.Call{
ExpectedCalls: []taskfile.Call{
{
Task: "task-a",
Vars: taskfile.Vars{
"CONTENT": taskfile.Var{Static: "with some spaces"},
Vars: &taskfile.Vars{
Keys: []string{"CONTENT"},
Mapping: map[string]taskfile.Var{
"CONTENT": taskfile.Var{Static: "with some spaces"},
},
},
},
},
},
{
Args: []string{"FOO=bar", "task-a"},
Err: args.ErrVariableWithoutTask,
Args: []string{"FOO=bar", "task-a", "task-b"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO"},
Mapping: map[string]taskfile.Var{
"FOO": {Static: "bar"},
},
},
},
{
Args: nil,
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{"FOO=bar", "BAR=baz"},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO", "BAR"},
Mapping: map[string]taskfile.Var{
"FOO": {Static: "bar"},
"BAR": {Static: "baz"},
},
},
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, err := args.Parse(test.Args...)
assert.Equal(t, test.Err, err)
assert.Equal(t, test.Expected, calls)
calls, globals := args.Parse(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls)
assert.Equal(t, test.ExpectedGlobals, globals)
})
}
}

View File

@@ -7,6 +7,6 @@ import (
// Compiler handles compilation of a task before its execution.
// E.g. variable merger, template processing, etc.
type Compiler interface {
GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error)
GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
HandleDynamicVar(v taskfile.Var) (string, error)
}

View File

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

View File

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

View File

@@ -19,8 +19,8 @@ var _ compiler.Compiler = &CompilerV2{}
type CompilerV2 struct {
Dir string
Taskvars taskfile.Vars
TaskfileVars taskfile.Vars
Taskvars *taskfile.Vars
TaskfileVars *taskfile.Vars
Expansions int
@@ -36,9 +36,10 @@ type CompilerV2 struct {
// 3. Taskfile variables
// 4. Taskvars file variables
// 5. Environment variables
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error) {
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
vr := varResolver{c: c, vars: compiler.GetEnviron()}
for _, vars := range []taskfile.Vars{c.Taskvars, c.TaskfileVars, call.Vars, t.Vars} {
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)
}
@@ -48,16 +49,16 @@ func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (taskfil
type varResolver struct {
c *CompilerV2
vars taskfile.Vars
vars *taskfile.Vars
err error
}
func (vr *varResolver) merge(vars taskfile.Vars) {
func (vr *varResolver) merge(vars *taskfile.Vars) {
if vr.err != nil {
return
}
tr := templater.Templater{Vars: vr.vars}
for k, v := range vars {
vars.Range(func(k string, v taskfile.Var) error {
v = taskfile.Var{
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),
@@ -65,10 +66,11 @@ func (vr *varResolver) merge(vars taskfile.Vars) {
static, err := vr.c.HandleDynamicVar(v)
if err != nil {
vr.err = err
return
return err
}
vr.vars[k] = taskfile.Var{Static: static}
}
vr.vars.Set(k, taskfile.Var{Static: static})
return nil
})
vr.err = tr.Err()
}
@@ -103,7 +105,7 @@ func (c *CompilerV2) HandleDynamicVar(v taskfile.Var) (string, error) {
result := strings.TrimSuffix(stdout.String(), "\n")
c.dynamicCache[v.Sh] = result
c.Logger.VerboseErrf(`task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
c.Logger.VerboseErrf(logger.Magenta, `task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
return result, nil
}

View File

@@ -0,0 +1,98 @@
package v3
import (
"bytes"
"context"
"fmt"
"strings"
"sync"
"github.com/go-task/task/v2/internal/compiler"
"github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v2/internal/templater"
)
var _ compiler.Compiler = &CompilerV3{}
type CompilerV3 struct {
Dir string
TaskfileVars *taskfile.Vars
Logger *logger.Logger
dynamicCache map[string]string
muDynamicCache sync.Mutex
}
func (c *CompilerV3) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
result := compiler.GetEnviron()
result.Set("TASK", taskfile.Var{Static: t.Task})
rangeFunc := func(k string, v taskfile.Var) error {
tr := templater.Templater{Vars: result, RemoveNoValue: true}
v = taskfile.Var{
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),
}
if err := tr.Err(); err != nil {
return err
}
static, err := c.HandleDynamicVar(v)
if err != nil {
return err
}
result.Set(k, taskfile.Var{Static: static})
return nil
}
if err := c.TaskfileVars.Range(rangeFunc); err != nil {
return nil, err
}
if err := call.Vars.Range(rangeFunc); err != nil {
return nil, err
}
if err := t.Vars.Range(rangeFunc); err != nil {
return nil, err
}
return result, nil
}
func (c *CompilerV3) HandleDynamicVar(v taskfile.Var) (string, error) {
if v.Static != "" || v.Sh == "" {
return v.Static, nil
}
c.muDynamicCache.Lock()
defer c.muDynamicCache.Unlock()
if c.dynamicCache == nil {
c.dynamicCache = make(map[string]string, 30)
}
if result, ok := c.dynamicCache[v.Sh]; ok {
return result, nil
}
var stdout bytes.Buffer
opts := &execext.RunCommandOptions{
Command: v.Sh,
Dir: c.Dir,
Stdout: &stdout,
Stderr: c.Logger.Stderr,
}
if err := execext.RunCommand(context.Background(), opts); err != nil {
return "", fmt.Errorf(`task: Command "%s" in taskvars file failed: %s`, opts.Command, err)
}
// Trim a single trailing newline from the result to make most command
// output easier to use in shell commands.
result := strings.TrimSuffix(stdout.String(), "\n")
c.dynamicCache[v.Sh] = result
c.Logger.VerboseErrf(logger.Magenta, `task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
return result, nil
}

View File

@@ -0,0 +1,13 @@
package execext
import (
"io"
)
var _ io.ReadWriteCloser = devNull{}
type devNull struct{}
func (devNull) Read(p []byte) (int, error) { return 0, io.EOF }
func (devNull) Write(p []byte) (int, error) { return len(p), nil }
func (devNull) Close() error { return nil }

View File

@@ -8,10 +8,10 @@ import (
"path/filepath"
"strings"
"mvdan.cc/sh/expand"
"mvdan.cc/sh/interp"
"mvdan.cc/sh/shell"
"mvdan.cc/sh/syntax"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/shell"
"mvdan.cc/sh/v3/syntax"
)
// RunCommandOptions is the options for the RunCommand func
@@ -49,8 +49,12 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
interp.Dir(opts.Dir),
interp.Env(expand.ListEnviron(environ...)),
interp.Module(interp.DefaultExec),
interp.Module(interp.OpenDevImpls(interp.DefaultOpen)),
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.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
)
@@ -62,12 +66,10 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
// IsExitError returns true the given error is an exis status error
func IsExitError(err error) bool {
switch err.(type) {
case interp.ExitStatus, interp.ShellExitStatus:
if _, ok := interp.IsExitStatus(err); ok {
return true
default:
return false
}
return false
}
// Expand is a helper to mvdan.cc/shell.Fields that returns the first field

View File

@@ -1,38 +1,64 @@
package logger
import (
"fmt"
"io"
"github.com/fatih/color"
)
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()
)
// Logger is just a wrapper that prints stuff to STDOUT or STDERR,
// with optional color.
type Logger struct {
Stdout io.Writer
Stderr io.Writer
Verbose bool
Color bool
}
func (l *Logger) Outf(s string, args ...interface{}) {
// Outf prints stuff to STDOUT.
func (l *Logger) Outf(print PrintFunc, s string, args ...interface{}) {
if len(args) == 0 {
s, args = "%s", []interface{}{s}
}
fmt.Fprintf(l.Stdout, s+"\n", args...)
if !l.Color {
print = Default
}
print(l.Stdout, s+"\n", args...)
}
func (l *Logger) VerboseOutf(s string, args ...interface{}) {
// VerboseOutf prints stuff to STDOUT if verbose mode is enabled.
func (l *Logger) VerboseOutf(print PrintFunc, s string, args ...interface{}) {
if l.Verbose {
l.Outf(s, args...)
l.Outf(print, s, args...)
}
}
func (l *Logger) Errf(s string, args ...interface{}) {
// Errf prints stuff to STDERR.
func (l *Logger) Errf(print PrintFunc, s string, args ...interface{}) {
if len(args) == 0 {
s, args = "%s", []interface{}{s}
}
fmt.Fprintf(l.Stderr, s+"\n", args...)
if !l.Color {
print = Default
}
print(l.Stderr, s+"\n", args...)
}
func (l *Logger) VerboseErrf(s string, args ...interface{}) {
// VerboseErrf prints stuff to STDERR if verbose mode is enabled.
func (l *Logger) VerboseErrf(print PrintFunc, s string, args ...interface{}) {
if l.Verbose {
l.Errf(s, args...)
l.Errf(print, s, args...)
}
}

View File

@@ -7,7 +7,7 @@ import (
type Group struct{}
func (Group) WrapWriter(w io.Writer, _ string) io.WriteCloser {
func (Group) WrapWriter(w io.Writer, _ string) io.Writer {
return &groupWriter{writer: w}
}

View File

@@ -6,18 +6,6 @@ import (
type Interleaved struct{}
func (Interleaved) WrapWriter(w io.Writer, _ string) io.WriteCloser {
return nopWriterCloser{w: w}
}
type nopWriterCloser struct {
w io.Writer
}
func (wc nopWriterCloser) Write(p []byte) (int, error) {
return wc.w.Write(p)
}
func (wc nopWriterCloser) Close() error {
return nil
func (Interleaved) WrapWriter(w io.Writer, _ string) io.Writer {
return w
}

View File

@@ -5,5 +5,5 @@ import (
)
type Output interface {
WrapWriter(w io.Writer, prefix string) io.WriteCloser
WrapWriter(w io.Writer, prefix string) io.Writer
}

View File

@@ -3,6 +3,7 @@ package output_test
import (
"bytes"
"fmt"
"io"
"testing"
"github.com/go-task/task/v2/internal/output"
@@ -24,7 +25,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, "")
var w = o.WrapWriter(&b, "").(io.WriteCloser)
fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, "", b.String())
@@ -37,7 +38,7 @@ func TestGroup(t *testing.T) {
func TestPrefixed(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Prefixed{}
var w = o.WrapWriter(&b, "prefix")
var w = o.WrapWriter(&b, "prefix").(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.WriteCloser {
func (Prefixed) WrapWriter(w io.Writer, prefix string) io.Writer {
return &prefixWriter{writer: w, prefix: prefix}
}
@@ -34,12 +34,12 @@ func (pw *prefixWriter) Close() error {
func (pw *prefixWriter) writeOutputLines(force bool) error {
for {
line, err := pw.buff.ReadString('\n')
if err == nil {
switch line, err := pw.buff.ReadString('\n'); err {
case nil:
if err = pw.writeLine(line); err != nil {
return err
}
} else if err == io.EOF {
case io.EOF:
// if this line was not a complete line, re-add to the buffer
if !force && !strings.HasSuffix(line, "\n") {
_, err = pw.buff.WriteString(line)
@@ -47,7 +47,7 @@ func (pw *prefixWriter) writeOutputLines(force bool) error {
}
return pw.writeLine(line)
} else {
default:
return err
}
}

View File

@@ -14,20 +14,25 @@ import (
// Checksum validades if a task is up to date by calculating its source
// files checksum
type Checksum struct {
Dir string
Task string
Sources []string
Dry bool
Dir string
Task string
Sources []string
Generates []string
Dry bool
}
// IsUpToDate implements the Checker interface
func (c *Checksum) IsUpToDate() (bool, error) {
if len(c.Sources) == 0 {
return false, nil
}
checksumFile := c.checksumFilePath()
data, _ := ioutil.ReadFile(checksumFile)
oldMd5 := strings.TrimSpace(string(data))
sources, err := glob(c.Dir, c.Sources)
sources, err := globs(c.Dir, c.Sources)
if err != nil {
return false, err
}
@@ -43,6 +48,23 @@ func (c *Checksum) IsUpToDate() (bool, error) {
return false, err
}
}
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)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, err
}
if len(generates) == 0 {
return false, nil
}
}
}
return oldMd5 == newMd5, nil
}
@@ -50,21 +72,14 @@ func (c *Checksum) checksum(files ...string) (string, error) {
h := md5.New()
for _, f := range files {
// also sum the filename, so checksum changes for renaming a file
if _, err := io.Copy(h, strings.NewReader(filepath.Base(f))); err != nil {
return "", err
}
f, err := os.Open(f)
if err != nil {
return "", err
}
info, err := f.Stat()
if err != nil {
return "", err
}
if info.IsDir() {
continue
}
// also sum the filename, so checksum changes for renaming a file
if _, err = io.Copy(h, strings.NewReader(info.Name())); err != nil {
return "", err
}
if _, err = io.Copy(h, f); err != nil {
return "", err
}
@@ -73,11 +88,21 @@ func (c *Checksum) checksum(files ...string) (string, error) {
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
// Value implements the Checker Interface
func (c *Checksum) Value() (interface{}, error) {
return c.checksum()
}
// OnError implements the Checker interface
func (c *Checksum) OnError() error {
return os.Remove(c.checksumFilePath())
}
// Kind implements the Checker Interface
func (*Checksum) Kind() string {
return "checksum"
}
func (c *Checksum) checksumFilePath() string {
return filepath.Join(c.Dir, ".task", "checksum", c.normalizeFilename(c.Task))
}

View File

@@ -1,6 +1,7 @@
package status
import (
"os"
"path/filepath"
"sort"
@@ -9,21 +10,41 @@ import (
"github.com/mattn/go-zglob"
)
func glob(dir string, globs []string) (files []string, err error) {
func globs(dir string, globs []string) ([]string, error) {
files := make([]string, 0)
for _, g := range globs {
if !filepath.IsAbs(g) {
g = filepath.Join(dir, g)
}
g, err = execext.Expand(g)
if err != nil {
return nil, err
}
f, err := zglob.Glob(g)
f, err := glob(dir, g)
if err != nil {
continue
}
files = append(files, f...)
}
sort.Strings(files)
return
return files, nil
}
func glob(dir string, g string) ([]string, error) {
files := make([]string, 0)
if !filepath.IsAbs(g) {
g = filepath.Join(dir, g)
}
g, err := execext.Expand(g)
if err != nil {
return nil, err
}
fs, err := zglob.Glob(g)
if err != nil {
return nil, err
}
for _, f := range fs {
info, err := os.Stat(f)
if err != nil {
return nil, err
}
if info.IsDir() {
continue
}
files = append(files, f)
}
return files, nil
}

View File

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

View File

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

View File

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

103
internal/summary/summary.go Normal file
View File

@@ -0,0 +1,103 @@
package summary
import (
"strings"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/taskfile"
)
func PrintTasks(l *logger.Logger, t *taskfile.Taskfile, c []taskfile.Call) {
for i, call := range c {
PrintSpaceBetweenSummaries(l, i)
PrintTask(l, t.Tasks[call.Task])
}
}
func PrintSpaceBetweenSummaries(l *logger.Logger, i int) {
spaceRequired := i > 0
if !spaceRequired {
return
}
l.Outf(logger.Default, "")
l.Outf(logger.Default, "")
}
func PrintTask(l *logger.Logger, t *taskfile.Task) {
printTaskName(l, t)
printTaskDescribingText(t, l)
printTaskDependencies(l, t)
printTaskCommands(l, t)
}
func printTaskDescribingText(t *taskfile.Task, l *logger.Logger) {
if hasSummary(t) {
printTaskSummary(l, t)
} else if hasDescription(t) {
printTaskDescription(l, t)
} else {
printNoDescriptionOrSummary(l)
}
}
func hasSummary(t *taskfile.Task) bool {
return t.Summary != ""
}
func printTaskSummary(l *logger.Logger, t *taskfile.Task) {
lines := strings.Split(t.Summary, "\n")
for i, line := range lines {
notLastLine := i+1 < len(lines)
if notLastLine || line != "" {
l.Outf(logger.Default, line)
}
}
}
func printTaskName(l *logger.Logger, t *taskfile.Task) {
l.Outf(logger.Default, "task: %s", t.Task)
l.Outf(logger.Default, "")
}
func hasDescription(t *taskfile.Task) bool {
return t.Desc != ""
}
func printTaskDescription(l *logger.Logger, t *taskfile.Task) {
l.Outf(logger.Default, t.Desc)
}
func printNoDescriptionOrSummary(l *logger.Logger) {
l.Outf(logger.Default, "(task does not have description or summary)")
}
func printTaskDependencies(l *logger.Logger, t *taskfile.Task) {
if len(t.Deps) == 0 {
return
}
l.Outf(logger.Default, "")
l.Outf(logger.Default, "dependencies:")
for _, d := range t.Deps {
l.Outf(logger.Default, " - %s", d.Task)
}
}
func printTaskCommands(l *logger.Logger, t *taskfile.Task) {
if len(t.Cmds) == 0 {
return
}
l.Outf(logger.Default, "")
l.Outf(logger.Default, "commands:")
for _, c := range t.Cmds {
isCommand := c.Cmd != ""
if isCommand {
l.Outf(logger.Default, " - %s", c.Cmd)
} else {
l.Outf(logger.Default, " - Task: %s", c.Task)
}
}
}

View File

@@ -0,0 +1,173 @@
package summary_test
import (
"bytes"
"strings"
"testing"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/summary"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/stretchr/testify/assert"
)
func TestPrintsDependenciesIfPresent(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Deps: []*taskfile.Dep{
{Task: "dep1"},
{Task: "dep2"},
{Task: "dep3"},
},
}
summary.PrintTask(&l, task)
assert.Contains(t, buffer.String(), "\ndependencies:\n - dep1\n - dep2\n - dep3\n")
}
func createDummyLogger() (*bytes.Buffer, logger.Logger) {
buffer := &bytes.Buffer{}
l := logger.Logger{
Stderr: buffer,
Stdout: buffer,
Verbose: false,
}
return buffer, l
}
func TestDoesNotPrintDependenciesIfMissing(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Deps: []*taskfile.Dep{},
}
summary.PrintTask(&l, task)
assert.NotContains(t, buffer.String(), "dependencies:")
}
func TestPrintTaskName(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Task: "my-task-name",
}
summary.PrintTask(&l, task)
assert.Contains(t, buffer.String(), "task: my-task-name\n")
}
func TestPrintTaskCommandsIfPresent(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Cmds: []*taskfile.Cmd{
{Cmd: "command-1"},
{Cmd: "command-2"},
{Task: "task-1"},
},
}
summary.PrintTask(&l, task)
assert.Contains(t, buffer.String(), "\ncommands:\n")
assert.Contains(t, buffer.String(), "\n - command-1\n")
assert.Contains(t, buffer.String(), "\n - command-2\n")
assert.Contains(t, buffer.String(), "\n - Task: task-1\n")
}
func TestDoesNotPrintCommandIfMissing(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Cmds: []*taskfile.Cmd{},
}
summary.PrintTask(&l, task)
assert.NotContains(t, buffer.String(), "commands")
}
func TestLayout(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Task: "sample-task",
Summary: "line1\nline2\nline3\n",
Deps: []*taskfile.Dep{
{Task: "dependency"},
},
Cmds: []*taskfile.Cmd{
{Cmd: "command"},
},
}
summary.PrintTask(&l, task)
assert.Equal(t, expectedOutput(), buffer.String())
}
func expectedOutput() string {
expected := `task: sample-task
line1
line2
line3
dependencies:
- dependency
commands:
- command
`
return expected
}
func TestPrintDescriptionAsFallback(t *testing.T) {
buffer, l := createDummyLogger()
taskWithoutSummary := &taskfile.Task{
Desc: "description",
}
taskWithSummary := &taskfile.Task{
Desc: "description",
Summary: "summary",
}
taskWithoutSummaryOrDescription := &taskfile.Task{}
summary.PrintTask(&l, taskWithoutSummary)
assert.Contains(t, buffer.String(), "description")
buffer.Reset()
summary.PrintTask(&l, taskWithSummary)
assert.NotContains(t, buffer.String(), "description")
buffer.Reset()
summary.PrintTask(&l, taskWithoutSummaryOrDescription)
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n")
}
func TestPrintAllWithSpaces(t *testing.T) {
buffer, l := createDummyLogger()
t1 := &taskfile.Task{Task: "t1"}
t2 := &taskfile.Task{Task: "t2"}
t3 := &taskfile.Task{Task: "t3"}
tasks := make(taskfile.Tasks, 3)
tasks["t1"] = t1
tasks["t2"] = t2
tasks["t3"] = t3
summary.PrintTasks(&l,
&taskfile.Taskfile{Tasks: tasks},
[]taskfile.Call{{Task: "t1"}, {Task: "t2"}, {Task: "t3"}})
assert.True(t, strings.HasPrefix(buffer.String(), "task: t1"))
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n\n\ntask: t2")
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n\n\ntask: t3")
}

View File

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

View File

@@ -10,14 +10,14 @@ type Cmd struct {
Cmd string
Silent bool
Task string
Vars Vars
Vars *Vars
IgnoreError bool
}
// Dep is a task dependency
type Dep struct {
Task string
Vars Vars
Vars *Vars
}
var (
@@ -51,7 +51,7 @@ func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
var taskCall struct {
Task string
Vars Vars
Vars *Vars
}
if err := unmarshal(&taskCall); err == nil {
c.Task = taskCall.Task
@@ -70,7 +70,7 @@ func (d *Dep) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
var taskCall struct {
Task string
Vars Vars
Vars *Vars
}
if err := unmarshal(&taskCall); err == nil {
d.Task = taskCall.Task

View File

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

View File

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

View File

@@ -0,0 +1,45 @@
package taskfile
import (
"errors"
"fmt"
)
var (
// ErrCantUnmarshalPrecondition is returned for invalid precond YAML.
ErrCantUnmarshalPrecondition = errors.New("task: Can't unmarshal precondition value")
)
// Precondition represents a precondition necessary for a task to run
type Precondition struct {
Sh string
Msg string
}
// UnmarshalYAML implements yaml.Unmarshaler interface.
func (p *Precondition) UnmarshalYAML(unmarshal func(interface{}) error) error {
var cmd string
if err := unmarshal(&cmd); err == nil {
p.Sh = cmd
p.Msg = fmt.Sprintf("`%s` failed", cmd)
return nil
}
var sh struct {
Sh string
Msg string
}
if err := unmarshal(&sh); err != nil {
return err
}
p.Sh = sh.Sh
p.Msg = sh.Msg
if p.Msg == "" {
p.Msg = fmt.Sprintf("%s failed", sh.Sh)
}
return nil
}

View File

@@ -0,0 +1,48 @@
package taskfile_test
import (
"testing"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)
func TestPreconditionParse(t *testing.T) {
tests := []struct {
content string
v interface{}
expected interface{}
}{
{
"test -f foo.txt",
&taskfile.Precondition{},
&taskfile.Precondition{Sh: `test -f foo.txt`, Msg: "`test -f foo.txt` failed"},
},
{
"sh: '[ 1 = 0 ]'",
&taskfile.Precondition{},
&taskfile.Precondition{Sh: "[ 1 = 0 ]", Msg: "[ 1 = 0 ] failed"},
},
{`
sh: "[ 1 = 2 ]"
msg: "1 is not 2"
`,
&taskfile.Precondition{},
&taskfile.Precondition{Sh: "[ 1 = 2 ]", Msg: "1 is not 2"},
},
{`
sh: "[ 1 = 2 ]"
msg: "1 is not 2"
`,
&taskfile.Precondition{},
&taskfile.Precondition{Sh: "[ 1 = 2 ]", Msg: "1 is not 2"},
},
}
for _, test := range tests {
err := yaml.Unmarshal([]byte(test.content), test.v)
assert.NoError(t, err)
assert.Equal(t, test.expected, test.v)
}
}

View File

@@ -8,31 +8,51 @@ import (
"runtime"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v2/internal/templater"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)
var (
// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
// ErrNoTaskfileFound is returned when Taskfile.yml is not found
ErrNoTaskfileFound = errors.New(`task: No Taskfile.yml found. Use "task --init" to create a new one`)
)
// Taskfile reads a Taskfile for a given directory
func Taskfile(dir string) (*taskfile.Taskfile, error) {
path := filepath.Join(dir, "Taskfile.yml")
func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
path := filepath.Join(dir, entrypoint)
if _, err := os.Stat(path); err != nil {
return nil, ErrNoTaskfileFound
return nil, fmt.Errorf(`task: No Taskfile found on "%s". Use "task --init" to create a new one`, path)
}
t, err := readTaskfile(path)
if err != nil {
return nil, err
}
for namespace, path := range t.Includes {
path = filepath.Join(dir, path)
v, err := t.ParsedVersion()
if err != nil {
return nil, err
}
for namespace, includedTask := range t.Includes {
if v >= 3.0 {
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
includedTask = taskfile.IncludedTaskfile{
Taskfile: tr.Replace(includedTask.Taskfile),
Dir: tr.Replace(includedTask.Dir),
AdvancedImport: includedTask.AdvancedImport,
}
if err := tr.Err(); err != nil {
return nil, err
}
}
if filepath.IsAbs(includedTask.Taskfile) {
path = includedTask.Taskfile
} else {
path = filepath.Join(dir, includedTask.Taskfile)
}
info, err := os.Stat(path)
if err != nil {
return nil, err
@@ -47,19 +67,30 @@ func Taskfile(dir string) (*taskfile.Taskfile, error) {
if len(includedTaskfile.Includes) > 0 {
return nil, ErrIncludedTaskfilesCantHaveIncludes
}
if includedTask.AdvancedImport {
for _, task := range includedTaskfile.Tasks {
if !filepath.IsAbs(task.Dir) {
task.Dir = filepath.Join(includedTask.Dir, task.Dir)
}
}
}
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
return nil, err
}
}
path = filepath.Join(dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
if _, err = os.Stat(path); err == nil {
osTaskfile, err := readTaskfile(path)
if err != nil {
return nil, err
}
if err = taskfile.Merge(t, osTaskfile); err != nil {
return nil, err
if v < 3.0 {
path = filepath.Join(dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
if _, err = os.Stat(path); err == nil {
osTaskfile, err := readTaskfile(path)
if err != nil {
return nil, err
}
if err = taskfile.Merge(t, osTaskfile); err != nil {
return nil, err
}
}
}

View File

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

View File

@@ -1,22 +1,86 @@
package taskfile
// Tasks representas a group of tasks
import (
"errors"
)
// Tasks represents a group of tasks
type Tasks map[string]*Task
// Task represents a task
type Task struct {
Task string
Cmds []*Cmd
Deps []*Dep
Desc string
Sources []string
Generates []string
Status []string
Dir string
Vars Vars
Env Vars
Silent bool
Method string
Prefix string
IgnoreError bool `yaml:"ignore_error"`
Task string
Cmds []*Cmd
Deps []*Dep
Desc string
Summary string
Sources []string
Generates []string
Status []string
Preconditions []*Precondition
Dir string
Vars *Vars
Env *Vars
Silent bool
Method string
Prefix string
IgnoreError bool
}
var (
// ErrCantUnmarshalTask is returned for invalid task YAML
ErrCantUnmarshalTask = errors.New("task: can't unmarshal task value")
)
func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
var cmd Cmd
if err := unmarshal(&cmd); err == nil && cmd.Cmd != "" {
t.Cmds = append(t.Cmds, &cmd)
return nil
}
var cmds []*Cmd
if err := unmarshal(&cmds); err == nil && len(cmds) > 0 {
t.Cmds = cmds
return nil
}
var task struct {
Cmds []*Cmd
Deps []*Dep
Desc string
Summary string
Sources []string
Generates []string
Status []string
Preconditions []*Precondition
Dir string
Vars *Vars
Env *Vars
Silent bool
Method string
Prefix string
IgnoreError bool `yaml:"ignore_error"`
}
if err := unmarshal(&task); err == nil {
t.Cmds = task.Cmds
t.Deps = task.Deps
t.Desc = task.Desc
t.Summary = task.Summary
t.Sources = task.Sources
t.Generates = task.Generates
t.Status = task.Status
t.Preconditions = task.Preconditions
t.Dir = task.Dir
t.Vars = task.Vars
t.Env = task.Env
t.Silent = task.Silent
t.Method = task.Method
t.Prefix = task.Prefix
t.IgnoreError = task.IgnoreError
return nil
}
return ErrCantUnmarshalTask
}

View File

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

View File

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

View File

@@ -3,6 +3,8 @@ package taskfile
import (
"errors"
"strings"
"gopkg.in/yaml.v3"
)
var (
@@ -11,26 +13,99 @@ var (
)
// Vars is a string[string] variables map.
type Vars map[string]Var
type Vars struct {
Keys []string
Mapping map[string]Var
}
// ToStringMap converts Vars to a string map containing only the static
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (vs *Vars) UnmarshalYAML(node *yaml.Node) error {
if node.Kind != yaml.MappingNode {
return errors.New("task: vars is not a map")
}
// NOTE(@andreynering): on this style of custom unmarsheling,
// even number contains the keys, while odd numbers contains
// the values.
for i := 0; i < len(node.Content); i += 2 {
keyNode := node.Content[i]
valueNode := node.Content[i+1]
var v Var
if err := valueNode.Decode(&v); err != nil {
return err
}
vs.Set(keyNode.Value, v)
}
return nil
}
// Merge merges the given Vars into the caller one
func (vs *Vars) Merge(other *Vars) {
other.Range(func(key string, value Var) error {
vs.Set(key, value)
return nil
})
}
// Set sets a value to a given key
func (vs *Vars) Set(key string, value Var) {
if vs.Mapping == nil {
vs.Mapping = make(map[string]Var, 1)
}
if !strSliceContains(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 {
return nil
}
for _, k := range vs.Keys {
if err := yield(k, vs.Mapping[k]); err != nil {
return err
}
}
return nil
}
// ToCacheMap converts Vars to a map containing only the static
// variables
func (vs Vars) ToStringMap() (m map[string]string) {
m = make(map[string]string, len(vs))
for k, v := range vs {
func (vs *Vars) ToCacheMap() (m map[string]interface{}) {
m = make(map[string]interface{}, len(vs.Keys))
vs.Range(func(k string, v Var) error {
if v.Sh != "" {
// Dynamic variable is not yet resolved; trigger
// <no value> to be used in templates.
continue
return nil
}
m[k] = v.Static
}
if v.Live != nil {
m[k] = v.Live
} else {
m[k] = v.Static
}
return nil
})
return
}
// Var represents either a static or dynamic variable.
type Var struct {
Static string
Live interface{}
Sh string
}

View File

@@ -1,46 +0,0 @@
package version
import (
"github.com/Masterminds/semver"
)
var (
v1 = mustVersion("1")
v2 = mustVersion("2")
v21 = mustVersion("2.1")
v22 = mustVersion("2.2")
v23 = mustVersion("2.3")
)
// IsV1 returns if is a given Taskfile version is version 1
func IsV1(v *semver.Constraints) bool {
return v.Check(v1)
}
// IsV2 returns if is a given Taskfile version is at least version 2
func IsV2(v *semver.Constraints) bool {
return v.Check(v2)
}
// IsV21 returns if is a given Taskfile version is at least version 2.1
func IsV21(v *semver.Constraints) bool {
return v.Check(v21)
}
// IsV22 returns if is a given Taskfile version is at least version 2.2
func IsV22(v *semver.Constraints) bool {
return v.Check(v22)
}
// IsV23 returns if is a given Taskfile version is at least version 2.3
func IsV23(v *semver.Constraints) bool {
return v.Check(v23)
}
func mustVersion(s string) *semver.Version {
v, err := semver.NewVersion(s)
if err != nil {
panic(err)
}
return v
}

View File

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

View File

@@ -2,6 +2,7 @@ package templater
import (
"bytes"
"strings"
"text/template"
"github.com/go-task/task/v2/internal/taskfile"
@@ -12,10 +13,15 @@ import (
// happen will be assigned to r.err, and consecutive calls to funcs will just
// return the zero value.
type Templater struct {
Vars taskfile.Vars
Vars *taskfile.Vars
RemoveNoValue bool
strMap map[string]string
err error
cacheMap map[string]interface{}
err error
}
func (r *Templater) ResetCache() {
r.cacheMap = r.Vars.ToCacheMap()
}
func (r *Templater) Replace(str string) string {
@@ -29,15 +35,18 @@ func (r *Templater) Replace(str string) string {
return ""
}
if r.strMap == nil {
r.strMap = r.Vars.ToStringMap()
if r.cacheMap == nil {
r.cacheMap = r.Vars.ToCacheMap()
}
var b bytes.Buffer
if err = templ.Execute(&b, r.strMap); err != nil {
if err = templ.Execute(&b, r.cacheMap); err != nil {
r.err = err
return ""
}
if r.RemoveNoValue {
return strings.ReplaceAll(b.String(), "<no value>", "")
}
return b.String()
}
@@ -53,19 +62,22 @@ func (r *Templater) ReplaceSlice(strs []string) []string {
return new
}
func (r *Templater) ReplaceVars(vars taskfile.Vars) taskfile.Vars {
if r.err != nil || len(vars) == 0 {
func (r *Templater) ReplaceVars(vars *taskfile.Vars) *taskfile.Vars {
if r.err != nil || vars == nil || len(vars.Keys) == 0 {
return nil
}
new := make(taskfile.Vars, len(vars))
for k, v := range vars {
new[k] = taskfile.Var{
var new taskfile.Vars
vars.Range(func(k string, v taskfile.Var) error {
new.Set(k, taskfile.Var{
Static: r.Replace(v.Static),
Live: v.Live,
Sh: r.Replace(v.Sh),
}
}
return new
})
return nil
})
return &new
}
func (r *Templater) Err() error {

32
precondition.go Normal file
View File

@@ -0,0 +1,32 @@
package task
import (
"context"
"errors"
"github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/taskfile"
)
var (
// ErrPreconditionFailed is returned when a precondition fails
ErrPreconditionFailed = errors.New("task: precondition not met")
)
func (e *Executor) areTaskPreconditionsMet(ctx context.Context, t *taskfile.Task) (bool, error) {
for _, p := range t.Preconditions {
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
Command: p.Sh,
Dir: t.Dir,
Env: getEnviron(t),
})
if err != nil {
e.Logger.Errf(logger.Magenta, "task: %s", p.Msg)
return false, ErrPreconditionFailed
}
}
return true, nil
}

View File

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

220
task.go
View File

@@ -2,22 +2,23 @@ package task
import (
"context"
"errors"
"fmt"
"io"
"os"
"sync"
"sync/atomic"
"github.com/go-task/task/v2/internal/compiler"
compilerv1 "github.com/go-task/task/v2/internal/compiler/v1"
compilerv2 "github.com/go-task/task/v2/internal/compiler/v2"
compilerv3 "github.com/go-task/task/v2/internal/compiler/v3"
"github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/output"
"github.com/go-task/task/v2/internal/summary"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v2/internal/taskfile/read"
"github.com/go-task/task/v2/internal/taskfile/version"
"github.com/Masterminds/semver"
"golang.org/x/sync/errgroup"
)
@@ -30,12 +31,17 @@ const (
// Executor executes a Taskfile
type Executor struct {
Taskfile *taskfile.Taskfile
Dir string
Force bool
Watch bool
Verbose bool
Silent bool
Dry bool
Dir string
Entrypoint string
Force bool
Watch bool
Verbose bool
Silent bool
Dry bool
Summary bool
Parallel bool
Color bool
Stdin io.Reader
Stdout io.Writer
@@ -46,9 +52,10 @@ type Executor struct {
Output output.Output
OutputStyle string
taskvars taskfile.Vars
taskvars *taskfile.Vars
taskCallCount map[string]*int32
mkdirMutexMap map[string]*sync.Mutex
}
// Run runs Task
@@ -62,33 +69,58 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
}
}
if e.Summary {
for i, c := range calls {
compiledTask, err := e.CompiledTask(c)
if err != nil {
return nil
}
summary.PrintSpaceBetweenSummaries(e.Logger, i)
summary.PrintTask(e.Logger, compiledTask)
}
return nil
}
if e.Watch {
return e.watchTasks(calls...)
}
g, ctx := errgroup.WithContext(ctx)
for _, c := range calls {
if err := e.RunTask(ctx, c); err != nil {
return err
c := c
if e.Parallel {
g.Go(func() error { return e.RunTask(ctx, c) })
} else {
if err := e.RunTask(ctx, c); err != nil {
return err
}
}
}
return nil
return g.Wait()
}
// Setup setups Executor's internal state
func (e *Executor) Setup() error {
var err error
e.Taskfile, err = read.Taskfile(e.Dir)
if err != nil {
return err
if e.Entrypoint == "" {
e.Entrypoint = "Taskfile.yml"
}
e.taskvars, err = read.Taskvars(e.Dir)
var err error
e.Taskfile, err = read.Taskfile(e.Dir, e.Entrypoint)
if err != nil {
return err
}
v, err := semver.NewConstraint(e.Taskfile.Version)
v, err := e.Taskfile.ParsedVersion()
if err != nil {
return fmt.Errorf(`task: could not parse taskfile version "%s": %v`, e.Taskfile.Version, err)
return err
}
if v < 3.0 {
e.taskvars, err = read.Taskvars(e.Dir)
if err != nil {
return err
}
}
if e.Stdin == nil {
@@ -104,15 +136,28 @@ func (e *Executor) Setup() error {
Stdout: e.Stdout,
Stderr: e.Stderr,
Verbose: e.Verbose,
Color: e.Color,
}
switch {
case version.IsV1(v):
e.Compiler = &compilerv1.CompilerV1{
Dir: e.Dir,
Vars: e.taskvars,
Logger: e.Logger,
}
case version.IsV2(v), version.IsV21(v), version.IsV22(v):
if v < 2 {
return fmt.Errorf(`task: Taskfile versions prior to v2 are not supported anymore`)
}
// consider as equal to the greater version if round
if v == 2.0 {
v = 2.6
}
if v > 3.0 {
return fmt.Errorf(`task: Taskfile versions greater than v3.0 not implemented in the version of Task`)
}
// Color available only on v3
if v < 3 {
e.Logger.Color = false
}
if v < 3 {
e.Compiler = &compilerv2.CompilerV2{
Dir: e.Dir,
Taskvars: e.taskvars,
@@ -120,16 +165,24 @@ func (e *Executor) Setup() error {
Expansions: e.Taskfile.Expansions,
Logger: e.Logger,
}
case version.IsV23(v):
return fmt.Errorf(`task: Taskfile versions greater than v2.3 not implemented in the version of Task`)
} else {
e.Compiler = &compilerv3.CompilerV3{
Dir: e.Dir,
TaskfileVars: e.Taskfile.Vars,
Logger: e.Logger,
}
}
if !version.IsV21(v) && e.Taskfile.Output != "" {
if v < 2.1 && e.Taskfile.Output != "" {
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
}
if !version.IsV22(v) && len(e.Taskfile.Includes) > 0 {
if v < 2.2 && len(e.Taskfile.Includes) > 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
}
@@ -144,8 +197,16 @@ func (e *Executor) Setup() error {
return fmt.Errorf(`task: output option "%s" not recognized`, e.Taskfile.Output)
}
if !version.IsV21(v) {
err := fmt.Errorf(`task: Taskfile option "ignore_error" is only available starting on Taskfile version v2.1`)
if e.Taskfile.Method == "" {
if v >= 3 {
e.Taskfile.Method = "checksum"
} else {
e.Taskfile.Method = "timestamp"
}
}
if v <= 2.1 {
err := errors.New(`task: Taskfile option "ignore_error" is only available starting on Taskfile version v2.1`)
for _, task := range e.Taskfile.Tasks {
if task.IgnoreError {
@@ -159,9 +220,27 @@ func (e *Executor) Setup() error {
}
}
if v < 2.6 {
for _, task := range e.Taskfile.Tasks {
if len(task.Preconditions) > 0 {
return errors.New(`task: Task option "preconditions" is only available starting on Taskfile version v2.6`)
}
}
}
if v < 3 {
for _, taskfile := range e.Taskfile.Includes {
if taskfile.AdvancedImport {
return errors.New(`task: Import with additional parameters is only available starting on Taskfile version v3`)
}
}
}
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{}
}
return nil
}
@@ -181,26 +260,36 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
}
if !e.Force {
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
if err != nil {
return err
}
upToDate, err := e.isTaskUpToDate(ctx, t)
if err != nil {
return err
}
if upToDate {
if upToDate && preCondMet {
if !e.Silent {
e.Logger.Errf(`task: Task "%s" is up to date`, t.Task)
e.Logger.Errf(logger.Magenta, `task: Task "%s" is up to date`, t.Task)
}
return nil
}
}
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("task: error cleaning status on error: %v", err2)
e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v", err2)
}
if execext.IsExitError(err) && t.IgnoreError {
e.Logger.VerboseErrf("task: task error ignored: %v", err)
e.Logger.VerboseErrf(logger.Yellow, "task: task error ignored: %v", err)
continue
}
@@ -210,6 +299,23 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
return nil
}
func (e *Executor) mkdir(t *taskfile.Task) error {
if t.Dir == "" {
return nil
}
mutex := e.mkdirMutexMap[t.Task]
mutex.Lock()
defer mutex.Unlock()
if _, err := os.Stat(t.Dir); os.IsNotExist(err) {
if err := os.MkdirAll(t.Dir, 0755); err != nil {
return err
}
}
return nil
}
func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error {
g, ctx := errgroup.WithContext(ctx)
@@ -217,7 +323,11 @@ func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error {
d := d
g.Go(func() error {
return e.RunTask(ctx, taskfile.Call{Task: d.Task, Vars: d.Vars})
err := e.RunTask(ctx, taskfile.Call{Task: d.Task, Vars: d.Vars})
if err != nil {
return err
}
return nil
})
}
@@ -229,10 +339,14 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
switch {
case cmd.Task != "":
return e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars})
err := e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars})
if err != nil {
return err
}
return nil
case cmd.Cmd != "":
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Silent) {
e.Logger.Errf(cmd.Cmd)
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Taskfile.Silent && !e.Silent) {
e.Logger.Errf(logger.Green, "task: %s", cmd.Cmd)
}
if e.Dry {
@@ -241,8 +355,18 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
stdOut := e.Output.WrapWriter(e.Stdout, t.Prefix)
stdErr := e.Output.WrapWriter(e.Stderr, t.Prefix)
defer stdOut.Close()
defer stdErr.Close()
defer func() {
if _, ok := stdOut.(*os.File); !ok {
if closer, ok := stdOut.(io.Closer); ok {
closer.Close()
}
}
if _, ok := stdErr.(*os.File); !ok {
if closer, ok := stdErr.(io.Closer); ok {
closer.Close()
}
}
}()
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
Command: cmd.Cmd,
@@ -253,7 +377,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
Stderr: stdErr,
})
if execext.IsExitError(err) && cmd.IgnoreError {
e.Logger.VerboseErrf("task: command error ignored: %v", err)
e.Logger.VerboseErrf(logger.Yellow, "task: command error ignored: %v", err)
return nil
}
return err
@@ -268,8 +392,10 @@ func getEnviron(t *taskfile.Task) []string {
}
environ := os.Environ()
for k, v := range t.Env.ToStringMap() {
environ = append(environ, fmt.Sprintf("%s=%s", k, v))
for k, v := range t.Env.ToCacheMap() {
if s, ok := v.(string); ok {
environ = append(environ, fmt.Sprintf("%s=%s", k, s))
}
}
return environ
}

View File

@@ -7,13 +7,13 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/go-task/task/v2"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/mitchellh/go-homedir"
"github.com/stretchr/testify/assert"
)
@@ -69,43 +69,6 @@ func TestEnv(t *testing.T) {
tt.Run(t)
}
func TestVarsV1(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/vars/v1",
Target: "default",
TrimSpace: true,
Files: map[string]string{
// hello task:
"foo.txt": "foo",
"bar.txt": "bar",
"baz.txt": "baz",
"tmpl_foo.txt": "foo",
"tmpl_bar.txt": "<no value>",
"tmpl_foo2.txt": "foo2",
"tmpl_bar2.txt": "bar2",
"shtmpl_foo.txt": "foo",
"shtmpl_foo2.txt": "foo2",
"nestedtmpl_foo.txt": "{{.FOO}}",
"nestedtmpl_foo2.txt": "foo2",
"foo2.txt": "foo2",
"bar2.txt": "bar2",
"baz2.txt": "baz2",
"tmpl2_foo.txt": "<no value>",
"tmpl2_foo2.txt": "foo2",
"tmpl2_bar.txt": "<no value>",
"tmpl2_bar2.txt": "<no value>",
"shtmpl2_foo.txt": "<no value>",
"shtmpl2_foo2.txt": "foo2",
"nestedtmpl2_foo2.txt": "{{.FOO2}}",
"override.txt": "bar",
},
}
tt.Run(t)
// Ensure identical results when running hello task directly.
tt.Target = "hello"
tt.Run(t)
}
func TestVarsV2(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/vars/v2",
@@ -135,6 +98,7 @@ func TestVarsV2(t *testing.T) {
"nestedtmpl2_foo2.txt": "<no value>",
"override.txt": "bar",
"nested.txt": "Taskvars-TaskfileVars-TaskVars",
"task_name.txt": "hello",
},
}
tt.Run(t)
@@ -143,8 +107,22 @@ func TestVarsV2(t *testing.T) {
tt.Run(t)
}
func TestVarsV3(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/vars/v3",
Target: "default",
Files: map[string]string{
"missing-var.txt": "\n",
"var-order.txt": "ABCDEF\n",
"dependent-sh.txt": "123456\n",
"with-call.txt": "Hi, ABC123!\n",
},
}
tt.Run(t)
}
func TestMultilineVars(t *testing.T) {
for _, dir := range []string{"testdata/vars/v1/multiline", "testdata/vars/v2/multiline"} {
for _, dir := range []string{"testdata/vars/v2/multiline"} {
tt := fileContentTest{
Dir: dir,
Target: "default",
@@ -168,7 +146,7 @@ func TestMultilineVars(t *testing.T) {
func TestVarsInvalidTmpl(t *testing.T) {
const (
dir = "testdata/vars/v1"
dir = "testdata/vars/v2"
target = "invalid-var-tmpl"
expectError = "template: :1: unexpected EOF"
)
@@ -236,7 +214,7 @@ func TestDeps(t *testing.T) {
for _, f := range files {
f = filepath.Join(dir, f)
if _, err := os.Stat(f); err != nil {
t.Errorf("File %s should exists", f)
t.Errorf("File %s should exist", f)
}
}
}
@@ -248,7 +226,7 @@ func TestStatus(t *testing.T) {
_ = os.Remove(file)
if _, err := os.Stat(file); err == nil {
t.Errorf("File should not exists: %v", err)
t.Errorf("File should not exist: %v", err)
}
var buff bytes.Buffer
@@ -262,7 +240,7 @@ func TestStatus(t *testing.T) {
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
if _, err := os.Stat(file); err != nil {
t.Errorf("File should exists: %v", err)
t.Errorf("File should exist: %v", err)
}
e.Silent = false
@@ -273,6 +251,47 @@ func TestStatus(t *testing.T) {
}
}
func TestPrecondition(t *testing.T) {
const dir = "testdata/precondition"
var buff bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
// A precondition that has been met
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "foo"}))
if buff.String() != "" {
t.Errorf("Got Output when none was expected: %s", buff.String())
}
// A precondition that was not met
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "impossible"}))
if buff.String() != "task: 1 != 0 obviously!\n" {
t.Errorf("Wrong output message: %s", buff.String())
}
buff.Reset()
// Calling a task with a precondition in a dependency fails the task
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "depends_on_impossible"}))
if buff.String() != "task: 1 != 0 obviously!\n" {
t.Errorf("Wrong output message: %s", buff.String())
}
buff.Reset()
// Calling a task with a precondition in a cmd fails the task
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "executes_failing_task_as_cmd"}))
if buff.String() != "task: 1 != 0 obviously!\n" {
t.Errorf("Wrong output message: %s", buff.String())
}
buff.Reset()
}
func TestGenerates(t *testing.T) {
const (
srcTask = "sub/src.txt"
@@ -290,7 +309,7 @@ func TestGenerates(t *testing.T) {
path := filepath.Join(dir, task)
_ = os.Remove(path)
if _, err := os.Stat(path); err == nil {
t.Errorf("File should not exists: %v", err)
t.Errorf("File should not exist: %v", err)
}
}
@@ -311,10 +330,10 @@ func TestGenerates(t *testing.T) {
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: theTask}))
if _, err := os.Stat(srcFile); err != nil {
t.Errorf("File should exists: %v", err)
t.Errorf("File should exist: %v", err)
}
if _, err := os.Stat(destFile); err != nil {
t.Errorf("File should exists: %v", err)
t.Errorf("File should exist: %v", err)
}
// Ensure task was not incorrectly found to be up-to-date on first run.
if buff.String() == upToDate {
@@ -365,13 +384,41 @@ func TestStatusChecksum(t *testing.T) {
assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String())
}
func TestStatusVariables(t *testing.T) {
const dir = "testdata/status_vars"
_ = os.RemoveAll(filepath.Join(dir, ".task"))
_ = os.Remove(filepath.Join(dir, "generated.txt"))
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: false,
Verbose: true,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
assert.Contains(t, buff.String(), "d41d8cd98f00b204e9800998ecf8427e")
inf, err := os.Stat(filepath.Join(dir, "source.txt"))
assert.NoError(t, err)
ts := fmt.Sprintf("%d", inf.ModTime().Unix())
tf := fmt.Sprintf("%s", inf.ModTime())
assert.Contains(t, buff.String(), ts)
assert.Contains(t, buff.String(), tf)
}
func TestInit(t *testing.T) {
const dir = "testdata/init"
var file = filepath.Join(dir, "Taskfile.yml")
_ = os.Remove(file)
if _, err := os.Stat(file); err == nil {
t.Errorf("Taskfile.yml should not exists")
t.Errorf("Taskfile.yml should not exist")
}
if err := task.InitTaskfile(ioutil.Discard, dir); err != nil {
@@ -379,7 +426,7 @@ func TestInit(t *testing.T) {
}
if _, err := os.Stat(file); err != nil {
t.Errorf("Taskfile.yml should exists")
t.Errorf("Taskfile.yml should exist")
}
}
@@ -400,7 +447,6 @@ func TestTaskVersion(t *testing.T) {
Dir string
Version string
}{
{"testdata/version/v1", "1"},
{"testdata/version/v2", "2"},
}
@@ -437,7 +483,7 @@ func TestTaskIgnoreErrors(t *testing.T) {
func TestExpand(t *testing.T) {
const dir = "testdata/expand"
home, err := homedir.Dir()
home, err := os.UserHomeDir()
if err != nil {
t.Errorf("Couldn't get $HOME: %v", err)
}
@@ -470,7 +516,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, "touch file.txt", strings.TrimSpace(buff.String()))
assert.Equal(t, "task: touch file.txt", strings.TrimSpace(buff.String()))
if _, err := os.Stat(file); err == nil {
t.Errorf("File should not exist %s", file)
}
@@ -508,14 +554,33 @@ func TestIncludes(t *testing.T) {
Target: "default",
TrimSpace: true,
Files: map[string]string{
"main.txt": "main",
"included_directory.txt": "included_directory",
"included_taskfile.txt": "included_taskfile",
"main.txt": "main",
"included_directory.txt": "included_directory",
"included_directory_without_dir.txt": "included_directory_without_dir",
"included_taskfile_without_dir.txt": "included_taskfile_without_dir",
"./module2/included_directory_with_dir.txt": "included_directory_with_dir",
"./module2/included_taskfile_with_dir.txt": "included_taskfile_with_dir",
"os_include.txt": "os",
},
}
tt.Run(t)
}
func TestIncorrectVersionIncludes(t *testing.T) {
const dir = "testdata/incorrect_includes"
expectedError := "task: Import with additional parameters is only available starting on Taskfile version v3"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: true,
}
assert.EqualError(t, e.Setup(), expectedError)
}
func TestIncludesEmptyMain(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/includes_empty",
@@ -553,3 +618,117 @@ func TestIncludesCallingRoot(t *testing.T) {
}
tt.Run(t)
}
func TestSummary(t *testing.T) {
const dir = "testdata/summary"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Summary: true,
Silent: true,
}
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"))
assert.NoError(t, err)
expectedOutput := string(data)
if runtime.GOOS == "windows" {
expectedOutput = strings.Replace(expectedOutput, "\r\n", "\n", -1)
}
assert.Equal(t, expectedOutput, buff.String())
}
func TestWhenNoDirAttributeItRunsInSameDirAsTaskfile(t *testing.T) {
const expected = "dir"
const dir = "testdata/" + expected
var out bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &out,
Stderr: &out,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "whereami"}))
// got should be the "dir" part of "testdata/dir"
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
assert.Equal(t, expected, got, "Mismatch in the working directory")
}
func TestWhenDirAttributeAndDirExistsItRunsInThatDir(t *testing.T) {
const expected = "exists"
const dir = "testdata/dir/explicit_exists"
var out bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &out,
Stderr: &out,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "whereami"}))
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
assert.Equal(t, expected, got, "Mismatch in the working directory")
}
func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) {
const expected = "createme"
const dir = "testdata/dir/explicit_doesnt_exist/"
const toBeCreated = dir + expected
const target = "whereami"
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 TestDisplaysErrorOnUnsupportedVersion(t *testing.T) {
e := task.Executor{
Dir: "testdata/version/v1",
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
}
err := e.Setup()
assert.Error(t, err)
assert.Equal(t, "task: Taskfile versions prior to v2 are not supported anymore", err.Error())
}
func TestShortTaskNotation(t *testing.T) {
const dir = "testdata/short_task_notation"
var buff bytes.Buffer
e := task.Executor{
Dir: 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\nstring-slice-2\nstring\n", buff.String())
}

View File

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

View File

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

View File

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

7
testdata/dir/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
version: '3'
tasks:
whereami:
cmds:
- pwd
silent: true

View File

@@ -0,0 +1,8 @@
version: '3'
tasks:
whereami:
dir: createme
cmds:
- pwd
silent: true

View File

@@ -0,0 +1,8 @@
version: '3'
tasks:
whereami:
dir: exists
cmds:
- pwd
silent: true

View File

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,19 @@
version: '2'
version: '3'
includes:
included: ./included
included_taskfile: ./Taskfile2.yml
included_without_dir:
taskfile: ./module1
included_taskfile_without_dir:
taskfile: ./module1/Taskfile.yml
included_with_dir:
taskfile: ./module2
dir: ./module2
included_taskfile_with_dir:
taskfile: ./module2/Taskfile.yml
dir: ./module2
included_os: ./Taskfile_{{OS}}.yml
tasks:
default:
@@ -10,6 +21,11 @@ tasks:
- task: gen
- task: included:gen
- task: included_taskfile:gen
- task: included_without_dir:gen_file
- task: included_taskfile_without_dir:gen_dir
- task: included_with_dir:gen_file
- task: included_taskfile_with_dir:gen_dir
- task: included_os:gen
gen:
cmds:

View File

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

4
testdata/includes/Taskfile_darwin.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
version: '3'
tasks:
gen: echo 'os' > os_include.txt

4
testdata/includes/Taskfile_linux.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
version: '3'
tasks:
gen: echo 'os' > os_include.txt

View File

@@ -0,0 +1,4 @@
version: '3'
tasks:
gen: echo 'os' > os_include.txt

View File

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

10
testdata/includes/module1/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
version: '3'
tasks:
gen_dir:
cmds:
- echo included_directory_without_dir > included_directory_without_dir.txt
gen_file:
cmds:
- echo included_taskfile_without_dir > included_taskfile_without_dir.txt

10
testdata/includes/module2/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
version: '3'
tasks:
gen_dir:
cmds:
- echo included_directory_with_dir > included_directory_with_dir.txt
gen_file:
cmds:
- echo included_taskfile_with_dir > included_taskfile_with_dir.txt

View File

@@ -1,4 +1,4 @@
version: '2'
version: '3'
includes:
included: Taskfile2.yml

View File

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

View File

@@ -1,4 +1,4 @@
version: '2'
version: '3'
includes:
included: Taskfile2.yml

View File

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

View File

@@ -1,4 +1,4 @@
version: '2'
version: '3'
includes:
included: Taskfile2.yml

View File

@@ -1,4 +1,4 @@
version: '2'
version: '3'
vars:
FILE: file.txt

View File

@@ -0,0 +1,10 @@
version: '2.6'
includes:
included:
taskfile: ./included
tasks:
default:
cmds:
- task: gen

View File

@@ -0,0 +1,6 @@
version: '2.6'
tasks:
gen:
cmds:
- echo incorrect includes test

View File

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

View File

@@ -1,2 +0,0 @@
PORTUGUESE_HELLO_WORLD: Olá, mundo!
GERMAN: "Hello"

19
testdata/precondition/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
version: '3'
tasks:
foo:
preconditions:
- test -f foo.txt
impossible:
preconditions:
- sh: "[ 1 = 0 ]"
msg: "1 != 0 obviously!"
depends_on_impossible:
deps:
- impossible
executes_failing_task_as_cmd:
cmds:
- task: impossible

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