Compare commits

...

226 Commits

Author SHA1 Message Date
Andrey Nering
5d22cf4327 v3.16.0 2022-09-29 22:01:16 -03:00
Andrey Nering
219d3ad193 Website: Fix link to file 2022-09-29 21:38:26 -03:00
Andrey Nering
e72157e26a Merge pull request #871 from go-task/release-to-npm
Add package.json to allow users to install Task using npm
2022-09-29 21:34:39 -03:00
Andrey Nering
50a377a7c4 Add package.json to allow users to install Task using npm
Closes #870
2022-09-29 21:27:30 -03:00
Andrey Nering
9d7ddff60c README: Add more links 2022-09-29 13:33:20 -03:00
Andrey Nering
081d878f86 Website > Installation: Update Arch Linux installation method
Package and maintainer changed. See #869.
2022-09-20 13:05:18 -03:00
Andrey Nering
a95191d29e Website: Improve syntax highlighting 2022-09-17 13:51:54 -03:00
Andrey Nering
111f6e7f18 Add CHANGELOG entry and API documentation for #818 2022-09-17 13:11:16 -03:00
Andrey Nering
4a5c1e9ec4 Merge branch 'internal-tasks' of https://github.com/pd93/task into pd93-internal-tasks 2022-09-17 12:59:19 -03:00
Andrey Nering
8f0893b5f7 Website: Adjustments to Carbon 2022-09-17 11:11:02 -03:00
Andrey Nering
b16e705a6c Website: Update Open Graph image 2022-09-15 11:17:01 -03:00
Andrey Nering
3cad318b70 v3.15.2
I pushed v3.15.1 tagged in a commit in a detached branch by mistake.

Repushing as v3.15.2
2022-09-08 21:26:18 -03:00
Andrey Nering
8c6002cae6 v3.15.1 2022-09-08 21:22:19 -03:00
Andrey Nering
0355bbaf3b Merge pull request #861 from cdaguerre/feat/make-zsh-completion-configurable
Make zsh completion list configurable
2022-09-08 21:01:04 -03:00
Andrey Nering
2ba083a650 Merge pull request #863 from MarioSchwalbe/bash-completion
Fix handling of CLI_ARGS
2022-09-08 20:59:16 -03:00
Andrey Nering
c79ea5a257 Merge pull request #866 from pd93/fix-read-dotenv
fix: ignore empty dotfile file names
2022-09-08 20:58:25 -03:00
Pete Davison
44706f4957 fix: ignore empty dotfile file names 2022-09-08 18:51:39 +00:00
Mario Schwalbe
a1b3bb03ed * Fix handling of CLI_ARGS 2022-09-08 19:03:29 +02:00
Christian Daguerre
76caa16909 Make zsh completion list configurable 2022-09-07 10:33:55 +02:00
Andrey Nering
160b788198 Merge pull request #859 from jfhovinne/docs-reprobuilds
Document how to reproduce release binaries
2022-09-05 14:29:42 -03:00
jfhovinne
eada62f62c Document how to reproduce executables 2022-09-05 19:08:37 +02:00
Andrey Nering
bd9419e6db Website: Use "Roboto" as the main font
This is to add consistency between different operating systems, specially
Windows which used "Segoe UI" before this commit.
2022-09-04 17:38:29 -03:00
Andrey Nering
bdd9de3001 CHANGELOG: Add extra link 2022-09-03 18:28:24 -03:00
Andrey Nering
200ba4ed04 v3.15.0 2022-09-03 18:21:40 -03:00
Andrey Nering
1e8939dd58 Merge pull request #857 from go-task/feature/add-dir-special-variables
Add ROOT_DIR and TASKFILE_DIR special variables
2022-09-03 18:18:43 -03:00
Andrey Nering
f45dd11e53 Add ROOT_DIR and TASKFILE_DIR special variables
Closes #215
2022-09-03 18:14:54 -03:00
Andrey Nering
1a0cc1d64d Update favicon.ico 2022-08-31 13:21:43 -03:00
Wes McNamee
421cb522d9 Merge pull request #846 from rootulp/patch-1
fix: grammar in docs
2022-08-24 22:07:23 -07:00
Rootul Patel
1b18b041d6 fix: grammar in docs 2022-08-24 20:30:21 -04:00
Andrey Nering
8788703ac6 CHANGELOG for #831
Closes #826
2022-08-23 18:43:04 -03:00
Andrey Nering
b6c25e3ad9 Mention #844 on CHANGELOG 2022-08-23 18:40:34 -03:00
Andrey Nering
73eaa68cd1 Merge pull request #844 from MarioSchwalbe/bash-completion
Use --silent to get the list of tasks (bash completion)
2022-08-23 18:39:47 -03:00
Andrey Nering
beb927f7b4 Merge pull request #831 from ilewin/check_path_for_symlinks_issue_826
Attempt to fix Task not following symlinks
2022-08-23 18:38:17 -03:00
ilewin
cdc969cd4e Added test to check if symlinks are evaluated for task source files 2022-08-23 18:36:19 +02:00
ilewin
2a67499f12 Issue #826. Replaced zglob.Glob func with GlobFollowSymlinks to evaluate symlinks 2022-08-23 18:25:11 +02:00
Mario Schwalbe
6a3cc79daa * Use --silent to get list of tasks 2022-08-23 18:03:15 +02:00
Andrey Nering
97d4a947ee Merge pull request #838 from pcgeek86/patch-1
Update releasing.md
2022-08-17 20:50:18 -03:00
Wes McNamee
e0e47ad9a0 Merge pull request #841 from cristaloleg/fix-linter
Fix go-critic suggestions
2022-08-17 12:38:12 -07:00
Oleg Kovalov
b08eac58e9 Fix go-critic suggestions 2022-08-17 19:37:58 +02:00
Pete Davison
11409ccf21 fix: list + silent flags shouldn't display internal tasks 2022-08-16 17:07:05 +00:00
Pete Davison
e3b6c97c3b fix: the merged task should be internal if the task OR the taskfile are internal 2022-08-16 17:07:05 +00:00
Pete Davison
d3da086ebf docs: added usage 2022-08-16 17:07:05 +00:00
Pete Davison
3507fa40f1 feat: add internal to included files 2022-08-16 17:07:05 +00:00
Pete Davison
6f8f1f1409 feat(task): tasks can be internal (not accessible from cli) 2022-08-16 17:06:25 +00:00
Trevor Sullivan
c2148a359d Update releasing.md 2022-08-16 04:33:54 -06:00
Andrey Nering
c172185a24 Update CHANGELOG 2022-08-15 09:55:11 -03:00
Andrey Nering
1140a5c4ae Merge pull request #835 from MarioSchwalbe/bash-completion
Improved bash completion
2022-08-15 09:52:07 -03:00
Mario Schwalbe
3cc378c960 * Convert indentation to 2 spaces 2022-08-13 21:55:35 +02:00
Andrey Nering
9b3a961303 Merge pull request #832 from jfhovinne/reprobuilds
Modify goreleaser defaults for reproducible builds
2022-08-13 12:04:27 -03:00
Mario Schwalbe
d048555149 Improved bash completion for task 2022-08-11 20:48:41 +02:00
jfhovinne
7533858a52 Modify goreleaser defaults for reproducible builds 2022-08-10 15:33:17 +02:00
Andrey Nering
c4e10ef0aa Refactor: Add SmartJoin to handle IsAbs automatically 2022-08-06 18:19:07 -03:00
Andrey Nering
c20842e7cd v3.14.1: Add CHANGELOG to website 2022-08-03 22:26:39 -03:00
Andrey Nering
6cfdb21313 v3.14.1 2022-08-03 22:08:17 -03:00
Bevan Arps
e396f4d06f Resolve relative include paths relative to the including Taskfile
Closes #823
Closes #822
2022-08-03 21:59:17 -03:00
Andrey Nering
47c1bb6a5b Merge pull request #816 from go-task/dependabot/npm_and_yarn/docs/terser-5.14.2
Bump terser from 5.13.1 to 5.14.2 in /docs
2022-08-03 21:00:52 -03:00
Andrey Nering
adfb0b513e Merge pull request #827 from go-task/go-1-19
Upgrade to Go v1.19
2022-08-03 21:00:44 -03:00
Andrey Nering
98d78b9d8a Upgrade to Go v1.19 2022-08-03 20:58:06 -03:00
Wes McNamee
a1c32a56ea Merge pull request #817 from white-gecko/patch-1
Resolve contradiction.
2022-07-27 23:36:25 -07:00
Natanael Arndt
6584bcf87f Resolve contradiction. 2022-07-21 14:30:51 +02:00
dependabot[bot]
7ac75af622 Bump terser from 5.13.1 to 5.14.2 in /docs
Bumps [terser](https://github.com/terser/terser) from 5.13.1 to 5.14.2.
- [Release notes](https://github.com/terser/terser/releases)
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/terser/terser/commits)

---
updated-dependencies:
- dependency-name: terser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-21 05:16:51 +00:00
Andrey Nering
b3c283b282 Merge pull request #814 from abner-chenc/master
go.mod: update to current golang.org/x/sys revision
2022-07-18 10:30:57 -03:00
Guoqi Chen
c2615dd746 go.mod: update to current golang.org/x/sys revision
go get -d golang.org/x/sys@c0bba94af5f85fbad9f6dc2e04ed5b8fac9696cf
go mod tidy

This brings in linux/loong64 support.
2022-07-18 10:11:48 +08:00
Andrey Nering
789518f70d Merge pull request #811 from go-task/imgbot
[ImgBot] Optimize images
2022-07-16 23:28:31 -03:00
ImgBotApp
ad3008d855 [ImgBot] Optimize images
*Total -- 61.47kb -> 34.61kb (43.69%)

/docs/static/img/pix.png -- 12.63kb -> 1.25kb (90.13%)
/docs/static/img/logo.png -- 21.79kb -> 13.21kb (39.38%)
/docs/static/img/og-image.png -- 26.26kb -> 19.38kb (26.2%)
/docs/static/img/logo.svg -- 0.44kb -> 0.42kb (2.47%)
/docs/static/img/logo_mono.svg -- 0.36kb -> 0.35kb (1.37%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2022-07-17 02:23:22 +00:00
Andrey Nering
3da426603f Website: Improve "Install Script" documentation
Add note about common installation paths.

Closes #775
2022-07-16 22:55:57 -03:00
Andrey Nering
8d26e34b0a Use --list-all to PowerShell completion + Add CHANGELOG to #803 2022-07-16 22:07:24 -03:00
Andrey Nering
110d1d7245 Merge pull request #803 from carlsmedstad/zsh-completion
Fix Zsh completion for tasks without description
2022-07-16 21:38:50 -03:00
Andrey Nering
f7384623df Merge pull request #802 from nokome/patch-1
Update api_reference.md
2022-07-16 21:37:19 -03:00
Carl Smedstad
5d24e166ab Fix Zsh completion for tasks without description
Use --list-all instead of --list in order include tasks without
description in the auto-completion.
2022-07-10 01:52:38 +02:00
Andrey Nering
d9ec5bcd24 v3.14.0 2022-07-08 15:19:32 -03:00
Andrey Nering
bf9cd7625b Refactor task.Setup(): Move to its own file and split in separated functions 2022-07-08 15:16:04 -03:00
Andrey Nering
afbf98c974 Website: Add ENV section to API page 2022-07-08 14:49:52 -03:00
Andrey Nering
fedb68cde7 Allow override the .task dir location with the TASK_TEMP_DIR env 2022-07-08 14:40:10 -03:00
Nokome Bentley
f787937a30 Update api_reference.md
Using `cmd` (singular) with a single string is valid syntax also.
2022-07-08 14:01:41 +12:00
Alexander Mancevice
f54fef7e7b Allow users to override colors using environment variables
Closes #568
Closes #792
2022-07-06 11:46:59 -03:00
Andrey Nering
e36c77aaf3 Fix bug with STDOUT and STDERR in the "group" output mode
Took the oportunity to refactor a bit how we handle closing of the streams.

Fixes #779
2022-07-06 10:45:07 -03:00
Andrey Nering
de45e48c37 Release ARM binaries to Snap. Deprecate i386
Actually done here: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml

Closes #795
2022-07-04 11:50:36 -03:00
Andrey Nering
ef0ce8224e Merge pull request #797 from go-task/dependabot/go_modules/github.com/stretchr/testify-1.8.0
Bump github.com/stretchr/testify from 1.7.2 to 1.8.0
2022-07-04 11:18:25 -03:00
Andrey Nering
5b4d5387bf Website: Link to Twitter account 2022-07-04 11:14:20 -03:00
Andrey Nering
5c460b38c9 Website: Add more entries to the Community page 2022-07-04 10:50:03 -03:00
Andrey Nering
8b836ab446 CHANGELOG: Mention #769 forggoten on SIGINT entry 2022-07-04 10:44:17 -03:00
dependabot[bot]
1be1fccc76 Bump github.com/stretchr/testify from 1.7.2 to 1.8.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.2 to 1.8.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.2...v1.8.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-04 13:41:49 +00:00
Andrey Nering
2c1fda97f0 Upgrade mvdan.cc/sh, which fixes a bug with associative arrays
Fixes #785
2022-07-04 10:40:25 -03:00
Andrey Nering
71f7b719b5 Merge pull request #791 from MPLew-is/patch-1
Correct `method: timestamp` example block in usage docs
2022-06-27 17:52:18 -03:00
Andrey Nering
e39006b511 Merge pull request #790 from Trim21/patch-2
docs: scoop extra bucket is not required anymore
2022-06-27 17:51:17 -03:00
Mike Lewis
37d07d415a Correct method: timestamp example block
The text above this block currently references a `timestamp` method for checking if a task should be run, then the example uses `checksum` instead.
2022-06-27 14:06:25 -06:00
Trim21
b80c8f78fc extra bucket is not required anymore 2022-06-28 03:22:32 +08:00
Andrey Nering
7707179b93 Merge pull request #783 from ssbarnea/fix/gha-group
Fix GHA grouping examples
2022-06-21 18:08:47 -03:00
Sorin Sbarnea
4e7d8bacdb Fix GHA grouping examples
Our example was not using the correct syntax required for grouping
command output on GHA.
2022-06-21 17:40:11 +01:00
Andrey Nering
0c46fa5a56 Website: Fix typo 2022-06-17 10:05:11 -03:00
Andrey Nering
36aca00de3 Website: Update link 2022-06-15 14:33:15 -03:00
Andrey Nering
0ec8cf1b53 Hide signals tests behind a build tag
Testing signals requires additional executables to be available in the $PATH
and can intermittently fail as well.

Add a build tag, which means these specific tests will only run when requested.

Closes #780
2022-06-15 10:06:38 -03:00
Andrey Nering
4b2b713e59 v3.13.0 2022-06-13 21:08:26 -03:00
Andrey Nering
cc0afce237 Add CHANGELOG entry for #777 2022-06-13 21:02:57 -03:00
Andrey Nering
620f3fc919 Merge pull request #777 from ascheman/feature/776-add-n-for-dry-run
Add `-n` as short cut for `--dry` - solves #776
2022-06-13 21:00:41 -03:00
Andrey Nering
af949ef0dd Restore @sisp work on SVG logos
This was unintentionally lost on the website migration from Docsify to
Docusaurus.

Ref #725
Ref #726
Closes #772

Co-authored by: Sigurd Spieckermann <sigurd.spieckermann@gmail.com>
2022-06-13 20:41:15 -03:00
Gerd Aschemann
475c5dc19a Add -n as short cut for --dry - solves #776 2022-06-13 12:47:03 +02:00
Andrey Nering
71cfe2364a Website: Add a decent Open Graph image 2022-06-12 17:33:40 -03:00
Andrey Nering
c1466f8aca Merge pull request #769 from go-task/fix-signals
Fix behavior of interrupt (SIGINT, SIGTERM) signals
2022-06-11 23:01:51 -03:00
Andrey Nering
7989f73f06 Fix behavior of interrupt (SIGINT, SIGTERM) signals
Task will now give time for the processes running to do cleanup work

Ref #458
Ref #479
Fixes #728

Co-authored-by: Marco Molteni <marco.molteni@pix4d.com>
Co-authored-by: aliculPix4D <aleksandar.licul_ext@pix4d.com>
2022-06-11 22:43:22 -03:00
Andrey Nering
c9a582fbcc Add CHANGELOG, documentation and make small adjutsment for #755 2022-06-11 20:01:48 -03:00
Andrey Nering
63aad1e501 Merge pull request #755 from BrunoDelor/feature/carry-error-code
Add --exit-code (-x) flag to enable carrying error codes from task cmds
2022-06-11 19:53:24 -03:00
Andrey Nering
16ba12239c Website: Installation: Fix markdown 2022-06-11 19:17:29 -03:00
Andrey Nering
b00ae9256b docs/Taskfile.yml: Allow to change server port 2022-06-11 19:16:11 -03:00
Andrey Nering
936045e01c Merge pull request #767 from mefuller/patch-1
Add Fedora Linux to installation
2022-06-11 19:10:37 -03:00
Andrey Nering
b1570ab117 Merge pull request #766 from go-task/dependabot/go_modules/github.com/stretchr/testify-1.7.2
Bump github.com/stretchr/testify from 1.7.1 to 1.7.2
2022-06-11 19:04:38 -03:00
Mark E Fuller
f066e3b1e0 Add Fedora Linux to installation
Added Task to Fedora official repositories
2022-06-11 17:31:25 +03:00
dependabot[bot]
1487c0e51b Bump github.com/stretchr/testify from 1.7.1 to 1.7.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.1 to 1.7.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.1...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-11 11:04:02 +00:00
Andrey Nering
9f244b9bd9 Merge pull request #765 from lhuang61/master
Fix typo
2022-06-10 10:11:57 -03:00
Andrey Nering
374ec27ab5 Merge pull request #759 from Cornul11/patch-1
fixed grammatical errors
2022-06-10 10:11:34 -03:00
Andrey Nering
b222c34f12 Website: Rename Logo.png -> logo.png 2022-06-10 10:05:30 -03:00
Andrey Nering
007a096632 Website: Adjustment on Algolia Search 2022-06-10 10:03:37 -03:00
Andrey Nering
26e02e3773 Website: Attemp to fix og:image meta tag 2022-06-10 09:52:18 -03:00
Andrey Nering
b8668ca3ce Website: Enable search powered by Algolia
https://docusaurus.io/docs/search#using-algolia-docsearch
2022-06-10 09:49:26 -03:00
Lei Huang
21a2f9f93b Fix typo 2022-06-10 17:18:37 +10:00
Dan
8a74141e4b fixed grammatical errors 2022-06-07 16:06:52 +02:00
Bruno Delor
752d9d5316 Renames option to align with existing tools
Shorthand: -x
Longhand: --exit-code
2022-06-06 09:46:27 +02:00
Bruno Delor
58c7cc5d05 Adds test TestErrorCode 2022-06-02 16:44:23 +02:00
Bruno Delor
a790fb7afe Adds --carry flag to enable carrying error codes from task cmds 2022-06-02 14:22:00 +02:00
Andrey Nering
5836cb1728 Website: Add link to GitHub Sponsors 2022-06-01 22:57:50 -03:00
Andrey Nering
19fd219409 Update FUNDING.yml 2022-06-01 22:37:38 -03:00
Andrey Nering
43f99e0bf7 Merge pull request #750 from go-task/api-reference
taskfile.dev: Add page with API Reference
2022-05-31 18:40:06 -03:00
Andrey Nering
6f83f6c1b5 taskfile.dev: Add page with API Reference 2022-05-31 18:36:59 -03:00
Andrey Nering
3f9c177d76 Goreleaser / GitHub Actions: Update committer 2022-05-31 18:22:56 -03:00
Andrey Nering
55dd7e20a0 Merge pull request #747 from go-task/yaml.v3
go.mod: Upgrade to gopkg.in/yaml.v3
2022-05-29 20:21:47 -03:00
Andrey Nering
bfbf29b78f go.mod: Upgrade to gopkg.in/yaml.v3 2022-05-29 20:14:42 -03:00
Andrey Nering
1b6d421a5b Fix missing https://taskfile.dev/install.sh
Fixes #746
2022-05-29 18:58:23 -03:00
Andrey Nering
112d9c4086 README: Fix logo path 2022-05-29 14:36:27 -03:00
Andrey Nering
adb089dc78 Merge pull request #745 from go-task/migrate-to-docusaurus
Migrate website to Docusaurus
2022-05-29 14:32:01 -03:00
Andrey Nering
5024d270ec Migrate website to Docusaurus
https://docusaurus.io/
2022-05-29 14:29:40 -03:00
Andrey Nering
f4d5abfc5b Merge pull request #743 from go-task/dependabot/go_modules/mvdan.cc/sh/v3-3.5.1
Bump mvdan.cc/sh/v3 from 3.5.0 to 3.5.1
2022-05-27 15:31:18 -03:00
dependabot[bot]
f55cf3ab8d Bump mvdan.cc/sh/v3 from 3.5.0 to 3.5.1
Bumps [mvdan.cc/sh/v3](https://github.com/mvdan/sh) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/mvdan/sh/releases)
- [Changelog](https://github.com/mvdan/sh/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mvdan/sh/compare/v3.5.0...v3.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-27 18:24:08 +00:00
Andrey Nering
d35141a369 Fix link to Discord server
I didn't know links to Discord servers had an expiration time 🤦

This one is unlimited.

Ref #742
2022-05-25 09:41:53 -03:00
Andrey Nering
d450444596 Temporarily revert #479 2022-05-22 16:54:35 -03:00
Andrey Nering
7e11815409 Merge pull request #737 from abrahammurciano/patch-1
Mention how label affects rerunning of tasks
2022-05-22 16:13:04 -03:00
Andrey Nering
017cd4cd6c Merge pull request #738 from carlsmedstad/bash-completion
Make progname easier to configure in task.{bash,fish}
2022-05-22 16:11:24 -03:00
Carl Smedstad
ac6c2ff769 Make progname easier to configur in task.fish 2022-05-22 16:52:53 +02:00
Carl Smedstad
c4e8ca4b32 Enable fish tab completion for tasks without description 2022-05-22 16:50:47 +02:00
Carl Smedstad
c9aec2f281 Make progname easier to configure in task.bash
As task is a very generic name that conflicts with for example
taskwarrior, some packagers might choose to change it. See my package in
the AUR: https://aur.archlinux.org/packages/go-task.

This change makes it easier to configure the name for downstream
packagers.
2022-05-22 16:16:52 +02:00
Carl Smedstad
591561f657 Address shellcheck warnings in task.bash 2022-05-22 16:15:15 +02:00
Carl Smedstad
2ad4054133 Add extension sh & bash to .editorconfig 2022-05-22 16:14:04 +02:00
Abraham Murciano
7883977a56 Mention how label affects rerunning of tasks 2022-05-22 00:03:22 +03:00
Andrey Nering
d5cb842db2 Merge pull request #733 from wyfo/patch-1
Fix interactive task example
2022-05-19 10:46:47 -03:00
Joseph Perez
03bbb0571e Fix interactive task example
A task name is missing in the example.
2022-05-19 09:17:55 +02:00
Andrey Nering
1d355d74ef Merge pull request #729 from go-task/add-staticcheck
Add golangci-lint
2022-05-16 09:34:14 -03:00
Andrey Nering
7f9913590e taskfile.dev: Fix some Docsify plugins not working
Only the Carbon plugin needs to be defined before the `window.$docsify`
declaration. Some others have to be after that to work correctly
(namely search and tabs).
2022-05-15 22:16:06 -03:00
Andrey Nering
9e1d4e7855 Add golangci-lint
https://golangci-lint.run/
2022-05-15 21:30:07 -03:00
Andrey Nering
a1f9b584dc Remove unneeded log on test 2022-05-14 19:50:15 -03:00
Andrey Nering
7d474db765 Make signal test work both locally and in CI 2022-05-14 19:36:15 -03:00
aliculPix4D
367c0b38a6 fix: remove redundant newline (linter error) 2022-05-13 17:36:52 -07:00
Marco Molteni
cacd57f72b fix ci test breakage (fork/exec ./bin/task: no such file or directory)
On Github actions, the destination path of "go install" ($GOPATH/bin) is not
in $PATH, thus the error.
For the life of me I could not understand how to change the $PATH environment
variable in an Actions workflow, so I encode the full path of the just-built
task executable in the tests, which probably was the right thing to do since
the beginning.
2022-05-13 17:36:52 -07:00
Marco Molteni
22dfc1e265 execext.RunCommand: fix: do not pass a cancellable context to mvdan.cc/sh
We used to pass to mvdan.cc/sh/interp.Runner a context that was cancelled on
reception of a OS signal. This caused the Runner to terminate the subprocess
abruptly.

The correct behavior instead is for us to completely ignore the signal and let
the subprocess deal with it. If the subprocess doesn't handle the signal, it
will be terminated. If the subprocess does handle the signal, it knows better
than us wether it wants to cleanup and terminate or do something different.

So now we pass an empty context just to make the API of interp.Runner happy

Fixes go-task/task/#458
2022-05-13 17:36:52 -07:00
Marco Molteni
bffb6e1a07 add regression test for SIGINT behavior
See go-task/task/#458

Helper (sleepit) and test code based on https://github.com/marco-m/timeit
2022-05-13 17:36:52 -07:00
Andrey Nering
cdff0c60d9 Merge pull request #727 from go-task/dependabot/go_modules/mvdan.cc/sh/v3-3.5.0
Bump mvdan.cc/sh/v3 from 3.4.3 to 3.5.0
2022-05-11 23:08:09 -03:00
dependabot[bot]
f55eb3cba9 Bump mvdan.cc/sh/v3 from 3.4.3 to 3.5.0
Bumps [mvdan.cc/sh/v3](https://github.com/mvdan/sh) from 3.4.3 to 3.5.0.
- [Release notes](https://github.com/mvdan/sh/releases)
- [Changelog](https://github.com/mvdan/sh/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mvdan/sh/compare/v3.4.3...v3.5.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-12 02:02:57 +00:00
Andrey Nering
6b8d4dd101 taskfile.dev: Add link to Discord server 2022-05-11 22:50:54 -03:00
Andrey Nering
d05e130250 taskfile.dev: Add Carbon 2022-05-11 22:15:41 -03:00
Andrey Nering
d33e50f367 Merge pull request #726 from sisp/logo-mono
Add monochromatic SVG logo
2022-05-11 20:48:47 -03:00
Sigurd Spieckermann
f3c9c53b6c Add monochromatic SVG logo 2022-05-11 16:12:10 +02:00
Andrey Nering
7d5b9c78b1 Merge pull request #725 from sisp/logo
Clean up SVG logo markup
2022-05-11 09:46:26 -03:00
Sigurd Spieckermann
e6a4b7bbba Clean up SVG logo markup 2022-05-11 14:31:28 +02:00
Andrey Nering
ad0b269d53 v3.12.1 2022-05-10 18:00:45 -03:00
Andrey Nering
5472570958 Add docs/Logo.svg 2022-05-09 13:03:19 -03:00
Andrey Nering
4576ba4db0 Website: Add GA manually for now
Docsify GA plugin seems to be broken:

- https://github.com/docsifyjs/docsify/issues/1695
- https://github.com/docsifyjs/docsify/pull/1702
2022-05-09 11:06:47 -03:00
Andrey Nering
efcfab0955 Website: Re-add GA 2022-05-09 09:54:22 -03:00
Andrey Nering
1acd59c7d6 Replace \r\n on Windows as we do for \n on Linux
Closes #717
2022-05-08 17:37:24 -03:00
Andrey Nering
4951a2bf7a Merge pull request #718 from budimanjojo/master
Fix broken completion when no taskfile is found
2022-05-08 17:25:11 -03:00
budimanjojo
95fc26d4ad Fix broken completion when no taskfile is found 2022-05-07 23:20:08 +07:00
Andrey Nering
b65935d6cf Merge pull request #695 from joshuatz/typo-fixes
Docs: typo fixes
2022-04-01 08:44:06 -03:00
Joshua Tzucker
2155fdd756 Docs: typo fixes 2022-03-31 22:14:38 -07:00
Andrey Nering
f2abc13ce2 v3.12.0 2022-03-31 21:44:59 -03:00
Andrey Nering
0f4621fb02 CHANGELOG: Add entry for #691 2022-03-31 21:40:16 -03:00
Andrey Nering
c6ff641f6d Merge branch 'list-task-names' of https://github.com/ardnew/task into ardnew-list-task-names 2022-03-31 21:31:56 -03:00
Andrey Nering
350f74a53d CHANGELOG: Add entry for #656 2022-03-31 21:19:16 -03:00
Andrey Nering
41cd7acc87 Merge pull request #656 from tylermmorton/master
Add support for multi-level includes
2022-03-31 21:12:15 -03:00
Andrey Nering
c6eea26660 go mod tidy 2022-03-21 15:26:41 -03:00
Andrey Nering
61c5718663 Upgrade to Go 1.18 is out. Set 1.17 as the minimal version 2022-03-21 15:23:06 -03:00
ardnew
9897f4b527 refactor with support for --list and --list-all 2022-03-21 12:59:25 -05:00
ardnew
978a6e5ecb quickly print task names only with flags --silent and --list 2022-03-21 12:02:56 -05:00
Andrey Nering
a018997ddc CHANGELOG: Fix typo 2022-03-21 10:31:48 -03:00
Andrey Nering
de09843467 Improvements + CHANGELOG for #677 2022-03-19 18:41:03 -03:00
Andrey Nering
78a57fdb4b Merge branch 'dballweg/vars_in_includedtaskfile' of https://github.com/dballweg/task into dballweg-dballweg/vars_in_includedtaskfile 2022-03-19 17:31:11 -03:00
Andrey Nering
0bc2fd72f0 Merge pull request #689 from go-task/dependabot/go_modules/github.com/stretchr/testify-1.7.1
Bump github.com/stretchr/testify from 1.7.0 to 1.7.1
2022-03-19 11:01:09 -03:00
dependabot[bot]
dda5164efd Bump github.com/stretchr/testify from 1.7.0 to 1.7.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.0...v1.7.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-19 11:07:08 +00:00
Andrey Nering
3df2396b63 Merge pull request #688 from vicmattos/vicmattosissue-683-docs-install-choco
Add chocolatey in installation documentation
2022-03-18 20:46:50 -03:00
Victor Mattos
d3da84e724 docs: add ownership of choco installation method 2022-03-18 17:41:34 -03:00
Victor Mattos
eb61015477 fix: reference chocolatey link anchor
Co-authored-by: Andrey Nering <andrey@nering.com.br>
2022-03-18 17:38:29 -03:00
Victor Mattos
40c644f006 docs: add chocolatey installation method
Resolves #683
2022-03-09 15:39:54 -03:00
Andrey Nering
c9aa0180a8 Merge pull request #679 from philpennock/zsh-completion
completion: zsh: overhaul and sync to current flags
2022-02-27 16:01:11 -03:00
Phil Pennock
a06e46885d completion: zsh: overhaul and sync to current flags
* List all current option flags
* Provide descriptions for every flag
* Pass the `task -l` descriptions as descriptions for the task completions
  + The prior logic had 4 invocations of sed and 1 of awk, and only kept the
    task name
  + Do all filtering in zsh without forking (except for `task` itself)
* When `--taskfile` is used, complete tasks from _that_ file
  + And otherwise, enable completions if only the `.dist` variant files are
    present
* Ensure mutually exclusive options preclude each other
  + the `+ '(groupname)'` clause defines this
* Fix `--dir` to take directories, not files
2022-02-25 01:07:43 -05:00
Dan Ballweg
60fa6e6c0a update 2022-02-24 13:18:35 -06:00
Dan Ballweg
2f18f7927d test include variables 2022-02-24 13:17:20 -06:00
Dan Ballweg
292cf75836 add vars to included taskfiles 2022-02-23 16:53:46 -06:00
tylermmorton
fc95061f4c Add missing newlines 2022-02-21 15:33:54 -05:00
tylermmorton
1f1275255c Fix bug in includes where default taskfiles were not being checked. 2022-02-21 15:31:55 -05:00
Andrey Nering
d8555e5a5d v3.11.0 2022-02-19 19:40:29 -03:00
Andrey Nering
b323531dd5 Improvements and CHANGELOG for #651 2022-02-19 19:31:27 -03:00
Andrey Nering
cfb665310e Merge branch 'group-begin-message' of https://github.com/janslow/task into janslow-group-begin-message 2022-02-19 18:42:34 -03:00
Andrey Nering
51c6ebcd4d Add tests, documentation and changelog for #666 2022-02-19 18:24:43 -03:00
Andrey Nering
e94d1b6b9f Merge branch 'task-498-taskfile-dist' of https://github.com/tylermmorton/task into tylermmorton-task-498-taskfile-dist 2022-02-19 18:01:00 -03:00
Andrey Nering
ca7b32105d Merge pull request #674 from go-task/dependabot/go_modules/mvdan.cc/sh/v3-3.4.3
Bump mvdan.cc/sh/v3 from 3.4.2 to 3.4.3
2022-02-19 17:55:30 -03:00
dependabot[bot]
264db2737b Bump mvdan.cc/sh/v3 from 3.4.2 to 3.4.3
Bumps [mvdan.cc/sh/v3](https://github.com/mvdan/sh) from 3.4.2 to 3.4.3.
- [Release notes](https://github.com/mvdan/sh/releases)
- [Changelog](https://github.com/mvdan/sh/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mvdan/sh/compare/v3.4.2...v3.4.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-19 20:51:22 +00:00
Andrey Nering
5f2c9a6e45 Merge pull request #671 from JessThrysoee/bashcompletion
bash-completion refactoring
2022-02-13 19:51:40 -03:00
Jess Thrysoee
19be1f1bf0 bash-completion refactoring
1. 'compgen -c' lists _all_ command names on the system, which is not
   appropriate for this script, furthermore echo does not read from stdin
   so the output is lost.

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

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

3. Add options completion

4. Use task --list-all
2022-02-13 23:44:04 +01:00
tylermmorton
7cdf0000d9 Fix error message assertion in task_test 2022-02-03 22:23:01 -05:00
tylermmorton
13606e5e00 Remove note about multi level includes from usage documentation 2022-02-03 22:20:49 -05:00
tylermmorton
35af240faa Add newlines to multi-level test Taskfiles 2022-02-03 22:19:07 -05:00
tylermmorton
0ac56f8973 Add newlines to test Taskfiles 2022-02-03 22:13:43 -05:00
tylermmorton
6e5f8b1fb0 Append task prefix to log messages 2022-02-03 22:12:58 -05:00
tylermmorton
15e831c0b0 Revert "Update docs to account for new feature"
This reverts commit 66748ab5e5.
2022-02-03 22:09:57 -05:00
tylermmorton
248952bc8f Add dist fallbacks to defaultTaskfiles 2022-01-29 11:53:36 -05:00
Andrey Nering
2373743eac Merge pull request #652 from jcwillox/master
Add completions to nfpms packages
2022-01-17 10:18:22 -03:00
Andrey Nering
f119596be6 Docs: Small change to title 2022-01-17 09:38:32 -03:00
Andrey Nering
b7cb41b388 Merge pull request #657 from lechuckroh/intellij-plugin
Update 'community.md' to add IntelliJ plugin link
2022-01-17 09:37:06 -03:00
Lechuck Roh
a65ee26446 Update 'community.md' to add IntelliJ plugin link 2022-01-16 22:24:12 +09:00
tylermmorton
d3e2fbf1e2 Merge branch 'master' of https://github.com/tylermmorton/task 2022-01-15 23:37:40 -05:00
tylermmorton
66748ab5e5 Update docs to account for new feature 2022-01-15 23:37:39 -05:00
tylermmorton
c73a2c8f84 Move circular include logic to a separate function 2022-01-15 23:34:59 -05:00
Tyler Morton
4bbcd99b8b Merge branch 'go-task:master' into master 2022-01-14 22:39:27 -05:00
tylermmorton
02e7ff27c7 Add support for multi-level includes and cyclic include detection 2022-01-14 22:38:37 -05:00
Josh Willox
7ed3cea40b Add completions to nfpms packages 2022-01-14 20:48:28 +11:00
Jay Anslow
74f5cf8f29 Add support for begin/end messages with grouped output
Fixes #647

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

## Example

The following Taskfile

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

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

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

<img width="771" alt="image" src="https://user-images.githubusercontent.com/1253367/149429832-6cb0c1b5-0758-442e-9375-c4daa65771bc.png">
<img width="394" alt="image" src="https://user-images.githubusercontent.com/1253367/149429851-1d5d2ab5-9095-4795-9b57-f91750720d40.png">
2022-01-14 00:22:14 +00:00
Andrey Nering
086d13ca2f Docs: Remove line reference from file link 2022-01-13 10:39:38 -03:00
Andrey Nering
2780e96179 Merge pull request #648 from sagikazarmark/docs-nix-install
docs: add nix as an installation method
2022-01-13 10:38:49 -03:00
Mark Sagi-Kazar
191678f9d6 docs: add nix as an installation method 2022-01-13 00:15:04 +01:00
133 changed files with 12273 additions and 885 deletions

View File

@@ -8,6 +8,6 @@ charset = utf-8
trim_trailing_whitespace = true
indent_style = tab
[*.{md,yml,yaml,json,toml,htm,html}]
[*.{md,yml,yaml,json,toml,htm,html,js,css,svg,sh,bash}]
indent_style = space
indent_size = 2

1
.github/FUNDING.yml vendored
View File

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

24
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Lint
on:
pull_request:
push:
tags:
- v*
branches:
- master
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.18.x
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.46.1

View File

@@ -10,12 +10,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: 1.17.x
go-version: 1.19.x
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2

View File

@@ -1,22 +1,30 @@
name: Test
on: [push, pull_request]
on:
pull_request:
push:
tags:
- v*
branches:
- master
jobs:
test:
name: Test
strategy:
matrix:
go-version: [1.16.x, 1.17.x]
go-version: [1.18.x, 1.19.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{matrix.platform}}
steps:
- name: Set up Go ${{matrix.go-version}}
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ${{matrix.go-version}}
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Download Go modules
run: go mod download
@@ -27,4 +35,4 @@ jobs:
run: go build -o ./bin/task -v ./cmd/task
- name: Test
run: ./bin/task test
run: ./bin/task test --output=group --output-group-begin='::group::{{.TASK}}' --output-group-end='::endgroup::'

35
.github/workflows/website-deploy.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Website Deploy
on:
push:
branches:
- master
jobs:
website-deploy:
name: Website Deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16.x
cache: yarn
cache-dependency-path: ./docs/yarn.lock
- name: Install dependencies
run: yarn install --frozen-lockfile
working-directory: ./docs
- name: Build website
run: yarn build
working-directory: ./docs
- name: Website Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/build
user_name: task-bot
user_email: 106601941+task-bot@users.noreply.github.com

27
.github/workflows/website-test.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Website Test
on:
pull_request:
branches:
- master
jobs:
website-test:
name: Website Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16.x
cache: yarn
cache-dependency-path: ./docs/yarn.lock
- name: Install dependencies
run: yarn install --frozen-lockfile
working-directory: ./docs
- name: Test build website
run: yarn build
working-directory: ./docs

4
.gitignore vendored
View File

@@ -26,6 +26,8 @@ dist/
# exuberant ctags
tags
/bin
/bin/*
!/bin/.keep
/testdata/vars/v1
/tmp
node_modules

View File

@@ -17,6 +17,9 @@ build:
goarch: 386
env:
- CGO_ENABLED=0
mod_timestamp: '{{ .CommitTimestamp }}'
flags:
- -trimpath
ldflags:
- -s -w # Don't set main.version.
@@ -44,8 +47,8 @@ checksum:
nfpms:
- vendor: Task
homepage: https://github.com/go-task/task
maintainer: Andrey Nering <andrey.nering@gmail.com>
homepage: https://taskfile.dev
maintainer: Andrey Nering <andrey@nering.com.br>
description: Simple task runner written in Go
license: MIT
conflicts:
@@ -54,6 +57,13 @@ nfpms:
- deb
- rpm
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
contents:
- src: completion/bash/task.bash
dst: /etc/bash_completion.d/task
- src: completion/fish/task.fish
dst: /usr/share/fish/completions/task.fish
- src: completion/zsh/_task
dst: /usr/local/share/zsh/site-functions/_task
brews:
- name: go-task
@@ -71,3 +81,6 @@ brews:
bash_completion.install "completion/bash/task.bash" => "task"
zsh_completion.install "completion/zsh/_task" => "_task"
fish_completion.install "completion/fish/task.fish"
commit_author:
name: task-bot
email: 106601941+task-bot@users.noreply.github.com

View File

@@ -1,5 +1,102 @@
# Changelog
## v3.16.0 - 2022-09-29
- Add `npm` as new installation method: `npm i -g @go-task/cli`
([#870](https://github.com/go-task/task/issues/870), [#871](https://github.com/go-task/task/pull/871), [npm package](https://www.npmjs.com/package/@go-task/cli)).
- Add support to marking tasks and includes as internal, which will hide them
from `--list` and `--list-all`
([#818](https://github.com/go-task/task/pull/818)).
## v3.15.2 - 2022-09-08
- Fix error when using variable in `env:` introduced in the previous release
([#858](https://github.com/go-task/task/issues/858), [#866](https://github.com/go-task/task/pull/866)).
- Fix handling of `CLI_ARGS` (`--`) in Bash completion
([#863](https://github.com/go-task/task/pull/863)).
- On zsh completion, add ability to replace `--list-all` with `--list` as
already possible on the Bash completion
([#861](https://github.com/go-task/task/pull/861)).
## v3.15.0 - 2022-09-03
- Add new special variables `ROOT_DIR` and `TASKFILE_DIR`. This was a highly
requested feature
([#215](https://github.com/go-task/task/issues/215), [#857](https://github.com/go-task/task/pull/857), [Documentation](https://taskfile.dev/api/#special-variables)).
- Follow symlinks on `sources`
([#826](https://github.com/go-task/task/issues/826), [#831](https://github.com/go-task/task/pull/831)).
- Improvements and fixes to Bash completion
([#835](https://github.com/go-task/task/pull/835), [#844](https://github.com/go-task/task/pull/844)).
## v3.14.1 - 2022-08-03
- Always resolve relative include paths relative to the including Taskfile
([#822](https://github.com/go-task/task/issues/822), [#823](https://github.com/go-task/task/pull/823)).
- Fix ZSH and PowerShell completions to consider all tasks instead of just the
public ones (those with descriptions)
([#803](https://github.com/go-task/task/pull/803)).
## v3.14.0 - 2022-07-08
- Add ability to override the `.task` directory location with the
`TASK_TEMP_DIR` environment variable.
- Allow to override Task colors using environment variables:
`TASK_COLOR_RESET`, `TASK_COLOR_BLUE`, `TASK_COLOR_GREEN`,
`TASK_COLOR_CYAN`, `TASK_COLOR_YELLOW`, `TASK_COLOR_MAGENTA`
and `TASK_COLOR_RED`
([#568](https://github.com/go-task/task/pull/568), [#792](https://github.com/go-task/task/pull/792)).
- Fixed bug when using the `output: group` mode where STDOUT and STDERR were
being print in separated blocks instead of in the right order
([#779](https://github.com/go-task/task/issues/779)).
- Starting on this release, ARM architecture binaries are been released to Snap
as well
([#795](https://github.com/go-task/task/issues/795)).
- i386 binaries won't be available anymore on Snap because Ubuntu removed the support
for this architecture.
- Upgrade mvdan.cc/sh, which fixes a bug with associative arrays
([#785](https://github.com/go-task/task/issues/785), [mvdan/sh#884](https://github.com/mvdan/sh/issues/884), [mvdan/sh#893](https://github.com/mvdan/sh/pull/893)).
## v3.13.0 - 2022-06-13
- Added `-n` as an alias to `--dry`
([#776](https://github.com/go-task/task/issues/776), [#777](https://github.com/go-task/task/pull/777)).
- Fix behavior of interrupt (SIGINT, SIGTERM) signals. Task will now give time
for the processes running to do cleanup work
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728), [#769](https://github.com/go-task/task/pull/769)).
- Add new `--exit-code` (`-x`) flag that will pass-through the exit form the
command being ran
([#755](https://github.com/go-task/task/pull/755)).
## v3.12.1 - 2022-05-10
- Fixed bug where, on Windows, variables were ending with `\r` because we were
only removing the final `\n` but not `\r\n`
([#717](https://github.com/go-task/task/issues/717)).
## v3.12.0 - 2022-03-31
- The `--list` and `--list-all` flags can now be combined with the `--silent`
flag to print the task names only, without their description
([#691](https://github.com/go-task/task/pull/691)).
- Added support for multi-level inclusion of Taskfiles. This means that
included Taskfiles can also include other Taskfiles. Before this was limited
to one level
([#390](https://github.com/go-task/task/issues/390), [#623](https://github.com/go-task/task/discussions/623), [#656](https://github.com/go-task/task/pull/656)).
- Add ability to specify vars when including a Taskfile.
[Check out the documentation](https://taskfile.dev/#/usage?id=vars-of-included-taskfiles)
for more information.
([#677](https://github.com/go-task/task/pull/677)).
## v3.11.0 - 2022-02-19
- Task now supports printing begin and end messages when using the `group`
output mode, useful for grouping tasks in CI systems.
[Check out the documentation](http://taskfile.dev/#/usage?id=output-syntax) for more information
([#647](https://github.com/go-task/task/issues/647), [#651](https://github.com/go-task/task/pull/651)).
- Add `Taskfile.dist.yml` and `Taskfile.dist.yaml` to the supported file
name list. [Check out the documentation](https://taskfile.dev/#/usage?id=supported-file-names) for more information
([#498](https://github.com/go-task/task/issues/498), [#666](https://github.com/go-task/task/pull/666)).
## v3.10.0 - 2022-01-04
- A new `--list-all` (alias `-a`) flag is now available. It's similar to the

View File

@@ -1,6 +1,6 @@
<div align="center">
<a href="https://taskfile.dev">
<img src="docs/Logo.png" width="200px" height="200px" />
<img src="docs/static/img/logo.svg" width="200px" height="200px" />
</a>
<h1>Task</h1>
@@ -10,6 +10,6 @@
</p>
<p>
See <a href="https://taskfile.dev">taskfile.dev</a> for the documentation.
<a href="https://taskfile.dev/installation/">Installation</a> | <a href="https://taskfile.dev/usage/">Documentation</a> | <a href="https://twitter.com/taskfiledev">Twitter</a> | <a href="https://discord.gg/6TY36E39UK">Discord</a>
</p>
</div>

View File

@@ -18,10 +18,13 @@ env:
tasks:
default:
cmds:
- task: lint
- task: test
install:
desc: Installs Task
sources:
- './**/*.go'
cmds:
- go install -v -ldflags="-w -s -X main.version={{.GIT_COMMIT}}" ./cmd/task
@@ -31,25 +34,33 @@ tasks:
- go mod download
- go mod tidy
cli-deps:
desc: Downloads CLI dependencies
cmds:
- task: go-get
vars: {REPO: golang.org/x/lint/golint}
- task: go-get
vars: {REPO: github.com/goreleaser/goreleaser}
- task: go-get
vars: {REPO: github.com/goreleaser/godownloader}
clean:
desc: Cleans temp files and folders
cmds:
- rm -rf dist/
- rm -rf tmp/
lint:
desc: Runs golint
desc: Runs golangci-lint
sources:
- './**/*.go'
cmds:
- golint {{catLines .GO_PACKAGES}}
- golangci-lint run
sleepit:build:
desc: Builds the sleepit test helper
sources:
- ./cmd/sleepit/**/*.go
generates:
- ./bin/sleepit
cmds:
- go build -o ./bin/sleepit{{exeExt}} ./cmd/sleepit
sleepit:run:
desc: Builds the sleepit test helper
deps: [sleepit:build]
cmds:
- ./bin/sleepit {{.CLI_ARGS}}
silent: true
test:
@@ -58,24 +69,34 @@ tasks:
cmds:
- go test {{catLines .GO_PACKAGES}}
test:signals:
desc: Runs test suite with signals tests included
deps: [install, sleepit:build]
cmds:
- go test {{catLines .GO_PACKAGES}} -tags signals
test-release:
desc: Tests release process without publishing
cmds:
- goreleaser --snapshot --rm-dist
gen-install-script:
desc: Generate install script using https://github.com/goreleaser/godownloader
docs:changelog:
desc: Copy CHANGELOG.md to the documentation website
vars:
FILE: docs/docs/changelog.md
cmds:
- godownloader --repo go-task/task -o install-task.sh
- cp ./install-task.sh ./docs/install.sh
- rm {{.FILE}}
- 'echo "---" >> {{.FILE}}'
- 'echo "slug: /changelog/" >> {{.FILE}}'
- 'echo "sidebar_position: 6" >> {{.FILE}}'
- 'echo "---" >> {{.FILE}}'
- 'echo "" >> {{.FILE}}'
- 'cat CHANGELOG.md >> {{.FILE}}'
ci:
- task: go-get
vars: {REPO: golang.org/x/lint/golint}
- task: lint
- task: test
go-get: go get -u {{.REPO}}
npm:publish:
desc: Publish release to npm
cmds:
- npm publish --access=public
packages:
cmds:

0
bin/.keep Normal file
View File

173
cmd/sleepit/sleepit.go Normal file
View File

@@ -0,0 +1,173 @@
// This code is released under the MIT License
// Copyright (c) 2020 Marco Molteni and the timeit contributors.
package main
import (
"flag"
"fmt"
"os"
"os/signal"
"time"
)
const usage = `sleepit: sleep for the specified duration, optionally handling signals
When the line "sleepit: ready" is printed, it means that it is safe to send signals to it
Usage: sleepit <command> [<args>]
Commands
default Use default action: on reception of SIGINT terminate abruptly
handle Handle signals: on reception of SIGINT perform cleanup before exiting
version Show the sleepit version`
var (
// Filled by the linker.
fullVersion = "unknown" // example: v0.0.9-8-g941583d027-dirty
)
func main() {
os.Exit(run(os.Args[1:]))
}
func run(args []string) int {
if len(args) < 1 {
fmt.Fprintln(os.Stderr, usage)
return 2
}
defaultCmd := flag.NewFlagSet("default", flag.ExitOnError)
defaultSleep := defaultCmd.Duration("sleep", 5*time.Second, "Sleep duration")
handleCmd := flag.NewFlagSet("handle", flag.ExitOnError)
handleSleep := handleCmd.Duration("sleep", 5*time.Second, "Sleep duration")
handleCleanup := handleCmd.Duration("cleanup", 5*time.Second, "Cleanup duration")
handleTermAfter := handleCmd.Int("term-after", 0,
"Terminate immediately after `N` signals.\n"+
"Default is to terminate only when the cleanup phase has completed.")
versionCmd := flag.NewFlagSet("version", flag.ExitOnError)
switch args[0] {
case "default":
_ = defaultCmd.Parse(args[1:])
if len(defaultCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "default: unexpected arguments: %v\n", defaultCmd.Args())
return 2
}
return supervisor(*defaultSleep, 0, 0, nil)
case "handle":
_ = handleCmd.Parse(args[1:])
if *handleTermAfter == 1 {
fmt.Fprintf(os.Stderr, "handle: term-after cannot be 1\n")
return 2
}
if len(handleCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "handle: unexpected arguments: %v\n", handleCmd.Args())
return 2
}
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt) // Ctrl-C -> SIGINT
return supervisor(*handleSleep, *handleCleanup, *handleTermAfter, sigCh)
case "version":
_ = versionCmd.Parse(args[1:])
if len(versionCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "version: unexpected arguments: %v\n", versionCmd.Args())
return 2
}
fmt.Printf("sleepit version %s\n", fullVersion)
return 0
default:
fmt.Fprintln(os.Stderr, usage)
return 2
}
}
func supervisor(
sleep time.Duration,
cleanup time.Duration,
termAfter int,
sigCh <-chan os.Signal,
) int {
fmt.Printf("sleepit: ready\n")
fmt.Printf("sleepit: PID=%d sleep=%v cleanup=%v\n",
os.Getpid(), sleep, cleanup)
cancelWork := make(chan struct{})
workerDone := worker(cancelWork, sleep, "work")
cancelCleaner := make(chan struct{})
var cleanerDone <-chan struct{}
sigCount := 0
for {
select {
case sig := <-sigCh:
sigCount++
fmt.Printf("sleepit: got signal=%s count=%d\n", sig, sigCount)
if sigCount == 1 {
// since `cancelWork` is unbuffered, sending will be synchronous:
// we are ensured that the worker has terminated before starting cleanup.
// This is important in some real-life situations.
cancelWork <- struct{}{}
cleanerDone = worker(cancelCleaner, cleanup, "cleanup")
}
if sigCount == termAfter {
cancelCleaner <- struct{}{}
return 4
}
case <-workerDone:
return 0
case <-cleanerDone:
return 3
}
}
}
// Start a worker goroutine and return immediately a `workerDone` channel.
// The goroutine will prepend its prints with the prefix `name`.
// The goroutine will simulate some work and will terminate when one of the following
// conditions happens:
// 1. When `howlong` is elapsed. This case will be signaled on the `workerDone` channel.
// 2. When something happens on channel `canceled`. Note that this simulates real-life,
// so cancellation is not instantaneous: if the caller wants a synchronous cancel,
// it should send a message; if instead it wants an asynchronous cancel, it should
// close the channel.
func worker(
canceled <-chan struct{},
howlong time.Duration,
name string,
) <-chan struct{} {
workerDone := make(chan struct{})
deadline := time.Now().Add(howlong)
go func() {
fmt.Printf("sleepit: %s started\n", name)
for {
select {
case <-canceled:
fmt.Printf("sleepit: %s canceled\n", name)
return
default:
if doSomeWork(deadline) {
fmt.Printf("sleepit: %s done\n", name) // <== NOTE THIS LINE
workerDone <- struct{}{}
return
}
}
}
}()
return workerDone
}
// Do some work and then return, so that the caller can decide wether to continue or not.
// Return true when all work is done.
func doSomeWork(deadline time.Time) bool {
if time.Now().After(deadline) {
return true
}
timeout := 100 * time.Millisecond
time.Sleep(timeout)
return false
}

View File

@@ -5,11 +5,9 @@ import (
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"runtime/debug"
"strings"
"syscall"
"github.com/spf13/pflag"
"mvdan.cc/sh/v3/syntax"
@@ -68,11 +66,12 @@ func main() {
silent bool
dry bool
summary bool
exitCode bool
parallel bool
concurrency int
dir string
entrypoint string
output string
output taskfile.Output
color bool
)
@@ -87,11 +86,14 @@ func main() {
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.BoolVarP(&dry, "dry", "n", 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.BoolVarP(&exitCode, "exit-code", "x", false, "pass-through the exit code of the task command")
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.StringVarP(&output.Name, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
pflag.StringVar(&output.Group.Begin, "output-group-begin", "", "message template to print before a task's grouped output")
pflag.StringVar(&output.Group.End, "output-group-end", "", "message template to print after a task's grouped output")
pflag.BoolVarP(&color, "color", "c", true, "colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable")
pflag.IntVarP(&concurrency, "concurrency", "C", 0, "limit number tasks to run concurrently")
pflag.Parse()
@@ -126,6 +128,17 @@ func main() {
entrypoint = filepath.Base(entrypoint)
}
if output.Name != "group" {
if output.Group.Begin != "" {
log.Fatal("task: You can't set --output-group-begin without --output=group")
return
}
if output.Group.End != "" {
log.Fatal("task: You can't set --output-group-end without --output=group")
return
}
}
e := task.Executor{
Force: force,
Watch: watch,
@@ -145,6 +158,12 @@ func main() {
OutputStyle: output,
}
if (list || listAll) && silent {
e.ListTaskNames(listAll)
return
}
if err := e.Setup(); err != nil {
log.Fatal(err)
}
@@ -183,11 +202,12 @@ func main() {
globals.Set("CLI_ARGS", taskfile.Var{Static: cliArgs})
e.Taskfile.Vars.Merge(globals)
ctx := context.Background()
if !watch {
ctx = getSignalContext()
e.InterceptInterruptSignals()
}
ctx := context.Background()
if status {
if err := e.Status(ctx, calls...); err != nil {
log.Fatal(err)
@@ -197,6 +217,12 @@ func main() {
if err := e.Run(ctx, calls...); err != nil {
e.Logger.Errf(logger.Red, "%v", err)
if exitCode {
if err, ok := err.(*task.TaskRunError); ok {
os.Exit(err.ExitCode())
}
}
os.Exit(1)
}
}
@@ -222,18 +248,6 @@ func getArgs() ([]string, string, error) {
return args[:doubleDashPos], strings.Join(quotedCliArgs, " "), nil
}
func getSignalContext() context.Context {
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background())
go func() {
sig := <-ch
log.Printf("task: signal received: %s", sig)
cancel()
}()
return ctx
}
func getVersion() string {
if version != "" {
return version

View File

@@ -1,21 +1,55 @@
_task_completion()
# vim: set tabstop=2 shiftwidth=2 expandtab:
_GO_TASK_COMPLETION_LIST_OPTION='--list-all'
function _task()
{
local scripts;
local curr_arg;
local cur prev words cword
_init_completion -n : || return
# Remove colon from word breaks
COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
# Check for `--` within command-line and quit or strip suffix.
local i
for i in "${!words[@]}"; do
if [ "${words[$i]}" == "--" ]; then
# Do not complete words following `--` passed to CLI_ARGS.
[ $cword -gt $i ] && return
# Remove the words following `--` to not put --list in CLI_ARGS.
words=( "${words[@]:0:$i}" )
break
fi
done
scripts=$(task -l | sed '1d' | awk '{ print $2 }' | sed 's/:$//');
# Handle special arguments of options.
case "$prev" in
-d|--dir)
_filedir -d
return $?
;;
-t|--taskfile)
_filedir yaml || return $?
_filedir yml
return $?
;;
-o|--output)
COMPREPLY=( $( compgen -W "interleaved group prefixed" -- $cur ) )
return 0
;;
esac
curr_arg="${COMP_WORDS[COMP_CWORD]:-"."}"
# Handle normal options.
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "$(_parse_help $1)" -- $cur ) )
return 0
;;
esac
# Do not accept more than 1 argument
if [ "${#COMP_WORDS[@]}" != "2" ]; then
return
fi
# Prepare task name completions.
local tasks=( $( "${words[@]}" --silent $_GO_TASK_COMPLETION_LIST_OPTION 2> /dev/null ) )
COMPREPLY=( $( compgen -W "${tasks[*]}" -- "$cur" ) )
COMPREPLY=($(compgen -c | echo "$scripts" | grep -- $curr_arg));
# Post-process because task names might contain colons.
__ltrim_colon_completions "$cur"
}
complete -F _task_completion task
complete -F _task task

View File

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

View File

@@ -3,7 +3,7 @@ $scriptBlock = {
$curReg = "task{.exe}? (.*?)$"
$startsWith = $wordToComplete | Select-String $curReg -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value }
$reg = "\* ($startsWith.+?):"
$listOutput = $(task -l)
$listOutput = $(task --list-all)
$listOutput | Select-String $reg -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value + " " }
}

View File

@@ -1,25 +1,62 @@
#compdef task
# Listing commands from Taskfile.yml
function __list() {
local -a scripts
local context state state_descr line
typeset -A opt_args
if [ -f Taskfile.yml ] || [ -f Taskfile.yaml ]; then
scripts=($(task -l | sed '1d' | sed 's/^\* //' | awk '{ print $1 }' | sed 's/:$//' | sed 's/:/\\:/g'))
_describe 'script' scripts
_GO_TASK_COMPLETION_LIST_OPTION="${GO_TASK_COMPLETION_LIST_OPTION:---list-all}"
# Listing commands from Taskfile.yml
function __task_list() {
local -a scripts cmd
local -i enabled=0
local taskfile item task desc
cmd=(task)
taskfile="${(v)opt_args[(i)-t|--taskfile]}"
if [[ -n "$taskfile" && -f "$taskfile" ]]; then
enabled=1
cmd+=(--taskfile "$taskfile")
else
for taskfile in Taskfile{,.dist}.{yaml,yml}; do
if [[ -f "$taskfile" ]]; then
enabled=1
break
fi
done
fi
(( enabled )) || return 0
scripts=()
for item in "${(@)${(f)$("${cmd[@]}" $_GO_TASK_COMPLETION_LIST_OPTION)}[2,-1]#\* }"; do
task="${item%%:[[:space:]]*}"
desc="${item##[^[:space:]]##[[:space:]]##}"
scripts+=( "${task//:/\\:}:$desc" )
done
_describe 'Task to run' scripts
}
_arguments \
'(-d --dir)'{-d,--dir}': :_files' \
'(--dry)'--dry \
'(-f --force)'{-f,--force} \
'(-i --init)'{-i,--init} \
'(-l --list)'{-l,--list} \
'(-s --silent)'{-s,--silent} \
'(--status)'--status \
'(-v --verbose)'{-v,--verbose} \
'(--version)'--version \
'(-w --watch)'{-w,--watch} \
'(- *)'{-h,--help} \
'*: :__list' \
'(-C --concurrency)'{-C,--concurrency}'[limit number of concurrent tasks]: ' \
'(-p --parallel)'{-p,--parallel}'[run command-line tasks in parallel]' \
'(-f --force)'{-f,--force}'[run even if task is up-to-date]' \
'(-c --color)'{-c,--color}'[colored output]' \
'(-d --dir)'{-d,--dir}'[dir to run in]:execution dir:_dirs' \
'(--dry)--dry[dry-run mode, compile and print tasks only]' \
'(-o --output)'{-o,--output}'[set output style]:style:(interleaved group prefixed)' \
'(--output-group-begin)--output-group-begin[message template before grouped output]:template text: ' \
'(--output-group-end)--output-group-end[message template after grouped output]:template text: ' \
'(-s --silent)'{-s,--silent}'[disable echoing]' \
'(--status)--status[exit non-zero if supplied tasks not up-to-date]' \
'(--summary)--summary[show summary\: field from tasks instead of running them]' \
'(-t --taskfile)'{-t,--taskfile}'[specify a different taskfile]:taskfile:_files' \
'(-v --verbose)'{-v,--verbose}'[verbose mode]' \
'(-w --watch)'{-w,--watch}'[watch-mode for given tasks, re-run when inputs change]' \
+ '(operation)' \
{-l,--list}'[list describable tasks]' \
{-a,--list-all}'[list all tasks]' \
{-i,--init}'[create new Taskfile.yaml]' \
'(-*)'{-h,--help}'[show help]' \
'(-*)--version[show version and exit]' \
'*: :__task_list'

20
docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,20 @@
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -1 +0,0 @@
taskfile.dev

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,17 +1,30 @@
version: '3'
tasks:
install:
desc: Installs docsify to work the on the documentation site
setup:
desc: Setup Docusaurus locally
cmds:
- npm install docsify-cli -g
- yarn install
serve:
desc: Serves the documentation site locally
start:
desc: Start website
vars:
HOST: '{{default "localhost" .HOST}}'
PORT: '{{default "3001" .PORT}}'
cmds:
- docsify serve .
- npx docusaurus start --no-open --host={{.HOST}} --port={{.PORT}}
ico:
desc: Generate favicon.ico from Logo.png
build:
desc: Build website
cmds:
- convert -background transparent "Logo.png" -define icon:auto-resize=16,24,32,48,64,72,96,128,256 "favicon.ico"
- npx docusaurus build
clean:
desc: Clean temp directories
cmds:
- rm -rf ./build
deploy:
desc: Build and deploy Docusaurus. Requires GIT_USER and GIT_PASS envs to be previous set
cmds:
- npx docusaurus deploy

View File

@@ -1,8 +0,0 @@
- [Installation](installation.md)
- [Usage](usage.md)
- [Styleguide](styleguide.md)
- [Taskfile Versions](taskfile_versions.md)
- [Community](community.md)
- [Releasing Task](releasing_task.md)
- [Donate](donate.md)
- [GitHub](https://github.com/go-task/task)

3
docs/babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};

5
docs/blog/authors.yml Normal file
View File

@@ -0,0 +1,5 @@
andreynering:
name: Andrey Nering
title: Maintainer of Task
url: https://github.com/andreynering
image_url: https://github.com/andreynering.png

237
docs/docs/api_reference.md Normal file
View File

@@ -0,0 +1,237 @@
---
slug: /api/
sidebar_position: 4
---
# API Reference
## CLI
Task command line tool has the following syntax:
```bash
task [--flags] [tasks...] [-- CLI_ARGS...]
```
:::tip
If `--` is given, all remaning arguments will be assigned to a special `CLI_ARGS`
variable
:::
| Short | Flag | Type | Default | Description |
| - | - | - | - | - |
| `-c` | `--color` | `bool` | `true` | Colored output. Enabled by default. Set flag to `false` or use `NO_COLOR=1` to disable. |
| `-C` | `--concurrency` | `int` | `0` | Limit number tasks to run concurrently. Zero means unlimited. |
| `-d` | `--dir` | `string` | Working directory | Sets directory of execution. |
| `-n` | `--dry` | `bool` | `false` | Compiles and prints tasks in the order that they would be run, without executing them. |
| `-x` | `--exit-code` | `bool` | `false` | Pass-through the exit code of the task command. |
| `-f` | `--force` | `bool` | `false` | Forces execution even when the task is up-to-date. |
| `-h` | `--help` | `bool` | `false` | Shows Task usage. |
| `-i` | `--init` | `bool` | `false` | Creates a new Taskfile.yaml in the current folder. |
| `-l` | `--list` | `bool` | `false` | Lists tasks with description of current Taskfile. |
| `-a` | `--list-all` | `bool` | `false` | Lists tasks with or without a description. |
| `-o` | `--output` | `string` | Default set in the Taskfile or `intervealed` | Sets output style: [`interleaved`/`group`/`prefixed`]. |
| | `--output-group-begin` | `string` | | Message template to print before a task's grouped output. |
| | `--output-group-end ` | `string` | | Message template to print after a task's grouped output. |
| `-p` | `--parallel` | `bool` | `false` | Executes tasks provided on command line in parallel. |
| `-s` | `--silent` | `bool` | `false` | Disables echoing. |
| | `--status` | `bool` | `false` | Exits with non-zero exit code if any of the given tasks is not up-to-date. |
| | `--summary` | `bool` | `false` | Show summary about a task. |
| `-t` | `--taskfile` | `string` | `Taskfile.yml` or `Taskfile.yaml` | |
| `-v` | `--verbose` | `bool` | `false` | Enables verbose mode. |
| | `--version` | `bool` | `false` | Show Task version. |
| `-w` | `--watch` | `bool` | `false` | Enables watch of the given task. |
## Special Variables
There are some special variables that is available on the templating system:
| Var | Description |
| - | - |
| `CLI_ARGS` | Contain all extra arguments passed after `--` when calling Task through the CLI. |
| `TASK` | The name of the current task. |
| `ROOT_DIR` | The absolute path of the root Taskfile. |
| `TASKFILE_DIR` | The absolute path of the included Taskfile. |
| `CHECKSUM` | The checksum of the files listed in `sources`. Only available within the `status` prop and if method is set to `checksum`. |
| `TIMESTAMP` | The date object of the greatest timestamp of the files listes in `sources`. Only available within the `status` prop and if method is set to `timestamp`. |
## ENV
Some environment variables can be overriden to adjust Task behavior.
| ENV | Default | Description |
| - | - | - |
| `TASK_TEMP_DIR` | `.task` | Location of the temp dir. Can relative to the project like `tmp/task` or absolute like `/tmp/.task` or `~/.task`. |
| `TASK_COLOR_RESET` | `0` | Color used for white. |
| `TASK_COLOR_BLUE` | `34` | Color used for blue. |
| `TASK_COLOR_GREEN` | `32` | Color used for green. |
| `TASK_COLOR_CYAN` | `36` | Color used for cyan. |
| `TASK_COLOR_YELLOW` | `33` | Color used for yellow. |
| `TASK_COLOR_MAGENTA` | `35` | Color used for magenta. |
| `TASK_COLOR_RED` | `31` | Color used for red. |
## Schema
### Taskfile
| Attribute | Type | Default | Description |
| - | - | - | - |
| `version` | `string` | | Version of the Taskfile. The current version is `3`. |
| `includes` | [`map[string]Include`](#include) | | Additional Taskfiles to be included. |
| `output` | `string` | `interleaved` | Output mode. Available options: `interleaved`, `group` and `prefixed`. |
| `method` | `string` | `checksum` | Default method in this Taskfile. Can be overriden in a task by task basis. Available options: `checksum`, `timestamp` and `none`. |
| `silent` | `bool` | `false` | Default "silent" options for this Taskfile. If `false`, can be overidden with `true` in a task by task basis. |
| `run` | `string` | `always` | Default "run" option for this Taskfile. Available options: `always`, `once` and `when_changed`. |
| `vars` | [`map[string]Variable`](#variable) | | Global variables. |
| `env` | [`map[string]Variable`](#variable) | | Global environment. |
| `dotenv` | `[]string` | | A list of `.env` file paths to be parsed. |
| `tasks` | [`map[string]Task`](#task) | | The task definitions. |
### Include
| Attribute | Type | Default | Description |
| - | - | - | - |
| `taskfile` | `string` | | The path for the Taskfile or directory to be included. If a directory, Task will look for files named `Taskfile.yml` or `Taskfile.yaml` inside that directory. If a relative path, resolved relative to the directory containing the including Taskfile. |
| `dir` | `string` | The parent Taskfile directory | The working directory of the included tasks when run. |
| `optional` | `bool` | `false` | If `true`, no errors will be thrown if the specified file does not exist. |
| `internal` | `bool` | `false` | If `true`, tasks will be omitted from both `--list` and `--list-all`. |
:::info
Informing only a string like below is equivalent to setting that value to the `taskfile` attribute.
```yaml
includes:
foo: ./path
```
:::
### Task
| Attribute | Type | Default | Description |
| - | - | - | - |
| `desc` | `string` | | A short description of the task. This is listed when calling `task --list`. |
| `summary` | `string` | | A longer description of the task. This is listed when calling `task --summary [task]`. |
| `sources` | `[]string` | | List of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs. |
| `dir` | `string` | | The current directory which this task should run. |
| `method` | `string` | `checksum` | Method used by this task. Default to the one declared globally or `checksum`. Available options: `checksum`, `timestamp` and `none` |
| `silent` | `bool` | `false` | Skips some output for this task. Note that STDOUT and STDERR of the commands will still be redirected. |
| `internal` | `bool` | `false` | If `true`, omit this task from both `--list` and `--list-all`. |
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
| `prefix` | `string` | | Allows to override the prefix print before the STDOUT. Only relevant when using the `prefixed` output mode. |
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the commands. |
| `generates` | `[]string` | | List of files meant to be generated by this task. Relevant for `timestamp` method. Can be file paths or star globs. |
| `status` | `[]string` | | List of commands to check if this task should run. The task is skipped otherwise. This overrides `method`, `sources` and `generates`. |
| `preconditions` | [`[]Precondition`](#precondition) | | List of commands to check if this task should run. The task errors otherwise. |
| `vars` | [`map[string]Variable`](#variable) | | Task variables. |
| `env` | [`map[string]Variable`](#variable) | | Task environment. |
| `deps` | [`[]Dependency`](#dependency) | | List of dependencies of this task. |
| `cmds` | [`[]Command`](#command) | | List of commands to be executed. |
:::info
These alternative syntaxes are available. They will set the given values to
`cmds` and everything else will be set to their default values:
```yaml
tasks:
foo: echo "foo"
foobar:
- echo "foo"
- echo "bar"
baz:
cmd: echo "baz"
```
:::
### Dependency
| Attribute | Type | Default | Description |
| - | - | - | - |
| `task` | `string` | | The task to be execute as a dependency. |
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to this task. |
:::tip
If you don't want to set additional variables, it's enough to declare the
dependency as a list of strings (they will be assigned to `task`):
```yaml
tasks:
foo:
deps: [foo, bar]
```
:::
### Command
| Attribute | Type | Default | Description |
| - | - | - | - |
| `cmd` | `string` | | The shell command to be executed. |
| `defer` | `string` | | Alternative to `cmd`, but schedules the command to be executed at the end of this task instead of immediately. This cannot be used together with `cmd`. |
| `silent` | `bool` | `false` | Skips some output for this command. Note that STDOUT and STDERR of the commands will still be redirected. |
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
| `task` | `string` | | Set this to trigger execution of another task instead of running a command. This cannot be set together with `cmd`. |
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the referenced task. Only relevant when setting `task` instead of `cmd`. |
:::info
If given as a a string, the value will be assigned to `cmd`:
```yaml
tasks:
foo:
cmds:
- echo "foo"
- echo "bar"
```
:::
### Variable
| Attribute | Type | Default | Description |
| - | - | - | - |
| *itself* | `string` | | A static value that will be set to the variable. |
| `sh` | `string` | | A shell command. The output (`STDOUT`) will be assigned to the variable. |
:::info
Static and dynamic variables have different syntaxes, like below:
```yaml
vars:
STATIC: static
DYNAMIC:
sh: echo "dynamic"
```
:::
### Precondition
| Attribute | Type | Default | Description |
| - | - | - | - |
| `sh` | `string` | | Command to be executed. If a non-zero exit code is returned, the task errors without executing its commands. |
| `msg` | `string` | | Optional message to print if the precondition isn't met. |
:::tip
If you don't want to set a different message, you can declare a precondition
like this and the value will be assigned to `sh`:
```yaml
tasks:
foo:
precondition: test -f Taskfile.yml
```
:::

575
docs/docs/changelog.md Normal file
View File

@@ -0,0 +1,575 @@
---
slug: /changelog/
sidebar_position: 6
---
# Changelog
## v3.16.0 - 2022-09-29
- Add `npm` as new installation method: `npm i -g @go-task/cli`
([#870](https://github.com/go-task/task/issues/870), [#871](https://github.com/go-task/task/pull/871), [npm package](https://www.npmjs.com/package/@go-task/cli)).
- Add support to marking tasks and includes as internal, which will hide them
from `--list` and `--list-all`
([#818](https://github.com/go-task/task/pull/818)).
## v3.15.2 - 2022-09-08
- Fix error when using variable in `env:` introduced in the previous release
([#858](https://github.com/go-task/task/issues/858), [#866](https://github.com/go-task/task/pull/866)).
- Fix handling of `CLI_ARGS` (`--`) in Bash completion
([#863](https://github.com/go-task/task/pull/863)).
- On zsh completion, add ability to replace `--list-all` with `--list` as
already possible on the Bash completion
([#861](https://github.com/go-task/task/pull/861)).
## v3.15.0 - 2022-09-03
- Add new special variables `ROOT_DIR` and `TASKFILE_DIR`. This was a highly
requested feature
([#215](https://github.com/go-task/task/issues/215), [#857](https://github.com/go-task/task/pull/857), [Documentation](https://taskfile.dev/api/#special-variables)).
- Follow symlinks on `sources`
([#826](https://github.com/go-task/task/issues/826), [#831](https://github.com/go-task/task/pull/831)).
- Improvements and fixes to Bash completion
([#835](https://github.com/go-task/task/pull/835), [#844](https://github.com/go-task/task/pull/844)).
## v3.14.1 - 2022-08-03
- Always resolve relative include paths relative to the including Taskfile
([#822](https://github.com/go-task/task/issues/822), [#823](https://github.com/go-task/task/pull/823)).
- Fix ZSH and PowerShell completions to consider all tasks instead of just the
public ones (those with descriptions)
([#803](https://github.com/go-task/task/pull/803)).
## v3.14.0 - 2022-07-08
- Add ability to override the `.task` directory location with the
`TASK_TEMP_DIR` environment variable.
- Allow to override Task colors using environment variables:
`TASK_COLOR_RESET`, `TASK_COLOR_BLUE`, `TASK_COLOR_GREEN`,
`TASK_COLOR_CYAN`, `TASK_COLOR_YELLOW`, `TASK_COLOR_MAGENTA`
and `TASK_COLOR_RED`
([#568](https://github.com/go-task/task/pull/568), [#792](https://github.com/go-task/task/pull/792)).
- Fixed bug when using the `output: group` mode where STDOUT and STDERR were
being print in separated blocks instead of in the right order
([#779](https://github.com/go-task/task/issues/779)).
- Starting on this release, ARM architecture binaries are been released to Snap
as well
([#795](https://github.com/go-task/task/issues/795)).
- i386 binaries won't be available anymore on Snap because Ubuntu removed the support
for this architecture.
- Upgrade mvdan.cc/sh, which fixes a bug with associative arrays
([#785](https://github.com/go-task/task/issues/785), [mvdan/sh#884](https://github.com/mvdan/sh/issues/884), [mvdan/sh#893](https://github.com/mvdan/sh/pull/893)).
## v3.13.0 - 2022-06-13
- Added `-n` as an alias to `--dry`
([#776](https://github.com/go-task/task/issues/776), [#777](https://github.com/go-task/task/pull/777)).
- Fix behavior of interrupt (SIGINT, SIGTERM) signals. Task will now give time
for the processes running to do cleanup work
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728), [#769](https://github.com/go-task/task/pull/769)).
- Add new `--exit-code` (`-x`) flag that will pass-through the exit form the
command being ran
([#755](https://github.com/go-task/task/pull/755)).
## v3.12.1 - 2022-05-10
- Fixed bug where, on Windows, variables were ending with `\r` because we were
only removing the final `\n` but not `\r\n`
([#717](https://github.com/go-task/task/issues/717)).
## v3.12.0 - 2022-03-31
- The `--list` and `--list-all` flags can now be combined with the `--silent`
flag to print the task names only, without their description
([#691](https://github.com/go-task/task/pull/691)).
- Added support for multi-level inclusion of Taskfiles. This means that
included Taskfiles can also include other Taskfiles. Before this was limited
to one level
([#390](https://github.com/go-task/task/issues/390), [#623](https://github.com/go-task/task/discussions/623), [#656](https://github.com/go-task/task/pull/656)).
- Add ability to specify vars when including a Taskfile.
[Check out the documentation](https://taskfile.dev/#/usage?id=vars-of-included-taskfiles)
for more information.
([#677](https://github.com/go-task/task/pull/677)).
## v3.11.0 - 2022-02-19
- Task now supports printing begin and end messages when using the `group`
output mode, useful for grouping tasks in CI systems.
[Check out the documentation](http://taskfile.dev/#/usage?id=output-syntax) for more information
([#647](https://github.com/go-task/task/issues/647), [#651](https://github.com/go-task/task/pull/651)).
- Add `Taskfile.dist.yml` and `Taskfile.dist.yaml` to the supported file
name list. [Check out the documentation](https://taskfile.dev/#/usage?id=supported-file-names) for more information
([#498](https://github.com/go-task/task/issues/498), [#666](https://github.com/go-task/task/pull/666)).
## v3.10.0 - 2022-01-04
- A new `--list-all` (alias `-a`) flag is now available. It's similar to the
exiting `--list` (`-l`) but prints all tasks, even those without a
description
([#383](https://github.com/go-task/task/issues/383), [#401](https://github.com/go-task/task/pull/401)).
- It's now possible to schedule cleanup commands to run once a task finishes
with the `defer:` keyword
([Documentation](https://taskfile.dev/#/usage?id=doing-task-cleanup-with-defer), [#475](https://github.com/go-task/task/issues/475), [#626](https://github.com/go-task/task/pull/626)).
- Remove long deprecated and undocumented `$` variable prefix and `^` command
prefix
([#642](https://github.com/go-task/task/issues/642), [#644](https://github.com/go-task/task/issues/644), [#645](https://github.com/go-task/task/pull/645)).
- Add support for `.yaml` extension (as an alternative to `.yml`).
This was requested multiple times throughout the years. Enjoy!
([#183](https://github.com/go-task/task/issues/183), [#184](https://github.com/go-task/task/pull/184), [#369](https://github.com/go-task/task/issues/369), [#584](https://github.com/go-task/task/issues/584), [#621](https://github.com/go-task/task/pull/621)).
- Fixed error when computing a variable when the task directory do not exist
yet
([#481](https://github.com/go-task/task/issues/481), [#579](https://github.com/go-task/task/pull/579)).
## v3.9.2 - 2021-12-02
- Upgrade [mvdan/sh](https://github.com/mvdan/sh) which contains a fix a for
a important regression on Windows
([#619](https://github.com/go-task/task/issues/619), [mvdan/sh#768](https://github.com/mvdan/sh/issues/768), [mvdan/sh#769](https://github.com/mvdan/sh/pull/769)).
## v3.9.1 - 2021-11-28
- Add logging in verbose mode for when a task starts and finishes
([#533](https://github.com/go-task/task/issues/533), [#588](https://github.com/go-task/task/pull/588)).
- Fix an issue with preconditions and context errors
([#597](https://github.com/go-task/task/issues/597), [#598](https://github.com/go-task/task/pull/598)).
- Quote each `{{.CLI_ARGS}}` argument to prevent one with spaces to become many
([#613](https://github.com/go-task/task/pull/613)).
- Fix nil pointer when `cmd:` was left empty
([#612](https://github.com/go-task/task/issues/612), [#614](https://github.com/go-task/task/pull/614)).
- Upgrade [mvdan/sh](https://github.com/mvdan/sh) which contains two
relevant fixes:
- Fix quote of empty strings in `shellQuote`
([#609](https://github.com/go-task/task/issues/609), [mvdan/sh#763](https://github.com/mvdan/sh/issues/763)).
- Fix issue of wrong environment variable being picked when there's another
very similar one
([#586](https://github.com/go-task/task/issues/586), [mvdan/sh#745](https://github.com/mvdan/sh/pull/745)).
- Install shell completions automatically when installing via Homebrew
([#264](https://github.com/go-task/task/issues/264), [#592](https://github.com/go-task/task/pull/592), [go-task/homebrew-tap#2](https://github.com/go-task/homebrew-tap/pull/2)).
## v3.9.0 - 2021-10-02
- A new `shellQuote` function was added to the template system
(`{{shellQuote "a string"}}`) to ensure a string is safe for use in shell
([mvdan/sh#727](https://github.com/mvdan/sh/pull/727), [mvdan/sh#737](https://github.com/mvdan/sh/pull/737), [Documentation](https://pkg.go.dev/mvdan.cc/sh/v3@v3.4.0/syntax#Quote))
- In this version [mvdan.cc/sh](https://github.com/mvdan/sh) was upgraded
with some small fixes and features
- The `read -p` flag is now supported
([#314](https://github.com/go-task/task/issues/314), [mvdan/sh#551](https://github.com/mvdan/sh/issues/551), [mvdan/sh#772](https://github.com/mvdan/sh/pull/722))
- The `pwd -P` and `pwd -L` flags are now supported
([#553](https://github.com/go-task/task/issues/553), [mvdan/sh#724](https://github.com/mvdan/sh/issues/724), [mvdan/sh#728](https://github.com/mvdan/sh/pull/728))
- The `$GID` environment variable is now correctly being set
([#561](https://github.com/go-task/task/issues/561), [mvdan/sh#723](https://github.com/mvdan/sh/pull/723))
## v3.8.0 - 2021-09-26
- Add `interactive: true` setting to improve support for interactive CLI apps
([#217](https://github.com/go-task/task/issues/217), [#563](https://github.com/go-task/task/pull/563)).
- Fix some `nil` errors
([#534](https://github.com/go-task/task/issues/534), [#573](https://github.com/go-task/task/pull/573)).
- Add ability to declare an included Taskfile as optional
([#519](https://github.com/go-task/task/issues/519), [#552](https://github.com/go-task/task/pull/552)).
- Add support for including Taskfiles in the home directory by using `~`
([#539](https://github.com/go-task/task/issues/539), [#557](https://github.com/go-task/task/pull/557)).
## v3.7.3 - 2021-09-04
- Add official support to Apple M1 ([#564](https://github.com/go-task/task/pull/564), [#567](https://github.com/go-task/task/pull/567)).
- Our [official Homebrew tap](https://github.com/go-task/homebrew-tap) will
support more platforms, including Apple M1
## v3.7.0 - 2021-07-31
- Add `run:` setting to control if tasks should run multiple times or not.
Available options are `always` (the default), `when_changed` (if a variable
modified the task) and `once` (run only once no matter what).
This is a long time requested feature. Enjoy!
([#53](https://github.com/go-task/task/issues/53), [#359](https://github.com/go-task/task/pull/359)).
## v3.6.0 - 2021-07-10
- Allow using both `sources:` and `status:` in the same task
([#411](https://github.com/go-task/task/issues/411), [#427](https://github.com/go-task/task/issues/427), [#477](https://github.com/go-task/task/pull/477)).
- Small optimization and bug fix: don't compute variables if not needed for
`dotenv:` ([#517](https://github.com/go-task/task/issues/517)).
## v3.5.0 - 2021-07-04
- Add support for interpolation in `dotenv:`
([#433](https://github.com/go-task/task/discussions/433), [#434](https://github.com/go-task/task/issues/434), [#453](https://github.com/go-task/task/pull/453)).
## v3.4.3 - 2021-05-30
- Add support for the `NO_COLOR` environment variable.
([#459](https://github.com/go-task/task/issues/459), [fatih/color#137](https://github.com/fatih/color/pull/137)).
- Fix bug where sources were not considering the right directory
in `--watch` mode
([#484](https://github.com/go-task/task/issues/484), [#485](https://github.com/go-task/task/pull/485)).
## v3.4.2 - 2021-04-23
- On watch, report which file failed to read
([#472](https://github.com/go-task/task/pull/472)).
- Do not try to catch SIGKILL signal, which are not actually possible
([#476](https://github.com/go-task/task/pull/476)).
- Improve version reporting when building Task from source using Go Modules
([#462](https://github.com/go-task/task/pull/462), [#473](https://github.com/go-task/task/pull/473)).
## v3.4.1 - 2021-04-17
- Improve error reporting when parsing YAML: in some situations where you
would just see an generic error, you'll now see the actual error with
more detail: the YAML line the failed to parse, for example
([#467](https://github.com/go-task/task/issues/467)).
- A JSON Schema was published [here](https://json.schemastore.org/taskfile.json)
and is automatically being used by some editors like Visual Studio Code
([#135](https://github.com/go-task/task/issues/135)).
- Print task name before the command in the log output
([#398](https://github.com/go-task/task/pull/398)).
## v3.3.0 - 2021-03-20
- Add support for delegating CLI arguments to commands with `--` and a
special `CLI_ARGS` variable
([#327](https://github.com/go-task/task/issues/327)).
- Add a `--concurrency` (alias `-C`) flag, to limit the number of tasks that
run concurrently. This is useful for heavy workloads.
([#345](https://github.com/go-task/task/pull/345)).
## v3.2.2 - 2021-01-12
- Improve performance of `--list` and `--summary` by skipping running shell
variables for these flags
([#332](https://github.com/go-task/task/issues/332)).
- Fixed a bug where an environment in a Taskfile was not always overridable
by the system environment
([#425](https://github.com/go-task/task/issues/425)).
- Fixed environment from .env files not being available as variables
([#379](https://github.com/go-task/task/issues/379)).
- The install script is now working for ARM platforms
([#428](https://github.com/go-task/task/pull/428)).
## v3.2.1 - 2021-01-09
- Fixed some bugs and regressions regarding dynamic variables and directories
([#426](https://github.com/go-task/task/issues/426)).
- The [slim-sprig](https://github.com/go-task/slim-sprig) package was updated
with the upstream [sprig](https://github.com/Masterminds/sprig).
## v3.2.0 - 2021-01-07
- Fix the `.task` directory being created in the task directory instead of the
Taskfile directory
([#247](https://github.com/go-task/task/issues/247)).
- Fix a bug where dynamic variables (those declared with `sh:`) were not
running in the task directory when the task has a custom dir or it was
in an included Taskfile
([#384](https://github.com/go-task/task/issues/384)).
- The watch feature (via the `--watch` flag) got a few different bug fixes and
should be more stable now
([#423](https://github.com/go-task/task/pull/423), [#365](https://github.com/go-task/task/issues/365)).
## v3.1.0 - 2021-01-03
- Fix a bug when the checksum up-to-date resolution is used by a task
with a custom `label:` attribute
([#412](https://github.com/go-task/task/issues/412)).
- Starting from this release, we're releasing official ARMv6 and ARM64 binaries
for Linux
([#375](https://github.com/go-task/task/issues/375), [#418](https://github.com/go-task/task/issues/418)).
- Task now respects the order of declaration of included Taskfiles when
evaluating variables declaring by them
([#393](https://github.com/go-task/task/issues/393)).
- `set -e` is now automatically set on every command. This was done to fix an
issue where multiline string commands wouldn't really fail unless the
sentence was in the last line
([#403](https://github.com/go-task/task/issues/403)).
## v3.0.1 - 2020-12-26
- Allow use as a library by moving the required packages out of the `internal`
directory
([#358](https://github.com/go-task/task/pull/358)).
- Do not error if a specified dotenv file does not exist
([#378](https://github.com/go-task/task/issues/378), [#385](https://github.com/go-task/task/pull/385)).
- Fix panic when you have empty tasks in your Taskfile
([#338](https://github.com/go-task/task/issues/338), [#362](https://github.com/go-task/task/pull/362)).
## v3.0.0 - 2020-08-16
- On `v3`, all CLI variables will be considered global variables
([#336](https://github.com/go-task/task/issues/336), [#341](https://github.com/go-task/task/pull/341))
- Add support to `.env` like files
([#324](https://github.com/go-task/task/issues/324), [#356](https://github.com/go-task/task/pull/356)).
- Add `label:` to task so you can override the task name in the logs
([#321](https://github.com/go-task/task/issues/321]), [#337](https://github.com/go-task/task/pull/337)).
- Refactor how variables work on version 3
([#311](https://github.com/go-task/task/pull/311)).
- Disallow `expansions` on v3 since it has no effect.
- `Taskvars.yml` is not automatically included anymore.
- `Taskfile_{{OS}}.yml` is not automatically included anymore.
- Allow interpolation on `includes`, so you can manually include a Taskfile
based on operation system, for example.
- Expose `.TASK` variable in templates with the task name
([#252](https://github.com/go-task/task/issues/252)).
- Implement short task syntax
([#194](https://github.com/go-task/task/issues/194), [#240](https://github.com/go-task/task/pull/240)).
- Added option to make included Taskfile run commands on its own directory
([#260](https://github.com/go-task/task/issues/260), [#144](https://github.com/go-task/task/issues/144))
- 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)).
- 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 - 2020-05-20
- Fix error code for the `--help` flag
([#300](https://github.com/go-task/task/issues/300), [#330](https://github.com/go-task/task/pull/330)).
- Print version to stdout instead of stderr
([#299](https://github.com/go-task/task/issues/299), [#329](https://github.com/go-task/task/pull/329)).
- Supress `context` errors when using the `--watch` flag
([#313](https://github.com/go-task/task/issues/313), [#317](https://github.com/go-task/task/pull/317)).
- Support templating on description
([#276](https://github.com/go-task/task/issues/276), [#283](https://github.com/go-task/task/pull/283)).
## v2.8.0 - 2019-12-07
- Add `--parallel` flag (alias `-p`) to run tasks given by the command line in
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
by prefixing it with `:`
([#161](https://github.com/go-task/task/issues/161), [#172](https://github.com/go-task/task/issues/172)),
- Add flag to override the `output` option
([#173](https://github.com/go-task/task/pull/173));
- Fix bug where Task was persisting the new checksum on the disk when the Dry
Mode is enabled
([#166](https://github.com/go-task/task/issues/166));
- Fix file timestamp issue when the file name has spaces
([#176](https://github.com/go-task/task/issues/176));
- Mitigating path expanding issues on Windows
([#170](https://github.com/go-task/task/pull/170)).
## v2.3.0 - 2019-01-02
- On Windows, Task can now be installed using [Scoop](https://scoop.sh/)
([#152](https://github.com/go-task/task/pull/152));
- Fixed issue with file/directory globing
([#153](https://github.com/go-task/task/issues/153));
- Added ability to globally set environment variables
(
[#138](https://github.com/go-task/task/pull/138),
[#159](https://github.com/go-task/task/pull/159)
).
## v2.2.1 - 2018-12-09
- This repository now uses Go Modules (#143). We'll still keep the `vendor` directory in sync for some time, though;
- Fixing a bug when the Taskfile has no tasks but includes another Taskfile (#150);
- Fix a bug when calling another task or a dependency in an included Taskfile (#151).
## v2.2.0 - 2018-10-25
- Added support for [including other Taskfiles](https://taskfile.org/#/usage?id=including-other-taskfiles) (#98)
- This should be considered experimental. For now, only including local files is supported, but support for including remote Taskfiles is being discussed. If you have any feedback, please comment on #98.
- Task now have a dedicated documentation site: https://taskfile.org
- Thanks to [Docsify](https://docsify.js.org/) for making this pretty easy. To check the source code, just take a look at the [docs](https://github.com/go-task/task/tree/master/docs) directory of this repository. Contributions to the documentation is really appreciated.
## v2.1.1 - 2018-09-17
- Fix suggestion to use `task --init` not being shown anymore (when a `Taskfile.yml` is not found)
- Fix error when using checksum method and no file exists for a source glob (#131)
- Fix signal handling when the `--watch` flag is given (#132)
## v2.1.0 - 2018-08-19
- Add a `ignore_error` option to task and command (#123)
- Add a dry run mode (`--dry` flag) (#126)
## v2.0.3 - 2018-06-24
- Expand environment variables on "dir", "sources" and "generates" (#116)
- Fix YAML merging syntax (#112)
- Add ZSH completion (#111)
- Implement new `output` option. Please check out the [documentation](https://github.com/go-task/task#output-syntax)
## v2.0.2 - 2018-05-01
- Fix merging of YAML anchors (#112)
## v2.0.1 - 2018-03-11
- Fixes panic on `task --list`
## v2.0.0 - 2018-03-08
Version 2.0.0 is here, with a new Taskfile format.
Please, make sure to read the [Taskfile versions](https://github.com/go-task/task/blob/master/TASKFILE_VERSIONS.md) document, since it describes in depth what changed for this version.
* New Taskfile version 2 (https://github.com/go-task/task/issues/77)
* Possibility to have global variables in the `Taskfile.yml` instead of `Taskvars.yml` (https://github.com/go-task/task/issues/66)
* Small improvements and fixes
## v1.4.4 - 2017-11-19
- Handle SIGINT and SIGTERM (#75);
- List: print message with there's no task with description;
- Expand home dir ("~" symbol) on paths (#74);
- Add Snap as an installation method;
- Move examples to its own repo;
- Watch: also walk on tasks called on on "cmds", and not only on "deps";
- Print logs to stderr instead of stdout (#68);
- Remove deprecated `set` keyword;
- Add checksum based status check, alternative to timestamp based.
## v1.4.3 - 2017-09-07
- Allow assigning variables to tasks at run time via CLI (#33)
- Added suport for multiline variables from sh (#64)
- Fixes env: remove square braces and evaluate shell (#62)
- Watch: change watch library and few fixes and improvements
- When use watching, cancel and restart long running process on file change (#59 and #60)
## v1.4.2 - 2017-07-30
- Flag to set directory of execution
- Always echo command if is verbose mode
- Add silent mode to disable echoing of commands
- Fixes and improvements of variables (#56)
## v1.4.1 - 2017-07-15
- Allow use of YAML for dynamic variables instead of $ prefix
- `VAR: {sh: echo Hello}` instead of `VAR: $echo Hello`
- Add `--list` (or `-l`) flag to print existing tasks
- OS specific Taskvars file (e.g. `Taskvars_windows.yml`, `Taskvars_linux.yml`, etc)
- Consider task up-to-date on equal timestamps (#49)
- Allow absolute path in generates section (#48)
- Bugfix: allow templating when calling deps (#42)
- Fix panic for invalid task in cyclic dep detection
- Better error output for dynamic variables in Taskvars.yml (#41)
- Allow template evaluation in parameters
## v1.4.0 - 2017-07-06
- Cache dynamic variables
- Add verbose mode (`-v` flag)
- Support to task parameters (overriding vars) (#31) (#32)
- Print command, also when "set:" is specified (#35)
- Improve task command help text (#35)
## v1.3.1 - 2017-06-14
- Fix glob not working on commands (#28)
- Add ExeExt template function
- Add `--init` flag to create a new Taskfile
- Add status option to prevent task from running (#27)
- Allow interpolation on `generates` and `sources` attributes (#26)
## v1.3.0 - 2017-04-24
- Migrate from os/exec.Cmd to a native Go sh/bash interpreter
- This is a potentially breaking change if you use Windows.
- Now, `cmd` is not used anymore on Windows. Always use Bash-like syntax for your commands, even on Windows.
- Add "ToSlash" and "FromSlash" to template functions
- Use functions defined on github.com/Masterminds/sprig
- Do not redirect stdin while running variables commands
- Using `context` and `errgroup` packages (this will make other tasks to be cancelled, if one returned an error)
## v1.2.0 - 2017-04-02
- More tests and Travis integration
- Watch a task (experimental)
- Possibility to call another task
- Fix "=" not being reconized in variables/environment variables
- Tasks can now have a description, and help will print them (#10)
- Task dependencies now run concurrently
- Support for a default task (#16)
## v1.1.0 - 2017-03-08
- Support for YAML, TOML and JSON (#1)
- Support running command in another directory (#4)
- `--force` or `-f` flag to force execution of task even when it's up-to-date
- Detection of cyclic dependencies (#5)
- Support for variables (#6, #9, #14)
- Operation System specific commands and variables (#13)
## v1.0.0 - 2017-02-28
- Add LICENSE file

View File

@@ -1,3 +1,8 @@
---
slug: /community/
sidebar_position: 6
---
# Community
Some of the work to improve the Task ecosystem is done by the community, be
@@ -25,19 +30,27 @@ and is published [here](https://marketplace.visualstudio.com/items?itemName=paul
### Sublime Text 4 package
There is a convenience wrapper for initializing and running tasks from Sublime Text's command palette. The package is
There is a convenience wrapper for initializing and running tasks from Sublime Text's command palette. The package is
developed by [@biozz](https://github.com/biozz), the source code is available [here](https://github.com/biozz/sublime-taskfile)
and it is published on Package Control [here](https://packagecontrol.io/packages/Taskfile).
### IntelliJ plugin
There's a JetBrains IntelliJ plugin done by
[@lechuckroh](https://github.com/lechuckroh), which has its code [here](https://github.com/lechuckroh/task-intellij-plugin)
and is published [here](https://plugins.jetbrains.com/plugin/17058-taskfile).
## Installation methods
Some installation methods are maintained by third party:
- [GitHub Actions](https://github.com/arduino/setup-task)
by [@arduino](https://github.com/arduino)
- [AUR](https://aur.archlinux.org/packages/taskfile-git)
by [@kovetskiy](https://github.com/kovetskiy)
- [AUR](https://aur.archlinux.org/packages/go-task-bin)
by [@carlsmedstad](https://github.com/carlsmedstad)
- [Scoop](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json)
- [Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/)
- [NixOS](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/tools/go-task/default.nix)
## More

View File

@@ -1,3 +1,8 @@
---
slug: /donate/
sidebar_position: 9
---
# Donate
If you find this project useful, you can consider donating by using one of the
@@ -17,6 +22,10 @@ these options to donate:
- [$50 per month](https://opencollective.com/task/contribute/sponsor-28775/checkout)
- [Custom value - One-time donation option supported](https://opencollective.com/task/donate)
## GitHub Sponsors
- [@andreynering](https://github.com/sponsors/andreynering)
## PayPal
- [Any value - One-time donation](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A&currency_code=USD&source=url)
@@ -24,4 +33,4 @@ these options to donate:
## PIX (Brazil only)
If you're Brazilian, you can donate any value by
<a href="/pix.png" target="_blank">using this QR Code</a>.
[using this QR Code](/img/pix.png).

200
docs/docs/installation.md Normal file
View File

@@ -0,0 +1,200 @@
---
slug: /installation/
sidebar_position: 2
---
# Installation
Task offers many installation methods. Check out the available methods below.
## Package Managers
### Homebrew
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
```
### Snap
Task is available in [Snapcraft][snapcraft], but keep in mind that your
Linux distribution should allow classic confinement for Snaps to Task work
right:
```bash
sudo snap install task --classic
```
### Chocolatey
If you're on Windows and have [Chocolatey][choco] installed, getting
Task is as simple as running:
```bash
choco install go-task
```
This installation method is community owned.
### Scoop
If you're on Windows and have [Scoop][scoop] installed, getting
Task is as simple as running:
```cmd
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.
### AUR
If you're on Arch Linux you can install Task from
[AUR](https://aur.archlinux.org/packages/go-task-bin) using your favorite
package manager such as `yay`, `pacaur` or `yaourt`:
```cmd
yay -S go-task-bin
```
Alternatively, there's
[this package](https://aur.archlinux.org/packages/go-task) which installs from
the source code instead of downloading the binary from the
[releases page](https://github.com/go-task/task/releases):
```cmd
yay -S go-task
```
This installation method is community owned.
### Fedora
If you're on Fedora Linux you can install Task from the official
[Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/) repository using `dnf`:
```cmd
sudo dnf install go-task
```
This installation method is community owned. After a new release of Task, it
may take some time until it's available in [Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/).
### Nix
If you're on NixOS or have Nix installed
you can install Task from [nixpkgs](https://github.com/NixOS/nixpkgs):
```cmd
nix-env -iA nixpkgs.go-task
```
This installation method is community owned. After a new release of Task, it
may take some time until it's available in [nixpkgs](https://github.com/NixOS/nixpkgs).
### npm
You can also use Node and npm to install Task by installing
[this package](https://www.npmjs.com/package/@go-task/cli).
```bash
npm install -g @go-task/cli
```
## Get The Binary
### Binary
You can download the binary from the [releases page on GitHub][releases] and
add to your `$PATH`.
DEB and RPM packages are also available.
The `task_checksums.txt` file contains the SHA-256 checksum for each file.
### Install Script
We also have an [install script][installscript] which is very useful in
scenarios like CI. Many thanks to [GoDownloader][godownloader] for enabling the
easy generation of this script.
By default, it installs on the `./bin` directory relative to the working
directory:
```bash
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
```
It is possible to override the installation directory with the `-b` parameter.
On Linux, common choices are `~/.local/bin` and `~/bin` to install for the
current user or `/usr/local/bin` to install for all users:
```bash
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
```
:::caution
On macOS and Windows, `~/.local/bin` and `~/bin` are not added to `$PATH` by
default.
:::
### GitHub Actions
If you want to install Task in GitHub Actions you can try using
[this action](https://github.com/arduino/setup-task)
by the Arduino team:
```yaml
- name: Install Task
uses: arduino/setup-task@v1
```
This installation method is community owned.
## Build From Source
### Go Modules
First, make sure you have [Go][go] properly installed and setup.
You can easily install the latest release globally by running:
```bash
go install github.com/go-task/task/v3/cmd/task@latest
```
Or you can install into another directory:
```bash
env GOBIN=/bin go install github.com/go-task/task/v3/cmd/task@latest
```
If using Go 1.15 or earlier, instead use:
```bash
env GO111MODULE=on go get -u github.com/go-task/task/v3/cmd/task@latest
```
:::tip
For CI environments we recommend using the [install script](#get-the-binary)
instead, which is faster and more stable, since it'll just download the latest
released binary.
:::
[go]: https://golang.org/
[snapcraft]: https://snapcraft.io/task
[homebrew]: https://brew.sh/
[installscript]: https://github.com/go-task/task/blob/master/install-task.sh
[releases]: https://github.com/go-task/task/releases
[godownloader]: https://github.com/goreleaser/godownloader
[choco]: https://chocolatey.org/
[scoop]: https://scoop.sh/

View File

@@ -1,7 +1,13 @@
---
slug: /
sidebar_position: 1
title: Home
---
# Task
<div align="center">
<img id="logo" src="/Logo.png" height="250px" width="250px" />
<img id="logo" src="img/logo.svg" height="250px" width="250px" />
</div>
Task is a task runner / build tool that aims to be simpler and easier to use
@@ -14,7 +20,7 @@ setups just to use a build tool.
Once [installed](installation.md), you just need to describe your build tasks
using a simple [YAML][yaml] schema in a file called `Taskfile.yml`:
```yaml
```yaml title="Taskfile.yml"
version: '3'
tasks:
@@ -24,28 +30,28 @@ tasks:
silent: true
```
And call it by running `task hello` from you terminal.
And call it by running `task hello` from your terminal.
The above example is just the start, you can take a look at the [usage](usage.md)
The above example is just the start, you can take a look at the [usage](/usage)
guide to check the full schema documentation and Task features.
## Features
- [Easy installation](installation.md): just download a single binary, add to
$PATH and you're done! Or you can also install using [Homebrew][homebrew],
[Snapcraft][snapcraft], or [Scoop][scoop] if you want;
`$PATH` and you're done! Or you can also install using [Homebrew][homebrew],
[Snapcraft][snapcraft], or [Scoop][scoop] if you want.
- Available on CIs: by adding [this simple command](installation.md#install-script)
to install on your CI script and you're done to use Task as part of your CI pipeline;
to install on your CI script and you're ready to use Task as part of your CI pipeline;
- Truly cross-platform: while most build tools only work well on Linux or macOS,
Task also supports Windows thanks to [this awesome shell interpreter for Go][sh];
- Great for code generation: you can easily [prevent a task from running](usage.md#prevent-unnecessary-work)
Task also supports Windows thanks to [this shell interpreter for Go][sh].
- Great for code generation: you can easily [prevent a task from running](/usage#prevent-unnecessary-work)
if a given set of files haven't changed since last run (based either on its
timestamp or content).
[make]: https://www.gnu.org/software/make/
[go]: https://golang.org/
[go]: https://go.dev/
[yaml]: http://yaml.org/
[homebrew]: https://brew.sh/
[snapcraft]: https://snapcraft.io/
[scoop]: https://scoop.sh/
[sh]: https://mvdan.cc/sh
[sh]: https://github.com/mvdan/sh

60
docs/docs/releasing.md Normal file
View File

@@ -0,0 +1,60 @@
---
slug: /releasing/
sidebar_position: 7
---
# Releasing
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.
[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).
Since v3.15.0, raw executables can also be reproduced and verified locally by
checking out a specific tag and calling `goreleaser build`, using the Go version
defined in the above GitHub Actions.
# Homebrew
Goreleaser will automatically push a new commit to the
[Formula/go-task.rb][gotaskrb] file in the [Homebrew tap][homebrewtap]
repository to release the new version.
# npm
To release to npm update the version in the [`package.json`][packagejson] file
and then run `task npm:publish` to push it.
# Snapcraft
The [snap package][snappackage] requires to manual steps to release a new
version:
* Updating the current version on [snapcraft.yaml][snapcraftyaml].
* Moving both `amd64`, `armhf` and `arm64` new artifacts to the stable channel on
the [Snapcraft dashboard][snapcraftdashboard].
# Scoop
Scoop is a command-line package manager for the Windows operating system.
Scoop package manifests are maintained by the community.
Scoop owners usually take care of updating versions there by editing [this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json).
If you think its Task version is outdated, open an issue to let us know.
# Nix
Nix is a community owned installation method. Nix package maintainers usually take care
of updating versions there by editing
[this file](https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/development/tools/go-task/default.nix).
If you think its Task version is outdated, open an issue to let us know.
[goreleaser]: https://goreleaser.com/
[homebrewtap]: https://github.com/go-task/homebrew-tap
[gotaskrb]: https://github.com/go-task/homebrew-tap/blob/master/Formula/go-task.rb
[packagejson]: https://github.com/go-task/task/blob/master/package.json#L3
[snappackage]: https://github.com/go-task/snap
[snapcraftyaml]: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml#L2
[snapcraftdashboard]: https://snapcraft.io/task/releases

View File

@@ -1,11 +1,16 @@
---
slug: /styleguide/
sidebar_position: 5
---
# 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
This contains general guidelines, but they don't necessarily need to be strictly
followed. Feel free to disagree and proceed differently at some point if you
need or want to. Also, feel free to open issues or pull requests with
improvements to this guide.
@@ -28,9 +33,9 @@ officially supported. On Linux, only `Taskfile.yml` will work, though.
- `version:`
- `includes:`
- Configuration ones, like `output:` or `silent:`
- Configuration ones, like `output:`, `silent:`, `method:` and `run:`
- `vars:`
- `env:`
- `env:`, `dotenv:`
- `tasks:`
## Use 2 spaces for indentation

View File

@@ -1,3 +1,8 @@
---
slug: /taskfile-versions/
sidebar_position: 8
---
# Taskfile Versions
The Taskfile syntax and features changed with time. This document explains what

View File

@@ -1,3 +1,8 @@
---
slug: /usage/
sidebar_position: 3
---
# Usage
## Getting started
@@ -26,13 +31,27 @@ Running the tasks is as simple as running:
task assets build
```
Task uses [github.com/mvdan/sh](https://github.com/mvdan/sh), a native Go sh
interpreter. So you can write sh/bash commands and it will work even on
Task uses [mvdan.cc/sh](https://mvdan.cc/sh/), a native Go sh
interpreter. So you can write sh/bash commands, and it will work even on
Windows, where `sh` or `bash` are usually not available. Just remember any
executable called must be available by the OS or in PATH.
If you omit a task name, "default" will be assumed.
## Supported file names
Task will look for the following file names, in order of priority:
- Taskfile.yml
- Taskfile.yaml
- Taskfile.dist.yml
- Taskfile.dist.yaml
The intention of having the `.dist` variants is to allow projects to have one
committed version (`.dist`) while still allowing individual users to override
the Taskfile by adding an additional `Taskfile.yml` (which would be on
`.gitignore`).
## Environment variables
### Task
@@ -50,7 +69,7 @@ tasks:
GREETING: Hey, there!
```
Additionally, you can set globally environment variables, that'll be available
Additionally, you can set global environment variables that will be available
to all tasks:
```yaml
@@ -65,27 +84,27 @@ tasks:
- echo $GREETING
```
> NOTE: `env` supports expansion and retrieving output from a shell command
> just like variables, as you can see on the [Variables](#variables) section.
:::info
`env` supports expansion and retrieving output from a shell command
just like variables, as you can see in the [Variables](#variables) section.
:::
### .env files
You can also ask Task to include `.env` like files by using the `dotenv:`
setting:
```
# .env
```bash title=".env"
KEYNAME=VALUE
```
```
# testing/.env
```bash title="testing/.env"
ENDPOINT=testing.com
```
```yaml
# Taskfile.yml
```yaml title="Taskfile.yml"
version: '3'
env:
@@ -117,6 +136,8 @@ 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.
Relative paths are resolved relative to the directory containing the including Taskfile.
### OS-specific Taskfiles
With `version: '2'`, task automatically includes any `Taskfile_{{OS}}.yml`
@@ -134,7 +155,7 @@ includes:
### Directory of included Taskfile
By default, included Taskfile's tasks are ran in the current directory, even
By default, included Taskfile's tasks are run 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:
@@ -147,12 +168,12 @@ includes:
dir: ./docs
```
> The included Taskfiles must be using the same schema version the main
> Taskfile uses.
:::info
> Also, for now included Taskfiles can't include other Taskfiles.
> This was a deliberate decision to keep use and implementation simple.
> If you disagree, open an GitHub issue and explain your use case. =)
The included Taskfiles must be using the same schema version as the main
Taskfile uses.
:::
### Optional includes
@@ -173,10 +194,78 @@ tasks:
- echo "This command can still be successfully executed if ./tests/Taskfile.yml does not exist"
```
### Internal includes
Includes marked as internal will set all the tasks of the included file to be
internal as well (see the [Internal tasks](#internal-tasks) section below).
This is useful when including utility tasks that are not intended to be used
directly by the user.
```yaml
version: '3'
includes:
tests:
taskfile: ./taskfiles/Utils.yml
internal: true
```
### Vars of included Taskfiles
You can also specify variables when including a Taskfile. This may be useful
for having reusable Taskfile that can be tweaked or even included more than once:
```yaml
version: '3'
includes:
backend:
taskfile: ./taskfiles/Docker.yml
vars:
DOCKER_IMAGE: backend_image
frontend:
taskfile: ./taskfiles/Docker.yml
vars:
DOCKER_IMAGE: frontend_image
```
:::info
Vars declared in the included Taskfile have preference over the
variables in the including Taskfile! If you want a variable in an included Taskfile to be overridable,
use the [default function](https://go-task.github.io/slim-sprig/defaults.html):
`MY_VAR: '{{.MY_VAR | default "my-default-value"}}'`.
:::
## Internal tasks
Internal tasks are tasks that cannot be called directly by the user. They will
not appear in the output when running `task --list|--list-all`. Other tasks may
call internal tasks in the usual way. This is useful for creating reusable,
function-like tasks that have no useful purpose on the command line.
```yaml
version: '3'
tasks:
build-image-1:
cmds:
- task: build-image
vars:
DOCKER_IMAGE: image-1
build-image:
internal: true
cmds:
- docker build -t {{.DOCKER_IMAGE}} .
```
## Task directory
By default, tasks will be executed in the directory where the Taskfile is
located. But you can easily make the task run in another folder informing
located. But you can easily make the task run in another folder, informing
`dir`:
```yaml
@@ -190,12 +279,12 @@ tasks:
- caddy
```
If the directory doesn't exist, `task` creates it.
If the directory does not exist, `task` creates it.
## Task dependencies
> Dependencies run in parallel, so dependencies of a task shouldn't depend one
> another. If you want to force tasks to run serially take a look at the
> Dependencies run in parallel, so dependencies of a task should not depend one
> another. If you want to force tasks to run serially, take a look at the
> [Calling Another Task](#calling-another-task) section below.
You may have tasks that depend on others. Just pointing them on `deps` will
@@ -239,8 +328,12 @@ 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`.
:::tip
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):
@@ -266,8 +359,8 @@ tasks:
## Calling another task
When a task has many dependencies, they are executed concurrently. This will
often result in a faster build pipeline. But in some situations you may need
to call other tasks serially. In this case, just use the following syntax:
often result in a faster build pipeline. However, in some situations, you may need
to call other tasks serially. In this case, use the following syntax:
```yaml
version: '3'
@@ -309,16 +402,20 @@ tasks:
The above syntax is also supported in `deps`.
> NOTE: If you want to call a task declared in the root Taskfile from within an
> [included Taskfile](#including-other-taskfiles), add a leading `:` like this:
> `task: :task-name`.
:::tip
NOTE: If you want to call a task declared in the root Taskfile from within an
[included Taskfile](#including-other-taskfiles), add a leading `:` like this:
`task: :task-name`.
:::
## 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.
files, so Task will prevent running them if not necessary.
```yaml
version: '3'
@@ -350,8 +447,6 @@ tasks:
Task will compare the checksum of the source files to determine if it's
necessary to run the task. If not, it will just print a message like
`Task "js" is up to date`.
You will probably want to ignore the `.task` folder in your `.gitignore` file
(It's there that Task stores the last checksum).
If you prefer this check to be made by the modification timestamp of the files,
instead of its checksum (content), just set the `method` property to `timestamp`.
@@ -367,14 +462,56 @@ tasks:
- ./*.go
generates:
- app{{exeExt}}
method: checksum
method: timestamp
```
> TIP: method `none` skips any validation and always run the task.
:::info
> NOTE: for the `checksum` (default) method to work, it's only necessary to
> inform the source files, but if you want to use the `timestamp` method, you
> also need to inform the generated files with `generates`.
By default, task stores checksums on a local `.task` directory in the project's
directory. Most of the time, you'll want to have this directory on `.gitignore`
(or equivalent) so it isn't committed. (If you have a task for code generation
that is committed it may make sense to commit the checksum of that task as
well, though).
If you want these files to be stored in another directory, you can set a
`TASK_TEMP_DIR` environment variable in your machine. It can contain a relative
path like `tmp/task` that will be interpreted as relative to the project
directory, or an absolute or home path like `/tmp/.task` or `~/.task`
(subdirectories will be created for each project).
```bash
export TASK_TEMP_DIR='~/.task'
```
:::
:::info
Each task has only one checksum stored for its `sources`. If you want
to distinguish a task by any of its input variables, you can add those
variables as part of the task's label, and it will be considered a different
task.
This is useful if you want to run a task once for each distinct set of
inputs until the sources actually change. For example, if the sources depend
on the value of a variable, or you if you want the task to rerun if some arguments
change even if the source has not.
:::
:::tip
The method `none` skips any validation and always run the task.
:::
:::info
For the `checksum` (default) method to work, it is only necessary to
inform the source files, but if you want to use the `timestamp` method, you
also need to inform the generated files with `generates`.
:::
### Using programmatic checks to indicate a task is up to date.
@@ -418,13 +555,13 @@ 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
### Using programmatic checks to cancel the execution of a task and its dependencies
In addition to `status` checks, there are also `preconditions` checks, which are
In addition to `status` checks, `preconditions` checks 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.
`preconditions` are similar to `status` lines, except they support `sh`
expansion, and they SHOULD all return 0.
```yaml
version: '3'
@@ -450,7 +587,7 @@ 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
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.
@@ -516,11 +653,13 @@ tasks:
## 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):
They are listed below in order of importance (i.e. most important first):
- Variables declared in the task definition
- Variables given while calling a task from another
(See [Calling another task](#calling-another-task) above)
- Variables of the [included Taskfile](#including-other-taskfiles) (when the task is included)
- Variables of the [inclusion of the Taskfile](#vars-of-included-taskfiles) (when the task is included)
- Global variables (those declared in the `vars:` option in the Taskfile)
- Environment variables
@@ -530,10 +669,14 @@ Example of sending parameters with environment variables:
$ TASK_VARIABLE=a-value task do-something
```
> TIP: A special variable `.TASK` is always available containing the task name.
:::tip
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
A special variable `.TASK` is always available containing the task name.
:::
Since some shells do not support the above syntax to set environment variables
(Windows) tasks also accept a similar style when not at the beginning of
the command.
```bash
@@ -570,7 +713,7 @@ tasks:
### Dynamic variables
The below syntax (`sh:` prop in a variable) is considered a dynamic variable.
The value will be treated as a command and the output assigned. If there is one
The value will be treated as a command and the output assigned. If there are one
or more trailing newlines, the last newline will be trimmed.
```yaml
@@ -614,7 +757,7 @@ With the `defer` keyword, it's possible to schedule cleanup to be run once
the task finishes. The difference with just putting it as the last command is
that this command will run even when the task fails.
In the example below `rm -rf tmpdir/` will run even if the third command fails:
In the example below, `rm -rf tmpdir/` will run even if the third command fails:
```yaml
version: '3'
@@ -627,7 +770,7 @@ tasks:
- echo 'Do work on tmpdir/'
```
If you want to move the cleanup command into another task, that's possible as
If you want to move the cleanup command into another task, that is possible as
well:
```yaml
@@ -643,10 +786,14 @@ tasks:
cleanup: rm -rf tmpdir/
```
> NOTE: Due to the nature of how the
:::info
Due to the nature of how the
[Go's own `defer` work](https://go.dev/tour/flowcontrol/13), the deferred
commands are executed in the reverse order if you schedule multiple of them.
:::
## Go's template engine
Task parse commands as [Go's template engine][gotemplate] before executing
@@ -666,7 +813,7 @@ tasks:
Task also adds the following functions:
- `OS`: Returns operating system. Possible values are "windows", "linux",
- `OS`: Returns the operating system. Possible values are "windows", "linux",
"darwin" (macOS) and "freebsd".
- `ARCH`: return the architecture Task was compiled to: "386", "amd64", "arm"
or "s390x".
@@ -791,8 +938,8 @@ Please note: *showing the summary will not execute the command*.
## Overriding task name
Sometimes you may want to override the task name print on summary, up-to-date
messages to STDOUT, etc. In this case you can just set `label:`, which can also
Sometimes you may want to override the task name printed on the summary, up-to-date
messages to STDOUT, etc. In this case, you can just set `label:`, which can also
be interpolated with variables:
```yaml
@@ -815,7 +962,7 @@ tasks:
## Silent mode
Silent mode disables echoing of commands before Task runs it.
Silent mode disables the echoing of commands before Task runs it.
For the following Taskfile:
```yaml
@@ -827,14 +974,14 @@ tasks:
- echo "Print something"
```
Normally this will be print:
Normally this will be printed:
```sh
echo "Print something"
Print something
```
With silent mode on, the below will be print instead:
With silent mode on, the below will be printed instead:
```sh
Print something
@@ -913,7 +1060,7 @@ tasks:
```
Task will abort the execution after running `exit 1` because the status code `1` stands for `EXIT_FAILURE`.
However it is possible to continue with execution using `ignore_error`:
However, it is possible to continue with execution using `ignore_error`:
```yaml
version: '3'
@@ -926,16 +1073,16 @@ tasks:
- echo "Hello World"
```
`ignore_error` can also be set for a task, which mean errors will be suppressed
for all commands. But keep in mind this option won't propagate to other tasks
`ignore_error` can also be set for a task, which means errors will be suppressed
for all commands. Nevertheless, keep in mind that this option will not propagate to other tasks
called either by `deps` or `cmds`!
## Output syntax
By default, Task just redirect the STDOUT and STDERR of the running commands
to the shell in real time. This is good for having live feedback for log
By default, Task just redirects the STDOUT and STDERR of the running commands
to the shell in real-time. This is good for having live feedback for logging
printed by commands, but the output can become messy if you have multiple
commands running at the same time and printing lots of stuff.
commands running simultaneously and printing lots of stuff.
To make this more customizable, there are currently three different output
options you can choose:
@@ -955,13 +1102,41 @@ tasks:
# ...
```
The `group` output will print the entire output of a command once, after it
finishes, so you won't have live feedback for commands that take a long time
to run.
The `group` output will print the entire output of a command once after it
finishes, so you will not have live feedback for commands that take a long time
to run.
The `prefix` output will prefix every line printed by a command with
`[task-name] ` as the prefix, but you can customize the prefix for a command
with the `prefix:` attribute:
When using the `group` output, you can optionally provide a templated message
to print at the start and end of the group. This can be useful for instructing
CI systems to group all of the output for a given task, such as with
[GitHub Actions' `::group::` command](https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#grouping-log-lines)
or [Azure Pipelines](https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/logging-commands?expand=1&view=azure-devops&tabs=bash#formatting-commands).
```yaml
version: '3'
output:
group:
begin: '::group::{{.TASK}}'
end: '::endgroup::'
tasks:
default:
cmds:
- echo 'Hello, World!'
silent: true
```
```bash
$ task default
::group::default
Hello, World!
::endgroup::
```
The `prefix` output will prefix every line printed by a command with
`[task-name] ` as the prefix, but you can customize the prefix for a command
with the `prefix:` attribute:
```yaml
version: '3'
@@ -992,28 +1167,33 @@ $ task default
[print-baz] baz
```
> The `output` option can also be specified by the `--output` or `-o` flags.
:::tip
The `output` option can also be specified by the `--output` or `-o` flags.
:::
## Interactive CLI application
When running interactive CLI applications inside Task they can sometimes behave
weirdly, specially when the [output mode](#output-syntax) is set to something
other than `interleaved` (the default), or when interactive apps are ran in
weirdly, especially when the [output mode](#output-syntax) is set to something
other than `interleaved` (the default), or when interactive apps are run in
parallel with other tasks.
The `interactive: true` tells Task this is an interactive application, and Task
The `interactive: true` tells Task this is an interactive application and Task
will try to optimize for it:
```yaml
version: '3'
tasks:
cmds:
- vim my-file.txt
interactive: true
default:
cmds:
- vim my-file.txt
interactive: true
```
If you still have problem running an interactive app through Task, please open
If you still have problems running an interactive app through Task, please open
an issue about it.
## Short task syntax

179
docs/docusaurus.config.js Normal file
View File

@@ -0,0 +1,179 @@
// @ts-check
// Note: type annotations allow type checking and IDEs autocompletion
const lightCodeTheme = require('./src/themes/prismLight');
const darkCodeTheme = require('./src/themes/prismDark');
const GITHUB_URL = 'https://github.com/go-task/task';
const TWITTER_URL = 'https://twitter.com/taskfiledev';
const DISCORD_URL = 'https://discord.gg/6TY36E39UK';
/** @type {import('@docusaurus/types').Config} */
const config = {
title: 'Task',
tagline: 'A task runner / simpler Make alternative written in Go ',
url: 'https://taskfile.dev',
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'throw',
favicon: 'img/favicon.ico',
organizationName: 'go-task',
projectName: 'task',
deploymentBranch: 'gh-pages',
i18n: {
defaultLocale: 'en',
locales: ['en']
},
presets: [
[
'classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
routeBasePath: '/',
sidebarPath: require.resolve('./sidebars.js')
},
blog: false,
theme: {
customCss: [
require.resolve('./src/css/custom.css'),
require.resolve('./src/css/carbon.css')
]
},
gtag: {
trackingID: 'G-4RT25NXQ7N',
anonymizeIP: true
},
sitemap: {
changefreq: 'weekly',
priority: 0.5,
ignorePatterns: ['/tags/**']
}
})
]
],
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
metadata: [
{
name: 'og:image',
content: 'https://taskfile.dev/img/og-image.png'
}
],
navbar: {
title: 'Task',
logo: {
alt: 'Task Logo',
src: 'img/logo.svg'
},
items: [
{
type: 'doc',
docId: 'installation',
position: 'left',
label: 'Installation'
},
{
type: 'doc',
docId: 'usage',
position: 'left',
label: 'Usage'
},
{
type: 'doc',
docId: 'api_reference',
position: 'left',
label: 'API'
},
{
type: 'doc',
docId: 'donate',
position: 'left',
label: 'Donate'
},
{
href: GITHUB_URL,
label: 'GitHub',
position: 'right'
},
{
href: TWITTER_URL,
label: 'Twitter',
position: 'right'
},
{
href: DISCORD_URL,
label: 'Discord',
position: 'right'
}
]
},
footer: {
style: 'dark',
links: [
{
title: 'Pages',
items: [
{
label: 'Installation',
to: '/installation/'
},
{
label: 'Usage',
to: '/usage/'
},
{
label: 'Donate',
to: '/donate/'
}
]
},
{
title: 'Community',
items: [
{
label: 'GitHub',
href: GITHUB_URL
},
{
label: 'Twitter',
href: TWITTER_URL
},
{
label: 'Discord',
href: DISCORD_URL
},
{
label: 'OpenCollective',
href: 'https://opencollective.com/task'
}
]
}
]
},
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme
},
// NOTE(@andreynering): Don't worry, these keys are meant to be public =)
algolia: {
appId: '7IZIJ13AI7',
apiKey: '34b64ae4fc8d9da43d9a13d9710aaddc',
indexName: 'taskfile'
}
}),
scripts: [
{
src: '/js/carbon.js',
async: true
}
]
};
module.exports = config;

View File

@@ -1,50 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Task</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="A task runner / simpler Make alternative written in Go">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple.css">
<meta name="google-site-verification" content="VGAYkbdmuaciIDGkBe-eAg9yfZg0C6ostgonbGxxOa0" />
<style>
#logo {
transition: all 0.7s ease;
}
#logo:hover {
-webkit-transform: rotateZ(360deg);
-ms-transform: rotateZ(360deg);
transform: rotateZ(360deg);
}
.app-name-link img {
width: 125px;
}
:root {
--base-font-size: 14px;
--theme-color: #29beb0;
}
</style>
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: 'Task',
repo: 'go-task/task',
logo: 'Logo.png',
themeColor: '#29beb0',
loadSidebar: true,
auto2top: true,
maxLevel: 3,
subMaxLevel: 3
}
</script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify-themeable/dist/js/docsify-themeable.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify-tabs"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-bash.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-yaml.min.js"></script>
</body>
</html>

View File

@@ -1,140 +0,0 @@
# Installation
Task offers many installation methods. Check out the available methods below.
## Package Managers
<!-- tabs:start -->
#### **Homebrew**
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
```
#### **Snap**
Task is available in [Snapcraft][snapcraft], but keep in mind that your
Linux distribution should allow classic confinement for Snaps to Task work
right:
```bash
sudo snap install task --classic
```
#### **Scoop**
If you're on Windows and have [Scoop][scoop] installed, use `extras` bucket
to install Task like:
```cmd
scoop bucket add extras
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.
#### **AUR**
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.
<!-- tabs:end -->
## Get The Binary
<!-- tabs:start -->
#### **Binary**
You can download the binary from the [releases page on GitHub][releases] and
add to your `$PATH`.
DEB and RPM packages are also available.
The `task_checksums.txt` file contains the SHA-256 checksum for each file.
#### **Install Script**
We also have an [install script][installscript] which is very useful in
scenarios like CI. Many thanks to [GoDownloader][godownloader] for enabling the
easy generation of this script.
```bash
# For Default Installation to ./bin with debug logging
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
# For Installation To /usr/local/bin for userwide access with debug logging
# May require sudo sh
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
```
> 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/setup-task)
by the Arduino team:
```yaml
- name: Install Task
uses: arduino/setup-task@v1
```
This installation method is community owned.
<!-- tabs:end -->
## Build From Source
<!-- tabs:start -->
#### **Go Modules**
First, make sure you have [Go][go] properly installed and setup.
You can easily install the latest release globally by running:
```bash
go install github.com/go-task/task/v3/cmd/task@latest
```
Or you can install into another directory:
```bash
env GOBIN=/bin go install github.com/go-task/task/v3/cmd/task@latest
```
If using Go 1.15 or earlier, instead use:
```bash
env GO111MODULE=on go get -u github.com/go-task/task/v3/cmd/task@latest
```
> For CI environments we recommend using the [Install Script](#get-the-binary)
> instead, which is faster and more stable, since it'll just download the latest
> released binary.
<!-- tabs:end -->
[go]: https://golang.org/
[snapcraft]: https://snapcraft.io/task
[homebrew]: https://brew.sh/
[installscript]: https://github.com/go-task/task/blob/master/install-task.sh
[releases]: https://github.com/go-task/task/releases
[godownloader]: https://github.com/goreleaser/godownloader
[scoop]: https://scoop.sh/

41
docs/package.json Normal file
View File

@@ -0,0 +1,41 @@
{
"name": "taskfile-dev",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "2.0.0-beta.20",
"@docusaurus/preset-classic": "2.0.0-beta.20",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.1.1",
"prism-react-renderer": "^1.3.1",
"raw-loader": "^4.0.2",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.0.0-beta.20"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

4
docs/prettier.config.js Normal file
View File

@@ -0,0 +1,4 @@
module.exports = {
trailingComma: 'none',
singleQuote: true
};

View File

@@ -1,39 +0,0 @@
# Releasing Task
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.
[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
To release a new version on the [Homebrew tap][homebrewtap] edit the
[Formula/go-task.rb][gotaskrb] file, updating with the new version, download
URL and sha256.
# Snapcraft
The exception is the publishing of a new version of the
[snap package][snappackage]. This current require two steps after publishing
the binaries:
* Updating the current version on [snapcraft.yaml][snapcraftyaml];
* Moving both `i386` and `amd64` new artifacts to the stable channel on
the [Snapcraft dashboard][snapcraftdashboard]
# Scoop
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/bucket/task.json).
If you think its Task version is outdated, open an issue to let us know.
[goreleaser]: https://goreleaser.com/#continuous_integration
[homebrewtap]: https://github.com/go-task/homebrew-tap
[gotaskrb]: https://github.com/go-task/homebrew-tap/blob/master/Formula/go-task.rb
[snappackage]: https://github.com/go-task/snap
[snapcraftyaml]: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml#L2
[snapcraftdashboard]: https://snapcraft.io/task/releases

14
docs/sidebars.js Normal file
View File

@@ -0,0 +1,14 @@
// @ts-check
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
tutorialSidebar: [
{ type: 'autogenerated', dirName: '.' },
{
type: 'html',
value: '<div id="sidebar-ads"></div>'
}
]
};
module.exports = sidebars;

View File

65
docs/src/css/carbon.css Normal file
View File

@@ -0,0 +1,65 @@
#carbonads * {
margin: initial;
padding: initial;
}
#carbonads {
display: flex;
max-width: 330px;
background-color: hsl(0, 0%, 98%);
box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, 0.1);
z-index: 100;
}
#carbonads a {
color: inherit;
text-decoration: none;
}
#carbonads a:hover {
color: inherit;
}
#carbonads span {
position: relative;
display: block;
overflow: hidden;
}
#carbonads .carbon-wrap {
display: flex;
}
#carbonads .carbon-img {
display: block;
margin: 0;
line-height: 1;
}
#carbonads .carbon-img img {
display: block;
}
#carbonads .carbon-text {
font-size: 13px;
padding: 10px;
margin-bottom: 16px;
line-height: 1.5;
text-align: left;
}
#carbonads .carbon-poweredby {
display: block;
padding: 6px 8px;
background: #f1f1f2;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
font-size: 8px;
line-height: 1;
border-top-left-radius: 3px;
position: absolute;
bottom: 0;
right: 0;
}
[data-theme='dark'] #carbonads {
background-color: hsl(0, 0%, 35%);
box-shadow: 0 1px 4px 1px hsl(0, 0%, 55%);
}
[data-theme='dark'] #carbonads .carbon-poweredby {
background-color: hsl(0, 0%, 55%);
}

29
docs/src/css/custom.css Normal file
View File

@@ -0,0 +1,29 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,400;0,700;1,400;1,700&family=Roboto:ital,wght@0,400;0,700;1,400;1,700&display=swap');
:root {
--ifm-font-family-base: Roboto, system-ui, -apple-system, Segoe UI, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
--ifm-font-family-monospace: 'Roboto Mono', SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
--ifm-color-primary: #43ABA2 ;
--ifm-color-primary-dark: #3AB2A6;
--ifm-color-primary-darker: #32B8AB;
--ifm-color-primary-darkest: #29BEB0;
--ifm-color-primary-light: #4CA59D;
--ifm-color-primary-lighter: #559F98;
--ifm-color-primary-lightest: #5D9993;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
}
[data-theme='dark'] {
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}
.code-block--max-width {
width: 100%;
}
#carbonads {
margin-top: 30px;
margin-right: 10px;
}

0
docs/src/pages/.keep Normal file
View File

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const darkTheme = require('prism-react-renderer/themes/vsDark/index.cjs.js');
module.exports = {
plain: {
color: '#D4D4D4',
backgroundColor: '#212121'
},
styles: [
...darkTheme.styles,
{
types: ['title'],
style: {
color: '#569CD6',
fontWeight: 'bold'
}
},
{
types: ['property', 'parameter'],
style: {
color: '#9CDCFE'
}
},
{
types: ['script'],
style: {
color: '#D4D4D4'
}
},
{
types: ['boolean', 'arrow', 'atrule', 'tag'],
style: {
color: '#569CD6'
}
},
{
types: ['number', 'color', 'unit'],
style: {
color: '#B5CEA8'
}
},
{
types: ['font-matter'],
style: {
color: '#CE9178'
}
},
{
types: ['keyword', 'rule'],
style: {
color: '#C586C0'
}
},
{
types: ['regex'],
style: {
color: '#D16969'
}
},
{
types: ['maybe-class-name'],
style: {
color: '#4EC9B0'
}
},
{
types: ['constant'],
style: {
color: '#4FC1FF'
}
}
]
};

View File

@@ -0,0 +1,100 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const lightTheme = require('prism-react-renderer/themes/github/index.cjs.js');
module.exports = {
...lightTheme,
styles: [
...lightTheme.styles,
{
types: ['title'],
style: {
color: '#0550AE',
fontWeight: 'bold'
}
},
{
types: ['parameter'],
style: {
color: '#953800'
}
},
{
types: ['boolean', 'rule', 'color', 'number', 'constant', 'property'],
style: {
color: '#005CC5'
}
},
{
types: ['atrule', 'tag'],
style: {
color: '#22863A'
}
},
{
types: ['script'],
style: {
color: '#24292E'
}
},
{
types: ['operator', 'unit', 'rule'],
style: {
color: '#D73A49'
}
},
{
types: ['font-matter', 'string', 'attr-value'],
style: {
color: '#C6105F'
}
},
{
types: ['class-name'],
style: {
color: '#116329'
}
},
{
types: ['attr-name'],
style: {
color: '#0550AE'
}
},
{
types: ['keyword'],
style: {
color: '#CF222E'
}
},
{
types: ['function'],
style: {
color: '#8250DF'
}
},
{
types: ['selector'],
style: {
color: '#6F42C1'
}
},
{
types: ['variable'],
style: {
color: '#E36209'
}
},
{
types: ['comment'],
style: {
color: '#6B6B6B'
}
}
]
};

0
docs/static/.nojekyll vendored Normal file
View File

1
docs/static/CNAME vendored Normal file
View File

@@ -0,0 +1 @@
taskfile.dev

View File

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 170 KiB

BIN
docs/static/img/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

1
docs/static/img/logo.svg vendored Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 375 375"><path fill="#29beb0" d="M 187.570312 190.933594 L 187.570312 375 L 30.070312 279.535156 L 30.070312 95.464844 Z"/><path fill="#69d2c8" d="M 187.570312 190.933594 L 187.570312 375 L 345.070312 279.535156 L 345.070312 95.464844 Z"/><path fill="#94dfd8" d="M 187.570312 190.933594 L 30.070312 95.464844 L 187.570312 0 L 345.070312 95.464844 Z"/></svg>

After

Width:  |  Height:  |  Size: 435 B

1
docs/static/img/logo_mono.svg vendored Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 375 375"><path d="M 29.021972 281.445466 L 183.370289 375 L 183.370289 194.622441 L 29.021972 101.064089 Z M 345.978037 281.445466 L 345.978037 101.064079 L 191.629731 194.622431 L 191.629731 375 Z M 342.140723 93.731759 L 187.5 0 L 32.859297 93.731759 L 187.5 187.467349 Z"/></svg>

After

Width:  |  Height:  |  Size: 360 B

BIN
docs/static/img/og-image.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
docs/static/img/pix.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

29
docs/static/js/carbon.js vendored Normal file
View File

@@ -0,0 +1,29 @@
(function () {
function attachAd() {
var el = document.createElement('script');
el.setAttribute('type', 'text/javascript');
el.setAttribute('id', '_carbonads_js');
el.setAttribute(
'src',
'//cdn.carbonads.com/carbon.js?serve=CESI65QJ&placement=taskfiledev'
);
el.setAttribute('async', 'async');
var wrapper = document.getElementById('sidebar-ads');
wrapper.innerHTML = '';
wrapper.appendChild(el);
}
setTimeout(function () {
attachAd();
var currentPath = window.location.pathname;
setInterval(function () {
if (currentPath !== window.location.pathname) {
currentPath = window.location.pathname;
attachAd();
}
}, 1000);
}, 1000);
})();

7688
docs/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,8 @@ package task
import (
"errors"
"fmt"
"mvdan.cc/sh/v3/interp"
)
var (
@@ -15,16 +17,32 @@ type taskNotFoundError struct {
}
func (err *taskNotFoundError) Error() string {
return fmt.Sprintf(`task: Task "%s" not found`, err.taskName)
return fmt.Sprintf(`task: Task %q not found`, err.taskName)
}
type taskRunError struct {
type taskInternalError struct {
taskName string
}
func (err *taskInternalError) Error() string {
return fmt.Sprintf(`task: Task "%s" is internal`, err.taskName)
}
type TaskRunError struct {
taskName string
err error
}
func (err *taskRunError) Error() string {
return fmt.Sprintf(`task: Failed to run task "%s": %v`, err.taskName, err.err)
func (err *TaskRunError) Error() string {
return fmt.Sprintf(`task: Failed to run task %q: %v`, err.taskName, err.err)
}
func (err *TaskRunError) ExitCode() int {
if c, ok := interp.IsExitStatus(err.err); ok {
return int(c)
}
return 1
}
// MaximumTaskCallExceededError is returned when a task is called too
@@ -36,7 +54,7 @@ type MaximumTaskCallExceededError struct {
func (e *MaximumTaskCallExceededError) Error() string {
return fmt.Sprintf(
`task: maximum task call exceeded (%d) for task "%s": probably an cyclic dep or infinite loop`,
`task: maximum task call exceeded (%d) for task %q: probably an cyclic dep or infinite loop`,
MaximumTaskCall,
e.task,
)

18
go.mod
View File

@@ -8,10 +8,20 @@ require (
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/radovskyb/watcher v1.0.7
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.8.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
mvdan.cc/sh/v3 v3.4.2
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899
)
go 1.16
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
go 1.18

49
go.sum
View File

@@ -1,30 +1,17 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/frankban/quicktest v1.13.1 h1:xVm/f9seEhZFL9+n5kv5XLrGwy6elc4V9v/XFY2vmd8=
github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/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.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@@ -34,39 +21,35 @@ github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To=
github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665 h1:QOQNt6vCjMpXE7JSK5VvAzJC1byuN3FgTNSBwf+CJgI=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/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.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
mvdan.cc/sh/v3 v3.4.2 h1:d3TKODXfZ1bjWU/StENN+GDg5xOzNu5+C8AEu405E5U=
mvdan.cc/sh/v3 v3.4.2/go.mod h1:p/tqPPI4Epfk2rICAe2RoaNd8HBSJ8t9Y2DA9yQlbzY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899 h1:nwm4t5PtLlFd/H342GP50CtYf7vyMCOZkPx3g9shO0c=
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=

42
help.go
View File

@@ -2,7 +2,11 @@ package task
import (
"fmt"
"io"
"log"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/go-task/task/v3/internal/logger"
@@ -12,13 +16,11 @@ import (
// ListTasksWithDesc reports tasks that have a description spec.
func (e *Executor) ListTasksWithDesc() {
e.printTasks(false)
return
}
// ListAllTasks reports all tasks, with or without a description spec.
func (e *Executor) ListAllTasks() {
e.printTasks(true)
return
}
func (e *Executor) printTasks(listAll bool) {
@@ -50,7 +52,9 @@ func (e *Executor) printTasks(listAll bool) {
func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
for _, task := range e.Taskfile.Tasks {
tasks = append(tasks, task)
if !task.Internal {
tasks = append(tasks, task)
}
}
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
return
@@ -59,7 +63,7 @@ func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
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 != "" {
if !task.Internal && task.Desc != "" {
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
if err == nil {
task = compiledTask
@@ -70,3 +74,33 @@ func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
return
}
// PrintTaskNames prints only the task names in a Taskfile.
// Only tasks with a non-empty description are printed if allTasks is false.
// Otherwise, all task names are printed.
func (e *Executor) ListTaskNames(allTasks bool) {
// if called from cmd/task.go, e.Taskfile has not yet been parsed
if e.Taskfile == nil {
if err := e.readTaskfile(); err != nil {
log.Fatal(err)
return
}
}
// use stdout if no output defined
var w io.Writer = os.Stdout
if e.Stdout != nil {
w = e.Stdout
}
// create a string slice from all map values (*taskfile.Task)
s := make([]string, 0, len(e.Taskfile.Tasks))
for _, t := range e.Taskfile.Tasks {
if (allTasks || t.Desc != "") && !t.Internal {
s = append(s, strings.TrimRight(t.Task, ":"))
}
}
// sort and print all task names
sort.Strings(s)
for _, t := range s {
fmt.Fprintln(w, t)
}
}

View File

@@ -4,7 +4,8 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"github.com/go-task/task/v3/internal/filepathext"
)
const defaultTaskfile = `# https://taskfile.dev
@@ -23,13 +24,13 @@ tasks:
// InitTaskfile Taskfile creates a new Taskfile
func InitTaskfile(w io.Writer, dir string) error {
f := filepath.Join(dir, "Taskfile.yaml")
f := filepathext.SmartJoin(dir, "Taskfile.yaml")
if _, err := os.Stat(f); err == nil {
return ErrTaskfileAlreadyExists
}
if err := os.WriteFile(f, []byte(defaultTaskfile), 0644); err != nil {
if err := os.WriteFile(f, []byte(defaultTaskfile), 0o644); err != nil {
return err
}
fmt.Fprintf(w, "Taskfile.yaml created in the current directory\n")

View File

@@ -71,7 +71,7 @@ func (vr *varResolver) merge(vars *taskfile.Vars) {
return
}
tr := templater.Templater{Vars: vr.vars}
vars.Range(func(k string, v taskfile.Var) error {
_ = vars.Range(func(k string, v taskfile.Var) error {
v = taskfile.Var{
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),

View File

@@ -4,12 +4,12 @@ import (
"bytes"
"context"
"fmt"
"path/filepath"
"strings"
"sync"
"github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
@@ -44,7 +44,13 @@ func (c *CompilerV3) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*ta
func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
result := compiler.GetEnviron()
if t != nil {
result.Set("TASK", taskfile.Var{Static: t.Task})
specialVars, err := c.getSpecialVars(t)
if err != nil {
return nil, err
}
for k, v := range specialVars {
result.Set(k, taskfile.Var{Static: v})
}
}
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
@@ -74,12 +80,33 @@ func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluat
}
rangeFunc := getRangeFunc(c.Dir)
var taskRangeFunc func(k string, v taskfile.Var) error
if t != nil {
// NOTE(@andreynering): We're manually joining these paths here because
// this is the raw task, not the compiled one.
tr := templater.Templater{Vars: result, RemoveNoValue: true}
dir := tr.Replace(t.Dir)
if err := tr.Err(); err != nil {
return nil, err
}
dir = filepathext.SmartJoin(c.Dir, dir)
taskRangeFunc = getRangeFunc(dir)
}
if err := c.TaskfileEnv.Range(rangeFunc); err != nil {
return nil, err
}
if err := c.TaskfileVars.Range(rangeFunc); err != nil {
return nil, err
}
if t != nil {
if err := t.IncludedTaskfileVars.Range(taskRangeFunc); err != nil {
return nil, err
}
if err := t.IncludeVars.Range(rangeFunc); err != nil {
return nil, err
}
}
if t == nil || call == nil {
return result, nil
@@ -88,19 +115,6 @@ func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluat
if err := call.Vars.Range(rangeFunc); err != nil {
return nil, err
}
// NOTE(@andreynering): We're manually joining these paths here because
// this is the raw task, not the compiled one.
tr := templater.Templater{Vars: result, RemoveNoValue: true}
dir := tr.Replace(t.Dir)
if err := tr.Err(); err != nil {
return nil, err
}
if !filepath.IsAbs(dir) {
dir = filepath.Join(c.Dir, dir)
}
taskRangeFunc := getRangeFunc(dir)
if err := t.Vars.Range(taskRangeFunc); err != nil {
return nil, err
}
@@ -141,7 +155,8 @@ func (c *CompilerV3) HandleDynamicVar(v taskfile.Var, dir string) (string, error
// 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")
result := strings.TrimSuffix(stdout.String(), "\r\n")
result = strings.TrimSuffix(result, "\n")
c.dynamicCache[v.Sh] = result
c.Logger.VerboseErrf(logger.Magenta, `task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
@@ -156,3 +171,23 @@ func (c *CompilerV3) ResetCache() {
c.dynamicCache = nil
}
func (c *CompilerV3) getSpecialVars(t *taskfile.Task) (map[string]string, error) {
taskfileDir, err := c.getTaskfileDir(t)
if err != nil {
return nil, err
}
return map[string]string{
"TASK": t.Task,
"ROOT_DIR": c.Dir,
"TASKFILE_DIR": taskfileDir,
}, nil
}
func (c *CompilerV3) getTaskfileDir(t *taskfile.Task) (string, error) {
if t.IncludedTaskfile != nil {
return t.IncludedTaskfile.FullDirPath()
}
return c.Dir, nil
}

View File

@@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"strings"
"time"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
@@ -48,6 +49,7 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
r, err := interp.New(
interp.Params("-e"),
interp.Env(expand.ListEnviron(environ...)),
interp.ExecHandler(interp.DefaultExecHandler(15*time.Second)),
interp.OpenHandler(openHandler),
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
dirOption(opts.Dir),
@@ -55,6 +57,7 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
if err != nil {
return err
}
return r.Run(ctx, p)
}
@@ -70,7 +73,7 @@ func IsExitError(err error) bool {
// if available.
func Expand(s string) (string, error) {
s = filepath.ToSlash(s)
s = strings.Replace(s, " ", `\ `, -1)
s = strings.ReplaceAll(s, " ", `\ `)
fields, err := shell.Fields(s, nil)
if err != nil {
return "", err

View File

@@ -0,0 +1,14 @@
package filepathext
import (
"path/filepath"
)
// SmartJoin joins two paths, but only if the second is not already an
// absolute path.
func SmartJoin(a, b string) string {
if filepath.IsAbs(b) {
return b
}
return filepath.Join(a, b)
}

View File

@@ -2,6 +2,8 @@ package logger
import (
"io"
"os"
"strconv"
"github.com/fatih/color"
)
@@ -9,13 +11,36 @@ import (
type Color func() PrintFunc
type PrintFunc func(io.Writer, string, ...interface{})
func Default() PrintFunc { return color.New(color.Reset).FprintfFunc() }
func Blue() PrintFunc { return color.New(color.FgBlue).FprintfFunc() }
func Green() PrintFunc { return color.New(color.FgGreen).FprintfFunc() }
func Cyan() PrintFunc { return color.New(color.FgCyan).FprintfFunc() }
func Yellow() PrintFunc { return color.New(color.FgYellow).FprintfFunc() }
func Magenta() PrintFunc { return color.New(color.FgMagenta).FprintfFunc() }
func Red() PrintFunc { return color.New(color.FgRed).FprintfFunc() }
func Default() PrintFunc {
return color.New(envColor("TASK_COLOR_RESET", color.Reset)).FprintfFunc()
}
func Blue() PrintFunc {
return color.New(envColor("TASK_COLOR_BLUE", color.FgBlue)).FprintfFunc()
}
func Green() PrintFunc {
return color.New(envColor("TASK_COLOR_GREEN", color.FgGreen)).FprintfFunc()
}
func Cyan() PrintFunc {
return color.New(envColor("TASK_COLOR_CYAN", color.FgCyan)).FprintfFunc()
}
func Yellow() PrintFunc {
return color.New(envColor("TASK_COLOR_YELLOW", color.FgYellow)).FprintfFunc()
}
func Magenta() PrintFunc {
return color.New(envColor("TASK_COLOR_MAGENTA", color.FgMagenta)).FprintfFunc()
}
func Red() PrintFunc {
return color.New(envColor("TASK_COLOR_RED", color.FgRed)).FprintfFunc()
}
func envColor(env string, defaultColor color.Attribute) color.Attribute {
override, err := strconv.Atoi(os.Getenv(env))
if err == nil {
return color.Attribute(override)
}
return defaultColor
}
// Logger is just a wrapper that prints stuff to STDOUT or STDERR,
// with optional color.

View File

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

View File

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

View File

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

View File

@@ -6,6 +6,8 @@ import (
"io"
"testing"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3/internal/output"
@@ -14,7 +16,7 @@ import (
func TestInterleaved(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Interleaved{}
var w = o.WrapWriter(&b, "")
var w, _, _ = o.WrapWriter(&b, io.Discard, "", nil)
fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, "foo\nbar\n", b.String())
@@ -25,20 +27,58 @@ func TestInterleaved(t *testing.T) {
func TestGroup(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Group{}
var w = o.WrapWriter(&b, "").(io.WriteCloser)
var stdOut, stdErr, cleanup = o.WrapWriter(&b, io.Discard, "", nil)
fmt.Fprintln(w, "foo\nbar")
fmt.Fprintln(stdOut, "out\nout")
assert.Equal(t, "", b.String())
fmt.Fprintln(w, "baz")
fmt.Fprintln(stdErr, "err\nerr")
assert.Equal(t, "", b.String())
assert.NoError(t, w.Close())
assert.Equal(t, "foo\nbar\nbaz\n", b.String())
fmt.Fprintln(stdOut, "out")
assert.Equal(t, "", b.String())
fmt.Fprintln(stdErr, "err")
assert.Equal(t, "", b.String())
assert.NoError(t, cleanup())
assert.Equal(t, "out\nout\nerr\nerr\nout\nerr\n", b.String())
}
func TestGroupWithBeginEnd(t *testing.T) {
tmpl := templater.Templater{
Vars: &taskfile.Vars{
Keys: []string{"VAR1"},
Mapping: map[string]taskfile.Var{
"VAR1": {Static: "example-value"},
},
},
}
var o output.Output = output.Group{
Begin: "::group::{{ .VAR1 }}",
End: "::endgroup::",
}
t.Run("simple", func(t *testing.T) {
var b bytes.Buffer
var w, _, cleanup = o.WrapWriter(&b, io.Discard, "", &tmpl)
fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, "", b.String())
fmt.Fprintln(w, "baz")
assert.Equal(t, "", b.String())
assert.NoError(t, cleanup())
assert.Equal(t, "::group::example-value\nfoo\nbar\nbaz\n::endgroup::\n", b.String())
})
t.Run("no output", func(t *testing.T) {
var b bytes.Buffer
var _, _, cleanup = o.WrapWriter(&b, io.Discard, "", &tmpl)
assert.NoError(t, cleanup())
assert.Equal(t, "", b.String())
})
}
func TestPrefixed(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Prefixed{}
var w = o.WrapWriter(&b, "prefix").(io.WriteCloser)
var w, _, cleanup = o.WrapWriter(&b, io.Discard, "prefix", nil)
t.Run("simple use cases", func(t *testing.T) {
b.Reset()
@@ -47,6 +87,7 @@ func TestPrefixed(t *testing.T) {
assert.Equal(t, "[prefix] foo\n[prefix] bar\n", b.String())
fmt.Fprintln(w, "baz")
assert.Equal(t, "[prefix] foo\n[prefix] bar\n[prefix] baz\n", b.String())
assert.NoError(t, cleanup())
})
t.Run("multiple writes for a single line", func(t *testing.T) {
@@ -57,7 +98,7 @@ func TestPrefixed(t *testing.T) {
assert.Equal(t, "", b.String())
}
assert.NoError(t, w.Close())
assert.NoError(t, cleanup())
assert.Equal(t, "[prefix] Test!\n", b.String())
})
}

View File

@@ -9,8 +9,9 @@ import (
type Prefixed struct{}
func (Prefixed) WrapWriter(w io.Writer, prefix string) io.Writer {
return &prefixWriter{writer: w, prefix: prefix}
func (Prefixed) WrapWriter(stdOut, _ io.Writer, prefix string, _ Templater) (io.Writer, io.Writer, CloseFunc) {
pw := &prefixWriter{writer: stdOut, prefix: prefix}
return pw, pw, func() error { return pw.close() }
}
type prefixWriter struct {
@@ -28,7 +29,7 @@ func (pw *prefixWriter) Write(p []byte) (int, error) {
return n, pw.writeOutputLines(false)
}
func (pw *prefixWriter) Close() error {
func (pw *prefixWriter) close() error {
return pw.writeOutputLines(true)
}

176
internal/sleepit/main.go Normal file
View File

@@ -0,0 +1,176 @@
// This code is released under the MIT License
// Copyright (c) 2020 Marco Molteni and the timeit contributors.
package main
import (
"flag"
"fmt"
"os"
"os/signal"
"time"
)
const usage = `sleepit: sleep for the specified duration, optionally handling signals
When the line "sleepit: ready" is printed, it means that it is safe to send signals to it
Usage: sleepit <command> [<args>]
Commands
default Use default action: on reception of SIGINT terminate abruptly
handle Handle signals: on reception of SIGINT perform cleanup before exiting
version Show the sleepit version`
var (
// Filled by the linker.
fullVersion = "unknown" // example: v0.0.9-8-g941583d027-dirty
)
func main() {
os.Exit(run(os.Args[1:]))
}
func run(args []string) int {
if len(args) < 1 {
fmt.Fprintln(os.Stderr, usage)
return 2
}
defaultCmd := flag.NewFlagSet("default", flag.ExitOnError)
defaultSleep := defaultCmd.Duration("sleep", 5*time.Second, "Sleep duration")
handleCmd := flag.NewFlagSet("handle", flag.ExitOnError)
handleSleep := handleCmd.Duration("sleep", 5*time.Second, "Sleep duration")
handleCleanup := handleCmd.Duration("cleanup", 5*time.Second, "Cleanup duration")
handleTermAfter := handleCmd.Int("term-after", 0,
"Terminate immediately after `N` signals.\n"+
"Default is to terminate only when the cleanup phase has completed.")
versionCmd := flag.NewFlagSet("version", flag.ExitOnError)
switch args[0] {
case "default":
_ = defaultCmd.Parse(args[1:])
if len(defaultCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "default: unexpected arguments: %v\n", defaultCmd.Args())
return 2
}
return supervisor(*defaultSleep, 0, 0, nil)
case "handle":
_ = handleCmd.Parse(args[1:])
if *handleTermAfter == 1 {
fmt.Fprintf(os.Stderr, "handle: term-after cannot be 1\n")
return 2
}
if len(handleCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "handle: unexpected arguments: %v\n", handleCmd.Args())
return 2
}
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt) // Ctrl-C -> SIGINT
return supervisor(*handleSleep, *handleCleanup, *handleTermAfter, sigCh)
case "version":
_ = versionCmd.Parse(args[1:])
if len(versionCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "version: unexpected arguments: %v\n", versionCmd.Args())
return 2
}
fmt.Printf("sleepit version %s\n", fullVersion)
return 0
default:
fmt.Fprintln(os.Stderr, usage)
return 2
}
}
func supervisor(
sleep time.Duration,
cleanup time.Duration,
termAfter int,
sigCh <-chan os.Signal,
) int {
fmt.Printf("sleepit: ready\n")
fmt.Printf("sleepit: PID=%d sleep=%v cleanup=%v\n",
os.Getpid(), sleep, cleanup)
cancelWork := make(chan struct{})
workerDone := worker(cancelWork, sleep, "work")
cancelCleaner := make(chan struct{})
var cleanerDone <-chan struct{}
sigCount := 0
for {
select {
case sig := <-sigCh:
sigCount++
fmt.Printf("sleepit: got signal=%s count=%d\n", sig, sigCount)
if sigCount == 1 {
// since `cancelWork` is unbuffered, sending will be synchronous:
// we are ensured that the worker has terminated before starting cleanup.
// This is important in some real-life situations.
cancelWork <- struct{}{}
cleanerDone = worker(cancelCleaner, cleanup, "cleanup")
}
if sigCount == termAfter {
cancelCleaner <- struct{}{}
return 4
}
case <-workerDone:
return 0
case <-cleanerDone:
return 3
}
}
}
// Start a worker goroutine and return immediately a `workerDone` channel.
// The goroutine will prepend its prints with the prefix `name`.
// The goroutine will simulate some work and will terminate when one of the following
// conditions happens:
// 1. When `howlong` is elapsed. This case will be signaled on the `workerDone` channel.
// 2. When something happens on channel `canceled`. Note that this simulates real-life,
// so cancellation is not instantaneous: if the caller wants a synchronous cancel,
// it should send a message; if instead it wants an asynchronous cancel, it should
// close the channel.
func worker(
canceled <-chan struct{},
howlong time.Duration,
name string,
) <-chan struct{} {
workerDone := make(chan struct{})
deadline := time.Now().Add(howlong)
go func() {
fmt.Printf("sleepit: %s started\n", name)
for {
select {
case <-canceled:
fmt.Printf("sleepit: %s canceled\n", name)
return
default:
if doSomeWork(deadline) {
fmt.Printf("sleepit: %s done\n", name) // <== NOTE THIS LINE
workerDone <- struct{}{}
return
}
}
}
}()
return workerDone
}
// Do some work and then return, so that the caller can decide wether to continue or not.
// Return true when all work is done.
func doSomeWork(deadline time.Time) bool {
if time.Now().After(deadline) {
return true
}
timeout := 100 * time.Millisecond
time.Sleep(timeout)
return false
}

View File

@@ -8,12 +8,14 @@ import (
"path/filepath"
"regexp"
"strings"
"github.com/go-task/task/v3/internal/filepathext"
)
// Checksum validades if a task is up to date by calculating its source
// files checksum
type Checksum struct {
BaseDir string
TempDir string
TaskDir string
Task string
Sources []string
@@ -43,8 +45,8 @@ func (c *Checksum) IsUpToDate() (bool, error) {
}
if !c.Dry {
_ = os.MkdirAll(filepath.Join(c.BaseDir, ".task", "checksum"), 0755)
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
_ = os.MkdirAll(filepathext.SmartJoin(c.TempDir, "checksum"), 0o755)
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0o644); err != nil {
return false, err
}
}
@@ -107,7 +109,7 @@ func (*Checksum) Kind() string {
}
func (c *Checksum) checksumFilePath() string {
return filepath.Join(c.BaseDir, ".task", "checksum", c.normalizeFilename(c.Task))
return filepath.Join(c.TempDir, "checksum", c.normalizeFilename(c.Task))
}
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")

View File

@@ -2,12 +2,12 @@ package status
import (
"os"
"path/filepath"
"sort"
"github.com/mattn/go-zglob"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
)
func globs(dir string, globs []string) ([]string, error) {
@@ -25,17 +25,18 @@ func globs(dir string, globs []string) ([]string, error) {
func Glob(dir string, g string) ([]string, error) {
files := make([]string, 0)
if !filepath.IsAbs(g) {
g = filepath.Join(dir, g)
}
g = filepathext.SmartJoin(dir, g)
g, err := execext.Expand(g)
if err != nil {
return nil, err
}
fs, err := zglob.Glob(g)
fs, err := zglob.GlobFollowSymlinks(g)
if err != nil {
return nil, err
}
for _, f := range fs {
info, err := os.Stat(f)
if err != nil {

View File

@@ -19,11 +19,11 @@ func init() {
"OS": func() string { return runtime.GOOS },
"ARCH": func() string { return runtime.GOARCH },
"catLines": func(s string) string {
s = strings.Replace(s, "\r\n", " ", -1)
return strings.Replace(s, "\n", " ", -1)
s = strings.ReplaceAll(s, "\r\n", " ")
return strings.ReplaceAll(s, "\n", " ")
},
"splitLines": func(s string) []string {
s = strings.Replace(s, "\r\n", "\n", -1)
s = strings.ReplaceAll(s, "\r\n", "\n")
return strings.Split(s, "\n")
},
"fromSlash": func(path string) string {

View File

@@ -68,7 +68,7 @@ func (r *Templater) ReplaceVars(vars *taskfile.Vars) *taskfile.Vars {
}
var new taskfile.Vars
vars.Range(func(k string, v taskfile.Var) error {
_ = vars.Range(func(k string, v taskfile.Var) error {
new.Set(k, taskfile.Var{
Static: r.Replace(v.Static),
Live: v.Live,

32
package-lock.json generated Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "@go-task/cli",
"version": "3.16.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@go-task/cli",
"version": "3.16.0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@go-task/go-npm": "^0.1.15"
}
},
"node_modules/@go-task/go-npm": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/@go-task/go-npm/-/go-npm-0.1.15.tgz",
"integrity": "sha512-xG+6SsSQsa6MzWML1CABWHTwHrCrBqXc/D1POoMDGIgjsRE/PB1noSBGLFhvU5DWHdPksqbAt/w9VOjbqlXpYw==",
"bin": {
"go-npm": "bin/index.js"
}
}
},
"dependencies": {
"@go-task/go-npm": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/@go-task/go-npm/-/go-npm-0.1.15.tgz",
"integrity": "sha512-xG+6SsSQsa6MzWML1CABWHTwHrCrBqXc/D1POoMDGIgjsRE/PB1noSBGLFhvU5DWHdPksqbAt/w9VOjbqlXpYw=="
}
}
}

34
package.json Normal file
View File

@@ -0,0 +1,34 @@
{
"name": "@go-task/cli",
"version": "3.16.0",
"description": "A task runner / simpler Make alternative written in Go",
"scripts": {
"postinstall": "go-npm install",
"preuninstall": "go-npm uninstall"
},
"goBinary": {
"name": "task",
"path": "./bin",
"url": "https://github.com/go-task/task/releases/download/v{{version}}/task_{{platform}}_{{arch}}{{archive_ext}}"
},
"files": [],
"repository": {
"type": "git",
"url": "https://github.com/go-task/task.git"
},
"keywords": [
"task",
"taskfile",
"build-tool",
"task-runner"
],
"author": "Andrey Nering",
"license": "MIT",
"bugs": {
"url": "https://github.com/go-task/task/issues"
},
"homepage": "https://taskfile.dev",
"dependencies": {
"@go-task/go-npm": "^0.1.15"
}
}

299
setup.go Normal file
View File

@@ -0,0 +1,299 @@
package task
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
compilerv2 "github.com/go-task/task/v3/internal/compiler/v2"
compilerv3 "github.com/go-task/task/v3/internal/compiler/v3"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/taskfile"
"github.com/go-task/task/v3/taskfile/read"
)
func (e *Executor) Setup() error {
if err := e.setCurrentDir(); err != nil {
return err
}
if err := e.readTaskfile(); err != nil {
return err
}
v, err := e.Taskfile.ParsedVersion()
if err != nil {
return err
}
if err := e.setupTempDir(); err != nil {
return err
}
e.setupStdFiles()
e.setupLogger()
if err := e.setupOutput(); err != nil {
return err
}
if err := e.setupCompiler(v); err != nil {
return err
}
if err := e.readDotEnvFiles(v); err != nil {
return err
}
if err := e.doVersionChecks(v); err != nil {
return err
}
e.setupDefaults(v)
e.setupConcurrencyState()
return nil
}
func (e *Executor) setCurrentDir() error {
if e.Dir == "" {
wd, err := os.Getwd()
if err != nil {
return err
}
e.Dir = wd
} else if !filepath.IsAbs(e.Dir) {
abs, err := filepath.Abs(e.Dir)
if err != nil {
return err
}
e.Dir = abs
}
return nil
}
func (e *Executor) readTaskfile() error {
var err error
e.Taskfile, err = read.Taskfile(&read.ReaderNode{
Dir: e.Dir,
Entrypoint: e.Entrypoint,
Parent: nil,
Optional: false,
})
return err
}
func (e *Executor) setupTempDir() error {
if e.TempDir != "" {
return nil
}
if os.Getenv("TASK_TEMP_DIR") == "" {
e.TempDir = filepathext.SmartJoin(e.Dir, ".task")
} else if filepath.IsAbs(os.Getenv("TASK_TEMP_DIR")) || strings.HasPrefix(os.Getenv("TASK_TEMP_DIR"), "~") {
tempDir, err := execext.Expand(os.Getenv("TASK_TEMP_DIR"))
if err != nil {
return err
}
projectDir, _ := filepath.Abs(e.Dir)
projectName := filepath.Base(projectDir)
e.TempDir = filepathext.SmartJoin(tempDir, projectName)
} else {
e.TempDir = filepathext.SmartJoin(e.Dir, os.Getenv("TASK_TEMP_DIR"))
}
return nil
}
func (e *Executor) setupStdFiles() {
if e.Stdin == nil {
e.Stdin = os.Stdin
}
if e.Stdout == nil {
e.Stdout = os.Stdout
}
if e.Stderr == nil {
e.Stderr = os.Stderr
}
}
func (e *Executor) setupLogger() {
e.Logger = &logger.Logger{
Stdout: e.Stdout,
Stderr: e.Stderr,
Verbose: e.Verbose,
Color: e.Color,
}
}
func (e *Executor) setupOutput() error {
if !e.OutputStyle.IsSet() {
e.OutputStyle = e.Taskfile.Output
}
var err error
e.Output, err = output.BuildFor(&e.OutputStyle)
return err
}
func (e *Executor) setupCompiler(v float64) error {
if v < 3 {
var err error
e.taskvars, err = read.Taskvars(e.Dir)
if err != nil {
return err
}
e.Compiler = &compilerv2.CompilerV2{
Dir: e.Dir,
Taskvars: e.taskvars,
TaskfileVars: e.Taskfile.Vars,
Expansions: e.Taskfile.Expansions,
Logger: e.Logger,
}
} else {
e.Compiler = &compilerv3.CompilerV3{
Dir: e.Dir,
TaskfileEnv: e.Taskfile.Env,
TaskfileVars: e.Taskfile.Vars,
Logger: e.Logger,
}
}
return nil
}
func (e *Executor) readDotEnvFiles(v float64) error {
if v < 3.0 {
return nil
}
env, err := read.Dotenv(e.Compiler, e.Taskfile, e.Dir)
if err != nil {
return err
}
err = env.Range(func(key string, value taskfile.Var) error {
if _, ok := e.Taskfile.Env.Mapping[key]; !ok {
e.Taskfile.Env.Set(key, value)
}
return nil
})
return err
}
func (e *Executor) setupDefaults(v float64) {
// Color available only on v3
if v < 3 {
e.Logger.Color = false
}
if e.Taskfile.Method == "" {
if v >= 3 {
e.Taskfile.Method = "checksum"
} else {
e.Taskfile.Method = "timestamp"
}
}
if e.Taskfile.Run == "" {
e.Taskfile.Run = "always"
}
}
func (e *Executor) setupConcurrencyState() {
e.executionHashes = make(map[string]context.Context)
e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks))
e.mkdirMutexMap = make(map[string]*sync.Mutex, len(e.Taskfile.Tasks))
for k := range e.Taskfile.Tasks {
e.taskCallCount[k] = new(int32)
e.mkdirMutexMap[k] = &sync.Mutex{}
}
if e.Concurrency > 0 {
e.concurrencySemaphore = make(chan struct{}, e.Concurrency)
}
}
func (e *Executor) doVersionChecks(v float64) error {
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 {
v = 3.8
}
if v > 3.8 {
return fmt.Errorf(`task: Taskfile versions greater than v3.8 not implemented in the version of Task`)
}
if v < 2.1 && !e.Taskfile.Output.IsSet() {
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
}
if v < 2.2 && e.Taskfile.Includes.Len() > 0 {
return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`)
}
if v >= 3.0 && e.Taskfile.Expansions > 2 {
return fmt.Errorf(`task: The "expansions" setting is not available anymore on v3.0`)
}
if v < 3.8 && e.Taskfile.Output.Group.IsSet() {
return fmt.Errorf(`task: Taskfile option "output.group" is only available starting on Taskfile version v3.8`)
}
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 {
return err
}
for _, cmd := range task.Cmds {
if cmd.IgnoreError {
return err
}
}
}
}
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 {
err := e.Taskfile.Includes.Range(func(_ string, taskfile taskfile.IncludedTaskfile) error {
if taskfile.AdvancedImport {
return errors.New(`task: Import with additional parameters is only available starting on Taskfile version v3`)
}
return nil
})
if err != nil {
return err
}
}
if v < 3.7 {
if e.Taskfile.Run != "" {
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
}
for _, task := range e.Taskfile.Tasks {
if task.Run != "" {
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
}
}
}
return nil
}

31
signals.go Normal file
View File

@@ -0,0 +1,31 @@
package task
import (
"os"
"os/signal"
"syscall"
"github.com/go-task/task/v3/internal/logger"
)
// NOTE(@andreynering): This function intercepts SIGINT and SIGTERM signals
// so the Task process is not killed immediately and processes running have
// time to do cleanup work.
func (e *Executor) InterceptInterruptSignals() {
ch := make(chan os.Signal, 3)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
go func() {
for i := 1; i <= 3; i++ {
sig := <-ch
if i < 3 {
e.Logger.Outf(logger.Yellow, `task: Signal received: "%s"`, sig)
continue
}
e.Logger.Errf(logger.Red, `task: Signal received for the third time: "%s". Forcing shutdown`, sig)
os.Exit(1)
}
}()
}

245
signals_test.go Normal file
View File

@@ -0,0 +1,245 @@
//go:build signals
// +build signals
// This file contains tests for signal handling on Unix.
// Based on code from https://github.com/marco-m/timeit
// Due to how signals work, for robustness we always spawn a separate process;
// we never send signals to the test process.
package task_test
import (
"bytes"
"errors"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"testing"
"time"
)
var (
SLEEPIT, _ = filepath.Abs("./bin/sleepit")
)
func TestSignalSentToProcessGroup(t *testing.T) {
task, err := getTaskPath()
if err != nil {
t.Fatal(err)
}
testCases := map[string]struct {
args []string
sendSigs int
want []string
notWant []string
}{
// regression:
// - child is terminated, immediately, by "context canceled" (another bug???)
"child does not handle sigint: receives sigint and terminates immediately": {
args: []string{task, "--", SLEEPIT, "default", "-sleep=10s"},
sendSigs: 1,
want: []string{
"sleepit: ready\n",
"sleepit: work started\n",
"task: Signal received: \"interrupt\"\n",
// 130 = 128 + SIGINT
"task: Failed to run task \"default\": exit status 130\n",
},
notWant: []string{
"task: Failed to run task \"default\": context canceled\n",
},
},
// 2 regressions:
// - child receives 2 signals instead of 1
// - child is terminated, immediately, by "context canceled" (another bug???)
// TODO we need -cleanup=2s only to show reliably the bug; once the fix is committed,
// we can use -cleanup=50ms to speed the test up
"child intercepts sigint: receives sigint and does cleanup": {
args: []string{task, "--", SLEEPIT, "handle", "-sleep=10s", "-cleanup=2s"},
sendSigs: 1,
want: []string{
"sleepit: ready\n",
"sleepit: work started\n",
"task: Signal received: \"interrupt\"\n",
"sleepit: got signal=interrupt count=1\n",
"sleepit: work canceled\n",
"sleepit: cleanup started\n",
"sleepit: cleanup done\n",
"task: Failed to run task \"default\": exit status 3\n",
},
notWant: []string{
"sleepit: got signal=interrupt count=2\n",
"task: Failed to run task \"default\": context canceled\n",
},
},
// regression: child receives 2 signal instead of 1 and thus terminates abruptly
"child simulates terraform: receives 1 sigint and does cleanup": {
args: []string{task, "--", SLEEPIT, "handle", "-term-after=2", "-sleep=10s", "-cleanup=50ms"},
sendSigs: 1,
want: []string{
"sleepit: ready\n",
"sleepit: work started\n",
"task: Signal received: \"interrupt\"\n",
"sleepit: got signal=interrupt count=1\n",
"sleepit: work canceled\n",
"sleepit: cleanup started\n",
"sleepit: cleanup done\n",
"task: Failed to run task \"default\": exit status 3\n",
},
notWant: []string{
"sleepit: got signal=interrupt count=2\n",
"sleepit: cleanup canceled\n",
"task: Failed to run task \"default\": exit status 4\n",
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
var out bytes.Buffer
sut := exec.Command(tc.args[0], tc.args[1:]...)
sut.Stdout = &out
sut.Stderr = &out
sut.Dir = "testdata/ignore_signals"
// Create a new process group by setting the process group ID of the child
// to the child PID.
// By default, the child would inherit the process group of the parent, but
// we want to avoid this, to protect the parent (the test process) from the
// signal that this test will send. More info in the comments below for
// syscall.Kill().
sut.SysProcAttr = &syscall.SysProcAttr{Setpgid: true, Pgid: 0}
if err := sut.Start(); err != nil {
t.Fatalf("starting the SUT process: %v", err)
}
// After the child is started, we want to avoid a race condition where we send
// it a signal before it had time to setup its own signal handlers. Sleeping
// is way too flaky, instead we parse the child output until we get a line
// that we know is printed after the signal handlers are installed...
ready := false
timeout := time.Duration(time.Second)
start := time.Now()
for time.Since(start) < timeout {
if strings.Contains(out.String(), "sleepit: ready\n") {
ready = true
break
}
time.Sleep(10 * time.Millisecond)
}
if !ready {
t.Fatalf("sleepit not ready after %v\n"+
"additional information:\n"+
" output:\n%s",
timeout, out.String())
}
// When we have a running program in a shell and type CTRL-C, the tty driver
// will send a SIGINT signal to all the processes in the foreground process
// group (see https://en.wikipedia.org/wiki/Process_group).
//
// Here we want to emulate this behavior: send SIGINT to the process group of
// the test executable. Although Go for some reasons doesn't wrap the
// killpg(2) system call, what works is using syscall.Kill(-PID, SIGINT),
// where the negative PID means the corresponding process group. Note that
// this negative PID works only as long as the caller of the kill(2) system
// call has a different PID, which is the case for this test.
for i := 1; i <= tc.sendSigs; i++ {
if err := syscall.Kill(-sut.Process.Pid, syscall.SIGINT); err != nil {
t.Fatalf("sending INT signal to the process group: %v", err)
}
time.Sleep(1 * time.Millisecond)
}
err := sut.Wait()
// In case of a subprocess failing, Task always returns exit code 1, not the
// exit code returned by the subprocess. This is understandable, since Task
// supports parallel execution: if two parallel subprocess fail, each with a
// different exit code, which one should Task report? This would be a race.
var wantErr *exec.ExitError
const wantExitStatus = 1 // Task always returns exit code 1 in case of error
if errors.As(err, &wantErr) {
if wantErr.ExitCode() != wantExitStatus {
t.Errorf(
"waiting for child process: got exit status %v; want %d\n"+
"additional information:\n"+
" process state: %q",
wantErr.ExitCode(), wantExitStatus, wantErr.String())
}
} else {
t.Errorf("waiting for child process: got unexpected error type %v (%T); want (%T)",
err, err, wantErr)
}
gotLines := strings.SplitAfter(out.String(), "\n")
notFound := listDifference(tc.want, gotLines)
if len(notFound) > 0 {
t.Errorf("\nwanted but not found:\n%v", notFound)
}
found := listIntersection(tc.notWant, gotLines)
if len(found) > 0 {
t.Errorf("\nunwanted but found:\n%v", found)
}
if len(notFound) > 0 || len(found) > 0 {
t.Errorf("\noutput:\n%v", gotLines)
}
})
}
}
func getTaskPath() (string, error) {
if info, err := os.Stat("./bin/task"); err == nil {
return info.Name(), nil
}
if path, err := exec.LookPath("task"); err == nil {
return path, nil
}
return "", errors.New("task: \"task\" binary was not found!")
}
// Return the difference of the two lists: the elements that are present in the first
// list, but not in the second one. The notion of presence is not with `=` but with
// string.Contains(l2, l1).
// FIXME this does not enforce ordering. We might want to support both.
func listDifference(lines1, lines2 []string) []string {
difference := []string{}
for _, l1 := range lines1 {
found := false
for _, l2 := range lines2 {
if strings.Contains(l2, l1) {
found = true
break
}
}
if !found {
difference = append(difference, l1)
}
}
return difference
}
// Return the intersection of the two lists: the elements that are present in both lists.
// The notion of presence is not with '=' but with string.Contains(l2, l1)
// FIXME this does not enforce ordering. We might want to support both.
func listIntersection(lines1, lines2 []string) []string {
intersection := []string{}
for _, l1 := range lines1 {
for _, l2 := range lines2 {
if strings.Contains(l2, l1) {
intersection = append(intersection, l1)
break
}
}
}
return intersection
}

View File

@@ -95,7 +95,7 @@ func (e *Executor) timestampChecker(t *taskfile.Task) status.Checker {
func (e *Executor) checksumChecker(t *taskfile.Task) status.Checker {
return &status.Checksum{
BaseDir: e.Dir,
TempDir: e.TempDir,
TaskDir: t.Dir,
Task: t.Name(),
Sources: t.Sources,

230
task.go
View File

@@ -2,7 +2,6 @@ package task
import (
"context"
"errors"
"fmt"
"io"
"os"
@@ -10,14 +9,12 @@ import (
"sync/atomic"
"github.com/go-task/task/v3/internal/compiler"
compilerv2 "github.com/go-task/task/v3/internal/compiler/v2"
compilerv3 "github.com/go-task/task/v3/internal/compiler/v3"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/internal/summary"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
"github.com/go-task/task/v3/taskfile/read"
"golang.org/x/sync/errgroup"
)
@@ -33,6 +30,7 @@ type Executor struct {
Taskfile *taskfile.Taskfile
Dir string
TempDir string
Entrypoint string
Force bool
Watch bool
@@ -51,7 +49,7 @@ type Executor struct {
Logger *logger.Logger
Compiler compiler.Compiler
Output output.Output
OutputStyle string
OutputStyle taskfile.Output
taskvars *taskfile.Vars
@@ -66,11 +64,16 @@ type Executor struct {
func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
// check if given tasks exist
for _, c := range calls {
if _, ok := e.Taskfile.Tasks[c.Task]; !ok {
t, ok := e.Taskfile.Tasks[c.Task]
if !ok {
// FIXME: move to the main package
e.ListTasksWithDesc()
return &taskNotFoundError{taskName: c.Task}
}
if t.Internal {
e.ListTasksWithDesc()
return &taskInternalError{taskName: c.Task}
}
}
if e.Summary {
@@ -103,195 +106,6 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
return g.Wait()
}
// Setup setups Executor's internal state
func (e *Executor) Setup() error {
var err error
e.Taskfile, err = read.Taskfile(e.Dir, e.Entrypoint)
if err != nil {
return err
}
v, err := e.Taskfile.ParsedVersion()
if err != nil {
return err
}
if v < 3.0 {
e.taskvars, err = read.Taskvars(e.Dir)
if err != nil {
return err
}
}
if e.Stdin == nil {
e.Stdin = os.Stdin
}
if e.Stdout == nil {
e.Stdout = os.Stdout
}
if e.Stderr == nil {
e.Stderr = os.Stderr
}
e.Logger = &logger.Logger{
Stdout: e.Stdout,
Stderr: e.Stderr,
Verbose: e.Verbose,
Color: e.Color,
}
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 {
v = 3.7
}
if v > 3.7 {
return fmt.Errorf(`task: Taskfile versions greater than v3.7 not implemented in the version of Task`)
}
// Color available only on v3
if v < 3 {
e.Logger.Color = false
}
if v < 3 {
e.Compiler = &compilerv2.CompilerV2{
Dir: e.Dir,
Taskvars: e.taskvars,
TaskfileVars: e.Taskfile.Vars,
Expansions: e.Taskfile.Expansions,
Logger: e.Logger,
}
} else {
e.Compiler = &compilerv3.CompilerV3{
Dir: e.Dir,
TaskfileEnv: e.Taskfile.Env,
TaskfileVars: e.Taskfile.Vars,
Logger: e.Logger,
}
}
if v >= 3.0 {
env, err := read.Dotenv(e.Compiler, e.Taskfile, e.Dir)
if err != nil {
return err
}
err = env.Range(func(key string, value taskfile.Var) error {
if _, ok := e.Taskfile.Env.Mapping[key]; !ok {
e.Taskfile.Env.Set(key, value)
}
return nil
})
if err != nil {
return err
}
}
if v < 2.1 && e.Taskfile.Output != "" {
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
}
if v < 2.2 && e.Taskfile.Includes.Len() > 0 {
return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`)
}
if v >= 3.0 && e.Taskfile.Expansions > 2 {
return fmt.Errorf(`task: The "expansions" setting is not available anymore on v3.0`)
}
if e.OutputStyle != "" {
e.Taskfile.Output = e.OutputStyle
}
switch e.Taskfile.Output {
case "", "interleaved":
e.Output = output.Interleaved{}
case "group":
e.Output = output.Group{}
case "prefixed":
e.Output = output.Prefixed{}
default:
return fmt.Errorf(`task: output option "%s" not recognized`, e.Taskfile.Output)
}
if e.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 {
return err
}
for _, cmd := range task.Cmds {
if cmd.IgnoreError {
return err
}
}
}
}
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 {
err := e.Taskfile.Includes.Range(func(_ string, taskfile taskfile.IncludedTaskfile) error {
if taskfile.AdvancedImport {
return errors.New(`task: Import with additional parameters is only available starting on Taskfile version v3`)
}
return nil
})
if err != nil {
return err
}
}
if v < 3.7 {
if e.Taskfile.Run != "" {
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
}
for _, task := range e.Taskfile.Tasks {
if task.Run != "" {
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
}
}
}
if e.Taskfile.Run == "" {
e.Taskfile.Run = "always"
}
e.executionHashes = make(map[string]context.Context)
e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks))
e.mkdirMutexMap = make(map[string]*sync.Mutex, len(e.Taskfile.Tasks))
for k := range e.Taskfile.Tasks {
e.taskCallCount[k] = new(int32)
e.mkdirMutexMap[k] = &sync.Mutex{}
}
if e.Concurrency > 0 {
e.concurrencySemaphore = make(chan struct{}, e.Concurrency)
}
return nil
}
// RunTask runs a task by its name
func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
t, err := e.CompiledTask(call)
@@ -354,7 +168,7 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
continue
}
return &taskRunError{t.Task, err}
return &TaskRunError{t.Task, err}
}
}
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" finished`, call.Task)
@@ -372,7 +186,7 @@ func (e *Executor) mkdir(t *taskfile.Task) error {
defer mutex.Unlock()
if _, err := os.Stat(t.Dir); os.IsNotExist(err) {
if err := os.MkdirAll(t.Dir, 0755); err != nil {
if err := os.MkdirAll(t.Dir, 0o755); err != nil {
return err
}
}
@@ -435,23 +249,19 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
if t.Interactive {
outputWrapper = output.Interleaved{}
}
stdOut := outputWrapper.WrapWriter(e.Stdout, t.Prefix)
stdErr := outputWrapper.WrapWriter(e.Stderr, t.Prefix)
vars, err := e.Compiler.FastGetVariables(t, call)
outputTemplater := &templater.Templater{Vars: vars, RemoveNoValue: true}
if err != nil {
return fmt.Errorf("task: failed to get variables: %w", err)
}
stdOut, stdErr, close := outputWrapper.WrapWriter(e.Stdout, e.Stderr, t.Prefix, outputTemplater)
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()
}
if err := close(); err != nil {
e.Logger.Errf(logger.Red, "task: unable to close writter: %v", err)
}
}()
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
err = execext.RunCommand(ctx, &execext.RunCommandOptions{
Command: cmd.Cmd,
Dir: t.Dir,
Env: getEnviron(t),

View File

@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/taskfile"
)
@@ -37,11 +38,12 @@ func (fct fileContentTest) name(file string) string {
func (fct fileContentTest) Run(t *testing.T) {
for f := range fct.Files {
_ = os.Remove(filepath.Join(fct.Dir, f))
_ = os.Remove(filepathext.SmartJoin(fct.Dir, f))
}
e := &task.Executor{
Dir: fct.Dir,
TempDir: filepathext.SmartJoin(fct.Dir, ".task"),
Entrypoint: fct.Entrypoint,
Stdout: io.Discard,
Stderr: io.Discard,
@@ -51,13 +53,14 @@ func (fct fileContentTest) Run(t *testing.T) {
for name, expectContent := range fct.Files {
t.Run(fct.name(name), func(t *testing.T) {
b, err := os.ReadFile(filepath.Join(fct.Dir, name))
path := filepathext.SmartJoin(fct.Dir, name)
b, err := os.ReadFile(path)
assert.NoError(t, err, "Error reading file")
s := string(b)
if fct.TrimSpace {
s = strings.TrimSpace(s)
}
assert.Equal(t, expectContent, s, "unexpected file content")
assert.Equal(t, expectContent, s, "unexpected file content in %s", path)
})
}
}
@@ -161,6 +164,39 @@ func TestMultilineVars(t *testing.T) {
}
}
func TestSpecialVars(t *testing.T) {
const dir = "testdata/special_vars"
const target = "default"
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: target}))
toAbs := func(rel string) string {
abs, err := filepath.Abs(rel)
assert.NoError(t, err)
return abs
}
output := buff.String()
// Root Taskfile
assert.Contains(t, output, "root/TASK=print")
assert.Contains(t, output, "root/ROOT_DIR="+toAbs("testdata/special_vars"))
assert.Contains(t, output, "root/TASKFILE_DIR="+toAbs("testdata/special_vars"))
// Included Taskfile
assert.Contains(t, output, "included/TASK=included:print")
assert.Contains(t, output, "included/ROOT_DIR="+toAbs("testdata/special_vars"))
assert.Contains(t, output, "included/TASKFILE_DIR="+toAbs("testdata/special_vars/included"))
}
func TestVarsInvalidTmpl(t *testing.T) {
const (
dir = "testdata/vars/v2"
@@ -233,7 +269,7 @@ func TestDeps(t *testing.T) {
}
for _, f := range files {
_ = os.Remove(filepath.Join(dir, f))
_ = os.Remove(filepathext.SmartJoin(dir, f))
}
e := &task.Executor{
@@ -245,7 +281,7 @@ func TestDeps(t *testing.T) {
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
for _, f := range files {
f = filepath.Join(dir, f)
f = filepathext.SmartJoin(dir, f)
if _, err := os.Stat(f); err != nil {
t.Errorf("File %s should exist", f)
}
@@ -261,7 +297,7 @@ func TestStatus(t *testing.T) {
}
for _, f := range files {
path := filepath.Join(dir, f)
path := filepathext.SmartJoin(dir, f)
_ = os.Remove(path)
if _, err := os.Stat(path); err == nil {
t.Errorf("File should not exist: %v", err)
@@ -270,17 +306,18 @@ func TestStatus(t *testing.T) {
var buff bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: true,
Dir: dir,
TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: &buff,
Stderr: &buff,
Silent: true,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
for _, f := range files {
if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
if _, err := os.Stat(filepathext.SmartJoin(dir, f)); err != nil {
t.Errorf("File should exist: %v", err)
}
}
@@ -357,10 +394,10 @@ func TestGenerates(t *testing.T) {
fileWithSpaces = "my text file.txt"
)
var srcFile = filepath.Join(dir, srcTask)
var srcFile = filepathext.SmartJoin(dir, srcTask)
for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} {
path := filepath.Join(dir, task)
path := filepathext.SmartJoin(dir, task)
_ = os.Remove(path)
if _, err := os.Stat(path); err == nil {
t.Errorf("File should not exist: %v", err)
@@ -376,7 +413,7 @@ func TestGenerates(t *testing.T) {
assert.NoError(t, e.Setup())
for _, theTask := range []string{relTask, absTask, fileWithSpaces} {
var destFile = filepath.Join(dir, theTask)
var destFile = filepathext.SmartJoin(dir, theTask)
var upToDate = fmt.Sprintf("task: Task \"%s\" is up to date\n", srcTask) +
fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask)
@@ -413,23 +450,24 @@ func TestStatusChecksum(t *testing.T) {
}
for _, f := range files {
_ = os.Remove(filepath.Join(dir, f))
_ = os.Remove(filepathext.SmartJoin(dir, f))
_, err := os.Stat(filepath.Join(dir, f))
_, err := os.Stat(filepathext.SmartJoin(dir, f))
assert.Error(t, err)
}
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Dir: dir,
TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
for _, f := range files {
_, err := os.Stat(filepath.Join(dir, f))
_, err := os.Stat(filepathext.SmartJoin(dir, f))
assert.NoError(t, err)
}
@@ -573,12 +611,13 @@ func TestListCanListDescOnly(t *testing.T) {
func TestStatusVariables(t *testing.T) {
const dir = "testdata/status_vars"
_ = os.RemoveAll(filepath.Join(dir, ".task"))
_ = os.Remove(filepath.Join(dir, "generated.txt"))
_ = os.RemoveAll(filepathext.SmartJoin(dir, ".task"))
_ = os.Remove(filepathext.SmartJoin(dir, "generated.txt"))
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: &buff,
Stderr: &buff,
Silent: false,
@@ -589,10 +628,10 @@ func TestStatusVariables(t *testing.T) {
assert.Contains(t, buff.String(), "d41d8cd98f00b204e9800998ecf8427e")
inf, err := os.Stat(filepath.Join(dir, "source.txt"))
inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt"))
assert.NoError(t, err)
ts := fmt.Sprintf("%d", inf.ModTime().Unix())
tf := fmt.Sprintf("%s", inf.ModTime())
tf := inf.ModTime().String()
assert.Contains(t, buff.String(), ts)
assert.Contains(t, buff.String(), tf)
@@ -600,7 +639,7 @@ func TestStatusVariables(t *testing.T) {
func TestInit(t *testing.T) {
const dir = "testdata/init"
var file = filepath.Join(dir, "Taskfile.yaml")
var file = filepathext.SmartJoin(dir, "Taskfile.yaml")
_ = os.Remove(file)
if _, err := os.Stat(file); err == nil {
@@ -689,7 +728,7 @@ func TestExpand(t *testing.T) {
func TestDry(t *testing.T) {
const dir = "testdata/dry"
file := filepath.Join(dir, "file.txt")
file := filepathext.SmartJoin(dir, "file.txt")
_ = os.Remove(file)
var buff bytes.Buffer
@@ -714,14 +753,15 @@ func TestDry(t *testing.T) {
func TestDryChecksum(t *testing.T) {
const dir = "testdata/dry_checksum"
checksumFile := filepath.Join(dir, ".task/checksum/default")
checksumFile := filepathext.SmartJoin(dir, ".task/checksum/default")
_ = os.Remove(checksumFile)
e := task.Executor{
Dir: dir,
Stdout: io.Discard,
Stderr: io.Discard,
Dry: true,
Dir: dir,
TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: io.Discard,
Stderr: io.Discard,
Dry: true,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
@@ -753,6 +793,36 @@ func TestIncludes(t *testing.T) {
tt.Run(t)
}
func TestIncludesMultiLevel(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/includes_multi_level",
Target: "default",
TrimSpace: true,
Files: map[string]string{
"called_one.txt": "one",
"called_two.txt": "two",
"called_three.txt": "three",
},
}
tt.Run(t)
}
func TestIncludeCycle(t *testing.T) {
const dir = "testdata/includes_cycle"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: true,
}
err := e.Setup()
assert.Error(t, err)
assert.Contains(t, err.Error(), "task: include cycle detected between")
}
func TestIncorrectVersionIncludes(t *testing.T) {
const dir = "testdata/incorrect_includes"
expectedError := "task: Import with additional parameters is only available starting on Taskfile version v3"
@@ -818,27 +888,39 @@ func TestIncludesOptional(t *testing.T) {
}
func TestIncludesOptionalImplicitFalse(t *testing.T) {
const dir = "testdata/includes_optional_implicit_false"
wd, _ := os.Getwd()
message := "stat %s/%s/TaskfileOptional.yml: no such file or directory"
expected := fmt.Sprintf(message, wd, dir)
e := task.Executor{
Dir: "testdata/includes_optional_implicit_false",
Dir: dir,
Stdout: io.Discard,
Stderr: io.Discard,
}
err := e.Setup()
assert.Error(t, err)
assert.Equal(t, "stat testdata/includes_optional_implicit_false/TaskfileOptional.yml: no such file or directory", err.Error())
assert.Equal(t, expected, err.Error())
}
func TestIncludesOptionalExplicitFalse(t *testing.T) {
const dir = "testdata/includes_optional_explicit_false"
wd, _ := os.Getwd()
message := "stat %s/%s/TaskfileOptional.yml: no such file or directory"
expected := fmt.Sprintf(message, wd, dir)
e := task.Executor{
Dir: "testdata/includes_optional_explicit_false",
Dir: dir,
Stdout: io.Discard,
Stderr: io.Discard,
}
err := e.Setup()
assert.Error(t, err)
assert.Equal(t, "stat testdata/includes_optional_explicit_false/TaskfileOptional.yml: no such file or directory", err.Error())
assert.Equal(t, expected, err.Error())
}
func TestIncludesFromCustomTaskfile(t *testing.T) {
@@ -856,6 +938,128 @@ func TestIncludesFromCustomTaskfile(t *testing.T) {
tt.Run(t)
}
func TestIncludesRelativePath(t *testing.T) {
const dir = "testdata/includes_rel_path"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "common:pwd"}))
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
buff.Reset()
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "included:common:pwd"}))
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
}
func TestIncludesInternal(t *testing.T) {
const dir = "testdata/internal_task"
tests := []struct {
name string
task string
expectedErr bool
expectedOutput string
}{
{"included internal task via task", "task-1", false, "Hello, World!\n"},
{"included internal task via dep", "task-2", false, "Hello, World!\n"},
{
"included internal direct",
"included:task-3",
true,
"task: No tasks with description available. Try --list-all to list all tasks\n",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: true,
}
assert.NoError(t, e.Setup())
err := e.Run(context.Background(), taskfile.Call{Task: test.task})
if test.expectedErr {
assert.Error(t, err, test.expectedErr)
} else {
assert.NoError(t, err)
}
assert.Equal(t, test.expectedOutput, buff.String())
})
}
}
func TestInternalTask(t *testing.T) {
const dir = "testdata/internal_task"
tests := []struct {
name string
task string
expectedErr bool
expectedOutput string
}{
{"internal task via task", "task-1", false, "Hello, World!\n"},
{"internal task via dep", "task-2", false, "Hello, World!\n"},
{
"internal direct",
"task-3",
true,
"task: No tasks with description available. Try --list-all to list all tasks\n",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: true,
}
assert.NoError(t, e.Setup())
err := e.Run(context.Background(), taskfile.Call{Task: test.task})
if test.expectedErr {
assert.Error(t, err, test.expectedErr)
} else {
assert.NoError(t, err)
}
assert.Equal(t, test.expectedOutput, buff.String())
})
}
}
func TestSupportedFileNames(t *testing.T) {
fileNames := []string{
"Taskfile.yml",
"Taskfile.yaml",
"Taskfile.dist.yml",
"Taskfile.dist.yaml",
}
for _, fileName := range fileNames {
t.Run(fileName, func(t *testing.T) {
tt := fileContentTest{
Dir: fmt.Sprintf("testdata/file_names/%s", fileName),
Target: "default",
TrimSpace: true,
Files: map[string]string{
"output.txt": "hello",
},
}
tt.Run(t)
})
}
}
func TestSummary(t *testing.T) {
const dir = "testdata/summary"
@@ -870,12 +1074,12 @@ func TestSummary(t *testing.T) {
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-summary"}, taskfile.Call{Task: "other-task-with-summary"}))
data, err := os.ReadFile(filepath.Join(dir, "task-with-summary.txt"))
data, err := os.ReadFile(filepathext.SmartJoin(dir, "task-with-summary.txt"))
assert.NoError(t, err)
expectedOutput := string(data)
if runtime.GOOS == "windows" {
expectedOutput = strings.Replace(expectedOutput, "\r\n", "\n", -1)
expectedOutput = strings.ReplaceAll(expectedOutput, "\r\n", "\n")
}
assert.Equal(t, expectedOutput, buff.String())
@@ -1141,7 +1345,6 @@ task: [task-1] echo 'task-1 ran successfully'
task-1 ran successfully
`)
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "task-2"}))
fmt.Println(buff.String())
assert.Contains(t, buff.String(), expectedOutputOrder)
}
@@ -1171,3 +1374,109 @@ func TestIgnoreNilElements(t *testing.T) {
})
}
}
func TestOutputGroup(t *testing.T) {
const dir = "testdata/output_group"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
expectedOutputOrder := strings.TrimSpace(`
task: [hello] echo 'Hello!'
::group::hello
Hello!
::endgroup::
task: [bye] echo 'Bye!'
::group::bye
Bye!
::endgroup::
`)
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "bye"}))
t.Log(buff.String())
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
}
func TestIncludedVars(t *testing.T) {
const dir = "testdata/include_with_vars"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
expectedOutputOrder := strings.TrimSpace(`
task: [included1:task1] echo "VAR_1 is included1-var1"
VAR_1 is included1-var1
task: [included1:task1] echo "VAR_2 is included-default-var2"
VAR_2 is included-default-var2
task: [included2:task1] echo "VAR_1 is included2-var1"
VAR_1 is included2-var1
task: [included2:task1] echo "VAR_2 is included-default-var2"
VAR_2 is included-default-var2
task: [included3:task1] echo "VAR_1 is included-default-var1"
VAR_1 is included-default-var1
task: [included3:task1] echo "VAR_2 is included-default-var2"
VAR_2 is included-default-var2
`)
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task1"}))
t.Log(buff.String())
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
}
func TestErrorCode(t *testing.T) {
const dir = "testdata/error_code"
var buff bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: true,
}
assert.NoError(t, e.Setup())
err := e.Run(context.Background(), taskfile.Call{Task: "test-exit-code"})
assert.Error(t, err)
casted, ok := err.(*task.TaskRunError)
assert.True(t, ok, "cannot cast returned error to *task.TaskRunError")
assert.Equal(t, 42, casted.ExitCode(), "unexpected exit code from task")
}
func TestEvaluateSymlinksInPaths(t *testing.T) {
const dir = "testdata/evaluate_symlinks_in_paths"
var buff bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: false,
}
assert.NoError(t, e.Setup())
err := e.Run(context.Background(), taskfile.Call{Task: "default"})
assert.NoError(t, err)
assert.NotEqual(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "test-sym"})
assert.NoError(t, err)
assert.NotEqual(t, `task: Task "test-sym" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "default"})
assert.NoError(t, err)
assert.NotEqual(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "default"})
assert.NoError(t, err)
assert.Equal(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "reset"})
assert.NoError(t, err)
buff.Reset()
err = os.RemoveAll(dir + "/.task")
assert.NoError(t, err)
}

View File

@@ -2,16 +2,24 @@ package taskfile
import (
"errors"
"fmt"
"path/filepath"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"gopkg.in/yaml.v3"
)
// IncludedTaskfile represents information about included tasksfile
// IncludedTaskfile represents information about included taskfiles
type IncludedTaskfile struct {
Taskfile string
Dir string
Optional bool
Internal bool
AdvancedImport bool
Vars *Vars
BaseDir string // The directory from which the including taskfile was loaded; used to resolve relative paths
}
// IncludedTaskfiles represents information about included tasksfiles
@@ -52,7 +60,7 @@ func (tfs *IncludedTaskfiles) Len() int {
// Merge merges the given IncludedTaskfiles into the caller one
func (tfs *IncludedTaskfiles) Merge(other *IncludedTaskfiles) {
other.Range(func(key string, value IncludedTaskfile) error {
_ = other.Range(func(key string, value IncludedTaskfile) error {
tfs.Set(key, value)
return nil
})
@@ -94,6 +102,8 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
Taskfile string
Dir string
Optional bool
Internal bool
Vars *Vars
}
if err := unmarshal(&includedTaskfile); err != nil {
return err
@@ -101,6 +111,36 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
it.Taskfile = includedTaskfile.Taskfile
it.Dir = includedTaskfile.Dir
it.Optional = includedTaskfile.Optional
it.Internal = includedTaskfile.Internal
it.AdvancedImport = true
it.Vars = includedTaskfile.Vars
return nil
}
// FullTaskfilePath returns the fully qualified path to the included taskfile
func (it *IncludedTaskfile) FullTaskfilePath() (string, error) {
return it.resolvePath(it.Taskfile)
}
// FullDirPath returns the fully qualified path to the included taskfile's working directory
func (it *IncludedTaskfile) FullDirPath() (string, error) {
return it.resolvePath(it.Dir)
}
func (it *IncludedTaskfile) resolvePath(path string) (string, error) {
path, err := execext.Expand(path)
if err != nil {
return "", err
}
if filepath.IsAbs(path) {
return path, nil
}
result, err := filepath.Abs(filepathext.SmartJoin(it.BaseDir, path))
if err != nil {
return "", fmt.Errorf("task: error resolving path %s relative to %s: %w", path, it.BaseDir, err)
}
return result, nil
}

View File

@@ -9,15 +9,15 @@ import (
const NamespaceSeparator = ":"
// Merge merges the second Taskfile into the first
func Merge(t1, t2 *Taskfile, namespaces ...string) error {
func Merge(t1, t2 *Taskfile, internal bool, namespaces ...string) error {
if t1.Version != t2.Version {
return fmt.Errorf(`Taskfiles versions should match. First is "%s" but second is "%s"`, t1.Version, t2.Version)
return fmt.Errorf(`task: Taskfiles versions should match. First is "%s" but second is "%s"`, t1.Version, t2.Version)
}
if t2.Expansions != 0 && t2.Expansions != 2 {
t1.Expansions = t2.Expansions
}
if t2.Output != "" {
if t2.Output.IsSet() {
t1.Output = t2.Output
}
@@ -43,6 +43,8 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
// have serious side-effects in the future, since we're editing
// the original references instead of deep copying them.
v.Internal = v.Internal || internal
t1.Tasks[taskNameWithNamespace(k, namespaces...)] = v
for _, dep := range v.Deps {

55
taskfile/output.go Normal file
View File

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

View File

@@ -2,11 +2,11 @@ package read
import (
"os"
"path/filepath"
"github.com/joho/godotenv"
"github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
)
@@ -27,10 +27,11 @@ func Dotenv(c compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.V
for _, dotEnvPath := range tf.Dotenv {
dotEnvPath = tr.Replace(dotEnvPath)
if !filepath.IsAbs(dotEnvPath) {
dotEnvPath = filepath.Join(dir, dotEnvPath)
if dotEnvPath == "" {
continue
}
dotEnvPath = filepathext.SmartJoin(dir, dotEnvPath)
if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
continue
}

View File

@@ -9,35 +9,47 @@ import (
"gopkg.in/yaml.v3"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
)
var (
// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
// ErrIncludedTaskfilesCantHaveDotenvs is returned when a included Taskfile contains dotenvs
ErrIncludedTaskfilesCantHaveDotenvs = errors.New("task: Included Taskfiles can't have dotenv declarations. Please, move the dotenv declaration to the main Taskfile")
defaultTaskfiles = []string{"Taskfile.yml", "Taskfile.yaml"}
defaultTaskfiles = []string{
"Taskfile.yml",
"Taskfile.yaml",
"Taskfile.dist.yml",
"Taskfile.dist.yaml",
}
)
type ReaderNode struct {
Dir string
Entrypoint string
Optional bool
Parent *ReaderNode
}
// Taskfile reads a Taskfile for a given directory
// Uses current dir when dir is left empty. Uses Taskfile.yml
// or Taskfile.yaml when entrypoint is left empty
func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
if dir == "" {
func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
if readerNode.Dir == "" {
d, err := os.Getwd()
if err != nil {
return nil, err
}
dir = d
readerNode.Dir = d
}
path, err := exists(filepath.Join(dir, entrypoint))
path, err := exists(filepathext.SmartJoin(readerNode.Dir, readerNode.Entrypoint))
if err != nil {
return nil, err
}
readerNode.Entrypoint = filepath.Base(path)
t, err := readTaskfile(path)
if err != nil {
@@ -49,6 +61,16 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
return nil, err
}
// Annotate any included Taskfile reference with a base directory for resolving relative paths
_ = t.Includes.Range(func(key string, includedFile taskfile.IncludedTaskfile) error {
// Set the base directory for resolving relative paths, but only if not already set
if includedFile.BaseDir == "" {
includedFile.BaseDir = readerNode.Dir
t.Includes.Set(key, includedFile)
}
return nil
})
err = t.Includes.Range(func(namespace string, includedTask taskfile.IncludedTaskfile) error {
if v >= 3.0 {
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
@@ -56,20 +78,20 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
Taskfile: tr.Replace(includedTask.Taskfile),
Dir: tr.Replace(includedTask.Dir),
Optional: includedTask.Optional,
Internal: includedTask.Internal,
AdvancedImport: includedTask.AdvancedImport,
Vars: includedTask.Vars,
BaseDir: includedTask.BaseDir,
}
if err := tr.Err(); err != nil {
return err
}
}
path, err := execext.Expand(includedTask.Taskfile)
path, err := includedTask.FullTaskfilePath()
if err != nil {
return err
}
if !filepath.IsAbs(path) {
path = filepath.Join(dir, path)
}
path, err = exists(path)
if err != nil {
@@ -79,12 +101,23 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
return err
}
includedTaskfile, err := readTaskfile(path)
if err != nil {
includeReaderNode := &ReaderNode{
Dir: filepath.Dir(path),
Entrypoint: filepath.Base(path),
Parent: readerNode,
Optional: includedTask.Optional,
}
if err := checkCircularIncludes(includeReaderNode); err != nil {
return err
}
if includedTaskfile.Includes.Len() > 0 {
return ErrIncludedTaskfilesCantHaveIncludes
includedTaskfile, err := Taskfile(includeReaderNode)
if err != nil {
if includedTask.Optional {
return nil
}
return err
}
if v >= 3.0 && len(includedTaskfile.Dotenv) > 0 {
@@ -92,25 +125,31 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
}
if includedTask.AdvancedImport {
dir, err := includedTask.FullDirPath()
if err != nil {
return err
}
for k, v := range includedTaskfile.Vars.Mapping {
o := v
o.Dir = filepath.Join(dir, includedTask.Dir)
o.Dir = dir
includedTaskfile.Vars.Mapping[k] = o
}
for k, v := range includedTaskfile.Env.Mapping {
o := v
o.Dir = filepath.Join(dir, includedTask.Dir)
o.Dir = dir
includedTaskfile.Env.Mapping[k] = o
}
for _, task := range includedTaskfile.Tasks {
if !filepath.IsAbs(task.Dir) {
task.Dir = filepath.Join(includedTask.Dir, task.Dir)
}
task.Dir = filepathext.SmartJoin(dir, task.Dir)
task.IncludeVars = includedTask.Vars
task.IncludedTaskfileVars = includedTaskfile.Vars
task.IncludedTaskfile = &includedTask
}
}
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
if err = taskfile.Merge(t, includedTaskfile, includedTask.Internal, namespace); err != nil {
return err
}
return nil
@@ -120,13 +159,13 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
}
if v < 3.0 {
path = filepath.Join(dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
path = filepathext.SmartJoin(readerNode.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 {
if err = taskfile.Merge(t, osTaskfile, false); err != nil {
return nil, err
}
}
@@ -162,7 +201,7 @@ func exists(path string) (string, error) {
}
for _, n := range defaultTaskfiles {
fpath := filepath.Join(path, n)
fpath := filepathext.SmartJoin(path, n)
if _, err := os.Stat(fpath); err == nil {
return fpath, nil
}
@@ -170,3 +209,25 @@ func exists(path string) (string, error) {
return "", fmt.Errorf(`task: No Taskfile found in "%s". Use "task --init" to create a new one`, path)
}
func checkCircularIncludes(node *ReaderNode) error {
if node == nil {
return errors.New("task: failed to check for include cycle: node was nil")
}
if node.Parent == nil {
return errors.New("task: failed to check for include cycle: node.Parent was nil")
}
var curNode = node
var basePath = filepathext.SmartJoin(node.Dir, node.Entrypoint)
for curNode.Parent != nil {
curNode = curNode.Parent
curPath := filepathext.SmartJoin(curNode.Dir, curNode.Entrypoint)
if curPath == basePath {
return fmt.Errorf("task: include cycle detected between %s <--> %s",
curPath,
filepathext.SmartJoin(node.Parent.Dir, node.Parent.Entrypoint),
)
}
}
return nil
}

View File

@@ -3,11 +3,11 @@ package read
import (
"fmt"
"os"
"path/filepath"
"runtime"
"gopkg.in/yaml.v3"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/taskfile"
)
@@ -15,7 +15,7 @@ import (
func Taskvars(dir string) (*taskfile.Vars, error) {
vars := &taskfile.Vars{}
path := filepath.Join(dir, "Taskvars.yml")
path := filepathext.SmartJoin(dir, "Taskvars.yml")
if _, err := os.Stat(path); err == nil {
vars, err = readTaskvars(path)
if err != nil {
@@ -23,7 +23,7 @@ func Taskvars(dir string) (*taskfile.Vars, error) {
}
}
path = filepath.Join(dir, fmt.Sprintf("Taskvars_%s.yml", runtime.GOOS))
path = filepathext.SmartJoin(dir, fmt.Sprintf("Taskvars_%s.yml", runtime.GOOS))
if _, err := os.Stat(path); err == nil {
osVars, err := readTaskvars(path)
if err != nil {

View File

@@ -5,25 +5,29 @@ type Tasks map[string]*Task
// Task represents a task
type Task struct {
Task string
Cmds []*Cmd
Deps []*Dep
Label string
Desc string
Summary string
Sources []string
Generates []string
Status []string
Preconditions []*Precondition
Dir string
Vars *Vars
Env *Vars
Silent bool
Interactive bool
Method string
Prefix string
IgnoreError bool
Run string
Task string
Cmds []*Cmd
Deps []*Dep
Label string
Desc string
Summary string
Sources []string
Generates []string
Status []string
Preconditions []*Precondition
Dir string
Vars *Vars
Env *Vars
Silent bool
Interactive bool
Internal bool
Method string
Prefix string
IgnoreError bool
Run string
IncludeVars *Vars
IncludedTaskfileVars *Vars
IncludedTaskfile *IncludedTaskfile
}
func (t *Task) Name() string {
@@ -61,6 +65,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
Env *Vars
Silent bool
Interactive bool
Internal bool
Method string
Prefix string
IgnoreError bool `yaml:"ignore_error"`
@@ -83,6 +88,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
t.Env = task.Env
t.Silent = task.Silent
t.Interactive = task.Interactive
t.Internal = task.Internal
t.Method = task.Method
t.Prefix = task.Prefix
t.IgnoreError = task.IgnoreError

View File

@@ -9,7 +9,7 @@ import (
type Taskfile struct {
Version string
Expansions int
Output string
Output Output
Method string
Includes *IncludedTaskfiles
Vars *Vars
@@ -25,7 +25,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
var taskfile struct {
Version string
Expansions int
Output string
Output Output
Method string
Includes *IncludedTaskfiles
Vars *Vars

View File

@@ -36,7 +36,7 @@ func (vs *Vars) UnmarshalYAML(node *yaml.Node) error {
// Merge merges the given Vars into the caller one
func (vs *Vars) Merge(other *Vars) {
other.Range(func(key string, value Var) error {
_ = other.Range(func(key string, value Var) error {
vs.Set(key, value)
return nil
})
@@ -70,7 +70,7 @@ func (vs *Vars) Range(yield func(key string, value Var) error) error {
// variables
func (vs *Vars) ToCacheMap() (m map[string]interface{}) {
m = make(map[string]interface{}, vs.Len())
vs.Range(func(k string, v Var) error {
_ = 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.

6
testdata/error_code/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
version: '3'
tasks:
test-exit-code:
cmds:
- exit 42

View File

@@ -0,0 +1,17 @@
version: '3'
tasks:
default:
sources:
- src/**/*
cmds:
- echo "some job"
test-sym:
cmds:
- echo "shared file source changed" > src/shared/b
reset:
cmds:
- echo "shared file source" > src/shared/b
- echo "file source" > src/a

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