Compare commits

..

101 Commits

Author SHA1 Message Date
Andrey Nering
35599af04b v3.18.0 2022-11-12 13:36:52 -03:00
Andrey Nering
a74b35379e Merge pull request #932 from go-task/dependabot/go_modules/github.com/mattn/go-zglob-0.0.4
Bump github.com/mattn/go-zglob from 0.0.3 to 0.0.4
2022-11-12 13:13:04 -03:00
dependabot[bot]
7d16c9f68d Bump github.com/mattn/go-zglob from 0.0.3 to 0.0.4
Bumps [github.com/mattn/go-zglob](https://github.com/mattn/go-zglob) from 0.0.3 to 0.0.4.
- [Release notes](https://github.com/mattn/go-zglob/releases)
- [Commits](https://github.com/mattn/go-zglob/compare/v0.0.3...v0.0.4)

---
updated-dependencies:
- dependency-name: github.com/mattn/go-zglob
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-12 16:09:23 +00:00
Andrey Nering
890759cc5f Add CHANGELOG entry for #919 2022-11-12 10:19:11 -03:00
Andrey Nering
e710e2cc5d Merge pull request #919 from go-task/include-alises-in-list-when-silent
feat: include aliases in --list --silent output
2022-11-12 10:16:38 -03:00
Andrey Nering
d787faece4 Improve docs/Taskfile.yml 2022-11-12 09:26:35 -03:00
Andrey Nering
9702109ea9 Merge pull request #927 from go-task/dependabot/npm_and_yarn/docs/loader-utils-2.0.3
Bump loader-utils from 2.0.2 to 2.0.3 in /docs
2022-11-09 10:07:47 -03:00
dependabot[bot]
e547829505 Bump loader-utils from 2.0.2 to 2.0.3 in /docs
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.2 to 2.0.3.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.3/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v2.0.3)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-09 07:32:40 +00:00
Pete Davison
a664a26062 Merge pull request #890 from go-task/task-list-filtering
refactor: implement task list filtering
2022-11-02 14:49:06 +00:00
Pete Davison
fa105a8a93 refactor: implement task list filtering 2022-11-02 14:42:59 +00:00
Andrey Nering
3a0c7a8c36 Add CHANGELOG for #815
Closes #661
2022-11-02 11:39:05 -03:00
Andrey Nering
13f4b376e8 Merge pull request #815 from pd93/default-included-task
feat: run default task in included file when task is omitted
2022-11-02 11:34:19 -03:00
Pete Davison
5a08409a27 feat: run default task in included file when task is omitted 2022-11-02 14:27:15 +00:00
Andrey Nering
9bbdac3c2e Merge pull request #914 from go-task/update-contribution-guide
chore: add schema info to contribution guide
2022-11-02 11:22:19 -03:00
Andrey Nering
a990ffe53d Refactor: Use early return 2022-11-02 10:23:19 -03:00
Pete Davison
3a4b347d50 feat: include aliases in --list --silent output 2022-10-31 17:19:27 +00:00
Pete Davison
b80e1e4a43 chore: add schema info to contribution guide 2022-10-29 16:04:23 +00:00
Andrey Nering
fd71dfda6a Move docs/schema.json to docs/static/schema.json 2022-10-29 11:59:42 -03:00
Andrey Nering
fdbcbd395d Merge pull request #895 from Eun/add-arm-vx-builds
fix: add armv5 & armv7 builds
2022-10-29 11:57:46 -03:00
Andrey Nering
dba964b559 Merge pull request #910 from go-task/bring-schema-in-house
Bring schema in house
2022-10-29 11:54:08 -03:00
Andrey Nering
8e0816a09d Merge pull request #901 from go-task/github-actions-labels
feat: add actions to auto label issues
2022-10-29 11:52:37 -03:00
Andrey Nering
405b79f86c Merge pull request #902 from trim21/patch-1
make powershell completion works to fill task name
2022-10-29 11:46:45 -03:00
Pete Davison
620e6955e5 chore: update schema/docs to match
- Also reorder them both to match the struct definitions. This makes it easier to compare them
2022-10-29 14:45:32 +00:00
Pete Davison
a4997dd54d feat: copy schema as defined in schemastore 2022-10-29 14:43:47 +00:00
Andrey Nering
3efa9ac8c3 Documentation: Small adjustments to #906 2022-10-29 11:41:05 -03:00
Andrey Nering
fdd52d74e9 Merge pull request #906 from viconnex/add-completion-documentation
Document completion installation
2022-10-29 11:37:58 -03:00
Andrey Nering
ac81dea3ec Merge pull request #912 from go-task/dependabot/go_modules/github.com/stretchr/testify-1.8.1
Bump github.com/stretchr/testify from 1.8.0 to 1.8.1
2022-10-29 11:23:06 -03:00
dependabot[bot]
549c37ef87 Bump github.com/stretchr/testify from 1.8.0 to 1.8.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.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-10-29 11:03:18 +00:00
Victor Lebrun
6a369ee31c Document completion installation 2022-10-25 21:00:00 +02:00
Trim21
2e573d37ae Update task.ps1 2022-10-18 00:09:07 +08:00
Trim21
394afe2633 Update task.ps1 2022-10-17 23:52:56 +08:00
Tobias Salzmann
99ed3001f0 revert changes, add fix for arm downloads 2022-10-17 09:25:26 +02:00
Pete Davison
9e4cab2af9 feat: add actions to auto label issues 2022-10-15 17:47:39 +00:00
Andrey Nering
33b6927b79 v3.17.0 2022-10-14 21:11:48 -03:00
Andrey Nering
852a176e1f PR Template: Fix content 2022-10-14 21:08:53 -03:00
Andrey Nering
7511249514 GitHub: Attempt to fix PR template 2022-10-14 21:08:07 -03:00
Andrey Nering
3429cdd8af GitHub: Giving up on fancy templates (sigh)
GitHub is was not showing them and I have no idea why.
2022-10-14 21:05:03 -03:00
Andrey Nering
a1cd8eafd8 GitHub: Yet another attempt to fix issue templates 2022-10-14 20:58:31 -03:00
Andrey Nering
fbfb4ba9c4 GitHub: Attempt to fix issue templates 2022-10-14 20:55:46 -03:00
Andrey Nering
ba9ba63792 GitHub: Improving our issue & PR templates 2022-10-14 20:47:09 -03:00
Andrey Nering
460b89ce51 Merge branch 'feat/suggest-similar-task' 2022-10-14 20:08:00 -03:00
Andrey Nering
a4ec6e5257 Improve test #887 2022-10-14 19:50:43 -03:00
Andrey Nering
44aa2ee3b3 Add CHANGELOG and improvements to #887 2022-10-14 19:48:45 -03:00
Andrey Nering
80b417c4ab Merge pull request #887 from ronaudinho/fix/yaml-error-msg
Add file path to YAML error message
2022-10-14 19:36:58 -03:00
Andrey Nering
6d90c781c9 Merge pull request #879 from go-task/aliases
Aliases
2022-10-14 19:32:04 -03:00
Andrey Nering
c51f04eca8 Add some alises to our own Taskfiles 2022-10-14 19:28:27 -03:00
Andrey Nering
dda2004753 --list: Print aliases on its own column 2022-10-14 19:28:05 -03:00
Andrey Nering
297f9eccea For include aliases, consider the main task name as well 2022-10-14 19:27:20 -03:00
Andrey Nering
d2f2cba6d8 Merge branch 'master' into aliases 2022-10-14 18:44:56 -03:00
Andrey Nering
172d71435a Add CHANGELOG for #897 2022-10-14 18:32:12 -03:00
Andrey Nering
bb1aec8a7e Merge pull request #897 from mymmrac/improve-fish-completion
Improved fish shell completions
2022-10-14 18:31:19 -03:00
Artem Yadelskyi
476d9f5e70 Improved fish shell completions 2022-10-15 00:24:11 +03:00
Andrey Nering
99014ad38d Small adjustments + CHANGELOG for #865 2022-10-14 16:51:14 -03:00
Andrey Nering
403456d3dc Merge pull request #865 from ilewin/configurable_watch_interval_#813
Configurable watch interval #813
2022-10-14 16:26:13 -03:00
Andrey Nering
6335878317 Merge pull request #896 from go-task/ignore-fleet-settings
Ignore fleet configuration directory
2022-10-13 12:35:40 -03:00
Pete Davison
6bff658af0 chore: ignore fleet configuration directory 2022-10-13 14:37:52 +00:00
Tobias Salzmann
b111e7bd12 add armv5 & armv7 builds 2022-10-13 11:49:13 +02:00
Max Pushkarov
3e5ee2332a feat: suggest the most similar task name when a given task does not exist 2022-10-10 13:17:19 +03:00
Pete Davison
66f6998c86 docs: update changelog 2022-10-07 11:11:45 +00:00
Pete Davison
f2a8f8ad8f docs: update usage and api reference 2022-10-07 11:11:45 +00:00
Pete Davison
540f6ecfdb tests: update alias test to cover namespace aliases 2022-10-07 11:11:45 +00:00
Pete Davison
8ec89f1bbd refactor: use x/exp/slices instead of custom contains function 2022-10-07 11:11:14 +00:00
Pete Davison
d33906b6e4 fix: deep copy included tasks 2022-10-07 11:11:14 +00:00
Pete Davison
bb79fa1dc3 feat: namespace aliases 2022-10-07 11:11:14 +00:00
Pete Davison
376a6182eb feat: aliases
feat: add aliases to --list and --list-all flags

feat: add aliases to --summary

feat: enable aliases for included tasks

tests: added alias unit tests
2022-10-07 11:11:14 +00:00
ronaudinho
81de61d8db Add file path to YAML error message 2022-10-07 10:18:53 +00:00
Pete Davison
d2061ec898 Merge pull request #874 from go-task/845-colorize-output
Colorize --list, --list-all and --summary output
2022-10-07 11:05:48 +01:00
Pete Davison
077efbd2e7 Add CHANGELOG for #874 2022-10-07 10:01:54 +00:00
Pete Davison
8ce1782380 feat: add coloured output to --list, --list-all and --summary flags
feat: add coloured output to --list and --list-all

feat: add coloured output to --summary

chore: update colors

refactor: better consistency in writer calls in printTasks

feat: subtasks are printed green in --summary
2022-10-07 09:54:17 +00:00
ilewin
c2f20465ab Updated test for file watcher interval param
Updated Interval Setting Priority
2022-10-07 08:56:13 +02:00
Andrey Nering
fb0e43989d Add CHANGELOG for #877 2022-10-06 22:46:59 -03:00
Andrey Nering
754248395c Merge pull request #877 from biozz/master
Fix task names for `--list` and `--list-all`
2022-10-06 22:44:40 -03:00
Andrey Nering
6e975ca155 Merge pull request #882 from go-task/contribution-guide
Add a contributions guide
2022-10-06 22:38:44 -03:00
Pete Davison
79a2bc404e draft: add a contributions guide 2022-10-07 01:21:49 +00:00
Andrey Nering
42a26e1741 Revert "Website: Update Open Graph image"
This reverts commit b16e705a6c.
2022-10-06 22:03:55 -03:00
Andrey Nering
695711e124 Merge pull request #881 from danquah/document-sources-generate-status
Clearify that sources/generate and status can be used together
2022-10-06 21:27:16 -03:00
Mads H. Danquah
0d5811e502 docs: Add paragraphs about how to use generates/sources + status 2022-10-03 13:00:14 +00:00
Ivan Elfimov
b9d070f76b Fix test related to task lables 2022-09-30 19:17:04 +04:00
Ivan Elfimov
122c3f083e Fix task names for --list and --list-all
Task names should not be overrided by `label`.
2022-09-30 19:02:50 +04:00
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
ilewin
d8dc091267 Merge branch 'go-task:master' into configurable_watch_interval_#813 2022-09-19 20:22:26 +02:00
ilewin
1c44d8049a Issue 813. Made watch interval configurable through global setting in Taskfile and through CLI arg.
Separated Taskfile param and Arg flag
2022-09-19 20:14:35 +02: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
Christian Daguerre
76caa16909 Make zsh completion list configurable 2022-09-07 10:33:55 +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
72 changed files with 2056 additions and 206 deletions

View File

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

View File

@@ -1,13 +1,15 @@
---
name: Bug Report
about: Use the template to report bugs and issues
about: Use this to report bugs and issues
---
> Thanks for your bug report!
>
> Before submitting this issue, please make sure the same problem was
> not already reported by someone else.
>
> Please describe the bug you're facing. Consider pasting example
> Taskfiles showing how to reproduce the problem.
- Task version:
- Operating System:
### Example Taskfile showing the issue
```yaml
```

View File

@@ -1,5 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Help forum on Discord
url: https://discord.com/channels/974121106208354339/1025054680289660989
about: 'The Discord #help channel is the best way to get help from the community.'
- name: Questions, Ideas and General Discussions
url: https://github.com/go-task/task/discussions
about: Ask questions and discuss general ideas with the community
about: Ask questions and discuss general ideas with the community.

View File

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

5
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,5 @@
> Thanks for your pull request, we really appreciate contributions!
>
> Please understand that it may take some time to be reviewed.
>
> Also, make sure to follow the [Contribution Guide](https://taskfile.dev/contributing/).

View File

@@ -0,0 +1,42 @@
name: issue awaiting response
on:
issue_comment:
types: [created]
jobs:
issue-awaiting-response:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
script: |
const issue = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
})
const comments = await github.paginate(
github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
}
)
const labels = await github.paginate(
github.rest.issues.listLabelsOnIssue, {
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
}
)
if (labels.find(label => label.name === 'awaiting response')) {
if (comments[comments.length-1].user?.login === issue.data.user?.login) {
github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
name: 'awaiting response'
})
}
}

View File

@@ -0,0 +1,28 @@
name: issue needs triage
on:
issues:
types: [opened]
jobs:
needs-triage:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
script: |
const labels = await github.paginate(
github.rest.issues.listLabelsOnIssue, {
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
}
)
if (labels.length === 0) {
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['needs triage']
})
}

2
.gitignore vendored
View File

@@ -22,6 +22,7 @@ dist/
# editors
.idea/
.vscode/
.fleet/
# exuberant ctags
tags
@@ -30,3 +31,4 @@ tags
!/bin/.keep
/testdata/vars/v1
/tmp
node_modules

View File

@@ -1,6 +1,44 @@
# Changelog
## v3.15.1 - 2022-09-08
## v3.18.0
- Show aliases on `task --list --silent` (`task --ls`). This means that aliases
will be completed by the completion scripts
([#919](https://github.com/go-task/task/pull/919)).
- Tasks in the root Taskfile will now be displayed first in `--list`/`--list-all`
output ([#806](https://github.com/go-task/task/pull/806), [#890](https://github.com/go-task/task/pull/890)).
- It's now possible to call a `default` task in an included Taskfile by using
just the namespace. For example: `docs:default` is now automatically
aliased to `docs`
([#661](https://github.com/go-task/task/issues/661), [#815](https://github.com/go-task/task/pull/815)).
## v3.17.0
- Add a "Did you mean ...?" suggestion when a task does not exits another one
with a similar name is found
([#867](https://github.com/go-task/task/issues/867), [#880](https://github.com/go-task/task/pull/880)).
- Now YAML parse errors will print which Taskfile failed to parse
([#885](https://github.com/go-task/task/issues/885), [#887](https://github.com/go-task/task/pull/887)).
- Add ability to set `aliases` for tasks and namespaces ([#268](https://github.com/go-task/task/pull/268), [#340](https://github.com/go-task/task/pull/340), [#879](https://github.com/go-task/task/pull/879)).
- Improvements to Fish shell completion
([#897](https://github.com/go-task/task/pull/897)).
- Added ability to set a different watch interval by setting
`interval: '500ms'` or using the `--interval=500ms` flag
([#813](https://github.com/go-task/task/issues/813), [#865](https://github.com/go-task/task/pull/865)).
- Add colored output to `--list`, `--list-all` and `--summary` flags ([#845](https://github.com/go-task/task/pull/845), [#874](https://github.com/go-task/task/pull/874)).
- Fix unexpected behavior where `label:` was being shown instead of the task
name on `--list`
([#603](https://github.com/go-task/task/issues/603), [#877](https://github.com/go-task/task/pull/877)).
## 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)).

View File

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

@@ -2,6 +2,7 @@ version: '3'
includes:
docs:
aliases: [d]
taskfile: ./docs
dir: ./docs
@@ -23,6 +24,7 @@ tasks:
install:
desc: Installs Task
aliases: [i]
sources:
- './**/*.go'
cmds:
@@ -42,6 +44,7 @@ tasks:
lint:
desc: Runs golangci-lint
aliases: [l]
sources:
- './**/*.go'
cmds:
@@ -65,6 +68,7 @@ tasks:
test:
desc: Runs test suite
aliases: [t]
deps: [install]
cmds:
- go test {{catLines .GO_PACKAGES}}
@@ -93,6 +97,11 @@ tasks:
- 'echo "" >> {{.FILE}}'
- 'cat CHANGELOG.md >> {{.FILE}}'
npm:publish:
desc: Publish release to npm
cmds:
- npm publish --access=public
packages:
cmds:
- echo '{{.GO_PACKAGES}}'

View File

@@ -73,6 +73,7 @@ func main() {
entrypoint string
output taskfile.Output
color bool
interval string
)
pflag.BoolVar(&versionFlag, "version", false, "show Task version")
@@ -96,6 +97,7 @@ func main() {
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.StringVarP(&interval, "interval", "I", "5s", "interval to watch for changes")
pflag.Parse()
if versionFlag {
@@ -151,6 +153,7 @@ func main() {
Parallel: parallel,
Color: color,
Concurrency: concurrency,
Interval: interval,
Stdin: os.Stdin,
Stdout: os.Stdout,
@@ -174,12 +177,16 @@ func main() {
}
if list {
e.ListTasksWithDesc()
if ok := e.ListTasks(task.FilterOutInternal(), task.FilterOutNoDesc()); !ok {
e.Logger.Outf(logger.Yellow, "task: No tasks with description available. Try --list-all to list all tasks")
}
return
}
if listAll {
e.ListAllTasks()
if ok := e.ListTasks(task.FilterOutInternal()); !ok {
e.Logger.Outf(logger.Yellow, "task: No tasks available")
}
return
}

View File

@@ -1,16 +1,24 @@
set GO_TASK_PROGNAME task
function __task_get_tasks --description "Prints all available tasks with their description"
set -l output ($GO_TASK_PROGNAME --list-all | sed '1d; s/\* \(.*\):\s*\(.*\)/\1\t\2/' | string split0)
# Read the list of tasks (and potential errors)
$GO_TASK_PROGNAME --list-all 2>&1 | read -lz rawOutput
# Return on non-zero exit code (for cases when there is no Taskfile found or etc.)
if test $status -ne 0
return
end
# Grab names and descriptions (if any) of the tasks
set -l output (echo $rawOutput | sed '1d; s/\* \(.*\):\s*\(.*\)/\1\t\2/' | string split0)
if test $output
echo $output
echo $output
end
end
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 $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'

View File

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

View File

@@ -3,6 +3,8 @@
local context state state_descr line
typeset -A opt_args
_GO_TASK_COMPLETION_LIST_OPTION="${GO_TASK_COMPLETION_LIST_OPTION:---list-all}"
# Listing commands from Taskfile.yml
function __task_list() {
local -a scripts cmd
@@ -27,7 +29,7 @@ function __task_list() {
(( enabled )) || return 0
scripts=()
for item in "${(@)${(f)$("${cmd[@]}" --list-all)}[2,-1]#\* }"; do
for item in "${(@)${(f)$("${cmd[@]}" $_GO_TASK_COMPLETION_LIST_OPTION)}[2,-1]#\* }"; do
task="${item%%:[[:space:]]*}"
desc="${item##[^[:space:]]##[[:space:]]##}"
scripts+=( "${task//:/\\:}:$desc" )

View File

@@ -1,13 +1,18 @@
version: '3'
tasks:
setup:
yarn:install:
desc: Setup Docusaurus locally
cmds:
- yarn install
sources:
- package.json
- yarn.lock
start:
default:
desc: Start website
deps: [yarn:install]
aliases: [s, start]
vars:
HOST: '{{default "localhost" .HOST}}'
PORT: '{{default "3001" .PORT}}'
@@ -16,6 +21,7 @@ tasks:
build:
desc: Build website
deps: [yarn:install]
cmds:
- npx docusaurus build
@@ -25,6 +31,7 @@ tasks:
- rm -rf ./build
deploy:
desc: Build and deploy Docusaurus. Requires GIT_USER and GIT_PASS envs to be previous set
desc: Build and deploy Docusaurus
summary: Requires GIT_USER and GIT_PASS envs to be previous set
cmds:
- npx docusaurus deploy

View File

@@ -30,11 +30,12 @@ variable
| `-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. |
| `-I` | `--interval` | `string` | `5s` | Sets a different watch interval when using `--watch`, the default being 5 seconds. This string should be a valid [Go Duration](https://pkg.go.dev/time#ParseDuration). |
| `-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. |
| | `--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. |
@@ -79,16 +80,16 @@ Some environment variables can be overriden to adjust Task behavior.
| 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. |
| `includes` | [`map[string]Include`](#include) | | Additional Taskfiles to be included. |
| `vars` | [`map[string]Variable`](#variable) | | A set of global variables. |
| `env` | [`map[string]Variable`](#variable) | | A set of global environment variables. |
| `tasks` | [`map[string]Task`](#task) | | A set of task definitions. |
| `silent` | `bool` | `false` | Default 'silent' options for this Taskfile. If `false`, can be overidden with `true` in a task by task basis. |
| `dotenv` | `[]string` | | A list of `.env` file paths to be parsed. |
| `tasks` | [`map[string]Task`](#task) | | The task definitions. |
| `run` | `string` | `always` | Default 'run' option for this Taskfile. Available options: `always`, `once` and `when_changed`. |
| `interval` | `string` | `5s` | Sets a different watch interval when using `--watch`, the default being 5 seconds. This string should be a valid [Go Duration](https://pkg.go.dev/time#ParseDuration). |
### Include
@@ -97,6 +98,9 @@ Some environment variables can be overriden to adjust Task behavior.
| `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` | Stops any task in the included Taskfile from being callable on the command line. These commands will also be omitted from the output when used with `--list`. |
| `aliases` | `[]string` | | Alternative names for the namespace of the included Taskfile. |
| `vars` | `map[string]Variable` | | A set of variables to apply to the included Taskfile. |
:::info
@@ -113,22 +117,26 @@ includes:
| 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. |
| `cmds` | [`[]Command`](#command) | | A list of shell commands to be executed. |
| `deps` | [`[]Dependency`](#dependency) | | A list of dependencies of this task. Tasks defined here will run in parallel before this task. |
| `label` | `string` | | Overrides the name of the task in the output when a task is run. Supports variables. |
| `desc` | `string` | | A short description of the task. This is displayed when calling `task --list`. |
| `summary` | `string` | | A longer description of the task. This is displayed when calling `task --summary [task]`. |
| `aliases` | `[]string` | | A list of alternative names by which the task can be called. |
| `sources` | `[]string` | | A list of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs. |
| `generates` | `[]string` | | A list of files meant to be generated by this task. Relevant for `timestamp` method. Can be file paths or star globs. |
| `status` | `[]string` | | A list of commands to check if this task should run. The task is skipped otherwise. This overrides `method`, `sources` and `generates`. |
| `preconditions` | [`[]Precondition`](#precondition) | | A list of commands to check if this task should run. If a condition is not met, the task will error. |
| `dir` | `string` | | The directory in which this task should run. Defaults to the current working directory. |
| `vars` | [`map[string]Variable`](#variable) | | A set of variables that can be used in the task. |
| `env` | [`map[string]Variable`](#variable) | | A set of environment variables that will be made available to shell commands. |
| `silent` | `bool` | `false` | Hides task name and command from output. The command's output will still be redirected to `STDOUT` and `STDERR`. When combined with the `--list` flag, task descriptions will be hidden. |
| `interactive` | `bool` | `false` | Tells task that the command is interactive. |
| `internal` | `bool` | `false` | Stops a task from being callable on the command line. It will also be omitted from the output when used with `--list`. |
| `method` | `string` | `checksum` | Defines which method is used to check the task is up-to-date. `timestamp` will compare the timestamp of the sources and generates files. `checksum` will check the checksum (You probably want to ignore the .task folder in your .gitignore file). `none` skips any validation and always run the task. |
| `prefix` | `string` | | Defines a string to prefix the output of tasks running in parallel. Only used when the output mode is `prefixed`. |
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing commands. |
| `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
@@ -174,11 +182,11 @@ tasks:
| 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`. |
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
| `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`. |
:::info

View File

@@ -5,7 +5,45 @@ sidebar_position: 6
# Changelog
## v3.15.1 - 2022-09-08
## v3.18.0
- Show aliases on `task --list --silent` (`task --ls`). This means that aliases
will be completed by the completion scripts
([#919](https://github.com/go-task/task/pull/919)).
- Tasks in the root Taskfile will now be displayed first in `--list`/`--list-all`
output ([#806](https://github.com/go-task/task/pull/806), [#890](https://github.com/go-task/task/pull/890)).
- It's now possible to call a `default` task in an included Taskfile by using
just the namespace. For example: `docs:default` is now automatically
aliased to `docs`
([#661](https://github.com/go-task/task/issues/661), [#815](https://github.com/go-task/task/pull/815)).
## v3.17.0
- Add a "Did you mean ...?" suggestion when a task does not exits another one
with a similar name is found
([#867](https://github.com/go-task/task/issues/867), [#880](https://github.com/go-task/task/pull/880)).
- Now YAML parse errors will print which Taskfile failed to parse
([#885](https://github.com/go-task/task/issues/885), [#887](https://github.com/go-task/task/pull/887)).
- Add ability to set `aliases` for tasks and namespaces ([#268](https://github.com/go-task/task/pull/268), [#340](https://github.com/go-task/task/pull/340), [#879](https://github.com/go-task/task/pull/879)).
- Improvements to Fish shell completion
([#897](https://github.com/go-task/task/pull/897)).
- Added ability to set a different watch interval by setting
`interval: '500ms'` or using the `--interval=500ms` flag
([#813](https://github.com/go-task/task/issues/813), [#865](https://github.com/go-task/task/pull/865)).
- Add colored output to `--list`, `--list-all` and `--summary` flags ([#845](https://github.com/go-task/task/pull/845), [#874](https://github.com/go-task/task/pull/874)).
- Fix unexpected behavior where `label:` was being shown instead of the task
name on `--list`
([#603](https://github.com/go-task/task/issues/603), [#877](https://github.com/go-task/task/pull/877)).
## 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)).

View File

@@ -46,8 +46,8 @@ 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)

124
docs/docs/contributing.md Normal file
View File

@@ -0,0 +1,124 @@
---
slug: /contributing/
sidebar_position: 7
---
# Contributing
Contributions to Task are very welcome, but we ask that you read this document
before submitting a PR.
## Before you start
- **Check existing work** - Is there an existing PR? Are there issues discussing
the feature/change you want to make? Please make sure you consider/address these
discussions in your work.
- **Backwards compatibility** - Will your change break existing Taskfiles? It is
much more likely that your change will merged if it backwards compatible. Is
there an approach you can take that maintains this compatibility? If not,
consider opening an issue first so that API changes can be discussed before you
invest you time into a PR.
## 1. Setup
- **Go** - Task is written in [Go]. We always support the latest two major Go
versions, so make sure your version is recent enough.
- **Node.js** - [Node.js] is used to host Task's documentation server and is
required if you want to run this server locally.
- **Yarn** - [Yarn] is the Node.js package manager used by Task.
## 2. Making changes
- **Code style** - Try to maintain the existing code style where possible and
ensure that code is formatted by `gofmt`. We use `golangci-lint` in our CI to
enforce a consistent style and best-practise. There's a `lint` command in
the Taskfile to run this locally.
- **Documentation** - Ensure that you add/update any relevant documentation. See
the [updating documentation](#updating-documentation) section below.
- **Tests** - Ensure that you add/update any relevant tests and that all tests
are passing before submitting the PR. See the [writing tests](#writing-tests)
section below.
### Running your changes
To run Task with working changes, you can use `go run ./cmd/task`. To run a
development build of task against a test Taskfile in `testdata`, you can use `go
run ./cmd/task --dir ./testdata/<my_test_dir> <task_name>`.
### Updating documentation
Task uses [Docusaurus] to host a documentation server. This can be setup and run
locally by using `task docs:setup` and `task docs:start` respectively (requires
`nodejs` & `yarn`). All content is written in Markdown and is located in the
`docs/docs` directory. All Markdown documents should have an 80 character line
wrap limit.
When making a change, consider whether a change to the [Usage Guide](./usage.md)
is necessary. This document contains descriptions and examples of how to use
Task features. If you're adding a new feature, try to find an appropriate place
to add a new section. If you're updating an existing feature, ensure that the
documentation and any examples are up-to-date. Ensure that any examples follow
the [Taskfile Styleguide](./styleguide.md).
If you added a new field, command or flag, ensure that you add it to the [API
Reference](./api_reference.md). New fields also need to be added to the
[JSON Schema](../static/schema.json). The descriptions for fields in the API
reference and the schema should match.
### Writing tests
Most of Task's test are held in the `task_test.go` file in the project root and
this is where you'll most likely want to add new ones too. Most of these tests
also have a subdirectory in the `testdata` directory where any Taskfiles/data
required to run the tests are stored.
When making a changes, consider whether new tests are required. These tests
should ensure that the functionality you are adding will continue to work in the
future. Existing tests may also need updating if you have changed Task's
behaviour.
## 3. Committing your code
Try to write meaningful commit messages and avoid having too many commits on
the PR. Most PRs should likely have a single commit (although for bigger PRs it
may be reasonable to split it in a few). Git squash and rebase is your friend!
## 4. Submitting a PR
- **Describe your changes** - Ensure that you provide a comprehensive
description of your changes.
- **Issue/PR links** - Link any previous work such as related issues or PRs.
Please describe how your changes differ to/extend this work.
- **Examples** - Add any examples that you think are useful to demonstrate the
effect of your changes.
- **Draft PRs** - If your changes are incomplete, but you would like to discuss
them, open the PR as a draft and add a comment to start a discussion. Using
comments rather than the PR description allows the description to be updated
later while preserving any discussions.
## FAQ
> I want to contribute, where do I start?
Take a look at the list of [open issues]. We have a [good first issue] label for
simpler issues that are ideal for first time contributions.
All kinds of contributions are welcome, whether its a typo fix or a shiny new
feature. You can also contribute by upvoting/commenting on issues, helping to
answer questions or contributing to other [community projects](./community.md).
> I'm stuck, where can I get help?
If you have questions, feel free to ask them in the `#help` channel on our
[Discord server].
---
[Go]: https://go.dev
[install version 1.18+]: https://go.dev/doc/install
[Node.js]: https://nodejs.org/en/
[Yarn]: https://yarnpkg.com/
[Docusaurus]: https://docusaurus.io
[open issues]: https://github.com/go-task/task/issues
[good first issue]: https://github.com/go-task/task/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22
[Discord server]: https://discord.gg/6TY36E39UK

View File

@@ -1,6 +1,6 @@
---
slug: /donate/
sidebar_position: 9
sidebar_position: 10
---
# Donate

View File

@@ -39,7 +39,6 @@ choco install go-task
This installation method is community owned.
### Scoop
If you're on Windows and have [Scoop][scoop] installed, getting
@@ -55,15 +54,23 @@ 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
[AUR](https://aur.archlinux.org/packages/go-task-bin) using your favorite
package manager such as `yay`, `pacaur` or `yaourt`:
```cmd
yay -S taskfile-git
yay -S go-task-bin
```
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.
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
@@ -89,6 +96,15 @@ 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
@@ -173,6 +189,68 @@ released binary.
:::
## Setup completions
Download the autocompletion file corresponding to your shell.
[All completions are available on the Task repository](https://github.com/go-task/task/tree/master/completion).
### Bash
First, ensure that you installed bash-completion using your package manager.
Make the completion file executable:
```
chmod +x path/to/task.bash
```
After, add this to your `~/.bash_profile`:
```shell
source path/to/task.bash
```
### ZSH
Put the `_task` file somewhere in your `$FPATH`:
```shell
mv path/to/_task /usr/local/share/zsh/site-functions/_task
```
Ensure that the following is present in your `~/.zshrc`:
```shell
autoload -U compinit
compinit -i
```
ZSH version 5.7 or later is recommended.
### Fish
Move the `task.fish` completion script:
```shell
mv path/to/task.fish ~/.config/fish/completions/task.fish
```
### PowerShell
Open your profile script with:
```
mkdir -Path (Split-Path -Parent $profile) -ErrorAction SilentlyContinue
notepad $profile
```
Add the line and save the file:
```shell
Invoke-Expression -Command path/to/task.ps1
```
[go]: https://golang.org/
[snapcraft]: https://snapcraft.io/task
[homebrew]: https://brew.sh/

View File

@@ -1,6 +1,6 @@
---
slug: /releasing/
sidebar_position: 7
sidebar_position: 8
---
# Releasing
@@ -19,15 +19,19 @@ defined in the above GitHub Actions.
# 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.
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 exception is the publishing of a new version of the
[snap package][snappackage]. This current require two steps after publishing
the binaries:
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
@@ -50,6 +54,7 @@ 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,6 +1,6 @@
---
slug: /taskfile-versions/
sidebar_position: 8
sidebar_position: 9
---
# Taskfile Versions

View File

@@ -194,6 +194,22 @@ 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
@@ -214,6 +230,21 @@ includes:
DOCKER_IMAGE: frontend_image
```
### Namespace aliases
When including a Taskfile, you can give the namespace a list of `aliases`.
This works in the same way as [task aliases](#task-aliases) and can be used
together to create shorter and easier-to-type commands.
```yaml
version: '3'
includes:
generate:
taskfile: ./taskfiles/Generate.yml
aliases: [gen]
```
:::info
Vars declared in the included Taskfile have preference over the
@@ -223,6 +254,29 @@ use the [default function](https://go-task.github.io/slim-sprig/defaults.html):
:::
## 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
@@ -426,6 +480,11 @@ tasks:
method: timestamp
```
In situations where you need more flexibility the `status` keyword can be used.
You can even combine the two. See the documentation for
[status](#using-programmatic-checks-to-indicate-a-task-is-up-to-date) for an
example.
:::info
By default, task stores checksums on a local `.task` directory in the project's
@@ -516,6 +575,30 @@ 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.
`status` can be combined with the [fingerprinting](#by-fingerprinting-locally-generated-files-and-their-sources)
to have a task run if either the the source/generated artifacts changes, or the
programmatic check fails:
```yaml
version: '3'
tasks:
build:prod:
desc: Build for production usage.
cmds:
- composer install
# Run this task if source files changes.
sources:
- composer.json
- composer.lock
generates:
- ./vendor/composer/installed.json
- ./vendor/autoload.php
# But also run the task if the last build was not a production build.
status:
- grep -q '"dev": false' ./vendor/composer/installed.json
```
### Using programmatic checks to cancel the execution of a task and its dependencies
In addition to `status` checks, `preconditions` checks are
@@ -897,6 +980,30 @@ If the task does not have a summary or a description, a warning is printed.
Please note: *showing the summary will not execute the command*.
## Task aliases
Aliases are alternative names for tasks. They can be used to make it easier and
quicker to run tasks with long or hard-to-type names. You can use them on the
command line, when [calling sub-tasks](#calling-another-task) in your Taskfile
and when [including tasks](#including-other-taskfiles) with aliases from another
Taskfile. They can also be used together with [namespace
aliases](#namespace-aliases).
```yaml
version: '3'
tasks:
generate:
aliases: [gen]
cmds:
- task: gen-mocks
generate-mocks:
aliases: [gen-mocks]
cmds:
- echo "generating..."
```
## Overriding task name
Sometimes you may want to override the task name printed on the summary, up-to-date
@@ -1179,5 +1286,9 @@ With the flags `--watch` or `-w` task will watch for file changes
and run the task again. This requires the `sources` attribute to be given,
so task knows which files to watch.
The default watch interval is 5 seconds, but it's possible to change it by
either setting `interval: '500ms'` in the root of the Taskfile passing it
as an argument like `--interval=500ms`.
[gotemplate]: https://golang.org/pkg/text/template/
[minify]: https://github.com/tdewolff/minify/tree/master/cmd/minify

View File

@@ -1,8 +1,8 @@
// @ts-check
// Note: type annotations allow type checking and IDEs autocompletion
const lightCodeTheme = require('prism-react-renderer/themes/github');
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
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';

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'
}
}
]
};

View File

@@ -64,15 +64,21 @@ get_binaries() {
case "$PLATFORM" in
darwin/amd64) BINARIES="task" ;;
darwin/arm64) BINARIES="task" ;;
darwin/armv5) BINARIES="task" ;;
darwin/armv6) BINARIES="task" ;;
darwin/armv7) BINARIES="task" ;;
linux/386) BINARIES="task" ;;
linux/amd64) BINARIES="task" ;;
linux/arm64) BINARIES="task" ;;
linux/armv5) BINARIES="task" ;;
linux/armv6) BINARIES="task" ;;
linux/armv7) BINARIES="task" ;;
windows/386) BINARIES="task" ;;
windows/amd64) BINARIES="task" ;;
windows/arm64) BINARIES="task" ;;
windows/armv5) BINARIES="task" ;;
windows/armv6) BINARIES="task" ;;
windows/armv7) BINARIES="task" ;;
*)
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
exit 1
@@ -184,9 +190,9 @@ uname_arch() {
i686) arch="386" ;;
i386) arch="386" ;;
aarch64) arch="arm64" ;;
armv5*) arch="armv5" ;;
armv6*) arch="armv6" ;;
armv7*) arch="armv7" ;;
armv5*) arch="arm" ;;
armv6*) arch="arm" ;;
armv7*) arch="arm" ;;
esac
echo ${arch}
}
@@ -214,9 +220,7 @@ uname_arch_check() {
386) return 0 ;;
amd64) return 0 ;;
arm64) return 0 ;;
armv5) return 0 ;;
armv6) return 0 ;;
armv7) return 0 ;;
arm) return 0 ;;
ppc64) return 0 ;;
ppc64le) return 0 ;;
mips) return 0 ;;

View File

@@ -1,6 +1,6 @@
(function () {
function attachAd() {
const el = document.createElement('script');
var el = document.createElement('script');
el.setAttribute('type', 'text/javascript');
el.setAttribute('id', '_carbonads_js');
el.setAttribute(
@@ -9,7 +9,7 @@
);
el.setAttribute('async', 'async');
const wrapper = document.getElementById('sidebar-ads');
var wrapper = document.getElementById('sidebar-ads');
wrapper.innerHTML = '';
wrapper.appendChild(el);
}
@@ -17,8 +17,13 @@
setTimeout(function () {
attachAd();
window.addEventListener('popstate', function () {
attachAd();
});
var currentPath = window.location.pathname;
setInterval(function () {
if (currentPath !== window.location.pathname) {
currentPath = window.location.pathname;
attachAd();
}
}, 1000);
}, 1000);
})();

384
docs/static/schema.json vendored Normal file
View File

@@ -0,0 +1,384 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Taskfile YAML Schema",
"description": "Schema for Taskfile files.",
"definitions": {
"3": {
"env": {
"$ref": "#/definitions/3/vars"
},
"tasks": {
"type": "object",
"patternProperties": {
"^.*$": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"oneOf": [
{
"type": "string"
},
{
"$ref": "#/definitions/3/task_call"
}
]
}
},
{
"$ref": "#/definitions/3/task"
}
]
}
}
},
"task": {
"type": "object",
"additionalProperties": false,
"properties": {
"cmds": {
"description": "A list of commands to be executed.",
"$ref": "#/definitions/3/cmds"
},
"deps": {
"description": "A list of dependencies of this task. Tasks defined here will run in parallel before this task.",
"type": "array",
"items": {
"oneOf": [
{
"type": "string"
},
{
"$ref": "#/definitions/3/task_call"
}
]
}
},
"label": {
"description": "Overrides the name of the task in the output when a task is run. Supports variables.",
"type": "string"
},
"desc": {
"description": "A short description of the task. This is displayed when calling `task --list`.",
"type": "string"
},
"summary": {
"description": "A longer description of the task. This is displayed when calling `task --summary [task]`.",
"type": "string"
},
"aliases": {
"description": "A list of alternative names by which the task can be called.",
"type": "array",
"items": {
"type": "string"
}
},
"sources": {
"description": "A list of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs.",
"type": "array",
"items": {
"type": "string"
}
},
"generates": {
"description": "A list of files meant to be generated by this task. Relevant for `timestamp` method. Can be file paths or star globs.",
"type": "array",
"items": {
"type": "string"
}
},
"status": {
"description": "A list of commands to check if this task should run. The task is skipped otherwise. This overrides `method`, `sources` and `generates`.",
"type": "array",
"items": {
"type": "string"
}
},
"preconditions": {
"description": "A list of commands to check if this task should run. If a condition is not met, the task will error.",
"type": "array",
"items": {
"$ref": "#/definitions/3/precondition"
}
},
"dir": {
"description": "The directory in which this task should run. Defaults to the current working directory.",
"type": "string"
},
"vars": {
"description": "A set of variables that can be used in the task.",
"$ref": "#/definitions/3/vars"
},
"env": {
"description": "A set of environment variables that will be made available to shell commands.",
"$ref": "#/definitions/3/env"
},
"silent": {
"description": "Hides task name and command from output. The command's output will still be redirected to `STDOUT` and `STDERR`. When combined with the `--list` flag, task descriptions will be hidden.",
"type": "boolean",
"default": false
},
"interactive": {
"description": "Tells task that the command is interactive.",
"type": "boolean",
"default": false
},
"internal": {
"description": "Stops a task from being callable on the command line. It will also be omitted from the output when used with `--list`.",
"type": "boolean",
"default": false
},
"method": {
"description": "Defines which method is used to check the task is up-to-date. `timestamp` will compare the timestamp of the sources and generates files. `checksum` will check the checksum (You probably want to ignore the .task folder in your .gitignore file). `none` skips any validation and always run the task.",
"type": "string",
"enum": ["none", "checksum", "timestamp"],
"default": "none"
},
"prefix": {
"description": "Defines a string to prefix the output of tasks running in parallel. Only used when the output mode is `prefixed`.",
"type": "string"
},
"ignore_error": {
"description": "Continue execution if errors happen while executing commands.",
"type": "boolean"
},
"run": {
"description": "Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`.",
"$ref": "#/definitions/3/run"
}
}
},
"cmds": {
"type": "array",
"items": {
"$ref": "#/definitions/3/cmd"
}
},
"cmd": {
"anyOf": [
{
"type": "string"
},
{
"$ref": "#/definitions/3/cmd_call"
},
{
"$ref": "#/definitions/3/task_call"
}
]
},
"vars": {
"type": "object",
"patternProperties": {
"^.*$": {
"anyOf": [
{
"type": ["boolean", "integer", "null", "number", "string"]
},
{
"$ref": "#/definitions/3/dynamic_var"
}
]
}
}
},
"dynamic_var": {
"type": "object",
"properties": {
"sh": {
"type": "string",
"description": "The value will be treated as a command and the output assigned"
},
"additionalProperties": false
}
},
"task_call": {
"type": "object",
"properties": {
"task": {
"description": "Name of the task to run",
"type": "string"
},
"vars": {
"description": "Values passed to the task called",
"$ref": "#/definitions/3/vars"
}
}
},
"cmd_call": {
"type": "object",
"properties": {
"cmd": {
"description": "Command to run",
"type": "string"
},
"silent": {
"description": "Silent mode disables echoing of command before Task runs it",
"type": "boolean"
},
"ignore_error": {
"description": "Prevent command from aborting the execution of task even after receiving a status code of 1",
"type": "boolean"
},
"defer": {
"description": "",
"type": "boolean"
}
},
"additionalProperties": false,
"required": ["cmd"]
},
"precondition": {
"anyOf": [
{
"type": "string"
},
{
"$ref": "#/definitions/3/precondition_obj"
}
]
},
"precondition_obj": {
"type": "object",
"properties": {
"sh": {
"description": "Command to run. If that command returns 1, the condition will fail",
"type": "string"
},
"msg": {
"description": "Failure message to display when the condition fails",
"type": "string"
}
}
},
"run": {
"type": "string",
"enum": ["always", "once", "when_changed"]
}
}
},
"oneOf": [
{
"type": "object",
"properties": {
"version": {
"description": "Specify the Taskfile format that this file conforms to.",
"anyOf": [
{
"type": "number",
"enum": [3]
},
{
"type": "string",
"enum": ["3"]
}
]
},
"output": {
"description": "Defines how the STDOUT and STDERR are printed when running tasks in parallel. The interleaved output prints lines in real time (default). 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 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.",
"type": "string",
"enum": ["interleaved", "group", "prefixed"],
"default": "interleaved"
},
"method": {
"description": "Defines which method is used to check the task is up-to-date. (default: checksum)",
"type": "string",
"enum": ["none", "checksum", "timestamp"],
"default": "checksum"
},
"includes": {
"description": "Imports tasks from the specified taskfiles. The tasks described in the given Taskfiles will be available with the informed namespace.",
"type": "object",
"patternProperties": {
"^.*$": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"taskfile": {
"description": "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.",
"type": "string"
},
"dir": {
"description": "The working directory of the included tasks when run.",
"type": "string"
},
"optional": {
"description": "If `true`, no errors will be thrown if the specified file does not exist.",
"type": "boolean"
},
"internal": {
"description": "Stops any task in the included Taskfile from being callable on the command line. These commands will also be omitted from the output when used with `--list`.",
"type": "boolean"
},
"aliases": {
"description": "Alternative names for the namespace of the included Taskfile.",
"type": "array",
"items": {
"type": "string"
}
},
"vars": {
"description": "A set of variables to apply to the included Taskfile.",
"$ref": "#/definitions/3/vars"
}
}
}
]
}
}
},
"vars": {
"description": "A set of global variables.",
"$ref": "#/definitions/3/vars"
},
"env": {
"description": "A set of global environment variables.",
"$ref": "#/definitions/3/env"
},
"tasks": {
"description": "A set of task definitions.",
"$ref": "#/definitions/3/tasks"
},
"silent": {
"description": "Default 'silent' options for this Taskfile. If `false`, can be overidden with `true` in a task by task basis.",
"type": "boolean"
},
"dotenv": {
"type": "array",
"description": "A list of `.env` file paths to be parsed.",
"items": {
"type": "string"
}
},
"run": {
"description": "Default 'run' option for this Taskfile. Available options: `always`, `once` and `when_changed`.",
"$ref": "#/definitions/3/run"
},
"interval": {
"description": "Sets a different watch interval when using `--watch`, the default being 5 seconds. This string should be a valid Go duration: https://pkg.go.dev/time#ParseDuration.",
"$ref": "#/definitions/3/run"
}
},
"additionalProperties": false,
"required": ["version"],
"anyOf": [
{
"required": ["includes"]
},
{
"required": ["tasks"]
},
{
"required": ["includes", "tasks"]
}
]
}
]
}

View File

@@ -4858,9 +4858,9 @@ loader-runner@^4.2.0:
integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
loader-utils@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
version "2.0.3"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.3.tgz#d4b15b8504c63d1fc3f2ade52d41bc8459d6ede1"
integrity sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"

View File

@@ -3,6 +3,7 @@ package task
import (
"errors"
"fmt"
"strings"
"mvdan.cc/sh/v3/interp"
)
@@ -13,11 +14,37 @@ var (
)
type taskNotFoundError struct {
taskName string
taskName string
didYouMean string
}
func (err *taskNotFoundError) Error() string {
return fmt.Sprintf(`task: Task %q not found`, err.taskName)
if err.didYouMean != "" {
return fmt.Sprintf(
`task: Task %q does not exist. Did you mean %q?`,
err.taskName,
err.didYouMean,
)
}
return fmt.Sprintf(`task: Task %q does not exist`, err.taskName)
}
type multipleTasksWithAliasError struct {
aliasName string
taskNames []string
}
func (err *multipleTasksWithAliasError) Error() string {
return fmt.Sprintf(`task: Multiple tasks (%s) with alias %q found`, strings.Join(err.taskNames, ", "), err.aliasName)
}
type taskInternalError struct {
taskName string
}
func (err *taskInternalError) Error() string {
return fmt.Sprintf(`task: Task "%s" is internal`, err.taskName)
}
type TaskRunError struct {

8
go.mod
View File

@@ -4,11 +4,13 @@ require (
github.com/fatih/color v1.13.0
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
github.com/joho/godotenv v1.4.0
github.com/mattn/go-zglob v0.0.3
github.com/mattn/go-zglob v0.0.4
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/radovskyb/watcher v1.0.7
github.com/sajari/fuzzy v1.0.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0
github.com/stretchr/testify v1.8.1
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899
@@ -19,7 +21,7 @@ require (
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/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)

19
go.sum
View File

@@ -7,7 +7,7 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF
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.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
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.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@@ -17,8 +17,8 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To=
github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/mattn/go-zglob v0.0.4 h1:LQi2iOm0/fGgu80AioIJ/1j9w9Oh+9DZ39J4VAGzHQM=
github.com/mattn/go-zglob v0.0.4/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -26,24 +26,29 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
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.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY=
github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo=
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/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
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=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4=
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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=

73
help.go
View File

@@ -10,70 +10,34 @@ import (
"text/tabwriter"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/taskfile"
)
// ListTasksWithDesc reports tasks that have a description spec.
func (e *Executor) ListTasksWithDesc() {
e.printTasks(false)
}
// ListAllTasks reports all tasks, with or without a description spec.
func (e *Executor) ListAllTasks() {
e.printTasks(true)
}
func (e *Executor) printTasks(listAll bool) {
var tasks []*taskfile.Task
if listAll {
tasks = e.allTaskNames()
} else {
tasks = e.tasksWithDesc()
}
// ListTasks prints a list of tasks.
// Tasks that match the given filters will be excluded from the list.
// The function returns a boolean indicating whether or not tasks were found.
func (e *Executor) ListTasks(filters ...FilterFunc) bool {
tasks := e.GetTaskList(filters...)
if len(tasks) == 0 {
if listAll {
e.Logger.Outf(logger.Yellow, "task: No tasks available")
} else {
e.Logger.Outf(logger.Yellow, "task: No tasks with description available. Try --list-all to list all tasks")
}
return
return false
}
e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
// Format in tab-separated columns with a tab stop of 8.
w := tabwriter.NewWriter(e.Stdout, 0, 8, 0, '\t', 0)
w := tabwriter.NewWriter(e.Stdout, 0, 8, 6, ' ', 0)
for _, task := range tasks {
fmt.Fprintf(w, "* %s: \t%s\n", task.Name(), task.Desc)
e.Logger.FOutf(w, logger.Yellow, "* ")
e.Logger.FOutf(w, logger.Green, task.Task)
e.Logger.FOutf(w, logger.Default, ": \t%s", task.Desc)
if len(task.Aliases) > 0 {
e.Logger.FOutf(w, logger.Cyan, "\t(aliases: %s)", strings.Join(task.Aliases, ", "))
}
fmt.Fprint(w, "\n")
}
w.Flush()
return true
}
func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
for _, task := range e.Taskfile.Tasks {
tasks = append(tasks, task)
}
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
return
}
func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
for _, task := range e.Taskfile.Tasks {
if task.Desc != "" {
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
if err == nil {
task = compiledTask
}
tasks = append(tasks, 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.
// ListTaskNames 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) {
@@ -92,8 +56,11 @@ func (e *Executor) ListTaskNames(allTasks bool) {
// 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 != "" {
if (allTasks || t.Desc != "") && !t.Internal {
s = append(s, strings.TrimRight(t.Task, ":"))
for _, alias := range t.Aliases {
s = append(s, strings.TrimRight(alias, ":"))
}
}
}
// sort and print all task names

View File

@@ -64,15 +64,21 @@ get_binaries() {
case "$PLATFORM" in
darwin/amd64) BINARIES="task" ;;
darwin/arm64) BINARIES="task" ;;
darwin/armv5) BINARIES="task" ;;
darwin/armv6) BINARIES="task" ;;
darwin/armv7) BINARIES="task" ;;
linux/386) BINARIES="task" ;;
linux/amd64) BINARIES="task" ;;
linux/arm64) BINARIES="task" ;;
linux/armv5) BINARIES="task" ;;
linux/armv6) BINARIES="task" ;;
linux/armv7) BINARIES="task" ;;
windows/386) BINARIES="task" ;;
windows/amd64) BINARIES="task" ;;
windows/arm64) BINARIES="task" ;;
windows/armv5) BINARIES="task" ;;
windows/armv6) BINARIES="task" ;;
windows/armv7) BINARIES="task" ;;
*)
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
exit 1
@@ -184,9 +190,9 @@ uname_arch() {
i686) arch="386" ;;
i386) arch="386" ;;
aarch64) arch="arm64" ;;
armv5*) arch="armv5" ;;
armv6*) arch="armv6" ;;
armv7*) arch="armv7" ;;
armv5*) arch="arm" ;;
armv6*) arch="arm" ;;
armv7*) arch="arm" ;;
esac
echo ${arch}
}
@@ -214,9 +220,7 @@ uname_arch_check() {
386) return 0 ;;
amd64) return 0 ;;
arm64) return 0 ;;
armv5) return 0 ;;
armv6) return 0 ;;
armv7) return 0 ;;
arm) return 0 ;;
ppc64) return 0 ;;
ppc64le) return 0 ;;
mips) return 0 ;;

View File

@@ -1,6 +1,7 @@
package filepathext
import (
"os"
"path/filepath"
)
@@ -12,3 +13,19 @@ func SmartJoin(a, b string) string {
}
return filepath.Join(a, b)
}
// TryAbsToRel tries to convert an absolute path to relative based on the
// process working directory. If it can't, it returns the absolute path.
func TryAbsToRel(abs string) string {
wd, err := os.Getwd()
if err != nil {
return abs
}
rel, err := filepath.Rel(wd, abs)
if err != nil {
return abs
}
return rel
}

View File

@@ -53,6 +53,11 @@ type Logger struct {
// Outf prints stuff to STDOUT.
func (l *Logger) Outf(color Color, s string, args ...interface{}) {
l.FOutf(l.Stdout, color, s+"\n", args...)
}
// FOutf prints stuff to the given writer.
func (l *Logger) FOutf(w io.Writer, color Color, s string, args ...interface{}) {
if len(args) == 0 {
s, args = "%s", []interface{}{s}
}
@@ -60,7 +65,7 @@ func (l *Logger) Outf(color Color, s string, args ...interface{}) {
color = Default
}
print := color()
print(l.Stdout, s+"\n", args...)
print(w, s, args...)
}
// VerboseOutf prints stuff to STDOUT if verbose mode is enabled.

View File

@@ -28,6 +28,7 @@ func PrintTask(l *logger.Logger, t *taskfile.Task) {
printTaskName(l, t)
printTaskDescribingText(t, l)
printTaskDependencies(l, t)
printTaskAliases(l, t)
printTaskCommands(l, t)
}
@@ -56,10 +57,23 @@ func printTaskSummary(l *logger.Logger, t *taskfile.Task) {
}
func printTaskName(l *logger.Logger, t *taskfile.Task) {
l.Outf(logger.Default, "task: %s", t.Name())
l.FOutf(l.Stdout, logger.Default, "task: ")
l.FOutf(l.Stdout, logger.Green, "%s\n", t.Name())
l.Outf(logger.Default, "")
}
func printTaskAliases(l *logger.Logger, t *taskfile.Task) {
if len(t.Aliases) == 0 {
return
}
l.Outf(logger.Default, "")
l.Outf(logger.Default, "aliases:")
for _, alias := range t.Aliases {
l.FOutf(l.Stdout, logger.Default, " - ")
l.Outf(logger.Cyan, alias)
}
}
func hasDescription(t *taskfile.Task) bool {
return t.Desc != ""
}
@@ -94,10 +108,11 @@ func printTaskCommands(l *logger.Logger, t *taskfile.Task) {
l.Outf(logger.Default, "commands:")
for _, c := range t.Cmds {
isCommand := c.Cmd != ""
l.FOutf(l.Stdout, logger.Default, " - ")
if isCommand {
l.Outf(logger.Default, " - %s", c.Cmd)
l.FOutf(l.Stdout, logger.Yellow, "%s\n", c.Cmd)
} else {
l.Outf(logger.Default, " - Task: %s", c.Task)
l.FOutf(l.Stdout, logger.Green, "Task: %s\n", c.Task)
}
}
}

32
package-lock.json generated Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "@go-task/cli",
"version": "3.17.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@go-task/cli",
"version": "3.17.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.18.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"
}
}

View File

@@ -17,6 +17,8 @@ import (
"github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/taskfile"
"github.com/go-task/task/v3/taskfile/read"
"github.com/sajari/fuzzy"
)
func (e *Executor) Setup() error {
@@ -28,6 +30,8 @@ func (e *Executor) Setup() error {
return err
}
e.setupFuzzyModel()
v, err := e.Taskfile.ParsedVersion()
if err != nil {
return err
@@ -85,6 +89,27 @@ func (e *Executor) readTaskfile() error {
return err
}
func (e *Executor) setupFuzzyModel() {
if e.Taskfile != nil {
return
}
model := fuzzy.NewModel()
model.SetThreshold(1) // because we want to build grammar based on every task name
var words []string
for taskName := range e.Taskfile.Tasks {
words = append(words, taskName)
for _, task := range e.Taskfile.Tasks {
words = append(words, task.Aliases...)
}
}
model.Train(words)
e.fuzzyModel = model
}
func (e *Executor) setupTempDir() error {
if e.TempDir != "" {
return nil

137
task.go
View File

@@ -5,6 +5,8 @@ import (
"fmt"
"io"
"os"
"sort"
"strings"
"sync"
"sync/atomic"
@@ -16,6 +18,8 @@ import (
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
"github.com/sajari/fuzzy"
"golang.org/x/exp/slices"
"golang.org/x/sync/errgroup"
)
@@ -41,6 +45,7 @@ type Executor struct {
Parallel bool
Color bool
Concurrency int
Interval string
Stdin io.Reader
Stdout io.Writer
@@ -51,7 +56,8 @@ type Executor struct {
Output output.Output
OutputStyle taskfile.Output
taskvars *taskfile.Vars
taskvars *taskfile.Vars
fuzzyModel *fuzzy.Model
concurrencySemaphore chan struct{}
taskCallCount map[string]*int32
@@ -63,11 +69,16 @@ type Executor struct {
// Run runs Task
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 {
// FIXME: move to the main package
e.ListTasksWithDesc()
return &taskNotFoundError{taskName: c.Task}
for _, call := range calls {
task, err := e.GetTask(call)
if err != nil {
e.ListTasks(FilterOutInternal(), FilterOutNoDesc())
return err
}
if task.Internal {
e.ListTasks(FilterOutInternal(), FilterOutNoDesc())
return &taskInternalError{taskName: call.Task}
}
}
@@ -107,8 +118,8 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
if err != nil {
return err
}
if !e.Watch && atomic.AddInt32(e.taskCallCount[call.Task], 1) >= MaximumTaskCall {
return &MaximumTaskCallExceededError{task: call.Task}
if !e.Watch && atomic.AddInt32(e.taskCallCount[t.Task], 1) >= MaximumTaskCall {
return &MaximumTaskCallExceededError{task: t.Task}
}
release := e.acquireConcurrencyLimit()
@@ -325,3 +336,113 @@ func (e *Executor) startExecution(ctx context.Context, t *taskfile.Task, execute
return execute(ctx)
}
// GetTask will return the task with the name matching the given call from the taskfile.
// If no task is found, it will search for tasks with a matching alias.
// If multiple tasks contain the same alias or no matches are found an error is returned.
func (e *Executor) GetTask(call taskfile.Call) (*taskfile.Task, error) {
// Search for a matching task
matchingTask, ok := e.Taskfile.Tasks[call.Task]
if ok {
return matchingTask, nil
}
// If didn't find one, search for a task with a matching alias
var aliasedTasks []string
for _, task := range e.Taskfile.Tasks {
if slices.Contains(task.Aliases, call.Task) {
aliasedTasks = append(aliasedTasks, task.Task)
matchingTask = task
}
}
// If we found multiple tasks
if len(aliasedTasks) > 1 {
return nil, &multipleTasksWithAliasError{
aliasName: call.Task,
taskNames: aliasedTasks,
}
}
// If we found no tasks
if len(aliasedTasks) == 0 {
didYouMean := ""
if e.fuzzyModel != nil {
didYouMean = e.fuzzyModel.SpellCheck(call.Task)
}
return nil, &taskNotFoundError{
taskName: call.Task,
didYouMean: didYouMean,
}
}
return matchingTask, nil
}
type FilterFunc func(tasks []*taskfile.Task) []*taskfile.Task
func (e *Executor) GetTaskList(filters ...FilterFunc) []*taskfile.Task {
tasks := make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
// Fetch and compile the list of tasks
for _, task := range e.Taskfile.Tasks {
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
if err == nil {
task = compiledTask
}
tasks = append(tasks, task)
}
// Filter the tasks
for _, filter := range filters {
tasks = filter(tasks)
}
// Sort the tasks
// Tasks that are not namespaced should be listed before tasks that are.
// We detect this by searching for a ':' in the task name.
sort.Slice(tasks, func(i, j int) bool {
iContainsColon := strings.Contains(tasks[i].Task, ":")
jContainsColon := strings.Contains(tasks[j].Task, ":")
if iContainsColon == jContainsColon {
return tasks[i].Task < tasks[j].Task
}
if !iContainsColon && jContainsColon {
return true
}
return false
})
return tasks
}
// Filter is a generic task filtering function. It will remove each task in the
// slice where the result of the given function is true.
func Filter(f func(task *taskfile.Task) bool) FilterFunc {
return func(tasks []*taskfile.Task) []*taskfile.Task {
shift := 0
for _, task := range tasks {
if !f(task) {
tasks[shift] = task
shift++
}
}
// This loop stops any memory leaks
for j := shift; j < len(tasks); j++ {
tasks[j] = nil
}
return slices.Clip(tasks[:shift])
}
}
// FilterOutNoDesc removes all tasks that do not contain a description.
func FilterOutNoDesc() FilterFunc {
return Filter(func(task *taskfile.Task) bool {
return task.Desc == ""
})
}
// FilterOutInternal removes all tasks that are marked as internal.
func FilterOutInternal() FilterFunc {
return Filter(func(task *taskfile.Task) bool {
return task.Internal
})
}

View File

@@ -10,6 +10,7 @@ import (
"runtime"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
@@ -476,6 +477,55 @@ func TestStatusChecksum(t *testing.T) {
assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String())
}
func TestAlias(t *testing.T) {
const dir = "testdata/alias"
data, err := os.ReadFile(filepathext.SmartJoin(dir, "alias.txt"))
assert.NoError(t, err)
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: "f"}))
assert.Equal(t, string(data), buff.String())
}
func TestDuplicateAlias(t *testing.T) {
const dir = "testdata/alias"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "x"}))
assert.Equal(t, "", buff.String())
}
func TestAliasSummary(t *testing.T) {
const dir = "testdata/alias"
data, err := os.ReadFile(filepathext.SmartJoin(dir, "alias-summary.txt"))
assert.NoError(t, err)
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Summary: true,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "f"}))
assert.Equal(t, string(data), buff.String())
}
func TestLabelUpToDate(t *testing.T) {
const dir = "testdata/label_uptodate"
@@ -546,7 +596,7 @@ func TestLabelInSummary(t *testing.T) {
assert.Contains(t, buff.String(), "foobar")
}
func TestLabelInList(t *testing.T) {
func TestNoLabelInList(t *testing.T) {
const dir = "testdata/label_list"
var buff bytes.Buffer
@@ -556,8 +606,8 @@ func TestLabelInList(t *testing.T) {
Stderr: &buff,
}
assert.NoError(t, e.Setup())
e.ListTasksWithDesc()
assert.Contains(t, buff.String(), "foobar")
e.ListTasks(task.FilterOutInternal(), task.FilterOutNoDesc())
assert.Contains(t, buff.String(), "foo")
}
// task -al case 1: listAll list all tasks
@@ -574,7 +624,7 @@ func TestListAllShowsNoDesc(t *testing.T) {
assert.NoError(t, e.Setup())
var title string
e.ListAllTasks()
e.ListTasks(task.FilterOutInternal())
for _, title = range []string{
"foo",
"voo",
@@ -596,7 +646,7 @@ func TestListCanListDescOnly(t *testing.T) {
}
assert.NoError(t, e.Setup())
e.ListTasksWithDesc()
e.ListTasks(task.FilterOutInternal(), task.FilterOutNoDesc())
var title string
assert.Contains(t, buff.String(), "foo")
@@ -838,6 +888,22 @@ func TestIncorrectVersionIncludes(t *testing.T) {
assert.EqualError(t, e.Setup(), expectedError)
}
func TestIncludesIncorrect(t *testing.T) {
const dir = "testdata/includes_incorrect"
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: Failed to parse testdata/includes_incorrect/incomplete.yml:")
}
func TestIncludesEmptyMain(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/includes_empty",
@@ -958,6 +1024,100 @@ func TestIncludesRelativePath(t *testing.T) {
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, ""},
}
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)
} 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, ""},
}
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)
} else {
assert.NoError(t, err)
}
assert.Equal(t, test.expectedOutput, buff.String())
})
}
}
func TestIncludesShadowedDefault(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/includes_shadowed_default",
Target: "included",
TrimSpace: true,
Files: map[string]string{
"file.txt": "shadowed",
},
}
tt.Run(t)
}
func TestIncludesUnshadowedDefault(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/includes_unshadowed_default",
Target: "included",
TrimSpace: true,
Files: map[string]string{
"file.txt": "included",
},
}
tt.Run(t)
}
func TestSupportedFileNames(t *testing.T) {
fileNames := []string{
"Taskfile.yml",
@@ -1400,3 +1560,64 @@ func TestEvaluateSymlinksInPaths(t *testing.T) {
err = os.RemoveAll(dir + "/.task")
assert.NoError(t, err)
}
func TestFileWatcherInterval(t *testing.T) {
const dir = "testdata/watcher_interval"
expectedOutput := strings.TrimSpace(`
task: Started watching for tasks: default
task: [default] echo "Hello, World!"
Hello, World!
task: [default] echo "Hello, World!"
Hello, World!
`)
var buff bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Watch: true,
}
assert.NoError(t, e.Setup())
buff.Reset()
err := os.MkdirAll(filepathext.SmartJoin(dir, "src"), 0755)
assert.NoError(t, err)
err = os.WriteFile(filepathext.SmartJoin(dir, "src/a"), []byte("test"), 0644)
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
err := e.Run(ctx, taskfile.Call{Task: "default"})
if err != nil {
return
}
}
}
}(ctx)
time.Sleep(10 * time.Millisecond)
err = os.WriteFile(filepathext.SmartJoin(dir, "src/a"), []byte("test updated"), 0644)
if err != nil {
t.Fatal(err)
}
time.Sleep(700 * time.Millisecond)
cancel()
assert.Equal(t, expectedOutput, strings.TrimSpace(buff.String()))
buff.Reset()
err = os.RemoveAll(filepathext.SmartJoin(dir, ".task"))
assert.NoError(t, err)
err = os.RemoveAll(filepathext.SmartJoin(dir, "src"))
assert.NoError(t, err)
}

23
taskfile/copy.go Normal file
View File

@@ -0,0 +1,23 @@
package taskfile
import "golang.org/x/exp/constraints"
func deepCopySlice[T any](orig []T) []T {
if orig == nil {
return nil
}
c := make([]T, len(orig))
copy(c, orig)
return c
}
func deepCopyMap[K constraints.Ordered, V any](orig map[K]V) map[K]V {
if orig == nil {
return nil
}
c := make(map[K]V, len(orig))
for k, v := range orig {
c[k] = v
}
return c
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"golang.org/x/exp/slices"
"gopkg.in/yaml.v3"
)
@@ -16,6 +17,8 @@ type IncludedTaskfile struct {
Taskfile string
Dir string
Optional bool
Internal bool
Aliases []string
AdvancedImport bool
Vars *Vars
BaseDir string // The directory from which the including taskfile was loaded; used to resolve relative paths
@@ -70,7 +73,7 @@ func (tfs *IncludedTaskfiles) Set(key string, includedTaskfile IncludedTaskfile)
if tfs.Mapping == nil {
tfs.Mapping = make(map[string]IncludedTaskfile, 1)
}
if !stringSliceContains(tfs.Keys, key) {
if !slices.Contains(tfs.Keys, key) {
tfs.Keys = append(tfs.Keys, key)
}
tfs.Mapping[key] = includedTaskfile
@@ -101,6 +104,8 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
Taskfile string
Dir string
Optional bool
Internal bool
Aliases []string
Vars *Vars
}
if err := unmarshal(&includedTaskfile); err != nil {
@@ -109,11 +114,30 @@ 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.Aliases = includedTaskfile.Aliases
it.AdvancedImport = true
it.Vars = includedTaskfile.Vars
return nil
}
// DeepCopy creates a new instance of IncludedTaskfile and copies
// data by value from the source struct.
func (it *IncludedTaskfile) DeepCopy() *IncludedTaskfile {
if it == nil {
return nil
}
return &IncludedTaskfile{
Taskfile: it.Taskfile,
Dir: it.Dir,
Optional: it.Optional,
Internal: it.Internal,
AdvancedImport: it.AdvancedImport,
Vars: it.Vars.DeepCopy(),
BaseDir: it.BaseDir,
}
}
// FullTaskfilePath returns the fully qualified path to the included taskfile
func (it *IncludedTaskfile) FullTaskfilePath() (string, error) {
return it.resolvePath(it.Taskfile)

View File

@@ -9,7 +9,7 @@ import (
const NamespaceSeparator = ":"
// Merge merges the second Taskfile into the first
func Merge(t1, t2 *Taskfile, namespaces ...string) error {
func Merge(t1, t2 *Taskfile, includedTaskfile *IncludedTaskfile, namespaces ...string) error {
if t1.Version != t2.Version {
return fmt.Errorf(`task: Taskfiles versions should match. First is "%s" but second is "%s"`, t1.Version, t2.Version)
}
@@ -39,20 +39,38 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
t1.Tasks = make(Tasks)
}
for k, v := range t2.Tasks {
// FIXME(@andreynering): Refactor this block, otherwise we can
// have serious side-effects in the future, since we're editing
// the original references instead of deep copying them.
// We do a deep copy of the task struct here to ensure that no data can
// be changed elsewhere once the taskfile is merged.
task := v.DeepCopy()
t1.Tasks[taskNameWithNamespace(k, namespaces...)] = v
// Set the task to internal if EITHER the included task or the included
// taskfile are marked as internal
task.Internal = task.Internal || includedTaskfile.Internal
for _, dep := range v.Deps {
// Add namespaces to dependencies, commands and aliases
for _, dep := range task.Deps {
dep.Task = taskNameWithNamespace(dep.Task, namespaces...)
}
for _, cmd := range v.Cmds {
for _, cmd := range task.Cmds {
if cmd != nil && cmd.Task != "" {
cmd.Task = taskNameWithNamespace(cmd.Task, namespaces...)
}
}
for i, alias := range task.Aliases {
task.Aliases[i] = taskNameWithNamespace(alias, namespaces...)
}
// Add namespace aliases
if includedTaskfile != nil {
for _, namespaceAlias := range includedTaskfile.Aliases {
task.Aliases = append(task.Aliases, taskNameWithNamespace(task.Task, namespaceAlias))
for _, alias := range v.Aliases {
task.Aliases = append(task.Aliases, taskNameWithNamespace(alias, namespaceAlias))
}
}
}
// Add the task to the merged taskfile
t1.Tasks[taskNameWithNamespace(k, namespaces...)] = task
}
return nil

View File

@@ -78,6 +78,8 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
Taskfile: tr.Replace(includedTask.Taskfile),
Dir: tr.Replace(includedTask.Dir),
Optional: includedTask.Optional,
Internal: includedTask.Internal,
Aliases: includedTask.Aliases,
AdvancedImport: includedTask.AdvancedImport,
Vars: includedTask.Vars,
BaseDir: includedTask.BaseDir,
@@ -148,9 +150,16 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
}
}
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
if err = taskfile.Merge(t, includedTaskfile, &includedTask, namespace); err != nil {
return err
}
if includedTaskfile.Tasks["default"] != nil && t.Tasks[namespace] == nil {
defaultTaskName := fmt.Sprintf("%s:default", namespace)
t.Tasks[defaultTaskName].Aliases = append(t.Tasks[defaultTaskName].Aliases, namespace)
t.Tasks[defaultTaskName].Aliases = append(t.Tasks[defaultTaskName].Aliases, includedTask.Aliases...)
}
return nil
})
if err != nil {
@@ -164,7 +173,7 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
if err != nil {
return nil, err
}
if err = taskfile.Merge(t, osTaskfile); err != nil {
if err = taskfile.Merge(t, osTaskfile, nil); err != nil {
return nil, err
}
}
@@ -187,7 +196,10 @@ func readTaskfile(file string) (*taskfile.Taskfile, error) {
return nil, err
}
var t taskfile.Taskfile
return &t, yaml.NewDecoder(f).Decode(&t)
if err := yaml.NewDecoder(f).Decode(&t); err != nil {
return nil, fmt.Errorf("task: Failed to parse %s:\n%w", filepathext.TryAbsToRel(file), err)
}
return &t, nil
}
func exists(path string) (string, error) {

View File

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

View File

@@ -11,6 +11,7 @@ type Task struct {
Label string
Desc string
Summary string
Aliases []string
Sources []string
Generates []string
Status []string
@@ -20,6 +21,7 @@ type Task struct {
Env *Vars
Silent bool
Interactive bool
Internal bool
Method string
Prefix string
IgnoreError bool
@@ -55,6 +57,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
Label string
Desc string
Summary string
Aliases []string
Sources []string
Generates []string
Status []string
@@ -64,6 +67,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"`
@@ -76,6 +80,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
t.Deps = task.Deps
t.Label = task.Label
t.Desc = task.Desc
t.Aliases = task.Aliases
t.Summary = task.Summary
t.Sources = task.Sources
t.Generates = task.Generates
@@ -86,9 +91,42 @@ 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
t.Run = task.Run
return nil
}
// DeepCopy creates a new instance of Task and copies
// data by value from the source struct.
func (t *Task) DeepCopy() *Task {
c := &Task{
Task: t.Task,
Cmds: deepCopySlice(t.Cmds),
Deps: deepCopySlice(t.Deps),
Label: t.Label,
Desc: t.Desc,
Summary: t.Summary,
Aliases: deepCopySlice(t.Aliases),
Sources: deepCopySlice(t.Sources),
Generates: deepCopySlice(t.Generates),
Status: deepCopySlice(t.Status),
Preconditions: deepCopySlice(t.Preconditions),
Dir: t.Dir,
Vars: t.Vars.DeepCopy(),
Env: t.Env.DeepCopy(),
Silent: t.Silent,
Interactive: t.Interactive,
Internal: t.Internal,
Method: t.Method,
Prefix: t.Prefix,
IgnoreError: t.IgnoreError,
Run: t.Run,
IncludeVars: t.IncludeVars.DeepCopy(),
IncludedTaskfileVars: t.IncludedTaskfileVars.DeepCopy(),
IncludedTaskfile: t.IncludedTaskfile.DeepCopy(),
}
return c
}

View File

@@ -18,6 +18,7 @@ type Taskfile struct {
Silent bool
Dotenv []string
Run string
Interval string
}
// UnmarshalYAML implements yaml.Unmarshaler interface
@@ -34,10 +35,13 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
Silent bool
Dotenv []string
Run string
Interval string
}
if err := unmarshal(&taskfile); err != nil {
return err
}
tf.Version = taskfile.Version
tf.Expansions = taskfile.Expansions
tf.Output = taskfile.Output
@@ -49,6 +53,8 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
tf.Silent = taskfile.Silent
tf.Dotenv = taskfile.Dotenv
tf.Run = taskfile.Run
tf.Interval = taskfile.Interval
if tf.Expansions <= 0 {
tf.Expansions = 2
}

View File

@@ -3,6 +3,7 @@ package taskfile
import (
"errors"
"golang.org/x/exp/slices"
"gopkg.in/yaml.v3"
)
@@ -34,6 +35,18 @@ func (vs *Vars) UnmarshalYAML(node *yaml.Node) error {
return nil
}
// DeepCopy creates a new instance of Vars and copies
// data by value from the source struct.
func (vs *Vars) DeepCopy() *Vars {
if vs == nil {
return nil
}
return &Vars{
Keys: deepCopySlice(vs.Keys),
Mapping: deepCopyMap(vs.Mapping),
}
}
// Merge merges the given Vars into the caller one
func (vs *Vars) Merge(other *Vars) {
_ = other.Range(func(key string, value Var) error {
@@ -47,7 +60,7 @@ func (vs *Vars) Set(key string, value Var) {
if vs.Mapping == nil {
vs.Mapping = make(map[string]Var, 1)
}
if !stringSliceContains(vs.Keys, key) {
if !slices.Contains(vs.Keys, key) {
vs.Keys = append(vs.Keys, key)
}
vs.Mapping[key] = value

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

@@ -0,0 +1,19 @@
version: '3'
includes:
included:
taskfile: Taskfile2.yml
aliases: [inc, i]
tasks:
foo:
aliases: [f, x]
cmds:
- echo "foo"
- task: b
bar:
aliases: [b, x]
cmds:
- echo "bar"
- task: inc:q

7
testdata/alias/Taskfile2.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
version: '3'
tasks:
qux:
aliases: [q, x]
cmds:
- echo "qux"

11
testdata/alias/alias-summary.txt vendored Normal file
View File

@@ -0,0 +1,11 @@
task: foo
(task does not have description or summary)
aliases:
- f
- x
commands:
- echo "foo"
- Task: b

6
testdata/alias/alias.txt vendored Normal file
View File

@@ -0,0 +1,6 @@
task: [foo] echo "foo"
foo
task: [bar] echo "bar"
bar
task: [included:qux] echo "qux"
qux

View File

@@ -0,0 +1,4 @@
version: '3'
includes:
included: incomplete.yml

View File

@@ -0,0 +1,4 @@
version: '3'
name:
'test

15
testdata/includes_internal/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
version: '3'
includes:
included:
taskfile: Taskfile2.yml
internal: true
tasks:
task-1:
cmds:
- task: included:default
task-2:
deps:
- included:default

View File

@@ -0,0 +1,6 @@
version: '3'
tasks:
task-3:
cmds:
- echo "Hello, World!"

View File

@@ -0,0 +1,10 @@
version: '3'
includes:
included:
taskfile: Taskfile2.yml
tasks:
included:
cmds:
- echo "shadowed" > file.txt

View File

@@ -0,0 +1,6 @@
version: '3'
tasks:
default:
cmds:
- echo "included" > file.txt

View File

@@ -0,0 +1 @@
shadowed

View File

@@ -0,0 +1,5 @@
version: '3'
includes:
included:
taskfile: Taskfile2.yml

View File

@@ -0,0 +1,6 @@
version: '3'
tasks:
default:
cmds:
- echo "included" > file.txt

View File

@@ -0,0 +1 @@
included

15
testdata/internal_task/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
version: '3'
tasks:
task-1:
cmds:
- task: task-3
task-2:
deps:
- task-3
task-3:
internal: true
cmds:
- echo "Hello, World!"

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

@@ -0,0 +1 @@
src/*

16
testdata/watcher_interval/Taskfile.yaml vendored Normal file
View File

@@ -0,0 +1,16 @@
# https://taskfile.dev
version: '3'
vars:
GREETING: Hello, World!
interval: "500ms"
tasks:
default:
sources:
- "src/*"
cmds:
- echo "{{.GREETING}}"
silent: false

View File

@@ -22,13 +22,12 @@ func (e *Executor) FastCompiledTask(call taskfile.Call) (*taskfile.Task, error)
}
func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskfile.Task, error) {
origTask, ok := e.Taskfile.Tasks[call.Task]
if !ok {
return nil, &taskNotFoundError{call.Task}
origTask, err := e.GetTask(call)
if err != nil {
return nil, err
}
var vars *taskfile.Vars
var err error
if evaluateShVars {
vars, err = e.Compiler.GetVariables(origTask, call)
} else {
@@ -50,6 +49,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
Label: r.Replace(origTask.Label),
Desc: r.Replace(origTask.Desc),
Summary: r.Replace(origTask.Summary),
Aliases: origTask.Aliases,
Sources: r.ReplaceSlice(origTask.Sources),
Generates: r.ReplaceSlice(origTask.Generates),
Dir: r.Replace(origTask.Dir),
@@ -57,6 +57,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
Env: nil,
Silent: origTask.Silent,
Interactive: origTask.Interactive,
Internal: origTask.Internal,
Method: r.Replace(origTask.Method),
Prefix: r.Replace(origTask.Prefix),
IgnoreError: origTask.IgnoreError,

View File

@@ -16,7 +16,7 @@ import (
"github.com/radovskyb/watcher"
)
const watchInterval = 5 * time.Second
const defaultWatchInterval = 5 * time.Second
// watchTasks start watching the given tasks
func (e *Executor) watchTasks(calls ...taskfile.Call) error {
@@ -24,6 +24,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error {
for i, c := range calls {
tasks[i] = c.Task
}
e.Logger.Errf(logger.Green, "task: Started watching for tasks: %s", strings.Join(tasks, ", "))
ctx, cancel := context.WithCancel(context.Background())
@@ -36,6 +37,27 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error {
}()
}
var watchIntervalString string
if e.Interval != "" {
watchIntervalString = e.Interval
} else if e.Taskfile.Interval != "" {
watchIntervalString = e.Taskfile.Interval
}
watchInterval := defaultWatchInterval
if watchIntervalString != "" {
var err error
watchInterval, err = parseWatchInterval(watchIntervalString)
if err != nil {
cancel()
return err
}
}
e.Logger.VerboseOutf(logger.Green, "task: Watching for changes every %v", watchInterval)
w := watcher.New()
defer w.Close()
w.SetMaxEvents(1)
@@ -163,3 +185,11 @@ func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...taskfile.Ca
func shouldIgnoreFile(path string) bool {
return strings.Contains(path, "/.git") || strings.Contains(path, "/.task") || strings.Contains(path, "/node_modules")
}
func parseWatchInterval(watchInterval string) (time.Duration, error) {
v, err := time.ParseDuration(watchInterval)
if err != nil {
return 0, fmt.Errorf(`task: Could not parse watch interval "%s": %v`, watchInterval, err)
}
return v, nil
}