Compare commits
140 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33b6927b79 | ||
|
|
852a176e1f | ||
|
|
7511249514 | ||
|
|
3429cdd8af | ||
|
|
a1cd8eafd8 | ||
|
|
fbfb4ba9c4 | ||
|
|
ba9ba63792 | ||
|
|
460b89ce51 | ||
|
|
a4ec6e5257 | ||
|
|
44aa2ee3b3 | ||
|
|
80b417c4ab | ||
|
|
6d90c781c9 | ||
|
|
c51f04eca8 | ||
|
|
dda2004753 | ||
|
|
297f9eccea | ||
|
|
d2f2cba6d8 | ||
|
|
172d71435a | ||
|
|
bb1aec8a7e | ||
|
|
476d9f5e70 | ||
|
|
99014ad38d | ||
|
|
403456d3dc | ||
|
|
6335878317 | ||
|
|
6bff658af0 | ||
|
|
3e5ee2332a | ||
|
|
66f6998c86 | ||
|
|
f2a8f8ad8f | ||
|
|
540f6ecfdb | ||
|
|
8ec89f1bbd | ||
|
|
d33906b6e4 | ||
|
|
bb79fa1dc3 | ||
|
|
376a6182eb | ||
|
|
81de61d8db | ||
|
|
d2061ec898 | ||
|
|
077efbd2e7 | ||
|
|
8ce1782380 | ||
|
|
c2f20465ab | ||
|
|
fb0e43989d | ||
|
|
754248395c | ||
|
|
6e975ca155 | ||
|
|
79a2bc404e | ||
|
|
42a26e1741 | ||
|
|
695711e124 | ||
|
|
0d5811e502 | ||
|
|
b9d070f76b | ||
|
|
122c3f083e | ||
|
|
5d22cf4327 | ||
|
|
219d3ad193 | ||
|
|
e72157e26a | ||
|
|
50a377a7c4 | ||
|
|
9d7ddff60c | ||
|
|
081d878f86 | ||
|
|
d8dc091267 | ||
|
|
1c44d8049a | ||
|
|
a95191d29e | ||
|
|
111f6e7f18 | ||
|
|
4a5c1e9ec4 | ||
|
|
8f0893b5f7 | ||
|
|
b16e705a6c | ||
|
|
3cad318b70 | ||
|
|
8c6002cae6 | ||
|
|
0355bbaf3b | ||
|
|
2ba083a650 | ||
|
|
c79ea5a257 | ||
|
|
44706f4957 | ||
|
|
a1b3bb03ed | ||
|
|
76caa16909 | ||
|
|
160b788198 | ||
|
|
eada62f62c | ||
|
|
bd9419e6db | ||
|
|
bdd9de3001 | ||
|
|
200ba4ed04 | ||
|
|
1e8939dd58 | ||
|
|
f45dd11e53 | ||
|
|
1a0cc1d64d | ||
|
|
421cb522d9 | ||
|
|
1b18b041d6 | ||
|
|
8788703ac6 | ||
|
|
b6c25e3ad9 | ||
|
|
73eaa68cd1 | ||
|
|
beb927f7b4 | ||
|
|
cdc969cd4e | ||
|
|
2a67499f12 | ||
|
|
6a3cc79daa | ||
|
|
97d4a947ee | ||
|
|
e0e47ad9a0 | ||
|
|
b08eac58e9 | ||
|
|
11409ccf21 | ||
|
|
e3b6c97c3b | ||
|
|
d3da086ebf | ||
|
|
3507fa40f1 | ||
|
|
6f8f1f1409 | ||
|
|
c2148a359d | ||
|
|
c172185a24 | ||
|
|
1140a5c4ae | ||
|
|
3cc378c960 | ||
|
|
9b3a961303 | ||
|
|
d048555149 | ||
|
|
7533858a52 | ||
|
|
c4e10ef0aa | ||
|
|
c20842e7cd | ||
|
|
6cfdb21313 | ||
|
|
e396f4d06f | ||
|
|
47c1bb6a5b | ||
|
|
adfb0b513e | ||
|
|
98d78b9d8a | ||
|
|
a1c32a56ea | ||
|
|
6584bcf87f | ||
|
|
7ac75af622 | ||
|
|
b3c283b282 | ||
|
|
c2615dd746 | ||
|
|
789518f70d | ||
|
|
ad3008d855 | ||
|
|
3da426603f | ||
|
|
8d26e34b0a | ||
|
|
110d1d7245 | ||
|
|
f7384623df | ||
|
|
5d24e166ab | ||
|
|
d9ec5bcd24 | ||
|
|
bf9cd7625b | ||
|
|
afbf98c974 | ||
|
|
fedb68cde7 | ||
|
|
f787937a30 | ||
|
|
f54fef7e7b | ||
|
|
e36c77aaf3 | ||
|
|
de45e48c37 | ||
|
|
ef0ce8224e | ||
|
|
5b4d5387bf | ||
|
|
5c460b38c9 | ||
|
|
8b836ab446 | ||
|
|
1be1fccc76 | ||
|
|
2c1fda97f0 | ||
|
|
71f7b719b5 | ||
|
|
e39006b511 | ||
|
|
37d07d415a | ||
|
|
b80c8f78fc | ||
|
|
7707179b93 | ||
|
|
4e7d8bacdb | ||
|
|
0c46fa5a56 | ||
|
|
36aca00de3 | ||
|
|
0ec8cf1b53 |
@@ -8,6 +8,6 @@ charset = utf-8
|
|||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
indent_style = tab
|
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_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|||||||
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,13 +1,15 @@
|
|||||||
---
|
---
|
||||||
name: Bug Report
|
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:
|
- Task version:
|
||||||
- Operating System:
|
- Operating System:
|
||||||
|
|
||||||
### Example Taskfile showing the issue
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
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
|
- name: Questions, Ideas and General Discussions
|
||||||
url: https://github.com/go-task/task/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.
|
||||||
|
|||||||
14
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
name: Feature Request
|
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.
|
> Describe in detail what feature do you want to see in Task.
|
||||||
Give examples if possible.
|
> Give examples if possible.
|
||||||
|
>
|
||||||
Please, search if this wasn't proposed before, and if this is more like an idea
|
> Please, search if this wasn't proposed before, and if this is more like an idea
|
||||||
than a strong feature request, consider opening a
|
> than a strong feature request, consider opening a
|
||||||
[discussion](https://github.com/go-task/task/discussions) instead.
|
> [discussion](https://github.com/go-task/task/discussions) instead.
|
||||||
|
|||||||
5
.github/pull_request_template.md
vendored
Normal 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/).
|
||||||
2
.github/workflows/lint.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.18
|
go-version: 1.18.x
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.18.x
|
go-version: 1.19.x
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v2
|
uses: goreleaser/goreleaser-action@v2
|
||||||
|
|||||||
2
.github/workflows/test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
name: Test
|
name: Test
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.17.x, 1.18.x]
|
go-version: [1.18.x, 1.19.x]
|
||||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
runs-on: ${{matrix.platform}}
|
runs-on: ${{matrix.platform}}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
2
.gitignore
vendored
@@ -22,6 +22,7 @@ dist/
|
|||||||
# editors
|
# editors
|
||||||
.idea/
|
.idea/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.fleet/
|
||||||
|
|
||||||
# exuberant ctags
|
# exuberant ctags
|
||||||
tags
|
tags
|
||||||
@@ -30,3 +31,4 @@ tags
|
|||||||
!/bin/.keep
|
!/bin/.keep
|
||||||
/testdata/vars/v1
|
/testdata/vars/v1
|
||||||
/tmp
|
/tmp
|
||||||
|
node_modules
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ build:
|
|||||||
goarch: 386
|
goarch: 386
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
|
flags:
|
||||||
|
- -trimpath
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w # Don't set main.version.
|
- -s -w # Don't set main.version.
|
||||||
|
|
||||||
|
|||||||
76
CHANGELOG.md
@@ -1,12 +1,86 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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)).
|
||||||
|
- Fix handling of `CLI_ARGS` (`--`) in Bash completion
|
||||||
|
([#863](https://github.com/go-task/task/pull/863)).
|
||||||
|
- On zsh completion, add ability to replace `--list-all` with `--list` as
|
||||||
|
already possible on the Bash completion
|
||||||
|
([#861](https://github.com/go-task/task/pull/861)).
|
||||||
|
|
||||||
|
## v3.15.0 - 2022-09-03
|
||||||
|
|
||||||
|
- Add new special variables `ROOT_DIR` and `TASKFILE_DIR`. This was a highly
|
||||||
|
requested feature
|
||||||
|
([#215](https://github.com/go-task/task/issues/215), [#857](https://github.com/go-task/task/pull/857), [Documentation](https://taskfile.dev/api/#special-variables)).
|
||||||
|
- Follow symlinks on `sources`
|
||||||
|
([#826](https://github.com/go-task/task/issues/826), [#831](https://github.com/go-task/task/pull/831)).
|
||||||
|
- Improvements and fixes to Bash completion
|
||||||
|
([#835](https://github.com/go-task/task/pull/835), [#844](https://github.com/go-task/task/pull/844)).
|
||||||
|
|
||||||
|
## v3.14.1 - 2022-08-03
|
||||||
|
|
||||||
|
- Always resolve relative include paths relative to the including Taskfile
|
||||||
|
([#822](https://github.com/go-task/task/issues/822), [#823](https://github.com/go-task/task/pull/823)).
|
||||||
|
- Fix ZSH and PowerShell completions to consider all tasks instead of just the
|
||||||
|
public ones (those with descriptions)
|
||||||
|
([#803](https://github.com/go-task/task/pull/803)).
|
||||||
|
|
||||||
|
## v3.14.0 - 2022-07-08
|
||||||
|
|
||||||
|
- Add ability to override the `.task` directory location with the
|
||||||
|
`TASK_TEMP_DIR` environment variable.
|
||||||
|
- Allow to override Task colors using environment variables:
|
||||||
|
`TASK_COLOR_RESET`, `TASK_COLOR_BLUE`, `TASK_COLOR_GREEN`,
|
||||||
|
`TASK_COLOR_CYAN`, `TASK_COLOR_YELLOW`, `TASK_COLOR_MAGENTA`
|
||||||
|
and `TASK_COLOR_RED`
|
||||||
|
([#568](https://github.com/go-task/task/pull/568), [#792](https://github.com/go-task/task/pull/792)).
|
||||||
|
- Fixed bug when using the `output: group` mode where STDOUT and STDERR were
|
||||||
|
being print in separated blocks instead of in the right order
|
||||||
|
([#779](https://github.com/go-task/task/issues/779)).
|
||||||
|
- Starting on this release, ARM architecture binaries are been released to Snap
|
||||||
|
as well
|
||||||
|
([#795](https://github.com/go-task/task/issues/795)).
|
||||||
|
- i386 binaries won't be available anymore on Snap because Ubuntu removed the support
|
||||||
|
for this architecture.
|
||||||
|
- Upgrade mvdan.cc/sh, which fixes a bug with associative arrays
|
||||||
|
([#785](https://github.com/go-task/task/issues/785), [mvdan/sh#884](https://github.com/mvdan/sh/issues/884), [mvdan/sh#893](https://github.com/mvdan/sh/pull/893)).
|
||||||
|
|
||||||
## v3.13.0 - 2022-06-13
|
## v3.13.0 - 2022-06-13
|
||||||
|
|
||||||
- Added `-n` as an alias to `--dry`
|
- Added `-n` as an alias to `--dry`
|
||||||
([#776](https://github.com/go-task/task/issues/776), [#777](https://github.com/go-task/task/pull/777)).
|
([#776](https://github.com/go-task/task/issues/776), [#777](https://github.com/go-task/task/pull/777)).
|
||||||
- Fix behavior of interrupt (SIGINT, SIGTERM) signals. Task will now give time
|
- Fix behavior of interrupt (SIGINT, SIGTERM) signals. Task will now give time
|
||||||
for the processes running to do cleanup work
|
for the processes running to do cleanup work
|
||||||
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728)).
|
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728), [#769](https://github.com/go-task/task/pull/769)).
|
||||||
- Add new `--exit-code` (`-x`) flag that will pass-through the exit form the
|
- Add new `--exit-code` (`-x`) flag that will pass-through the exit form the
|
||||||
command being ran
|
command being ran
|
||||||
([#755](https://github.com/go-task/task/pull/755)).
|
([#755](https://github.com/go-task/task/pull/755)).
|
||||||
|
|||||||
@@ -10,6 +10,6 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
30
Taskfile.yml
@@ -2,6 +2,7 @@ version: '3'
|
|||||||
|
|
||||||
includes:
|
includes:
|
||||||
docs:
|
docs:
|
||||||
|
aliases: [d]
|
||||||
taskfile: ./docs
|
taskfile: ./docs
|
||||||
dir: ./docs
|
dir: ./docs
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ tasks:
|
|||||||
|
|
||||||
install:
|
install:
|
||||||
desc: Installs Task
|
desc: Installs Task
|
||||||
|
aliases: [i]
|
||||||
sources:
|
sources:
|
||||||
- './**/*.go'
|
- './**/*.go'
|
||||||
cmds:
|
cmds:
|
||||||
@@ -42,6 +44,7 @@ tasks:
|
|||||||
|
|
||||||
lint:
|
lint:
|
||||||
desc: Runs golangci-lint
|
desc: Runs golangci-lint
|
||||||
|
aliases: [l]
|
||||||
sources:
|
sources:
|
||||||
- './**/*.go'
|
- './**/*.go'
|
||||||
cmds:
|
cmds:
|
||||||
@@ -65,15 +68,40 @@ tasks:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
desc: Runs test suite
|
desc: Runs test suite
|
||||||
deps: [install, sleepit:build]
|
aliases: [t]
|
||||||
|
deps: [install]
|
||||||
cmds:
|
cmds:
|
||||||
- go test {{catLines .GO_PACKAGES}}
|
- go test {{catLines .GO_PACKAGES}}
|
||||||
|
|
||||||
|
test:signals:
|
||||||
|
desc: Runs test suite with signals tests included
|
||||||
|
deps: [install, sleepit:build]
|
||||||
|
cmds:
|
||||||
|
- go test {{catLines .GO_PACKAGES}} -tags signals
|
||||||
|
|
||||||
test-release:
|
test-release:
|
||||||
desc: Tests release process without publishing
|
desc: Tests release process without publishing
|
||||||
cmds:
|
cmds:
|
||||||
- goreleaser --snapshot --rm-dist
|
- goreleaser --snapshot --rm-dist
|
||||||
|
|
||||||
|
docs:changelog:
|
||||||
|
desc: Copy CHANGELOG.md to the documentation website
|
||||||
|
vars:
|
||||||
|
FILE: docs/docs/changelog.md
|
||||||
|
cmds:
|
||||||
|
- rm {{.FILE}}
|
||||||
|
- 'echo "---" >> {{.FILE}}'
|
||||||
|
- 'echo "slug: /changelog/" >> {{.FILE}}'
|
||||||
|
- 'echo "sidebar_position: 6" >> {{.FILE}}'
|
||||||
|
- 'echo "---" >> {{.FILE}}'
|
||||||
|
- 'echo "" >> {{.FILE}}'
|
||||||
|
- 'cat CHANGELOG.md >> {{.FILE}}'
|
||||||
|
|
||||||
|
npm:publish:
|
||||||
|
desc: Publish release to npm
|
||||||
|
cmds:
|
||||||
|
- npm publish --access=public
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
cmds:
|
cmds:
|
||||||
- echo '{{.GO_PACKAGES}}'
|
- echo '{{.GO_PACKAGES}}'
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ func main() {
|
|||||||
entrypoint string
|
entrypoint string
|
||||||
output taskfile.Output
|
output taskfile.Output
|
||||||
color bool
|
color bool
|
||||||
|
interval string
|
||||||
)
|
)
|
||||||
|
|
||||||
pflag.BoolVar(&versionFlag, "version", false, "show Task version")
|
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.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.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.IntVarP(&concurrency, "concurrency", "C", 0, "limit number tasks to run concurrently")
|
||||||
|
pflag.StringVarP(&interval, "interval", "I", "5s", "interval to watch for changes")
|
||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
|
|
||||||
if versionFlag {
|
if versionFlag {
|
||||||
@@ -151,6 +153,7 @@ func main() {
|
|||||||
Parallel: parallel,
|
Parallel: parallel,
|
||||||
Color: color,
|
Color: color,
|
||||||
Concurrency: concurrency,
|
Concurrency: concurrency,
|
||||||
|
Interval: interval,
|
||||||
|
|
||||||
Stdin: os.Stdin,
|
Stdin: os.Stdin,
|
||||||
Stdout: os.Stdout,
|
Stdout: os.Stdout,
|
||||||
|
|||||||
@@ -1,26 +1,55 @@
|
|||||||
#!/bin/bash
|
# vim: set tabstop=2 shiftwidth=2 expandtab:
|
||||||
|
|
||||||
GO_TASK_PROGNAME=task
|
_GO_TASK_COMPLETION_LIST_OPTION='--list-all'
|
||||||
|
|
||||||
_go_task_completion()
|
function _task()
|
||||||
{
|
{
|
||||||
local cur
|
local cur prev words cword
|
||||||
_get_comp_words_by_ref -n : cur
|
_init_completion -n : || return
|
||||||
|
|
||||||
case "$cur" in
|
# Check for `--` within command-line and quit or strip suffix.
|
||||||
--*)
|
local i
|
||||||
local options
|
for i in "${!words[@]}"; do
|
||||||
options="$(_parse_help task)"
|
if [ "${words[$i]}" == "--" ]; then
|
||||||
mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur")
|
# Do not complete words following `--` passed to CLI_ARGS.
|
||||||
|
[ $cword -gt $i ] && return
|
||||||
|
# Remove the words following `--` to not put --list in CLI_ARGS.
|
||||||
|
words=( "${words[@]:0:$i}" )
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Handle special arguments of options.
|
||||||
|
case "$prev" in
|
||||||
|
-d|--dir)
|
||||||
|
_filedir -d
|
||||||
|
return $?
|
||||||
;;
|
;;
|
||||||
*)
|
-t|--taskfile)
|
||||||
local tasks
|
_filedir yaml || return $?
|
||||||
tasks="$($GO_TASK_PROGNAME --list-all 2> /dev/null | awk 'NR>1 { sub(/:$/,"",$2); print $2 }')"
|
_filedir yml
|
||||||
mapfile -t COMPREPLY < <(compgen -W "$tasks" -- "$cur")
|
return $?
|
||||||
|
;;
|
||||||
|
-o|--output)
|
||||||
|
COMPREPLY=( $( compgen -W "interleaved group prefixed" -- $cur ) )
|
||||||
|
return 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# Handle normal options.
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=( $( compgen -W "$(_parse_help $1)" -- $cur ) )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Prepare task name completions.
|
||||||
|
local tasks=( $( "${words[@]}" --silent $_GO_TASK_COMPLETION_LIST_OPTION 2> /dev/null ) )
|
||||||
|
COMPREPLY=( $( compgen -W "${tasks[*]}" -- "$cur" ) )
|
||||||
|
|
||||||
|
# Post-process because task names might contain colons.
|
||||||
__ltrim_colon_completions "$cur"
|
__ltrim_colon_completions "$cur"
|
||||||
}
|
}
|
||||||
|
|
||||||
complete -F _go_task_completion $GO_TASK_PROGNAME
|
complete -F _task task
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
set GO_TASK_PROGNAME task
|
set GO_TASK_PROGNAME task
|
||||||
|
|
||||||
function __task_get_tasks --description "Prints all available tasks with their description"
|
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
|
if test $output
|
||||||
echo $output
|
echo $output
|
||||||
end
|
end
|
||||||
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
|
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)"
|
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 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 -s d -l dir -d 'sets directory of execution'
|
||||||
complete -c $GO_TASK_PROGNAME -l dry -d 'compiles and prints tasks in the order that they would be run, without executing them'
|
complete -c $GO_TASK_PROGNAME -l dry -d 'compiles and prints tasks in the order that they would be run, without executing them'
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ $scriptBlock = {
|
|||||||
$curReg = "task{.exe}? (.*?)$"
|
$curReg = "task{.exe}? (.*?)$"
|
||||||
$startsWith = $wordToComplete | Select-String $curReg -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value }
|
$startsWith = $wordToComplete | Select-String $curReg -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value }
|
||||||
$reg = "\* ($startsWith.+?):"
|
$reg = "\* ($startsWith.+?):"
|
||||||
$listOutput = $(task -l)
|
$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 + " " }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
local context state state_descr line
|
local context state state_descr line
|
||||||
typeset -A opt_args
|
typeset -A opt_args
|
||||||
|
|
||||||
|
_GO_TASK_COMPLETION_LIST_OPTION="${GO_TASK_COMPLETION_LIST_OPTION:---list-all}"
|
||||||
|
|
||||||
# Listing commands from Taskfile.yml
|
# Listing commands from Taskfile.yml
|
||||||
function __task_list() {
|
function __task_list() {
|
||||||
local -a scripts cmd
|
local -a scripts cmd
|
||||||
@@ -27,7 +29,7 @@ function __task_list() {
|
|||||||
(( enabled )) || return 0
|
(( enabled )) || return 0
|
||||||
|
|
||||||
scripts=()
|
scripts=()
|
||||||
for item in "${(@)${(f)$("${cmd[@]}" --list)}[2,-1]#\* }"; do
|
for item in "${(@)${(f)$("${cmd[@]}" $_GO_TASK_COMPLETION_LIST_OPTION)}[2,-1]#\* }"; do
|
||||||
task="${item%%:[[:space:]]*}"
|
task="${item%%:[[:space:]]*}"
|
||||||
desc="${item##[^[:space:]]##[[:space:]]##}"
|
desc="${item##[^[:space:]]##[[:space:]]##}"
|
||||||
scripts+=( "${task//:/\\:}:$desc" )
|
scripts+=( "${task//:/\\:}:$desc" )
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ tasks:
|
|||||||
|
|
||||||
start:
|
start:
|
||||||
desc: Start website
|
desc: Start website
|
||||||
|
aliases: [s]
|
||||||
vars:
|
vars:
|
||||||
|
HOST: '{{default "localhost" .HOST}}'
|
||||||
PORT: '{{default "3001" .PORT}}'
|
PORT: '{{default "3001" .PORT}}'
|
||||||
cmds:
|
cmds:
|
||||||
- npx docusaurus start --no-open --port={{.PORT}}
|
- npx docusaurus start --no-open --host={{.HOST}} --port={{.PORT}}
|
||||||
|
|
||||||
build:
|
build:
|
||||||
desc: Build website
|
desc: Build website
|
||||||
@@ -24,6 +26,7 @@ tasks:
|
|||||||
- rm -rf ./build
|
- rm -rf ./build
|
||||||
|
|
||||||
deploy:
|
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:
|
cmds:
|
||||||
- npx docusaurus deploy
|
- npx docusaurus deploy
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ task [--flags] [tasks...] [-- CLI_ARGS...]
|
|||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
|
|
||||||
If `--` is given, all remaning arguments to assigned to a special `CLI_ARGS`
|
If `--` is given, all remaning arguments will be assigned to a special `CLI_ARGS`
|
||||||
variable
|
variable
|
||||||
|
|
||||||
:::
|
:::
|
||||||
@@ -30,6 +30,7 @@ variable
|
|||||||
| `-f` | `--force` | `bool` | `false` | Forces execution even when the task is up-to-date. |
|
| `-f` | `--force` | `bool` | `false` | Forces execution even when the task is up-to-date. |
|
||||||
| `-h` | `--help` | `bool` | `false` | Shows Task usage. |
|
| `-h` | `--help` | `bool` | `false` | Shows Task usage. |
|
||||||
| `-i` | `--init` | `bool` | `false` | Creates a new Taskfile.yaml in the current folder. |
|
| `-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. |
|
| `-l` | `--list` | `bool` | `false` | Lists tasks with description of current Taskfile. |
|
||||||
| `-a` | `--list-all` | `bool` | `false` | Lists tasks with or without a description. |
|
| `-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`]. |
|
| `-o` | `--output` | `string` | Default set in the Taskfile or `intervealed` | Sets output style: [`interleaved`/`group`/`prefixed`]. |
|
||||||
@@ -44,6 +45,34 @@ variable
|
|||||||
| | `--version` | `bool` | `false` | Show Task version. |
|
| | `--version` | `bool` | `false` | Show Task version. |
|
||||||
| `-w` | `--watch` | `bool` | `false` | Enables watch of the given task. |
|
| `-w` | `--watch` | `bool` | `false` | Enables watch of the given task. |
|
||||||
|
|
||||||
|
## Special Variables
|
||||||
|
|
||||||
|
There are some special variables that is available on the templating system:
|
||||||
|
|
||||||
|
| Var | Description |
|
||||||
|
| - | - |
|
||||||
|
| `CLI_ARGS` | Contain all extra arguments passed after `--` when calling Task through the CLI. |
|
||||||
|
| `TASK` | The name of the current task. |
|
||||||
|
| `ROOT_DIR` | The absolute path of the root Taskfile. |
|
||||||
|
| `TASKFILE_DIR` | The absolute path of the included Taskfile. |
|
||||||
|
| `CHECKSUM` | The checksum of the files listed in `sources`. Only available within the `status` prop and if method is set to `checksum`. |
|
||||||
|
| `TIMESTAMP` | The date object of the greatest timestamp of the files listes in `sources`. Only available within the `status` prop and if method is set to `timestamp`. |
|
||||||
|
|
||||||
|
## ENV
|
||||||
|
|
||||||
|
Some environment variables can be overriden to adjust Task behavior.
|
||||||
|
|
||||||
|
| ENV | Default | Description |
|
||||||
|
| - | - | - |
|
||||||
|
| `TASK_TEMP_DIR` | `.task` | Location of the temp dir. Can relative to the project like `tmp/task` or absolute like `/tmp/.task` or `~/.task`. |
|
||||||
|
| `TASK_COLOR_RESET` | `0` | Color used for white. |
|
||||||
|
| `TASK_COLOR_BLUE` | `34` | Color used for blue. |
|
||||||
|
| `TASK_COLOR_GREEN` | `32` | Color used for green. |
|
||||||
|
| `TASK_COLOR_CYAN` | `36` | Color used for cyan. |
|
||||||
|
| `TASK_COLOR_YELLOW` | `33` | Color used for yellow. |
|
||||||
|
| `TASK_COLOR_MAGENTA` | `35` | Color used for magenta. |
|
||||||
|
| `TASK_COLOR_RED` | `31` | Color used for red. |
|
||||||
|
|
||||||
## Schema
|
## Schema
|
||||||
|
|
||||||
### Taskfile
|
### Taskfile
|
||||||
@@ -56,19 +85,21 @@ variable
|
|||||||
| `method` | `string` | `checksum` | Default method in this Taskfile. Can be overriden in a task by task basis. Available options: `checksum`, `timestamp` and `none`. |
|
| `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. |
|
| `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`. |
|
| `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). |
|
||||||
| `vars` | [`map[string]Variable`](#variable) | | Global variables. |
|
| `vars` | [`map[string]Variable`](#variable) | | Global variables. |
|
||||||
| `env` | [`map[string]Variable`](#variable) | | Global environment. |
|
| `env` | [`map[string]Variable`](#variable) | | Global environment. |
|
||||||
| `dotenv` | `[]string` | | A list of `.env` file paths to be parsed. |
|
| `dotenv` | `[]string` | | A list of `.env` file paths to be parsed. |
|
||||||
| `tasks` | [`map[string]Task`](#task) | | The task definitions. |
|
| `tasks` | [`map[string]Task`](#task) | | The task definitions. |
|
||||||
|
|
||||||
|
|
||||||
### Include
|
### Include
|
||||||
|
|
||||||
| Attribute | Type | Default | Description |
|
| Attribute | Type | Default | Description |
|
||||||
| - | - | - | - |
|
| - | - | - | - |
|
||||||
| `taskfile` | `string` | | The path for the Taskfile or directory to be included. If a directory, Task will look for files named `Taskfile.yml` or `Taskfile.yaml` inside that directory. |
|
| `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. |
|
| `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. |
|
| `optional` | `bool` | `false` | If `true`, no errors will be thrown if the specified file does not exist. |
|
||||||
|
| `internal` | `bool` | `false` | If `true`, tasks will not be callable from the command line and will be omitted from both `--list` and `--list-all`. |
|
||||||
|
| `aliases` | `[]string` | | Alternative names for the namespace of the included Taskfile. |
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
@@ -87,10 +118,13 @@ includes:
|
|||||||
| - | - | - | - |
|
| - | - | - | - |
|
||||||
| `desc` | `string` | | A short description of the task. This is listed when calling `task --list`. |
|
| `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]`. |
|
| `summary` | `string` | | A longer description of the task. This is listed when calling `task --summary [task]`. |
|
||||||
|
| `aliases` | `[]string` | | Alternative names for the task. |
|
||||||
|
| `label` | `string` | | Overrides the name of the task in the output when a task is run. Supports variables. |
|
||||||
| `sources` | `[]string` | | List of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs. |
|
| `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. |
|
| `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` |
|
| `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. |
|
| `silent` | `bool` | `false` | Skips some output for this task. Note that STDOUT and STDERR of the commands will still be redirected. |
|
||||||
|
| `internal` | `bool` | `false` | If `true`, this task will not be callable from the command line and will be omitted from both `--list` and `--list-all`. |
|
||||||
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
|
| `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. |
|
| `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. |
|
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the commands. |
|
||||||
@@ -114,6 +148,9 @@ tasks:
|
|||||||
foobar:
|
foobar:
|
||||||
- echo "foo"
|
- echo "foo"
|
||||||
- echo "bar"
|
- echo "bar"
|
||||||
|
|
||||||
|
baz:
|
||||||
|
cmd: echo "baz"
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|||||||
@@ -5,13 +5,87 @@ sidebar_position: 6
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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)).
|
||||||
|
- Fix handling of `CLI_ARGS` (`--`) in Bash completion
|
||||||
|
([#863](https://github.com/go-task/task/pull/863)).
|
||||||
|
- On zsh completion, add ability to replace `--list-all` with `--list` as
|
||||||
|
already possible on the Bash completion
|
||||||
|
([#861](https://github.com/go-task/task/pull/861)).
|
||||||
|
|
||||||
|
## v3.15.0 - 2022-09-03
|
||||||
|
|
||||||
|
- Add new special variables `ROOT_DIR` and `TASKFILE_DIR`. This was a highly
|
||||||
|
requested feature
|
||||||
|
([#215](https://github.com/go-task/task/issues/215), [#857](https://github.com/go-task/task/pull/857), [Documentation](https://taskfile.dev/api/#special-variables)).
|
||||||
|
- Follow symlinks on `sources`
|
||||||
|
([#826](https://github.com/go-task/task/issues/826), [#831](https://github.com/go-task/task/pull/831)).
|
||||||
|
- Improvements and fixes to Bash completion
|
||||||
|
([#835](https://github.com/go-task/task/pull/835), [#844](https://github.com/go-task/task/pull/844)).
|
||||||
|
|
||||||
|
## v3.14.1 - 2022-08-03
|
||||||
|
|
||||||
|
- Always resolve relative include paths relative to the including Taskfile
|
||||||
|
([#822](https://github.com/go-task/task/issues/822), [#823](https://github.com/go-task/task/pull/823)).
|
||||||
|
- Fix ZSH and PowerShell completions to consider all tasks instead of just the
|
||||||
|
public ones (those with descriptions)
|
||||||
|
([#803](https://github.com/go-task/task/pull/803)).
|
||||||
|
|
||||||
|
## v3.14.0 - 2022-07-08
|
||||||
|
|
||||||
|
- Add ability to override the `.task` directory location with the
|
||||||
|
`TASK_TEMP_DIR` environment variable.
|
||||||
|
- Allow to override Task colors using environment variables:
|
||||||
|
`TASK_COLOR_RESET`, `TASK_COLOR_BLUE`, `TASK_COLOR_GREEN`,
|
||||||
|
`TASK_COLOR_CYAN`, `TASK_COLOR_YELLOW`, `TASK_COLOR_MAGENTA`
|
||||||
|
and `TASK_COLOR_RED`
|
||||||
|
([#568](https://github.com/go-task/task/pull/568), [#792](https://github.com/go-task/task/pull/792)).
|
||||||
|
- Fixed bug when using the `output: group` mode where STDOUT and STDERR were
|
||||||
|
being print in separated blocks instead of in the right order
|
||||||
|
([#779](https://github.com/go-task/task/issues/779)).
|
||||||
|
- Starting on this release, ARM architecture binaries are been released to Snap
|
||||||
|
as well
|
||||||
|
([#795](https://github.com/go-task/task/issues/795)).
|
||||||
|
- i386 binaries won't be available anymore on Snap because Ubuntu removed the support
|
||||||
|
for this architecture.
|
||||||
|
- Upgrade mvdan.cc/sh, which fixes a bug with associative arrays
|
||||||
|
([#785](https://github.com/go-task/task/issues/785), [mvdan/sh#884](https://github.com/mvdan/sh/issues/884), [mvdan/sh#893](https://github.com/mvdan/sh/pull/893)).
|
||||||
|
|
||||||
## v3.13.0 - 2022-06-13
|
## v3.13.0 - 2022-06-13
|
||||||
|
|
||||||
- Added `-n` as an alias to `--dry`
|
- Added `-n` as an alias to `--dry`
|
||||||
([#776](https://github.com/go-task/task/issues/776), [#777](https://github.com/go-task/task/pull/777)).
|
([#776](https://github.com/go-task/task/issues/776), [#777](https://github.com/go-task/task/pull/777)).
|
||||||
- Fix behavior of interrupt (SIGINT, SIGTERM) signals. Task will now give time
|
- Fix behavior of interrupt (SIGINT, SIGTERM) signals. Task will now give time
|
||||||
for the processes running to do cleanup work
|
for the processes running to do cleanup work
|
||||||
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728)).
|
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728), [#769](https://github.com/go-task/task/pull/769)).
|
||||||
- Add new `--exit-code` (`-x`) flag that will pass-through the exit form the
|
- Add new `--exit-code` (`-x`) flag that will pass-through the exit form the
|
||||||
command being ran
|
command being ran
|
||||||
([#755](https://github.com/go-task/task/pull/755)).
|
([#755](https://github.com/go-task/task/pull/755)).
|
||||||
|
|||||||
@@ -46,9 +46,11 @@ Some installation methods are maintained by third party:
|
|||||||
|
|
||||||
- [GitHub Actions](https://github.com/arduino/setup-task)
|
- [GitHub Actions](https://github.com/arduino/setup-task)
|
||||||
by [@arduino](https://github.com/arduino)
|
by [@arduino](https://github.com/arduino)
|
||||||
- [AUR](https://aur.archlinux.org/packages/taskfile-git)
|
- [AUR](https://aur.archlinux.org/packages/go-task-bin)
|
||||||
by [@kovetskiy](https://github.com/kovetskiy)
|
by [@carlsmedstad](https://github.com/carlsmedstad)
|
||||||
- [Scoop](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json)
|
- [Scoop](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json)
|
||||||
|
- [Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/)
|
||||||
|
- [NixOS](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/tools/go-task/default.nix)
|
||||||
|
|
||||||
## More
|
## More
|
||||||
|
|
||||||
|
|||||||
122
docs/docs/contributing.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
---
|
||||||
|
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 command/flag, ensure that you add it to the [API
|
||||||
|
Reference](./api_reference.md).
|
||||||
|
|
||||||
|
### 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
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
slug: /donate/
|
slug: /donate/
|
||||||
sidebar_position: 9
|
sidebar_position: 10
|
||||||
---
|
---
|
||||||
|
|
||||||
# Donate
|
# Donate
|
||||||
|
|||||||
@@ -42,11 +42,10 @@ This installation method is community owned.
|
|||||||
|
|
||||||
### Scoop
|
### Scoop
|
||||||
|
|
||||||
If you're on Windows and have [Scoop][scoop] installed, use `extras` bucket
|
If you're on Windows and have [Scoop][scoop] installed, getting
|
||||||
to install Task like:
|
Task is as simple as running:
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
scoop bucket add extras
|
|
||||||
scoop install task
|
scoop install task
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -56,15 +55,23 @@ may take some time until it's available on Scoop.
|
|||||||
### AUR
|
### AUR
|
||||||
|
|
||||||
If you're on Arch Linux you can install Task from
|
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`:
|
package manager such as `yay`, `pacaur` or `yaourt`:
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
yay -S taskfile-git
|
yay -S go-task-bin
|
||||||
```
|
```
|
||||||
|
|
||||||
This installation method is community owned, but since it's `-git` version of
|
Alternatively, there's
|
||||||
the package, it's always latest available version based on the Git repository.
|
[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
|
### Fedora
|
||||||
|
|
||||||
@@ -90,6 +97,15 @@ nix-env -iA nixpkgs.go-task
|
|||||||
This installation method is community owned. After a new release of Task, it
|
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).
|
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
|
## Get The Binary
|
||||||
|
|
||||||
### Binary
|
### Binary
|
||||||
@@ -107,19 +123,25 @@ We also have an [install script][installscript] which is very useful in
|
|||||||
scenarios like CI. Many thanks to [GoDownloader][godownloader] for enabling the
|
scenarios like CI. Many thanks to [GoDownloader][godownloader] for enabling the
|
||||||
easy generation of this script.
|
easy generation of this script.
|
||||||
|
|
||||||
|
By default, it installs on the `./bin` directory relative to the working
|
||||||
|
directory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# For Default Installation to ./bin with debug logging
|
|
||||||
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
|
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
|
||||||
|
|
||||||
# For Installation To /usr/local/bin for userwide access with debug logging
|
|
||||||
# May require sudo sh
|
|
||||||
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
:::info
|
It is possible to override the installation directory with the `-b` parameter.
|
||||||
|
On Linux, common choices are `~/.local/bin` and `~/bin` to install for the
|
||||||
|
current user or `/usr/local/bin` to install for all users:
|
||||||
|
|
||||||
This method will download the binary on the local `./bin` directory by default.
|
```bash
|
||||||
|
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
On macOS and Windows, `~/.local/bin` and `~/bin` are not added to `$PATH` by
|
||||||
|
default.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ guide to check the full schema documentation and Task features.
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- [Easy installation](installation.md): just download a single binary, add to
|
- [Easy installation](installation.md): just download a single binary, add to
|
||||||
$PATH and you're done! Or you can also install using [Homebrew][homebrew],
|
`$PATH` and you're done! Or you can also install using [Homebrew][homebrew],
|
||||||
[Snapcraft][snapcraft], or [Scoop][scoop] if you want;
|
[Snapcraft][snapcraft], or [Scoop][scoop] if you want.
|
||||||
- Available on CIs: by adding [this simple command](installation.md#install-script)
|
- Available on CIs: by adding [this simple command](installation.md#install-script)
|
||||||
to install on your CI script and you're done to use Task as part of your CI pipeline;
|
to install on your CI script and you're ready to use Task as part of your CI pipeline;
|
||||||
- Truly cross-platform: while most build tools only work well on Linux or macOS,
|
- Truly cross-platform: while most build tools only work well on Linux or macOS,
|
||||||
Task also supports Windows thanks to [this shell interpreter for Go][sh];
|
Task also supports Windows thanks to [this shell interpreter for Go][sh].
|
||||||
- Great for code generation: you can easily [prevent a task from running](/usage#prevent-unnecessary-work)
|
- Great for code generation: you can easily [prevent a task from running](/usage#prevent-unnecessary-work)
|
||||||
if a given set of files haven't changed since last run (based either on its
|
if a given set of files haven't changed since last run (based either on its
|
||||||
timestamp or content).
|
timestamp or content).
|
||||||
@@ -54,4 +54,4 @@ guide to check the full schema documentation and Task features.
|
|||||||
[homebrew]: https://brew.sh/
|
[homebrew]: https://brew.sh/
|
||||||
[snapcraft]: https://snapcraft.io/
|
[snapcraft]: https://snapcraft.io/
|
||||||
[scoop]: https://scoop.sh/
|
[scoop]: https://scoop.sh/
|
||||||
[sh]: https://mvdan.cc/sh
|
[sh]: https://github.com/mvdan/sh
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
slug: /releasing/
|
slug: /releasing/
|
||||||
sidebar_position: 7
|
sidebar_position: 8
|
||||||
---
|
---
|
||||||
|
|
||||||
# Releasing
|
# Releasing
|
||||||
@@ -13,27 +13,35 @@ the `test-release` task of the Taskfile.
|
|||||||
artifacts automatically when a new Git tag is pushed to master
|
artifacts automatically when a new Git tag is pushed to master
|
||||||
(raw executables and DEB and RPM packages).
|
(raw executables and DEB and RPM packages).
|
||||||
|
|
||||||
|
Since v3.15.0, raw executables can also be reproduced and verified locally by
|
||||||
|
checking out a specific tag and calling `goreleaser build`, using the Go version
|
||||||
|
defined in the above GitHub Actions.
|
||||||
|
|
||||||
# Homebrew
|
# Homebrew
|
||||||
|
|
||||||
To release a new version on the [Homebrew tap][homebrewtap] edit the
|
Goreleaser will automatically push a new commit to the
|
||||||
[Formula/go-task.rb][gotaskrb] file, updating with the new version, download
|
[Formula/go-task.rb][gotaskrb] file in the [Homebrew tap][homebrewtap]
|
||||||
URL and sha256.
|
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
|
# Snapcraft
|
||||||
|
|
||||||
The exception is the publishing of a new version of the
|
The [snap package][snappackage] requires to manual steps to release a new
|
||||||
[snap package][snappackage]. This current require two steps after publishing
|
version:
|
||||||
the binaries:
|
|
||||||
|
|
||||||
* Updating the current version on [snapcraft.yaml][snapcraftyaml];
|
* Updating the current version on [snapcraft.yaml][snapcraftyaml].
|
||||||
* Moving both `i386` and `amd64` new artifacts to the stable channel on
|
* Moving both `amd64`, `armhf` and `arm64` new artifacts to the stable channel on
|
||||||
the [Snapcraft dashboard][snapcraftdashboard]
|
the [Snapcraft dashboard][snapcraftdashboard].
|
||||||
|
|
||||||
# Scoop
|
# Scoop
|
||||||
|
|
||||||
Scoop is a community owned installation method. Scoop owners usually take care
|
Scoop is a command-line package manager for the Windows operating system.
|
||||||
of updating versions there by editing
|
Scoop package manifests are maintained by the community.
|
||||||
[this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json).
|
Scoop owners usually take care of updating versions there by editing [this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json).
|
||||||
If you think its Task version is outdated, open an issue to let us know.
|
If you think its Task version is outdated, open an issue to let us know.
|
||||||
|
|
||||||
# Nix
|
# Nix
|
||||||
@@ -46,6 +54,7 @@ If you think its Task version is outdated, open an issue to let us know.
|
|||||||
[goreleaser]: https://goreleaser.com/
|
[goreleaser]: https://goreleaser.com/
|
||||||
[homebrewtap]: https://github.com/go-task/homebrew-tap
|
[homebrewtap]: https://github.com/go-task/homebrew-tap
|
||||||
[gotaskrb]: https://github.com/go-task/homebrew-tap/blob/master/Formula/go-task.rb
|
[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
|
[snappackage]: https://github.com/go-task/snap
|
||||||
[snapcraftyaml]: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml#L2
|
[snapcraftyaml]: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml#L2
|
||||||
[snapcraftdashboard]: https://snapcraft.io/task/releases
|
[snapcraftdashboard]: https://snapcraft.io/task/releases
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
slug: /taskfile-versions/
|
slug: /taskfile-versions/
|
||||||
sidebar_position: 8
|
sidebar_position: 9
|
||||||
---
|
---
|
||||||
|
|
||||||
# Taskfile Versions
|
# Taskfile Versions
|
||||||
|
|||||||
@@ -136,6 +136,8 @@ namespace. So, you'd call `task docs:serve` to run the `serve` task from
|
|||||||
`documentation/Taskfile.yml` or `task docker:build` to run the `build` task
|
`documentation/Taskfile.yml` or `task docker:build` to run the `build` task
|
||||||
from the `DockerTasks.yml` file.
|
from the `DockerTasks.yml` file.
|
||||||
|
|
||||||
|
Relative paths are resolved relative to the directory containing the including Taskfile.
|
||||||
|
|
||||||
### OS-specific Taskfiles
|
### OS-specific Taskfiles
|
||||||
|
|
||||||
With `version: '2'`, task automatically includes any `Taskfile_{{OS}}.yml`
|
With `version: '2'`, task automatically includes any `Taskfile_{{OS}}.yml`
|
||||||
@@ -192,6 +194,22 @@ tasks:
|
|||||||
- echo "This command can still be successfully executed if ./tests/Taskfile.yml does not exist"
|
- 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
|
### Vars of included Taskfiles
|
||||||
|
|
||||||
You can also specify variables when including a Taskfile. This may be useful
|
You can also specify variables when including a Taskfile. This may be useful
|
||||||
@@ -212,15 +230,53 @@ includes:
|
|||||||
DOCKER_IMAGE: frontend_image
|
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
|
:::info
|
||||||
|
|
||||||
Vars declared in the included Taskfile have preference over the
|
Vars declared in the included Taskfile have preference over the
|
||||||
included ones! If you want a variable in an included Taskfile to be overridable,
|
variables in the including Taskfile! If you want a variable in an included Taskfile to be overridable,
|
||||||
use the [default function](https://go-task.github.io/slim-sprig/defaults.html):
|
use the [default function](https://go-task.github.io/slim-sprig/defaults.html):
|
||||||
`MY_VAR: '{{.MY_VAR | default "my-default-value"}}'`.
|
`MY_VAR: '{{.MY_VAR | default "my-default-value"}}'`.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## Internal tasks
|
||||||
|
|
||||||
|
Internal tasks are tasks that cannot be called directly by the user. They will
|
||||||
|
not appear in the output when running `task --list|--list-all`. Other tasks may
|
||||||
|
call internal tasks in the usual way. This is useful for creating reusable,
|
||||||
|
function-like tasks that have no useful purpose on the command line.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-image-1:
|
||||||
|
cmds:
|
||||||
|
- task: build-image
|
||||||
|
vars:
|
||||||
|
DOCKER_IMAGE: image-1
|
||||||
|
|
||||||
|
build-image:
|
||||||
|
internal: true
|
||||||
|
cmds:
|
||||||
|
- docker build -t {{.DOCKER_IMAGE}} .
|
||||||
|
```
|
||||||
|
|
||||||
## Task directory
|
## Task directory
|
||||||
|
|
||||||
By default, tasks will be executed in the directory where the Taskfile is
|
By default, tasks will be executed in the directory where the Taskfile is
|
||||||
@@ -406,8 +462,6 @@ tasks:
|
|||||||
Task will compare the checksum of the source files to determine if it's
|
Task will compare the checksum of the source files to determine if it's
|
||||||
necessary to run the task. If not, it will just print a message like
|
necessary to run the task. If not, it will just print a message like
|
||||||
`Task "js" is up to date`.
|
`Task "js" is up to date`.
|
||||||
You will probably want to ignore the `.task` folder in your `.gitignore` file
|
|
||||||
(It is there that Task stores the last checksum).
|
|
||||||
|
|
||||||
If you prefer this check to be made by the modification timestamp of the files,
|
If you prefer this check to be made by the modification timestamp of the files,
|
||||||
instead of its checksum (content), just set the `method` property to `timestamp`.
|
instead of its checksum (content), just set the `method` property to `timestamp`.
|
||||||
@@ -423,9 +477,34 @@ tasks:
|
|||||||
- ./*.go
|
- ./*.go
|
||||||
generates:
|
generates:
|
||||||
- app{{exeExt}}
|
- app{{exeExt}}
|
||||||
method: checksum
|
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
|
||||||
|
directory. Most of the time, you'll want to have this directory on `.gitignore`
|
||||||
|
(or equivalent) so it isn't committed. (If you have a task for code generation
|
||||||
|
that is committed it may make sense to commit the checksum of that task as
|
||||||
|
well, though).
|
||||||
|
|
||||||
|
If you want these files to be stored in another directory, you can set a
|
||||||
|
`TASK_TEMP_DIR` environment variable in your machine. It can contain a relative
|
||||||
|
path like `tmp/task` that will be interpreted as relative to the project
|
||||||
|
directory, or an absolute or home path like `/tmp/.task` or `~/.task`
|
||||||
|
(subdirectories will be created for each project).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export TASK_TEMP_DIR='~/.task'
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
Each task has only one checksum stored for its `sources`. If you want
|
Each task has only one checksum stored for its `sources`. If you want
|
||||||
@@ -496,6 +575,30 @@ up-to-date.
|
|||||||
Also, `task --status [tasks]...` will exit with a non-zero exit code if any of
|
Also, `task --status [tasks]...` will exit with a non-zero exit code if any of
|
||||||
the tasks are not up-to-date.
|
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
|
### Using programmatic checks to cancel the execution of a task and its dependencies
|
||||||
|
|
||||||
In addition to `status` checks, `preconditions` checks are
|
In addition to `status` checks, `preconditions` checks are
|
||||||
@@ -877,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*.
|
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
|
## Overriding task name
|
||||||
|
|
||||||
Sometimes you may want to override the task name printed on the summary, up-to-date
|
Sometimes you may want to override the task name printed on the summary, up-to-date
|
||||||
@@ -1058,7 +1185,7 @@ version: '3'
|
|||||||
|
|
||||||
output:
|
output:
|
||||||
group:
|
group:
|
||||||
begin: '::begin::{{.TASK}}'
|
begin: '::group::{{.TASK}}'
|
||||||
end: '::endgroup::'
|
end: '::endgroup::'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
@@ -1070,7 +1197,7 @@ tasks:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ task default
|
$ task default
|
||||||
::begin::default
|
::group::default
|
||||||
Hello, World!
|
Hello, World!
|
||||||
::endgroup::
|
::endgroup::
|
||||||
```
|
```
|
||||||
@@ -1159,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,
|
and run the task again. This requires the `sources` attribute to be given,
|
||||||
so task knows which files to watch.
|
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/
|
[gotemplate]: https://golang.org/pkg/text/template/
|
||||||
[minify]: https://github.com/tdewolff/minify/tree/master/cmd/minify
|
[minify]: https://github.com/tdewolff/minify/tree/master/cmd/minify
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
// Note: type annotations allow type checking and IDEs autocompletion
|
// Note: type annotations allow type checking and IDEs autocompletion
|
||||||
|
|
||||||
const lightCodeTheme = require('prism-react-renderer/themes/github');
|
const lightCodeTheme = require('./src/themes/prismLight');
|
||||||
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
|
const darkCodeTheme = require('./src/themes/prismDark');
|
||||||
|
|
||||||
const GITHUB_URL = 'https://github.com/go-task/task';
|
const GITHUB_URL = 'https://github.com/go-task/task';
|
||||||
|
const TWITTER_URL = 'https://twitter.com/taskfiledev';
|
||||||
const DISCORD_URL = 'https://discord.gg/6TY36E39UK';
|
const DISCORD_URL = 'https://discord.gg/6TY36E39UK';
|
||||||
|
|
||||||
/** @type {import('@docusaurus/types').Config} */
|
/** @type {import('@docusaurus/types').Config} */
|
||||||
@@ -100,6 +101,11 @@ const config = {
|
|||||||
label: 'GitHub',
|
label: 'GitHub',
|
||||||
position: 'right'
|
position: 'right'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
href: TWITTER_URL,
|
||||||
|
label: 'Twitter',
|
||||||
|
position: 'right'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
href: DISCORD_URL,
|
href: DISCORD_URL,
|
||||||
label: 'Discord',
|
label: 'Discord',
|
||||||
@@ -134,6 +140,10 @@ const config = {
|
|||||||
label: 'GitHub',
|
label: 'GitHub',
|
||||||
href: GITHUB_URL
|
href: GITHUB_URL
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Twitter',
|
||||||
|
href: TWITTER_URL
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Discord',
|
label: 'Discord',
|
||||||
href: DISCORD_URL
|
href: DISCORD_URL
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,400;0,700;1,400;1,700&family=Roboto:ital,wght@0,400;0,700;1,400;1,700&display=swap');
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
--ifm-font-family-base: Roboto, system-ui, -apple-system, Segoe UI, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
||||||
|
--ifm-font-family-monospace: 'Roboto Mono', SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
||||||
|
|
||||||
--ifm-color-primary: #43ABA2 ;
|
--ifm-color-primary: #43ABA2 ;
|
||||||
--ifm-color-primary-dark: #3AB2A6;
|
--ifm-color-primary-dark: #3AB2A6;
|
||||||
--ifm-color-primary-darker: #32B8AB;
|
--ifm-color-primary-darker: #32B8AB;
|
||||||
@@ -11,13 +16,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-theme='dark'] {
|
[data-theme='dark'] {
|
||||||
--ifm-color-primary: #43ABA2 ;
|
|
||||||
--ifm-color-primary-dark: #3AB2A6;
|
|
||||||
--ifm-color-primary-darker: #32B8AB;
|
|
||||||
--ifm-color-primary-darkest: #29BEB0;
|
|
||||||
--ifm-color-primary-light: #4CA59D;
|
|
||||||
--ifm-color-primary-lighter: #559F98;
|
|
||||||
--ifm-color-primary-lightest: #5D9993;
|
|
||||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
|
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
79
docs/src/themes/prismDark.js
Normal 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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
100
docs/src/themes/prismLight.js
Normal 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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
BIN
docs/static/img/favicon.ico
vendored
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 170 KiB |
BIN
docs/static/img/logo.png
vendored
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 13 KiB |
6
docs/static/img/logo.svg
vendored
@@ -1,5 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 375 375" width="500" height="500">
|
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 375 375"><path fill="#29beb0" d="M 187.570312 190.933594 L 187.570312 375 L 30.070312 279.535156 L 30.070312 95.464844 Z"/><path fill="#69d2c8" d="M 187.570312 190.933594 L 187.570312 375 L 345.070312 279.535156 L 345.070312 95.464844 Z"/><path fill="#94dfd8" d="M 187.570312 190.933594 L 30.070312 95.464844 L 187.570312 0 L 345.070312 95.464844 Z"/></svg>
|
||||||
<path fill="#29beb0" d="M 187.570312 190.933594 L 187.570312 375 L 30.070312 279.535156 L 30.070312 95.464844 Z"/>
|
|
||||||
<path fill="#69d2c8" d="M 187.570312 190.933594 L 187.570312 375 L 345.070312 279.535156 L 345.070312 95.464844 Z"/>
|
|
||||||
<path fill="#94dfd8" d="M 187.570312 190.933594 L 30.070312 95.464844 L 187.570312 0 L 345.070312 95.464844 Z"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 435 B |
4
docs/static/img/logo_mono.svg
vendored
@@ -1,3 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 375 375" width="500" height="500">
|
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 375 375"><path d="M 29.021972 281.445466 L 183.370289 375 L 183.370289 194.622441 L 29.021972 101.064089 Z M 345.978037 281.445466 L 345.978037 101.064079 L 191.629731 194.622431 L 191.629731 375 Z M 342.140723 93.731759 L 187.5 0 L 32.859297 93.731759 L 187.5 187.467349 Z"/></svg>
|
||||||
<path d="M 29.021972 281.445466 L 183.370289 375 L 183.370289 194.622441 L 29.021972 101.064089 Z M 345.978037 281.445466 L 345.978037 101.064079 L 191.629731 194.622431 L 191.629731 375 Z M 342.140723 93.731759 L 187.5 0 L 32.859297 93.731759 L 187.5 187.467349 Z"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 365 B After Width: | Height: | Size: 360 B |
BIN
docs/static/img/og-image.png
vendored
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 19 KiB |
BIN
docs/static/img/pix.png
vendored
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 1.2 KiB |
15
docs/static/js/carbon.js
vendored
@@ -1,6 +1,6 @@
|
|||||||
(function () {
|
(function () {
|
||||||
function attachAd() {
|
function attachAd() {
|
||||||
const el = document.createElement('script');
|
var el = document.createElement('script');
|
||||||
el.setAttribute('type', 'text/javascript');
|
el.setAttribute('type', 'text/javascript');
|
||||||
el.setAttribute('id', '_carbonads_js');
|
el.setAttribute('id', '_carbonads_js');
|
||||||
el.setAttribute(
|
el.setAttribute(
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
);
|
);
|
||||||
el.setAttribute('async', 'async');
|
el.setAttribute('async', 'async');
|
||||||
|
|
||||||
const wrapper = document.getElementById('sidebar-ads');
|
var wrapper = document.getElementById('sidebar-ads');
|
||||||
wrapper.innerHTML = '';
|
wrapper.innerHTML = '';
|
||||||
wrapper.appendChild(el);
|
wrapper.appendChild(el);
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,13 @@
|
|||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
attachAd();
|
attachAd();
|
||||||
|
|
||||||
window.addEventListener('popstate', function () {
|
var currentPath = window.location.pathname;
|
||||||
attachAd();
|
|
||||||
});
|
setInterval(function () {
|
||||||
|
if (currentPath !== window.location.pathname) {
|
||||||
|
currentPath = window.location.pathname;
|
||||||
|
attachAd();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -1577,33 +1577,46 @@
|
|||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
"@jridgewell/gen-mapping@^0.3.0":
|
"@jridgewell/gen-mapping@^0.3.0":
|
||||||
version "0.3.1"
|
version "0.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9"
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
|
||||||
integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==
|
integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/set-array" "^1.0.0"
|
"@jridgewell/set-array" "^1.0.1"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
"@jridgewell/trace-mapping" "^0.3.9"
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
"@jridgewell/resolve-uri@^3.0.3":
|
"@jridgewell/resolve-uri@^3.0.3":
|
||||||
version "3.0.7"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe"
|
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
|
||||||
integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==
|
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
|
||||||
|
|
||||||
"@jridgewell/set-array@^1.0.0":
|
"@jridgewell/set-array@^1.0.0":
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea"
|
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea"
|
||||||
integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==
|
integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==
|
||||||
|
|
||||||
|
"@jridgewell/set-array@^1.0.1":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
|
||||||
|
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
|
||||||
|
|
||||||
|
"@jridgewell/source-map@^0.3.2":
|
||||||
|
version "0.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
|
||||||
|
integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/gen-mapping" "^0.3.0"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.4.10":
|
"@jridgewell/sourcemap-codec@^1.4.10":
|
||||||
version "1.4.13"
|
version "1.4.14"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c"
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||||
integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==
|
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||||
|
|
||||||
"@jridgewell/trace-mapping@^0.3.9":
|
"@jridgewell/trace-mapping@^0.3.9":
|
||||||
version "0.3.13"
|
version "0.3.14"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea"
|
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed"
|
||||||
integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==
|
integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/resolve-uri" "^3.0.3"
|
"@jridgewell/resolve-uri" "^3.0.3"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
@@ -4960,11 +4973,6 @@ lodash.some@^4.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
|
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
|
||||||
integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=
|
integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=
|
||||||
|
|
||||||
lodash.sortby@^4.7.0:
|
|
||||||
version "4.7.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
|
||||||
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
|
||||||
|
|
||||||
lodash.uniq@4.5.0, lodash.uniq@^4.5.0:
|
lodash.uniq@4.5.0, lodash.uniq@^4.5.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||||
@@ -6815,13 +6823,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||||
|
|
||||||
source-map@~0.8.0-beta.0:
|
|
||||||
version "0.8.0-beta.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11"
|
|
||||||
integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==
|
|
||||||
dependencies:
|
|
||||||
whatwg-url "^7.0.0"
|
|
||||||
|
|
||||||
space-separated-tokens@^1.0.0:
|
space-separated-tokens@^1.0.0:
|
||||||
version "1.1.5"
|
version "1.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899"
|
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899"
|
||||||
@@ -7036,13 +7037,13 @@ terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.3.1:
|
|||||||
terser "^5.7.2"
|
terser "^5.7.2"
|
||||||
|
|
||||||
terser@^5.10.0, terser@^5.7.2:
|
terser@^5.10.0, terser@^5.7.2:
|
||||||
version "5.13.1"
|
version "5.14.2"
|
||||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.13.1.tgz#66332cdc5a01b04a224c9fad449fc1a18eaa1799"
|
resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10"
|
||||||
integrity sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA==
|
integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@jridgewell/source-map" "^0.3.2"
|
||||||
acorn "^8.5.0"
|
acorn "^8.5.0"
|
||||||
commander "^2.20.0"
|
commander "^2.20.0"
|
||||||
source-map "~0.8.0-beta.0"
|
|
||||||
source-map-support "~0.5.20"
|
source-map-support "~0.5.20"
|
||||||
|
|
||||||
text-table@^0.2.0:
|
text-table@^0.2.0:
|
||||||
@@ -7092,13 +7093,6 @@ totalist@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
|
resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
|
||||||
integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
|
integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
|
||||||
|
|
||||||
tr46@^1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
|
|
||||||
integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
|
|
||||||
dependencies:
|
|
||||||
punycode "^2.1.0"
|
|
||||||
|
|
||||||
tr46@~0.0.3:
|
tr46@~0.0.3:
|
||||||
version "0.0.3"
|
version "0.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||||
@@ -7437,11 +7431,6 @@ webidl-conversions@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||||
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
|
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
|
||||||
|
|
||||||
webidl-conversions@^4.0.2:
|
|
||||||
version "4.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
|
||||||
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
|
|
||||||
|
|
||||||
webpack-bundle-analyzer@^4.5.0:
|
webpack-bundle-analyzer@^4.5.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
|
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
|
||||||
@@ -7585,15 +7574,6 @@ whatwg-url@^5.0.0:
|
|||||||
tr46 "~0.0.3"
|
tr46 "~0.0.3"
|
||||||
webidl-conversions "^3.0.0"
|
webidl-conversions "^3.0.0"
|
||||||
|
|
||||||
whatwg-url@^7.0.0:
|
|
||||||
version "7.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
|
|
||||||
integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
|
|
||||||
dependencies:
|
|
||||||
lodash.sortby "^4.7.0"
|
|
||||||
tr46 "^1.0.1"
|
|
||||||
webidl-conversions "^4.0.2"
|
|
||||||
|
|
||||||
which@^1.3.1:
|
which@^1.3.1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||||
|
|||||||
35
errors.go
@@ -3,6 +3,7 @@ package task
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"mvdan.cc/sh/v3/interp"
|
"mvdan.cc/sh/v3/interp"
|
||||||
)
|
)
|
||||||
@@ -13,11 +14,37 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type taskNotFoundError struct {
|
type taskNotFoundError struct {
|
||||||
taskName string
|
taskName string
|
||||||
|
didYouMean string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *taskNotFoundError) Error() string {
|
func (err *taskNotFoundError) Error() string {
|
||||||
return fmt.Sprintf(`task: Task "%s" 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 {
|
type TaskRunError struct {
|
||||||
@@ -26,7 +53,7 @@ type TaskRunError struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (err *TaskRunError) Error() string {
|
func (err *TaskRunError) Error() string {
|
||||||
return fmt.Sprintf(`task: Failed to run task "%s": %v`, err.taskName, err.err)
|
return fmt.Sprintf(`task: Failed to run task %q: %v`, err.taskName, err.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *TaskRunError) ExitCode() int {
|
func (err *TaskRunError) ExitCode() int {
|
||||||
@@ -46,7 +73,7 @@ type MaximumTaskCallExceededError struct {
|
|||||||
|
|
||||||
func (e *MaximumTaskCallExceededError) Error() string {
|
func (e *MaximumTaskCallExceededError) Error() string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
`task: maximum task call exceeded (%d) for task "%s": probably an cyclic dep or infinite loop`,
|
`task: maximum task call exceeded (%d) for task %q: probably an cyclic dep or infinite loop`,
|
||||||
MaximumTaskCall,
|
MaximumTaskCall,
|
||||||
e.task,
|
e.task,
|
||||||
)
|
)
|
||||||
|
|||||||
11
go.mod
@@ -7,11 +7,13 @@ require (
|
|||||||
github.com/mattn/go-zglob v0.0.3
|
github.com/mattn/go-zglob v0.0.3
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/radovskyb/watcher v1.0.7
|
github.com/radovskyb/watcher v1.0.7
|
||||||
|
github.com/sajari/fuzzy v1.0.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.7.2
|
github.com/stretchr/testify v1.8.0
|
||||||
|
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
mvdan.cc/sh/v3 v3.5.1
|
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -19,8 +21,9 @@ require (
|
|||||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.17
|
go 1.18
|
||||||
|
|||||||
44
go.sum
@@ -1,30 +1,17 @@
|
|||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
|
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
|
||||||
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
|
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
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/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
|
||||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
@@ -34,41 +21,38 @@ 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.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
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/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
|
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
|
||||||
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
|
||||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
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.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
golang.org/x/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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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-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-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
|
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899 h1:nwm4t5PtLlFd/H342GP50CtYf7vyMCOZkPx3g9shO0c=
|
||||||
mvdan.cc/sh/v3 v3.5.1 h1:hmP3UOw4f+EYexsJjFxvU38+kn+V/s2CclXHanIBkmQ=
|
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=
|
||||||
mvdan.cc/sh/v3 v3.5.1/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=
|
|
||||||
|
|||||||
20
help.go
@@ -42,9 +42,15 @@ func (e *Executor) printTasks(listAll bool) {
|
|||||||
e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
|
e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
|
||||||
|
|
||||||
// Format in tab-separated columns with a tab stop of 8.
|
// 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 {
|
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()
|
w.Flush()
|
||||||
}
|
}
|
||||||
@@ -52,7 +58,9 @@ func (e *Executor) printTasks(listAll bool) {
|
|||||||
func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
|
func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
|
||||||
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
||||||
for _, task := range e.Taskfile.Tasks {
|
for _, task := range e.Taskfile.Tasks {
|
||||||
tasks = append(tasks, task)
|
if !task.Internal {
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
|
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
|
||||||
return
|
return
|
||||||
@@ -61,7 +69,7 @@ func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
|
|||||||
func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
|
func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
|
||||||
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
||||||
for _, task := range e.Taskfile.Tasks {
|
for _, task := range e.Taskfile.Tasks {
|
||||||
if task.Desc != "" {
|
if !task.Internal && task.Desc != "" {
|
||||||
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
|
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
task = compiledTask
|
task = compiledTask
|
||||||
@@ -73,7 +81,7 @@ func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
|
|||||||
return
|
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.
|
// Only tasks with a non-empty description are printed if allTasks is false.
|
||||||
// Otherwise, all task names are printed.
|
// Otherwise, all task names are printed.
|
||||||
func (e *Executor) ListTaskNames(allTasks bool) {
|
func (e *Executor) ListTaskNames(allTasks bool) {
|
||||||
@@ -92,7 +100,7 @@ func (e *Executor) ListTaskNames(allTasks bool) {
|
|||||||
// create a string slice from all map values (*taskfile.Task)
|
// create a string slice from all map values (*taskfile.Task)
|
||||||
s := make([]string, 0, len(e.Taskfile.Tasks))
|
s := make([]string, 0, len(e.Taskfile.Tasks))
|
||||||
for _, t := range 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, ":"))
|
s = append(s, strings.TrimRight(t.Task, ":"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
init.go
@@ -4,7 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultTaskfile = `# https://taskfile.dev
|
const defaultTaskfile = `# https://taskfile.dev
|
||||||
@@ -23,13 +24,13 @@ tasks:
|
|||||||
|
|
||||||
// InitTaskfile Taskfile creates a new Taskfile
|
// InitTaskfile Taskfile creates a new Taskfile
|
||||||
func InitTaskfile(w io.Writer, dir string) error {
|
func InitTaskfile(w io.Writer, dir string) error {
|
||||||
f := filepath.Join(dir, "Taskfile.yaml")
|
f := filepathext.SmartJoin(dir, "Taskfile.yaml")
|
||||||
|
|
||||||
if _, err := os.Stat(f); err == nil {
|
if _, err := os.Stat(f); err == nil {
|
||||||
return ErrTaskfileAlreadyExists
|
return ErrTaskfileAlreadyExists
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(f, []byte(defaultTaskfile), 0644); err != nil {
|
if err := os.WriteFile(f, []byte(defaultTaskfile), 0o644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "Taskfile.yaml created in the current directory\n")
|
fmt.Fprintf(w, "Taskfile.yaml created in the current directory\n")
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/compiler"
|
"github.com/go-task/task/v3/internal/compiler"
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
@@ -44,7 +44,13 @@ func (c *CompilerV3) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*ta
|
|||||||
func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
|
func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
|
||||||
result := compiler.GetEnviron()
|
result := compiler.GetEnviron()
|
||||||
if t != nil {
|
if t != nil {
|
||||||
result.Set("TASK", taskfile.Var{Static: t.Task})
|
specialVars, err := c.getSpecialVars(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for k, v := range specialVars {
|
||||||
|
result.Set(k, taskfile.Var{Static: v})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
|
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
|
||||||
@@ -83,9 +89,7 @@ func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluat
|
|||||||
if err := tr.Err(); err != nil {
|
if err := tr.Err(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !filepath.IsAbs(dir) {
|
dir = filepathext.SmartJoin(c.Dir, dir)
|
||||||
dir = filepath.Join(c.Dir, dir)
|
|
||||||
}
|
|
||||||
taskRangeFunc = getRangeFunc(dir)
|
taskRangeFunc = getRangeFunc(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,3 +171,23 @@ func (c *CompilerV3) ResetCache() {
|
|||||||
|
|
||||||
c.dynamicCache = nil
|
c.dynamicCache = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CompilerV3) getSpecialVars(t *taskfile.Task) (map[string]string, error) {
|
||||||
|
taskfileDir, err := c.getTaskfileDir(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]string{
|
||||||
|
"TASK": t.Task,
|
||||||
|
"ROOT_DIR": c.Dir,
|
||||||
|
"TASKFILE_DIR": taskfileDir,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CompilerV3) getTaskfileDir(t *taskfile.Task) (string, error) {
|
||||||
|
if t.IncludedTaskfile != nil {
|
||||||
|
return t.IncludedTaskfile.FullDirPath()
|
||||||
|
}
|
||||||
|
return c.Dir, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func IsExitError(err error) bool {
|
|||||||
// if available.
|
// if available.
|
||||||
func Expand(s string) (string, error) {
|
func Expand(s string) (string, error) {
|
||||||
s = filepath.ToSlash(s)
|
s = filepath.ToSlash(s)
|
||||||
s = strings.Replace(s, " ", `\ `, -1)
|
s = strings.ReplaceAll(s, " ", `\ `)
|
||||||
fields, err := shell.Fields(s, nil)
|
fields, err := shell.Fields(s, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|||||||
31
internal/filepathext/filepathext.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package filepathext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SmartJoin joins two paths, but only if the second is not already an
|
||||||
|
// absolute path.
|
||||||
|
func SmartJoin(a, b string) string {
|
||||||
|
if filepath.IsAbs(b) {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return filepath.Join(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ package logger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
)
|
)
|
||||||
@@ -9,13 +11,36 @@ import (
|
|||||||
type Color func() PrintFunc
|
type Color func() PrintFunc
|
||||||
type PrintFunc func(io.Writer, string, ...interface{})
|
type PrintFunc func(io.Writer, string, ...interface{})
|
||||||
|
|
||||||
func Default() PrintFunc { return color.New(color.Reset).FprintfFunc() }
|
func Default() PrintFunc {
|
||||||
func Blue() PrintFunc { return color.New(color.FgBlue).FprintfFunc() }
|
return color.New(envColor("TASK_COLOR_RESET", color.Reset)).FprintfFunc()
|
||||||
func Green() PrintFunc { return color.New(color.FgGreen).FprintfFunc() }
|
}
|
||||||
func Cyan() PrintFunc { return color.New(color.FgCyan).FprintfFunc() }
|
func Blue() PrintFunc {
|
||||||
func Yellow() PrintFunc { return color.New(color.FgYellow).FprintfFunc() }
|
return color.New(envColor("TASK_COLOR_BLUE", color.FgBlue)).FprintfFunc()
|
||||||
func Magenta() PrintFunc { return color.New(color.FgMagenta).FprintfFunc() }
|
}
|
||||||
func Red() PrintFunc { return color.New(color.FgRed).FprintfFunc() }
|
func Green() PrintFunc {
|
||||||
|
return color.New(envColor("TASK_COLOR_GREEN", color.FgGreen)).FprintfFunc()
|
||||||
|
}
|
||||||
|
func Cyan() PrintFunc {
|
||||||
|
return color.New(envColor("TASK_COLOR_CYAN", color.FgCyan)).FprintfFunc()
|
||||||
|
}
|
||||||
|
func Yellow() PrintFunc {
|
||||||
|
return color.New(envColor("TASK_COLOR_YELLOW", color.FgYellow)).FprintfFunc()
|
||||||
|
}
|
||||||
|
func Magenta() PrintFunc {
|
||||||
|
return color.New(envColor("TASK_COLOR_MAGENTA", color.FgMagenta)).FprintfFunc()
|
||||||
|
}
|
||||||
|
func Red() PrintFunc {
|
||||||
|
return color.New(envColor("TASK_COLOR_RED", color.FgRed)).FprintfFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func envColor(env string, defaultColor color.Attribute) color.Attribute {
|
||||||
|
override, err := strconv.Atoi(os.Getenv(env))
|
||||||
|
if err == nil {
|
||||||
|
return color.Attribute(override)
|
||||||
|
|
||||||
|
}
|
||||||
|
return defaultColor
|
||||||
|
}
|
||||||
|
|
||||||
// Logger is just a wrapper that prints stuff to STDOUT or STDERR,
|
// Logger is just a wrapper that prints stuff to STDOUT or STDERR,
|
||||||
// with optional color.
|
// with optional color.
|
||||||
@@ -28,6 +53,11 @@ type Logger struct {
|
|||||||
|
|
||||||
// Outf prints stuff to STDOUT.
|
// Outf prints stuff to STDOUT.
|
||||||
func (l *Logger) Outf(color Color, s string, args ...interface{}) {
|
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 {
|
if len(args) == 0 {
|
||||||
s, args = "%s", []interface{}{s}
|
s, args = "%s", []interface{}{s}
|
||||||
}
|
}
|
||||||
@@ -35,7 +65,7 @@ func (l *Logger) Outf(color Color, s string, args ...interface{}) {
|
|||||||
color = Default
|
color = Default
|
||||||
}
|
}
|
||||||
print := color()
|
print := color()
|
||||||
print(l.Stdout, s+"\n", args...)
|
print(w, s, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerboseOutf prints stuff to STDOUT if verbose mode is enabled.
|
// VerboseOutf prints stuff to STDOUT if verbose mode is enabled.
|
||||||
|
|||||||
@@ -5,24 +5,24 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Group struct{
|
type Group struct {
|
||||||
Begin, End string
|
Begin, End string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Group) WrapWriter(w io.Writer, _ string, tmpl Templater) io.Writer {
|
func (g Group) WrapWriter(stdOut, _ io.Writer, _ string, tmpl Templater) (io.Writer, io.Writer, CloseFunc) {
|
||||||
gw := &groupWriter{writer: w}
|
gw := &groupWriter{writer: stdOut}
|
||||||
if g.Begin != "" {
|
if g.Begin != "" {
|
||||||
gw.begin = tmpl.Replace(g.Begin) + "\n"
|
gw.begin = tmpl.Replace(g.Begin) + "\n"
|
||||||
}
|
}
|
||||||
if g.End != "" {
|
if g.End != "" {
|
||||||
gw.end = tmpl.Replace(g.End) + "\n"
|
gw.end = tmpl.Replace(g.End) + "\n"
|
||||||
}
|
}
|
||||||
return gw
|
return gw, gw, func() error { return gw.close() }
|
||||||
}
|
}
|
||||||
|
|
||||||
type groupWriter struct {
|
type groupWriter struct {
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
buff bytes.Buffer
|
buff bytes.Buffer
|
||||||
begin, end string
|
begin, end string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ func (gw *groupWriter) Write(p []byte) (int, error) {
|
|||||||
return gw.buff.Write(p)
|
return gw.buff.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gw *groupWriter) Close() error {
|
func (gw *groupWriter) close() error {
|
||||||
if gw.buff.Len() == 0 {
|
if gw.buff.Len() == 0 {
|
||||||
// don't print begin/end messages if there's no buffered entries
|
// don't print begin/end messages if there's no buffered entries
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ import (
|
|||||||
|
|
||||||
type Interleaved struct{}
|
type Interleaved struct{}
|
||||||
|
|
||||||
func (Interleaved) WrapWriter(w io.Writer, _ string, _ Templater) io.Writer {
|
func (Interleaved) WrapWriter(stdOut, stdErr io.Writer, _ string, _ Templater) (io.Writer, io.Writer, CloseFunc) {
|
||||||
return w
|
return stdOut, stdErr, func() error { return nil }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ type Templater interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Output interface {
|
type Output interface {
|
||||||
WrapWriter(w io.Writer, prefix string, tmpl Templater) io.Writer
|
WrapWriter(stdOut, stdErr io.Writer, prefix string, tmpl Templater) (io.Writer, io.Writer, CloseFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CloseFunc func() error
|
||||||
|
|
||||||
// Build the Output for the requested taskfile.Output.
|
// Build the Output for the requested taskfile.Output.
|
||||||
func BuildFor(o *taskfile.Output) (Output, error) {
|
func BuildFor(o *taskfile.Output) (Output, error) {
|
||||||
switch o.Name {
|
switch o.Name {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
func TestInterleaved(t *testing.T) {
|
func TestInterleaved(t *testing.T) {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
var o output.Output = output.Interleaved{}
|
var o output.Output = output.Interleaved{}
|
||||||
var w = o.WrapWriter(&b, "", nil)
|
var w, _, _ = o.WrapWriter(&b, io.Discard, "", nil)
|
||||||
|
|
||||||
fmt.Fprintln(w, "foo\nbar")
|
fmt.Fprintln(w, "foo\nbar")
|
||||||
assert.Equal(t, "foo\nbar\n", b.String())
|
assert.Equal(t, "foo\nbar\n", b.String())
|
||||||
@@ -27,14 +27,19 @@ func TestInterleaved(t *testing.T) {
|
|||||||
func TestGroup(t *testing.T) {
|
func TestGroup(t *testing.T) {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
var o output.Output = output.Group{}
|
var o output.Output = output.Group{}
|
||||||
var w = o.WrapWriter(&b, "", nil).(io.WriteCloser)
|
var stdOut, stdErr, cleanup = o.WrapWriter(&b, io.Discard, "", nil)
|
||||||
|
|
||||||
fmt.Fprintln(w, "foo\nbar")
|
fmt.Fprintln(stdOut, "out\nout")
|
||||||
assert.Equal(t, "", b.String())
|
assert.Equal(t, "", b.String())
|
||||||
fmt.Fprintln(w, "baz")
|
fmt.Fprintln(stdErr, "err\nerr")
|
||||||
assert.Equal(t, "", b.String())
|
assert.Equal(t, "", b.String())
|
||||||
assert.NoError(t, w.Close())
|
fmt.Fprintln(stdOut, "out")
|
||||||
assert.Equal(t, "foo\nbar\nbaz\n", b.String())
|
assert.Equal(t, "", b.String())
|
||||||
|
fmt.Fprintln(stdErr, "err")
|
||||||
|
assert.Equal(t, "", b.String())
|
||||||
|
|
||||||
|
assert.NoError(t, cleanup())
|
||||||
|
assert.Equal(t, "out\nout\nerr\nerr\nout\nerr\n", b.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGroupWithBeginEnd(t *testing.T) {
|
func TestGroupWithBeginEnd(t *testing.T) {
|
||||||
@@ -53,19 +58,19 @@ func TestGroupWithBeginEnd(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Run("simple", func(t *testing.T) {
|
t.Run("simple", func(t *testing.T) {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
var w = o.WrapWriter(&b, "", &tmpl).(io.WriteCloser)
|
var w, _, cleanup = o.WrapWriter(&b, io.Discard, "", &tmpl)
|
||||||
|
|
||||||
fmt.Fprintln(w, "foo\nbar")
|
fmt.Fprintln(w, "foo\nbar")
|
||||||
assert.Equal(t, "", b.String())
|
assert.Equal(t, "", b.String())
|
||||||
fmt.Fprintln(w, "baz")
|
fmt.Fprintln(w, "baz")
|
||||||
assert.Equal(t, "", b.String())
|
assert.Equal(t, "", b.String())
|
||||||
assert.NoError(t, w.Close())
|
assert.NoError(t, cleanup())
|
||||||
assert.Equal(t, "::group::example-value\nfoo\nbar\nbaz\n::endgroup::\n", b.String())
|
assert.Equal(t, "::group::example-value\nfoo\nbar\nbaz\n::endgroup::\n", b.String())
|
||||||
})
|
})
|
||||||
t.Run("no output", func(t *testing.T) {
|
t.Run("no output", func(t *testing.T) {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
var w = o.WrapWriter(&b, "", &tmpl).(io.WriteCloser)
|
var _, _, cleanup = o.WrapWriter(&b, io.Discard, "", &tmpl)
|
||||||
assert.NoError(t, w.Close())
|
assert.NoError(t, cleanup())
|
||||||
assert.Equal(t, "", b.String())
|
assert.Equal(t, "", b.String())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -73,7 +78,7 @@ func TestGroupWithBeginEnd(t *testing.T) {
|
|||||||
func TestPrefixed(t *testing.T) {
|
func TestPrefixed(t *testing.T) {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
var o output.Output = output.Prefixed{}
|
var o output.Output = output.Prefixed{}
|
||||||
var w = o.WrapWriter(&b, "prefix", nil).(io.WriteCloser)
|
var w, _, cleanup = o.WrapWriter(&b, io.Discard, "prefix", nil)
|
||||||
|
|
||||||
t.Run("simple use cases", func(t *testing.T) {
|
t.Run("simple use cases", func(t *testing.T) {
|
||||||
b.Reset()
|
b.Reset()
|
||||||
@@ -82,6 +87,7 @@ func TestPrefixed(t *testing.T) {
|
|||||||
assert.Equal(t, "[prefix] foo\n[prefix] bar\n", b.String())
|
assert.Equal(t, "[prefix] foo\n[prefix] bar\n", b.String())
|
||||||
fmt.Fprintln(w, "baz")
|
fmt.Fprintln(w, "baz")
|
||||||
assert.Equal(t, "[prefix] foo\n[prefix] bar\n[prefix] baz\n", b.String())
|
assert.Equal(t, "[prefix] foo\n[prefix] bar\n[prefix] baz\n", b.String())
|
||||||
|
assert.NoError(t, cleanup())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("multiple writes for a single line", func(t *testing.T) {
|
t.Run("multiple writes for a single line", func(t *testing.T) {
|
||||||
@@ -92,7 +98,7 @@ func TestPrefixed(t *testing.T) {
|
|||||||
assert.Equal(t, "", b.String())
|
assert.Equal(t, "", b.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, w.Close())
|
assert.NoError(t, cleanup())
|
||||||
assert.Equal(t, "[prefix] Test!\n", b.String())
|
assert.Equal(t, "[prefix] Test!\n", b.String())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ import (
|
|||||||
|
|
||||||
type Prefixed struct{}
|
type Prefixed struct{}
|
||||||
|
|
||||||
func (Prefixed) WrapWriter(w io.Writer, prefix string, _ Templater) io.Writer {
|
func (Prefixed) WrapWriter(stdOut, _ io.Writer, prefix string, _ Templater) (io.Writer, io.Writer, CloseFunc) {
|
||||||
return &prefixWriter{writer: w, prefix: prefix}
|
pw := &prefixWriter{writer: stdOut, prefix: prefix}
|
||||||
|
return pw, pw, func() error { return pw.close() }
|
||||||
}
|
}
|
||||||
|
|
||||||
type prefixWriter struct {
|
type prefixWriter struct {
|
||||||
@@ -28,7 +29,7 @@ func (pw *prefixWriter) Write(p []byte) (int, error) {
|
|||||||
return n, pw.writeOutputLines(false)
|
return n, pw.writeOutputLines(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pw *prefixWriter) Close() error {
|
func (pw *prefixWriter) close() error {
|
||||||
return pw.writeOutputLines(true)
|
return pw.writeOutputLines(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checksum validades if a task is up to date by calculating its source
|
// Checksum validades if a task is up to date by calculating its source
|
||||||
// files checksum
|
// files checksum
|
||||||
type Checksum struct {
|
type Checksum struct {
|
||||||
BaseDir string
|
TempDir string
|
||||||
TaskDir string
|
TaskDir string
|
||||||
Task string
|
Task string
|
||||||
Sources []string
|
Sources []string
|
||||||
@@ -43,8 +45,8 @@ func (c *Checksum) IsUpToDate() (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !c.Dry {
|
if !c.Dry {
|
||||||
_ = os.MkdirAll(filepath.Join(c.BaseDir, ".task", "checksum"), 0755)
|
_ = os.MkdirAll(filepathext.SmartJoin(c.TempDir, "checksum"), 0o755)
|
||||||
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
|
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0o644); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,7 +109,7 @@ func (*Checksum) Kind() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checksum) checksumFilePath() string {
|
func (c *Checksum) checksumFilePath() string {
|
||||||
return filepath.Join(c.BaseDir, ".task", "checksum", c.normalizeFilename(c.Task))
|
return filepath.Join(c.TempDir, "checksum", c.normalizeFilename(c.Task))
|
||||||
}
|
}
|
||||||
|
|
||||||
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")
|
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ package status
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/mattn/go-zglob"
|
"github.com/mattn/go-zglob"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
)
|
)
|
||||||
|
|
||||||
func globs(dir string, globs []string) ([]string, error) {
|
func globs(dir string, globs []string) ([]string, error) {
|
||||||
@@ -25,17 +25,18 @@ func globs(dir string, globs []string) ([]string, error) {
|
|||||||
|
|
||||||
func Glob(dir string, g string) ([]string, error) {
|
func Glob(dir string, g string) ([]string, error) {
|
||||||
files := make([]string, 0)
|
files := make([]string, 0)
|
||||||
if !filepath.IsAbs(g) {
|
g = filepathext.SmartJoin(dir, g)
|
||||||
g = filepath.Join(dir, g)
|
|
||||||
}
|
|
||||||
g, err := execext.Expand(g)
|
g, err := execext.Expand(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fs, err := zglob.Glob(g)
|
|
||||||
|
fs, err := zglob.GlobFollowSymlinks(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range fs {
|
for _, f := range fs {
|
||||||
info, err := os.Stat(f)
|
info, err := os.Stat(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ func PrintTask(l *logger.Logger, t *taskfile.Task) {
|
|||||||
printTaskName(l, t)
|
printTaskName(l, t)
|
||||||
printTaskDescribingText(t, l)
|
printTaskDescribingText(t, l)
|
||||||
printTaskDependencies(l, t)
|
printTaskDependencies(l, t)
|
||||||
|
printTaskAliases(l, t)
|
||||||
printTaskCommands(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) {
|
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, "")
|
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 {
|
func hasDescription(t *taskfile.Task) bool {
|
||||||
return t.Desc != ""
|
return t.Desc != ""
|
||||||
}
|
}
|
||||||
@@ -94,10 +108,11 @@ func printTaskCommands(l *logger.Logger, t *taskfile.Task) {
|
|||||||
l.Outf(logger.Default, "commands:")
|
l.Outf(logger.Default, "commands:")
|
||||||
for _, c := range t.Cmds {
|
for _, c := range t.Cmds {
|
||||||
isCommand := c.Cmd != ""
|
isCommand := c.Cmd != ""
|
||||||
|
l.FOutf(l.Stdout, logger.Default, " - ")
|
||||||
if isCommand {
|
if isCommand {
|
||||||
l.Outf(logger.Default, " - %s", c.Cmd)
|
l.FOutf(l.Stdout, logger.Yellow, "%s\n", c.Cmd)
|
||||||
} else {
|
} else {
|
||||||
l.Outf(logger.Default, " - Task: %s", c.Task)
|
l.FOutf(l.Stdout, logger.Green, "Task: %s\n", c.Task)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ func init() {
|
|||||||
"OS": func() string { return runtime.GOOS },
|
"OS": func() string { return runtime.GOOS },
|
||||||
"ARCH": func() string { return runtime.GOARCH },
|
"ARCH": func() string { return runtime.GOARCH },
|
||||||
"catLines": func(s string) string {
|
"catLines": func(s string) string {
|
||||||
s = strings.Replace(s, "\r\n", " ", -1)
|
s = strings.ReplaceAll(s, "\r\n", " ")
|
||||||
return strings.Replace(s, "\n", " ", -1)
|
return strings.ReplaceAll(s, "\n", " ")
|
||||||
},
|
},
|
||||||
"splitLines": func(s string) []string {
|
"splitLines": func(s string) []string {
|
||||||
s = strings.Replace(s, "\r\n", "\n", -1)
|
s = strings.ReplaceAll(s, "\r\n", "\n")
|
||||||
return strings.Split(s, "\n")
|
return strings.Split(s, "\n")
|
||||||
},
|
},
|
||||||
"fromSlash": func(path string) string {
|
"fromSlash": func(path string) string {
|
||||||
|
|||||||
32
package-lock.json
generated
Normal 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
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "@go-task/cli",
|
||||||
|
"version": "3.17.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"
|
||||||
|
}
|
||||||
|
}
|
||||||
322
setup.go
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
compilerv2 "github.com/go-task/task/v3/internal/compiler/v2"
|
||||||
|
compilerv3 "github.com/go-task/task/v3/internal/compiler/v3"
|
||||||
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
|
"github.com/go-task/task/v3/internal/output"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
|
"github.com/go-task/task/v3/taskfile/read"
|
||||||
|
|
||||||
|
"github.com/sajari/fuzzy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *Executor) Setup() error {
|
||||||
|
if err := e.setCurrentDir(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.readTaskfile(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
e.setupFuzzyModel()
|
||||||
|
|
||||||
|
v, err := e.Taskfile.ParsedVersion()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.setupTempDir(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.setupStdFiles()
|
||||||
|
e.setupLogger()
|
||||||
|
if err := e.setupOutput(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.setupCompiler(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.readDotEnvFiles(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.doVersionChecks(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.setupDefaults(v)
|
||||||
|
e.setupConcurrencyState()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) setCurrentDir() error {
|
||||||
|
if e.Dir == "" {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.Dir = wd
|
||||||
|
} else if !filepath.IsAbs(e.Dir) {
|
||||||
|
abs, err := filepath.Abs(e.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.Dir = abs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) readTaskfile() error {
|
||||||
|
var err error
|
||||||
|
e.Taskfile, err = read.Taskfile(&read.ReaderNode{
|
||||||
|
Dir: e.Dir,
|
||||||
|
Entrypoint: e.Entrypoint,
|
||||||
|
Parent: nil,
|
||||||
|
Optional: false,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) setupFuzzyModel() {
|
||||||
|
if e.Taskfile != nil {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("TASK_TEMP_DIR") == "" {
|
||||||
|
e.TempDir = filepathext.SmartJoin(e.Dir, ".task")
|
||||||
|
} else if filepath.IsAbs(os.Getenv("TASK_TEMP_DIR")) || strings.HasPrefix(os.Getenv("TASK_TEMP_DIR"), "~") {
|
||||||
|
tempDir, err := execext.Expand(os.Getenv("TASK_TEMP_DIR"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
projectDir, _ := filepath.Abs(e.Dir)
|
||||||
|
projectName := filepath.Base(projectDir)
|
||||||
|
e.TempDir = filepathext.SmartJoin(tempDir, projectName)
|
||||||
|
} else {
|
||||||
|
e.TempDir = filepathext.SmartJoin(e.Dir, os.Getenv("TASK_TEMP_DIR"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) setupStdFiles() {
|
||||||
|
if e.Stdin == nil {
|
||||||
|
e.Stdin = os.Stdin
|
||||||
|
}
|
||||||
|
if e.Stdout == nil {
|
||||||
|
e.Stdout = os.Stdout
|
||||||
|
}
|
||||||
|
if e.Stderr == nil {
|
||||||
|
e.Stderr = os.Stderr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) setupLogger() {
|
||||||
|
e.Logger = &logger.Logger{
|
||||||
|
Stdout: e.Stdout,
|
||||||
|
Stderr: e.Stderr,
|
||||||
|
Verbose: e.Verbose,
|
||||||
|
Color: e.Color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) setupOutput() error {
|
||||||
|
if !e.OutputStyle.IsSet() {
|
||||||
|
e.OutputStyle = e.Taskfile.Output
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
e.Output, err = output.BuildFor(&e.OutputStyle)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) setupCompiler(v float64) error {
|
||||||
|
if v < 3 {
|
||||||
|
var err error
|
||||||
|
e.taskvars, err = read.Taskvars(e.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Compiler = &compilerv2.CompilerV2{
|
||||||
|
Dir: e.Dir,
|
||||||
|
Taskvars: e.taskvars,
|
||||||
|
TaskfileVars: e.Taskfile.Vars,
|
||||||
|
Expansions: e.Taskfile.Expansions,
|
||||||
|
Logger: e.Logger,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e.Compiler = &compilerv3.CompilerV3{
|
||||||
|
Dir: e.Dir,
|
||||||
|
TaskfileEnv: e.Taskfile.Env,
|
||||||
|
TaskfileVars: e.Taskfile.Vars,
|
||||||
|
Logger: e.Logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) readDotEnvFiles(v float64) error {
|
||||||
|
if v < 3.0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
env, err := read.Dotenv(e.Compiler, e.Taskfile, e.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = env.Range(func(key string, value taskfile.Var) error {
|
||||||
|
if _, ok := e.Taskfile.Env.Mapping[key]; !ok {
|
||||||
|
e.Taskfile.Env.Set(key, value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) setupDefaults(v float64) {
|
||||||
|
// Color available only on v3
|
||||||
|
if v < 3 {
|
||||||
|
e.Logger.Color = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Taskfile.Method == "" {
|
||||||
|
if v >= 3 {
|
||||||
|
e.Taskfile.Method = "checksum"
|
||||||
|
} else {
|
||||||
|
e.Taskfile.Method = "timestamp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Taskfile.Run == "" {
|
||||||
|
e.Taskfile.Run = "always"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) setupConcurrencyState() {
|
||||||
|
e.executionHashes = make(map[string]context.Context)
|
||||||
|
|
||||||
|
e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks))
|
||||||
|
e.mkdirMutexMap = make(map[string]*sync.Mutex, len(e.Taskfile.Tasks))
|
||||||
|
for k := range e.Taskfile.Tasks {
|
||||||
|
e.taskCallCount[k] = new(int32)
|
||||||
|
e.mkdirMutexMap[k] = &sync.Mutex{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Concurrency > 0 {
|
||||||
|
e.concurrencySemaphore = make(chan struct{}, e.Concurrency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) doVersionChecks(v float64) error {
|
||||||
|
if v < 2 {
|
||||||
|
return fmt.Errorf(`task: Taskfile versions prior to v2 are not supported anymore`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// consider as equal to the greater version if round
|
||||||
|
if v == 2.0 {
|
||||||
|
v = 2.6
|
||||||
|
}
|
||||||
|
if v == 3.0 {
|
||||||
|
v = 3.8
|
||||||
|
}
|
||||||
|
|
||||||
|
if v > 3.8 {
|
||||||
|
return fmt.Errorf(`task: Taskfile versions greater than v3.8 not implemented in the version of Task`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v < 2.1 && !e.Taskfile.Output.IsSet() {
|
||||||
|
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
|
||||||
|
}
|
||||||
|
if v < 2.2 && e.Taskfile.Includes.Len() > 0 {
|
||||||
|
return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`)
|
||||||
|
}
|
||||||
|
if v >= 3.0 && e.Taskfile.Expansions > 2 {
|
||||||
|
return fmt.Errorf(`task: The "expansions" setting is not available anymore on v3.0`)
|
||||||
|
}
|
||||||
|
if v < 3.8 && e.Taskfile.Output.Group.IsSet() {
|
||||||
|
return fmt.Errorf(`task: Taskfile option "output.group" is only available starting on Taskfile version v3.8`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v <= 2.1 {
|
||||||
|
err := errors.New(`task: Taskfile option "ignore_error" is only available starting on Taskfile version v2.1`)
|
||||||
|
|
||||||
|
for _, task := range e.Taskfile.Tasks {
|
||||||
|
if task.IgnoreError {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, cmd := range task.Cmds {
|
||||||
|
if cmd.IgnoreError {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v < 2.6 {
|
||||||
|
for _, task := range e.Taskfile.Tasks {
|
||||||
|
if len(task.Preconditions) > 0 {
|
||||||
|
return errors.New(`task: Task option "preconditions" is only available starting on Taskfile version v2.6`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v < 3 {
|
||||||
|
err := e.Taskfile.Includes.Range(func(_ string, taskfile taskfile.IncludedTaskfile) error {
|
||||||
|
if taskfile.AdvancedImport {
|
||||||
|
return errors.New(`task: Import with additional parameters is only available starting on Taskfile version v3`)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v < 3.7 {
|
||||||
|
if e.Taskfile.Run != "" {
|
||||||
|
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, task := range e.Taskfile.Tasks {
|
||||||
|
if task.Run != "" {
|
||||||
|
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NOTE(@andreynering): This function intercepts SIGINT and SIGTERM signals
|
// NOTE(@andreynering): This function intercepts SIGINT and SIGTERM signals
|
||||||
// so the Task process is not killed immediatelly and processes running have
|
// so the Task process is not killed immediately and processes running have
|
||||||
// time to do cleanup work.
|
// time to do cleanup work.
|
||||||
func (e *Executor) InterceptInterruptSignals() {
|
func (e *Executor) InterceptInterruptSignals() {
|
||||||
ch := make(chan os.Signal, 3)
|
ch := make(chan os.Signal, 3)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:build !windows
|
//go:build signals
|
||||||
// +build !windows
|
// +build signals
|
||||||
|
|
||||||
// This file contains tests for signal handling on Unix.
|
// This file contains tests for signal handling on Unix.
|
||||||
// Based on code from https://github.com/marco-m/timeit
|
// Based on code from https://github.com/marco-m/timeit
|
||||||
@@ -95,7 +95,7 @@ func (e *Executor) timestampChecker(t *taskfile.Task) status.Checker {
|
|||||||
|
|
||||||
func (e *Executor) checksumChecker(t *taskfile.Task) status.Checker {
|
func (e *Executor) checksumChecker(t *taskfile.Task) status.Checker {
|
||||||
return &status.Checksum{
|
return &status.Checksum{
|
||||||
BaseDir: e.Dir,
|
TempDir: e.TempDir,
|
||||||
TaskDir: t.Dir,
|
TaskDir: t.Dir,
|
||||||
Task: t.Name(),
|
Task: t.Name(),
|
||||||
Sources: t.Sources,
|
Sources: t.Sources,
|
||||||
|
|||||||
282
task.go
@@ -2,7 +2,6 @@ package task
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@@ -10,16 +9,15 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/compiler"
|
"github.com/go-task/task/v3/internal/compiler"
|
||||||
compilerv2 "github.com/go-task/task/v3/internal/compiler/v2"
|
|
||||||
compilerv3 "github.com/go-task/task/v3/internal/compiler/v3"
|
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/output"
|
"github.com/go-task/task/v3/internal/output"
|
||||||
"github.com/go-task/task/v3/internal/summary"
|
"github.com/go-task/task/v3/internal/summary"
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
"github.com/go-task/task/v3/taskfile/read"
|
|
||||||
|
|
||||||
|
"github.com/sajari/fuzzy"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,6 +32,7 @@ type Executor struct {
|
|||||||
Taskfile *taskfile.Taskfile
|
Taskfile *taskfile.Taskfile
|
||||||
|
|
||||||
Dir string
|
Dir string
|
||||||
|
TempDir string
|
||||||
Entrypoint string
|
Entrypoint string
|
||||||
Force bool
|
Force bool
|
||||||
Watch bool
|
Watch bool
|
||||||
@@ -44,6 +43,7 @@ type Executor struct {
|
|||||||
Parallel bool
|
Parallel bool
|
||||||
Color bool
|
Color bool
|
||||||
Concurrency int
|
Concurrency int
|
||||||
|
Interval string
|
||||||
|
|
||||||
Stdin io.Reader
|
Stdin io.Reader
|
||||||
Stdout io.Writer
|
Stdout io.Writer
|
||||||
@@ -54,7 +54,8 @@ type Executor struct {
|
|||||||
Output output.Output
|
Output output.Output
|
||||||
OutputStyle taskfile.Output
|
OutputStyle taskfile.Output
|
||||||
|
|
||||||
taskvars *taskfile.Vars
|
taskvars *taskfile.Vars
|
||||||
|
fuzzyModel *fuzzy.Model
|
||||||
|
|
||||||
concurrencySemaphore chan struct{}
|
concurrencySemaphore chan struct{}
|
||||||
taskCallCount map[string]*int32
|
taskCallCount map[string]*int32
|
||||||
@@ -66,11 +67,16 @@ type Executor struct {
|
|||||||
// Run runs Task
|
// Run runs Task
|
||||||
func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
||||||
// check if given tasks exist
|
// check if given tasks exist
|
||||||
for _, c := range calls {
|
for _, call := range calls {
|
||||||
if _, ok := e.Taskfile.Tasks[c.Task]; !ok {
|
task, err := e.GetTask(call)
|
||||||
// FIXME: move to the main package
|
if err != nil {
|
||||||
e.ListTasksWithDesc()
|
e.ListTasksWithDesc()
|
||||||
return &taskNotFoundError{taskName: c.Task}
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if task.Internal {
|
||||||
|
e.ListTasksWithDesc()
|
||||||
|
return &taskInternalError{taskName: call.Task}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,211 +110,14 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
|||||||
return g.Wait()
|
return g.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// readTaskfile selects and parses the entrypoint.
|
|
||||||
func (e *Executor) readTaskfile() error {
|
|
||||||
var err error
|
|
||||||
e.Taskfile, err = read.Taskfile(&read.ReaderNode{
|
|
||||||
Dir: e.Dir,
|
|
||||||
Entrypoint: e.Entrypoint,
|
|
||||||
Parent: nil,
|
|
||||||
Optional: false,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup setups Executor's internal state
|
|
||||||
func (e *Executor) Setup() error {
|
|
||||||
err := e.readTaskfile()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := e.Taskfile.ParsedVersion()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if v < 3.0 {
|
|
||||||
e.taskvars, err = read.Taskvars(e.Dir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.Stdin == nil {
|
|
||||||
e.Stdin = os.Stdin
|
|
||||||
}
|
|
||||||
if e.Stdout == nil {
|
|
||||||
e.Stdout = os.Stdout
|
|
||||||
}
|
|
||||||
if e.Stderr == nil {
|
|
||||||
e.Stderr = os.Stderr
|
|
||||||
}
|
|
||||||
e.Logger = &logger.Logger{
|
|
||||||
Stdout: e.Stdout,
|
|
||||||
Stderr: e.Stderr,
|
|
||||||
Verbose: e.Verbose,
|
|
||||||
Color: e.Color,
|
|
||||||
}
|
|
||||||
|
|
||||||
if v < 2 {
|
|
||||||
return fmt.Errorf(`task: Taskfile versions prior to v2 are not supported anymore`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// consider as equal to the greater version if round
|
|
||||||
if v == 2.0 {
|
|
||||||
v = 2.6
|
|
||||||
}
|
|
||||||
if v == 3.0 {
|
|
||||||
v = 3.8
|
|
||||||
}
|
|
||||||
|
|
||||||
if v > 3.8 {
|
|
||||||
return fmt.Errorf(`task: Taskfile versions greater than v3.8 not implemented in the version of Task`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color available only on v3
|
|
||||||
if v < 3 {
|
|
||||||
e.Logger.Color = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if v < 3 {
|
|
||||||
e.Compiler = &compilerv2.CompilerV2{
|
|
||||||
Dir: e.Dir,
|
|
||||||
Taskvars: e.taskvars,
|
|
||||||
TaskfileVars: e.Taskfile.Vars,
|
|
||||||
Expansions: e.Taskfile.Expansions,
|
|
||||||
Logger: e.Logger,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
e.Compiler = &compilerv3.CompilerV3{
|
|
||||||
Dir: e.Dir,
|
|
||||||
TaskfileEnv: e.Taskfile.Env,
|
|
||||||
TaskfileVars: e.Taskfile.Vars,
|
|
||||||
Logger: e.Logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v >= 3.0 {
|
|
||||||
env, err := read.Dotenv(e.Compiler, e.Taskfile, e.Dir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = env.Range(func(key string, value taskfile.Var) error {
|
|
||||||
if _, ok := e.Taskfile.Env.Mapping[key]; !ok {
|
|
||||||
e.Taskfile.Env.Set(key, value)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v < 2.1 && !e.Taskfile.Output.IsSet() {
|
|
||||||
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
|
|
||||||
}
|
|
||||||
if v < 2.2 && e.Taskfile.Includes.Len() > 0 {
|
|
||||||
return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`)
|
|
||||||
}
|
|
||||||
if v >= 3.0 && e.Taskfile.Expansions > 2 {
|
|
||||||
return fmt.Errorf(`task: The "expansions" setting is not available anymore on v3.0`)
|
|
||||||
}
|
|
||||||
if v < 3.8 && e.Taskfile.Output.Group.IsSet() {
|
|
||||||
return fmt.Errorf(`task: Taskfile option "output.group" is only available starting on Taskfile version v3.8`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !e.OutputStyle.IsSet() {
|
|
||||||
e.OutputStyle = e.Taskfile.Output
|
|
||||||
}
|
|
||||||
e.Output, err = output.BuildFor(&e.OutputStyle)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.Taskfile.Method == "" {
|
|
||||||
if v >= 3 {
|
|
||||||
e.Taskfile.Method = "checksum"
|
|
||||||
} else {
|
|
||||||
e.Taskfile.Method = "timestamp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v <= 2.1 {
|
|
||||||
err := errors.New(`task: Taskfile option "ignore_error" is only available starting on Taskfile version v2.1`)
|
|
||||||
|
|
||||||
for _, task := range e.Taskfile.Tasks {
|
|
||||||
if task.IgnoreError {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, cmd := range task.Cmds {
|
|
||||||
if cmd.IgnoreError {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v < 2.6 {
|
|
||||||
for _, task := range e.Taskfile.Tasks {
|
|
||||||
if len(task.Preconditions) > 0 {
|
|
||||||
return errors.New(`task: Task option "preconditions" is only available starting on Taskfile version v2.6`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v < 3 {
|
|
||||||
err := e.Taskfile.Includes.Range(func(_ string, taskfile taskfile.IncludedTaskfile) error {
|
|
||||||
if taskfile.AdvancedImport {
|
|
||||||
return errors.New(`task: Import with additional parameters is only available starting on Taskfile version v3`)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v < 3.7 {
|
|
||||||
if e.Taskfile.Run != "" {
|
|
||||||
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, task := range e.Taskfile.Tasks {
|
|
||||||
if task.Run != "" {
|
|
||||||
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.Taskfile.Run == "" {
|
|
||||||
e.Taskfile.Run = "always"
|
|
||||||
}
|
|
||||||
|
|
||||||
e.executionHashes = make(map[string]context.Context)
|
|
||||||
|
|
||||||
e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks))
|
|
||||||
e.mkdirMutexMap = make(map[string]*sync.Mutex, len(e.Taskfile.Tasks))
|
|
||||||
for k := range e.Taskfile.Tasks {
|
|
||||||
e.taskCallCount[k] = new(int32)
|
|
||||||
e.mkdirMutexMap[k] = &sync.Mutex{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.Concurrency > 0 {
|
|
||||||
e.concurrencySemaphore = make(chan struct{}, e.Concurrency)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunTask runs a task by its name
|
// RunTask runs a task by its name
|
||||||
func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
||||||
t, err := e.CompiledTask(call)
|
t, err := e.CompiledTask(call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !e.Watch && atomic.AddInt32(e.taskCallCount[call.Task], 1) >= MaximumTaskCall {
|
if !e.Watch && atomic.AddInt32(e.taskCallCount[t.Task], 1) >= MaximumTaskCall {
|
||||||
return &MaximumTaskCallExceededError{task: call.Task}
|
return &MaximumTaskCallExceededError{task: t.Task}
|
||||||
}
|
}
|
||||||
|
|
||||||
release := e.acquireConcurrencyLimit()
|
release := e.acquireConcurrencyLimit()
|
||||||
@@ -381,7 +190,7 @@ func (e *Executor) mkdir(t *taskfile.Task) error {
|
|||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
|
||||||
if _, err := os.Stat(t.Dir); os.IsNotExist(err) {
|
if _, err := os.Stat(t.Dir); os.IsNotExist(err) {
|
||||||
if err := os.MkdirAll(t.Dir, 0755); err != nil {
|
if err := os.MkdirAll(t.Dir, 0o755); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,19 +258,10 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("task: failed to get variables: %w", err)
|
return fmt.Errorf("task: failed to get variables: %w", err)
|
||||||
}
|
}
|
||||||
stdOut := outputWrapper.WrapWriter(e.Stdout, t.Prefix, outputTemplater)
|
stdOut, stdErr, close := outputWrapper.WrapWriter(e.Stdout, e.Stderr, t.Prefix, outputTemplater)
|
||||||
stdErr := outputWrapper.WrapWriter(e.Stderr, t.Prefix, outputTemplater)
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if _, ok := stdOut.(*os.File); !ok {
|
if err := close(); err != nil {
|
||||||
if closer, ok := stdOut.(io.Closer); ok {
|
e.Logger.Errf(logger.Red, "task: unable to close writter: %v", err)
|
||||||
closer.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, ok := stdErr.(*os.File); !ok {
|
|
||||||
if closer, ok := stdErr.(io.Closer); ok {
|
|
||||||
closer.Close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -534,3 +334,43 @@ func (e *Executor) startExecution(ctx context.Context, t *taskfile.Task, execute
|
|||||||
|
|
||||||
return execute(ctx)
|
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
|
||||||
|
}
|
||||||
|
|||||||
402
task_test.go
@@ -10,10 +10,12 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/go-task/task/v3"
|
"github.com/go-task/task/v3"
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,11 +39,12 @@ func (fct fileContentTest) name(file string) string {
|
|||||||
|
|
||||||
func (fct fileContentTest) Run(t *testing.T) {
|
func (fct fileContentTest) Run(t *testing.T) {
|
||||||
for f := range fct.Files {
|
for f := range fct.Files {
|
||||||
_ = os.Remove(filepath.Join(fct.Dir, f))
|
_ = os.Remove(filepathext.SmartJoin(fct.Dir, f))
|
||||||
}
|
}
|
||||||
|
|
||||||
e := &task.Executor{
|
e := &task.Executor{
|
||||||
Dir: fct.Dir,
|
Dir: fct.Dir,
|
||||||
|
TempDir: filepathext.SmartJoin(fct.Dir, ".task"),
|
||||||
Entrypoint: fct.Entrypoint,
|
Entrypoint: fct.Entrypoint,
|
||||||
Stdout: io.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: io.Discard,
|
Stderr: io.Discard,
|
||||||
@@ -51,13 +54,14 @@ func (fct fileContentTest) Run(t *testing.T) {
|
|||||||
|
|
||||||
for name, expectContent := range fct.Files {
|
for name, expectContent := range fct.Files {
|
||||||
t.Run(fct.name(name), func(t *testing.T) {
|
t.Run(fct.name(name), func(t *testing.T) {
|
||||||
b, err := os.ReadFile(filepath.Join(fct.Dir, name))
|
path := filepathext.SmartJoin(fct.Dir, name)
|
||||||
|
b, err := os.ReadFile(path)
|
||||||
assert.NoError(t, err, "Error reading file")
|
assert.NoError(t, err, "Error reading file")
|
||||||
s := string(b)
|
s := string(b)
|
||||||
if fct.TrimSpace {
|
if fct.TrimSpace {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
}
|
}
|
||||||
assert.Equal(t, expectContent, s, "unexpected file content")
|
assert.Equal(t, expectContent, s, "unexpected file content in %s", path)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,6 +165,39 @@ func TestMultilineVars(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSpecialVars(t *testing.T) {
|
||||||
|
const dir = "testdata/special_vars"
|
||||||
|
const target = "default"
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := &task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
Silent: true,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: target}))
|
||||||
|
|
||||||
|
toAbs := func(rel string) string {
|
||||||
|
abs, err := filepath.Abs(rel)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
return abs
|
||||||
|
}
|
||||||
|
|
||||||
|
output := buff.String()
|
||||||
|
|
||||||
|
// Root Taskfile
|
||||||
|
assert.Contains(t, output, "root/TASK=print")
|
||||||
|
assert.Contains(t, output, "root/ROOT_DIR="+toAbs("testdata/special_vars"))
|
||||||
|
assert.Contains(t, output, "root/TASKFILE_DIR="+toAbs("testdata/special_vars"))
|
||||||
|
|
||||||
|
// Included Taskfile
|
||||||
|
assert.Contains(t, output, "included/TASK=included:print")
|
||||||
|
assert.Contains(t, output, "included/ROOT_DIR="+toAbs("testdata/special_vars"))
|
||||||
|
assert.Contains(t, output, "included/TASKFILE_DIR="+toAbs("testdata/special_vars/included"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestVarsInvalidTmpl(t *testing.T) {
|
func TestVarsInvalidTmpl(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
dir = "testdata/vars/v2"
|
dir = "testdata/vars/v2"
|
||||||
@@ -233,7 +270,7 @@ func TestDeps(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
_ = os.Remove(filepath.Join(dir, f))
|
_ = os.Remove(filepathext.SmartJoin(dir, f))
|
||||||
}
|
}
|
||||||
|
|
||||||
e := &task.Executor{
|
e := &task.Executor{
|
||||||
@@ -245,7 +282,7 @@ func TestDeps(t *testing.T) {
|
|||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
f = filepath.Join(dir, f)
|
f = filepathext.SmartJoin(dir, f)
|
||||||
if _, err := os.Stat(f); err != nil {
|
if _, err := os.Stat(f); err != nil {
|
||||||
t.Errorf("File %s should exist", f)
|
t.Errorf("File %s should exist", f)
|
||||||
}
|
}
|
||||||
@@ -261,7 +298,7 @@ func TestStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
path := filepath.Join(dir, f)
|
path := filepathext.SmartJoin(dir, f)
|
||||||
_ = os.Remove(path)
|
_ = os.Remove(path)
|
||||||
if _, err := os.Stat(path); err == nil {
|
if _, err := os.Stat(path); err == nil {
|
||||||
t.Errorf("File should not exist: %v", err)
|
t.Errorf("File should not exist: %v", err)
|
||||||
@@ -270,17 +307,18 @@ func TestStatus(t *testing.T) {
|
|||||||
|
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
e := &task.Executor{
|
e := &task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
Stdout: &buff,
|
TempDir: filepathext.SmartJoin(dir, ".task"),
|
||||||
Stderr: &buff,
|
Stdout: &buff,
|
||||||
Silent: true,
|
Stderr: &buff,
|
||||||
|
Silent: true,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
|
if _, err := os.Stat(filepathext.SmartJoin(dir, f)); err != nil {
|
||||||
t.Errorf("File should exist: %v", err)
|
t.Errorf("File should exist: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,10 +395,10 @@ func TestGenerates(t *testing.T) {
|
|||||||
fileWithSpaces = "my text file.txt"
|
fileWithSpaces = "my text file.txt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var srcFile = filepath.Join(dir, srcTask)
|
var srcFile = filepathext.SmartJoin(dir, srcTask)
|
||||||
|
|
||||||
for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} {
|
for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} {
|
||||||
path := filepath.Join(dir, task)
|
path := filepathext.SmartJoin(dir, task)
|
||||||
_ = os.Remove(path)
|
_ = os.Remove(path)
|
||||||
if _, err := os.Stat(path); err == nil {
|
if _, err := os.Stat(path); err == nil {
|
||||||
t.Errorf("File should not exist: %v", err)
|
t.Errorf("File should not exist: %v", err)
|
||||||
@@ -376,7 +414,7 @@ func TestGenerates(t *testing.T) {
|
|||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
for _, theTask := range []string{relTask, absTask, fileWithSpaces} {
|
for _, theTask := range []string{relTask, absTask, fileWithSpaces} {
|
||||||
var destFile = filepath.Join(dir, theTask)
|
var destFile = filepathext.SmartJoin(dir, theTask)
|
||||||
var upToDate = fmt.Sprintf("task: Task \"%s\" is up to date\n", srcTask) +
|
var upToDate = fmt.Sprintf("task: Task \"%s\" is up to date\n", srcTask) +
|
||||||
fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask)
|
fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask)
|
||||||
|
|
||||||
@@ -413,12 +451,38 @@ func TestStatusChecksum(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
_ = os.Remove(filepath.Join(dir, f))
|
_ = os.Remove(filepathext.SmartJoin(dir, f))
|
||||||
|
|
||||||
_, err := os.Stat(filepath.Join(dir, f))
|
_, err := os.Stat(filepathext.SmartJoin(dir, f))
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
TempDir: filepathext.SmartJoin(dir, ".task"),
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
|
||||||
|
for _, f := range files {
|
||||||
|
_, err := os.Stat(filepathext.SmartJoin(dir, f))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buff.Reset()
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
|
||||||
|
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
|
var buff bytes.Buffer
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
@@ -426,16 +490,43 @@ func TestStatusChecksum(t *testing.T) {
|
|||||||
Stderr: &buff,
|
Stderr: &buff,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "f"}))
|
||||||
|
assert.Equal(t, string(data), buff.String())
|
||||||
|
}
|
||||||
|
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
|
func TestDuplicateAlias(t *testing.T) {
|
||||||
for _, f := range files {
|
const dir = "testdata/alias"
|
||||||
_, err := os.Stat(filepath.Join(dir, f))
|
|
||||||
assert.NoError(t, err)
|
data, err := os.ReadFile(filepathext.SmartJoin(dir, "alias-duplicate.txt"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
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, string(data), buff.String())
|
||||||
|
}
|
||||||
|
|
||||||
buff.Reset()
|
func TestAliasSummary(t *testing.T) {
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
|
const dir = "testdata/alias"
|
||||||
assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String())
|
|
||||||
|
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) {
|
func TestLabelUpToDate(t *testing.T) {
|
||||||
@@ -508,7 +599,7 @@ func TestLabelInSummary(t *testing.T) {
|
|||||||
assert.Contains(t, buff.String(), "foobar")
|
assert.Contains(t, buff.String(), "foobar")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLabelInList(t *testing.T) {
|
func TestNoLabelInList(t *testing.T) {
|
||||||
const dir = "testdata/label_list"
|
const dir = "testdata/label_list"
|
||||||
|
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
@@ -519,7 +610,7 @@ func TestLabelInList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
e.ListTasksWithDesc()
|
e.ListTasksWithDesc()
|
||||||
assert.Contains(t, buff.String(), "foobar")
|
assert.Contains(t, buff.String(), "foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
// task -al case 1: listAll list all tasks
|
// task -al case 1: listAll list all tasks
|
||||||
@@ -573,12 +664,13 @@ func TestListCanListDescOnly(t *testing.T) {
|
|||||||
func TestStatusVariables(t *testing.T) {
|
func TestStatusVariables(t *testing.T) {
|
||||||
const dir = "testdata/status_vars"
|
const dir = "testdata/status_vars"
|
||||||
|
|
||||||
_ = os.RemoveAll(filepath.Join(dir, ".task"))
|
_ = os.RemoveAll(filepathext.SmartJoin(dir, ".task"))
|
||||||
_ = os.Remove(filepath.Join(dir, "generated.txt"))
|
_ = os.Remove(filepathext.SmartJoin(dir, "generated.txt"))
|
||||||
|
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
|
TempDir: filepathext.SmartJoin(dir, ".task"),
|
||||||
Stdout: &buff,
|
Stdout: &buff,
|
||||||
Stderr: &buff,
|
Stderr: &buff,
|
||||||
Silent: false,
|
Silent: false,
|
||||||
@@ -589,7 +681,7 @@ func TestStatusVariables(t *testing.T) {
|
|||||||
|
|
||||||
assert.Contains(t, buff.String(), "d41d8cd98f00b204e9800998ecf8427e")
|
assert.Contains(t, buff.String(), "d41d8cd98f00b204e9800998ecf8427e")
|
||||||
|
|
||||||
inf, err := os.Stat(filepath.Join(dir, "source.txt"))
|
inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt"))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
ts := fmt.Sprintf("%d", inf.ModTime().Unix())
|
ts := fmt.Sprintf("%d", inf.ModTime().Unix())
|
||||||
tf := inf.ModTime().String()
|
tf := inf.ModTime().String()
|
||||||
@@ -600,7 +692,7 @@ func TestStatusVariables(t *testing.T) {
|
|||||||
|
|
||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
const dir = "testdata/init"
|
const dir = "testdata/init"
|
||||||
var file = filepath.Join(dir, "Taskfile.yaml")
|
var file = filepathext.SmartJoin(dir, "Taskfile.yaml")
|
||||||
|
|
||||||
_ = os.Remove(file)
|
_ = os.Remove(file)
|
||||||
if _, err := os.Stat(file); err == nil {
|
if _, err := os.Stat(file); err == nil {
|
||||||
@@ -689,7 +781,7 @@ func TestExpand(t *testing.T) {
|
|||||||
func TestDry(t *testing.T) {
|
func TestDry(t *testing.T) {
|
||||||
const dir = "testdata/dry"
|
const dir = "testdata/dry"
|
||||||
|
|
||||||
file := filepath.Join(dir, "file.txt")
|
file := filepathext.SmartJoin(dir, "file.txt")
|
||||||
_ = os.Remove(file)
|
_ = os.Remove(file)
|
||||||
|
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
@@ -714,14 +806,15 @@ func TestDry(t *testing.T) {
|
|||||||
func TestDryChecksum(t *testing.T) {
|
func TestDryChecksum(t *testing.T) {
|
||||||
const dir = "testdata/dry_checksum"
|
const dir = "testdata/dry_checksum"
|
||||||
|
|
||||||
checksumFile := filepath.Join(dir, ".task/checksum/default")
|
checksumFile := filepathext.SmartJoin(dir, ".task/checksum/default")
|
||||||
_ = os.Remove(checksumFile)
|
_ = os.Remove(checksumFile)
|
||||||
|
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
Stdout: io.Discard,
|
TempDir: filepathext.SmartJoin(dir, ".task"),
|
||||||
Stderr: io.Discard,
|
Stdout: io.Discard,
|
||||||
Dry: true,
|
Stderr: io.Discard,
|
||||||
|
Dry: true,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
||||||
@@ -769,7 +862,6 @@ func TestIncludesMultiLevel(t *testing.T) {
|
|||||||
|
|
||||||
func TestIncludeCycle(t *testing.T) {
|
func TestIncludeCycle(t *testing.T) {
|
||||||
const dir = "testdata/includes_cycle"
|
const dir = "testdata/includes_cycle"
|
||||||
expectedError := "task: include cycle detected between testdata/includes_cycle/Taskfile.yml <--> testdata/includes_cycle/one/two/Taskfile.yml"
|
|
||||||
|
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
@@ -779,7 +871,9 @@ func TestIncludeCycle(t *testing.T) {
|
|||||||
Silent: true,
|
Silent: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.EqualError(t, e.Setup(), expectedError)
|
err := e.Setup()
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "task: include cycle detected between")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIncorrectVersionIncludes(t *testing.T) {
|
func TestIncorrectVersionIncludes(t *testing.T) {
|
||||||
@@ -797,6 +891,22 @@ func TestIncorrectVersionIncludes(t *testing.T) {
|
|||||||
assert.EqualError(t, e.Setup(), expectedError)
|
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) {
|
func TestIncludesEmptyMain(t *testing.T) {
|
||||||
tt := fileContentTest{
|
tt := fileContentTest{
|
||||||
Dir: "testdata/includes_empty",
|
Dir: "testdata/includes_empty",
|
||||||
@@ -847,27 +957,39 @@ func TestIncludesOptional(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIncludesOptionalImplicitFalse(t *testing.T) {
|
func TestIncludesOptionalImplicitFalse(t *testing.T) {
|
||||||
|
const dir = "testdata/includes_optional_implicit_false"
|
||||||
|
wd, _ := os.Getwd()
|
||||||
|
|
||||||
|
message := "stat %s/%s/TaskfileOptional.yml: no such file or directory"
|
||||||
|
expected := fmt.Sprintf(message, wd, dir)
|
||||||
|
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: "testdata/includes_optional_implicit_false",
|
Dir: dir,
|
||||||
Stdout: io.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: io.Discard,
|
Stderr: io.Discard,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.Setup()
|
err := e.Setup()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Equal(t, "stat testdata/includes_optional_implicit_false/TaskfileOptional.yml: no such file or directory", err.Error())
|
assert.Equal(t, expected, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIncludesOptionalExplicitFalse(t *testing.T) {
|
func TestIncludesOptionalExplicitFalse(t *testing.T) {
|
||||||
|
const dir = "testdata/includes_optional_explicit_false"
|
||||||
|
wd, _ := os.Getwd()
|
||||||
|
|
||||||
|
message := "stat %s/%s/TaskfileOptional.yml: no such file or directory"
|
||||||
|
expected := fmt.Sprintf(message, wd, dir)
|
||||||
|
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: "testdata/includes_optional_explicit_false",
|
Dir: dir,
|
||||||
Stdout: io.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: io.Discard,
|
Stderr: io.Discard,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.Setup()
|
err := e.Setup()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Equal(t, "stat testdata/includes_optional_explicit_false/TaskfileOptional.yml: no such file or directory", err.Error())
|
assert.Equal(t, expected, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIncludesFromCustomTaskfile(t *testing.T) {
|
func TestIncludesFromCustomTaskfile(t *testing.T) {
|
||||||
@@ -885,6 +1007,106 @@ func TestIncludesFromCustomTaskfile(t *testing.T) {
|
|||||||
tt.Run(t)
|
tt.Run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIncludesRelativePath(t *testing.T) {
|
||||||
|
const dir = "testdata/includes_rel_path"
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "common:pwd"}))
|
||||||
|
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
|
||||||
|
|
||||||
|
buff.Reset()
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "included:common:pwd"}))
|
||||||
|
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIncludesInternal(t *testing.T) {
|
||||||
|
const dir = "testdata/internal_task"
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
task string
|
||||||
|
expectedErr bool
|
||||||
|
expectedOutput string
|
||||||
|
}{
|
||||||
|
{"included internal task via task", "task-1", false, "Hello, World!\n"},
|
||||||
|
{"included internal task via dep", "task-2", false, "Hello, World!\n"},
|
||||||
|
{
|
||||||
|
"included internal direct",
|
||||||
|
"included:task-3",
|
||||||
|
true,
|
||||||
|
"task: No tasks with description available. Try --list-all to list all tasks\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
Silent: true,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: test.task})
|
||||||
|
if test.expectedErr {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, test.expectedOutput, buff.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInternalTask(t *testing.T) {
|
||||||
|
const dir = "testdata/internal_task"
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
task string
|
||||||
|
expectedErr bool
|
||||||
|
expectedOutput string
|
||||||
|
}{
|
||||||
|
{"internal task via task", "task-1", false, "Hello, World!\n"},
|
||||||
|
{"internal task via dep", "task-2", false, "Hello, World!\n"},
|
||||||
|
{
|
||||||
|
"internal direct",
|
||||||
|
"task-3",
|
||||||
|
true,
|
||||||
|
"task: No tasks with description available. Try --list-all to list all tasks\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
Silent: true,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: test.task})
|
||||||
|
if test.expectedErr {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, test.expectedOutput, buff.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSupportedFileNames(t *testing.T) {
|
func TestSupportedFileNames(t *testing.T) {
|
||||||
fileNames := []string{
|
fileNames := []string{
|
||||||
"Taskfile.yml",
|
"Taskfile.yml",
|
||||||
@@ -921,12 +1143,12 @@ func TestSummary(t *testing.T) {
|
|||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-summary"}, taskfile.Call{Task: "other-task-with-summary"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-summary"}, taskfile.Call{Task: "other-task-with-summary"}))
|
||||||
|
|
||||||
data, err := os.ReadFile(filepath.Join(dir, "task-with-summary.txt"))
|
data, err := os.ReadFile(filepathext.SmartJoin(dir, "task-with-summary.txt"))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
expectedOutput := string(data)
|
expectedOutput := string(data)
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
expectedOutput = strings.Replace(expectedOutput, "\r\n", "\n", -1)
|
expectedOutput = strings.ReplaceAll(expectedOutput, "\r\n", "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, expectedOutput, buff.String())
|
assert.Equal(t, expectedOutput, buff.String())
|
||||||
@@ -1294,3 +1516,97 @@ func TestErrorCode(t *testing.T) {
|
|||||||
assert.True(t, ok, "cannot cast returned error to *task.TaskRunError")
|
assert.True(t, ok, "cannot cast returned error to *task.TaskRunError")
|
||||||
assert.Equal(t, 42, casted.ExitCode(), "unexpected exit code from task")
|
assert.Equal(t, 42, casted.ExitCode(), "unexpected exit code from task")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEvaluateSymlinksInPaths(t *testing.T) {
|
||||||
|
const dir = "testdata/evaluate_symlinks_in_paths"
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := &task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
Silent: false,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "default"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEqual(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
|
||||||
|
buff.Reset()
|
||||||
|
err = e.Run(context.Background(), taskfile.Call{Task: "test-sym"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEqual(t, `task: Task "test-sym" is up to date`, strings.TrimSpace(buff.String()))
|
||||||
|
buff.Reset()
|
||||||
|
err = e.Run(context.Background(), taskfile.Call{Task: "default"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEqual(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
|
||||||
|
buff.Reset()
|
||||||
|
err = e.Run(context.Background(), taskfile.Call{Task: "default"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
|
||||||
|
buff.Reset()
|
||||||
|
err = e.Run(context.Background(), taskfile.Call{Task: "reset"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
buff.Reset()
|
||||||
|
err = os.RemoveAll(dir + "/.task")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
@@ -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
|
||||||
|
}
|
||||||
@@ -2,17 +2,26 @@ package taskfile
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"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"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IncludedTaskfile represents information about included tasksfile
|
// IncludedTaskfile represents information about included taskfiles
|
||||||
type IncludedTaskfile struct {
|
type IncludedTaskfile struct {
|
||||||
Taskfile string
|
Taskfile string
|
||||||
Dir string
|
Dir string
|
||||||
Optional bool
|
Optional bool
|
||||||
|
Internal bool
|
||||||
|
Aliases []string
|
||||||
AdvancedImport bool
|
AdvancedImport bool
|
||||||
Vars *Vars
|
Vars *Vars
|
||||||
|
BaseDir string // The directory from which the including taskfile was loaded; used to resolve relative paths
|
||||||
}
|
}
|
||||||
|
|
||||||
// IncludedTaskfiles represents information about included tasksfiles
|
// IncludedTaskfiles represents information about included tasksfiles
|
||||||
@@ -64,7 +73,7 @@ func (tfs *IncludedTaskfiles) Set(key string, includedTaskfile IncludedTaskfile)
|
|||||||
if tfs.Mapping == nil {
|
if tfs.Mapping == nil {
|
||||||
tfs.Mapping = make(map[string]IncludedTaskfile, 1)
|
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.Keys = append(tfs.Keys, key)
|
||||||
}
|
}
|
||||||
tfs.Mapping[key] = includedTaskfile
|
tfs.Mapping[key] = includedTaskfile
|
||||||
@@ -95,6 +104,8 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
|
|||||||
Taskfile string
|
Taskfile string
|
||||||
Dir string
|
Dir string
|
||||||
Optional bool
|
Optional bool
|
||||||
|
Internal bool
|
||||||
|
Aliases []string
|
||||||
Vars *Vars
|
Vars *Vars
|
||||||
}
|
}
|
||||||
if err := unmarshal(&includedTaskfile); err != nil {
|
if err := unmarshal(&includedTaskfile); err != nil {
|
||||||
@@ -103,7 +114,54 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
|
|||||||
it.Taskfile = includedTaskfile.Taskfile
|
it.Taskfile = includedTaskfile.Taskfile
|
||||||
it.Dir = includedTaskfile.Dir
|
it.Dir = includedTaskfile.Dir
|
||||||
it.Optional = includedTaskfile.Optional
|
it.Optional = includedTaskfile.Optional
|
||||||
|
it.Internal = includedTaskfile.Internal
|
||||||
|
it.Aliases = includedTaskfile.Aliases
|
||||||
it.AdvancedImport = true
|
it.AdvancedImport = true
|
||||||
it.Vars = includedTaskfile.Vars
|
it.Vars = includedTaskfile.Vars
|
||||||
return nil
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullDirPath returns the fully qualified path to the included taskfile's working directory
|
||||||
|
func (it *IncludedTaskfile) FullDirPath() (string, error) {
|
||||||
|
return it.resolvePath(it.Dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *IncludedTaskfile) resolvePath(path string) (string, error) {
|
||||||
|
path, err := execext.Expand(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.IsAbs(path) {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := filepath.Abs(filepathext.SmartJoin(it.BaseDir, path))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("task: error resolving path %s relative to %s: %w", path, it.BaseDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
const NamespaceSeparator = ":"
|
const NamespaceSeparator = ":"
|
||||||
|
|
||||||
// Merge merges the second Taskfile into the first
|
// 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 {
|
if t1.Version != t2.Version {
|
||||||
return fmt.Errorf(`task: Taskfiles versions should match. First is "%s" but second is "%s"`, t1.Version, t2.Version)
|
return fmt.Errorf(`task: Taskfiles versions should match. First is "%s" but second is "%s"`, t1.Version, t2.Version)
|
||||||
}
|
}
|
||||||
@@ -39,20 +39,38 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
|
|||||||
t1.Tasks = make(Tasks)
|
t1.Tasks = make(Tasks)
|
||||||
}
|
}
|
||||||
for k, v := range t2.Tasks {
|
for k, v := range t2.Tasks {
|
||||||
// FIXME(@andreynering): Refactor this block, otherwise we can
|
// We do a deep copy of the task struct here to ensure that no data can
|
||||||
// have serious side-effects in the future, since we're editing
|
// be changed elsewhere once the taskfile is merged.
|
||||||
// the original references instead of deep copying them.
|
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...)
|
dep.Task = taskNameWithNamespace(dep.Task, namespaces...)
|
||||||
}
|
}
|
||||||
for _, cmd := range v.Cmds {
|
for _, cmd := range task.Cmds {
|
||||||
if cmd != nil && cmd.Task != "" {
|
if cmd != nil && cmd.Task != "" {
|
||||||
cmd.Task = taskNameWithNamespace(cmd.Task, namespaces...)
|
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
|
return nil
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package read
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/compiler"
|
"github.com/go-task/task/v3/internal/compiler"
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
@@ -27,10 +27,11 @@ func Dotenv(c compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.V
|
|||||||
|
|
||||||
for _, dotEnvPath := range tf.Dotenv {
|
for _, dotEnvPath := range tf.Dotenv {
|
||||||
dotEnvPath = tr.Replace(dotEnvPath)
|
dotEnvPath = tr.Replace(dotEnvPath)
|
||||||
|
if dotEnvPath == "" {
|
||||||
if !filepath.IsAbs(dotEnvPath) {
|
continue
|
||||||
dotEnvPath = filepath.Join(dir, dotEnvPath)
|
|
||||||
}
|
}
|
||||||
|
dotEnvPath = filepathext.SmartJoin(dir, dotEnvPath)
|
||||||
|
|
||||||
if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
|
if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
@@ -44,7 +44,8 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
|
|||||||
}
|
}
|
||||||
readerNode.Dir = d
|
readerNode.Dir = d
|
||||||
}
|
}
|
||||||
path, err := exists(filepath.Join(readerNode.Dir, readerNode.Entrypoint))
|
|
||||||
|
path, err := exists(filepathext.SmartJoin(readerNode.Dir, readerNode.Entrypoint))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -60,6 +61,16 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Annotate any included Taskfile reference with a base directory for resolving relative paths
|
||||||
|
_ = t.Includes.Range(func(key string, includedFile taskfile.IncludedTaskfile) error {
|
||||||
|
// Set the base directory for resolving relative paths, but only if not already set
|
||||||
|
if includedFile.BaseDir == "" {
|
||||||
|
includedFile.BaseDir = readerNode.Dir
|
||||||
|
t.Includes.Set(key, includedFile)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
err = t.Includes.Range(func(namespace string, includedTask taskfile.IncludedTaskfile) error {
|
err = t.Includes.Range(func(namespace string, includedTask taskfile.IncludedTaskfile) error {
|
||||||
if v >= 3.0 {
|
if v >= 3.0 {
|
||||||
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
|
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
|
||||||
@@ -67,21 +78,22 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
|
|||||||
Taskfile: tr.Replace(includedTask.Taskfile),
|
Taskfile: tr.Replace(includedTask.Taskfile),
|
||||||
Dir: tr.Replace(includedTask.Dir),
|
Dir: tr.Replace(includedTask.Dir),
|
||||||
Optional: includedTask.Optional,
|
Optional: includedTask.Optional,
|
||||||
|
Internal: includedTask.Internal,
|
||||||
|
Aliases: includedTask.Aliases,
|
||||||
AdvancedImport: includedTask.AdvancedImport,
|
AdvancedImport: includedTask.AdvancedImport,
|
||||||
Vars: includedTask.Vars,
|
Vars: includedTask.Vars,
|
||||||
|
BaseDir: includedTask.BaseDir,
|
||||||
}
|
}
|
||||||
if err := tr.Err(); err != nil {
|
if err := tr.Err(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err := execext.Expand(includedTask.Taskfile)
|
path, err := includedTask.FullTaskfilePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !filepath.IsAbs(path) {
|
|
||||||
path = filepath.Join(readerNode.Dir, path)
|
|
||||||
}
|
|
||||||
path, err = exists(path)
|
path, err = exists(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if includedTask.Optional {
|
if includedTask.Optional {
|
||||||
@@ -114,27 +126,31 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if includedTask.AdvancedImport {
|
if includedTask.AdvancedImport {
|
||||||
|
dir, err := includedTask.FullDirPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for k, v := range includedTaskfile.Vars.Mapping {
|
for k, v := range includedTaskfile.Vars.Mapping {
|
||||||
o := v
|
o := v
|
||||||
o.Dir = filepath.Join(readerNode.Dir, includedTask.Dir)
|
o.Dir = dir
|
||||||
includedTaskfile.Vars.Mapping[k] = o
|
includedTaskfile.Vars.Mapping[k] = o
|
||||||
}
|
}
|
||||||
for k, v := range includedTaskfile.Env.Mapping {
|
for k, v := range includedTaskfile.Env.Mapping {
|
||||||
o := v
|
o := v
|
||||||
o.Dir = filepath.Join(readerNode.Dir, includedTask.Dir)
|
o.Dir = dir
|
||||||
includedTaskfile.Env.Mapping[k] = o
|
includedTaskfile.Env.Mapping[k] = o
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, task := range includedTaskfile.Tasks {
|
for _, task := range includedTaskfile.Tasks {
|
||||||
if !filepath.IsAbs(task.Dir) {
|
task.Dir = filepathext.SmartJoin(dir, task.Dir)
|
||||||
task.Dir = filepath.Join(includedTask.Dir, task.Dir)
|
|
||||||
}
|
|
||||||
task.IncludeVars = includedTask.Vars
|
task.IncludeVars = includedTask.Vars
|
||||||
task.IncludedTaskfileVars = includedTaskfile.Vars
|
task.IncludedTaskfileVars = includedTaskfile.Vars
|
||||||
|
task.IncludedTaskfile = &includedTask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
|
if err = taskfile.Merge(t, includedTaskfile, &includedTask, namespace); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -144,13 +160,13 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if v < 3.0 {
|
if v < 3.0 {
|
||||||
path = filepath.Join(readerNode.Dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
|
path = filepathext.SmartJoin(readerNode.Dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
|
||||||
if _, err = os.Stat(path); err == nil {
|
if _, err = os.Stat(path); err == nil {
|
||||||
osTaskfile, err := readTaskfile(path)
|
osTaskfile, err := readTaskfile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = taskfile.Merge(t, osTaskfile); err != nil {
|
if err = taskfile.Merge(t, osTaskfile, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +189,10 @@ func readTaskfile(file string) (*taskfile.Taskfile, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var t taskfile.Taskfile
|
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) {
|
func exists(path string) (string, error) {
|
||||||
@@ -186,7 +205,7 @@ func exists(path string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range defaultTaskfiles {
|
for _, n := range defaultTaskfiles {
|
||||||
fpath := filepath.Join(path, n)
|
fpath := filepathext.SmartJoin(path, n)
|
||||||
if _, err := os.Stat(fpath); err == nil {
|
if _, err := os.Stat(fpath); err == nil {
|
||||||
return fpath, nil
|
return fpath, nil
|
||||||
}
|
}
|
||||||
@@ -203,14 +222,14 @@ func checkCircularIncludes(node *ReaderNode) error {
|
|||||||
return errors.New("task: failed to check for include cycle: node.Parent was nil")
|
return errors.New("task: failed to check for include cycle: node.Parent was nil")
|
||||||
}
|
}
|
||||||
var curNode = node
|
var curNode = node
|
||||||
var basePath = filepath.Join(node.Dir, node.Entrypoint)
|
var basePath = filepathext.SmartJoin(node.Dir, node.Entrypoint)
|
||||||
for curNode.Parent != nil {
|
for curNode.Parent != nil {
|
||||||
curNode = curNode.Parent
|
curNode = curNode.Parent
|
||||||
curPath := filepath.Join(curNode.Dir, curNode.Entrypoint)
|
curPath := filepathext.SmartJoin(curNode.Dir, curNode.Entrypoint)
|
||||||
if curPath == basePath {
|
if curPath == basePath {
|
||||||
return fmt.Errorf("task: include cycle detected between %s <--> %s",
|
return fmt.Errorf("task: include cycle detected between %s <--> %s",
|
||||||
curPath,
|
curPath,
|
||||||
filepath.Join(node.Parent.Dir, node.Parent.Entrypoint),
|
filepathext.SmartJoin(node.Parent.Dir, node.Parent.Entrypoint),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ package read
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
func Taskvars(dir string) (*taskfile.Vars, error) {
|
func Taskvars(dir string) (*taskfile.Vars, error) {
|
||||||
vars := &taskfile.Vars{}
|
vars := &taskfile.Vars{}
|
||||||
|
|
||||||
path := filepath.Join(dir, "Taskvars.yml")
|
path := filepathext.SmartJoin(dir, "Taskvars.yml")
|
||||||
if _, err := os.Stat(path); err == nil {
|
if _, err := os.Stat(path); err == nil {
|
||||||
vars, err = readTaskvars(path)
|
vars, err = readTaskvars(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -23,7 +23,7 @@ func Taskvars(dir string) (*taskfile.Vars, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
path = filepath.Join(dir, fmt.Sprintf("Taskvars_%s.yml", runtime.GOOS))
|
path = filepathext.SmartJoin(dir, fmt.Sprintf("Taskvars_%s.yml", runtime.GOOS))
|
||||||
if _, err := os.Stat(path); err == nil {
|
if _, err := os.Stat(path); err == nil {
|
||||||
osVars, err := readTaskvars(path)
|
osVars, err := readTaskvars(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package taskfile
|
|
||||||
|
|
||||||
func stringSliceContains(s []string, str string) bool {
|
|
||||||
for _, v := range s {
|
|
||||||
if v == str {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -11,6 +11,7 @@ type Task struct {
|
|||||||
Label string
|
Label string
|
||||||
Desc string
|
Desc string
|
||||||
Summary string
|
Summary string
|
||||||
|
Aliases []string
|
||||||
Sources []string
|
Sources []string
|
||||||
Generates []string
|
Generates []string
|
||||||
Status []string
|
Status []string
|
||||||
@@ -20,12 +21,14 @@ type Task struct {
|
|||||||
Env *Vars
|
Env *Vars
|
||||||
Silent bool
|
Silent bool
|
||||||
Interactive bool
|
Interactive bool
|
||||||
|
Internal bool
|
||||||
Method string
|
Method string
|
||||||
Prefix string
|
Prefix string
|
||||||
IgnoreError bool
|
IgnoreError bool
|
||||||
Run string
|
Run string
|
||||||
IncludeVars *Vars
|
IncludeVars *Vars
|
||||||
IncludedTaskfileVars *Vars
|
IncludedTaskfileVars *Vars
|
||||||
|
IncludedTaskfile *IncludedTaskfile
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) Name() string {
|
func (t *Task) Name() string {
|
||||||
@@ -54,6 +57,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
Label string
|
Label string
|
||||||
Desc string
|
Desc string
|
||||||
Summary string
|
Summary string
|
||||||
|
Aliases []string
|
||||||
Sources []string
|
Sources []string
|
||||||
Generates []string
|
Generates []string
|
||||||
Status []string
|
Status []string
|
||||||
@@ -63,6 +67,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
Env *Vars
|
Env *Vars
|
||||||
Silent bool
|
Silent bool
|
||||||
Interactive bool
|
Interactive bool
|
||||||
|
Internal bool
|
||||||
Method string
|
Method string
|
||||||
Prefix string
|
Prefix string
|
||||||
IgnoreError bool `yaml:"ignore_error"`
|
IgnoreError bool `yaml:"ignore_error"`
|
||||||
@@ -75,6 +80,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
t.Deps = task.Deps
|
t.Deps = task.Deps
|
||||||
t.Label = task.Label
|
t.Label = task.Label
|
||||||
t.Desc = task.Desc
|
t.Desc = task.Desc
|
||||||
|
t.Aliases = task.Aliases
|
||||||
t.Summary = task.Summary
|
t.Summary = task.Summary
|
||||||
t.Sources = task.Sources
|
t.Sources = task.Sources
|
||||||
t.Generates = task.Generates
|
t.Generates = task.Generates
|
||||||
@@ -85,9 +91,42 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
t.Env = task.Env
|
t.Env = task.Env
|
||||||
t.Silent = task.Silent
|
t.Silent = task.Silent
|
||||||
t.Interactive = task.Interactive
|
t.Interactive = task.Interactive
|
||||||
|
t.Internal = task.Internal
|
||||||
t.Method = task.Method
|
t.Method = task.Method
|
||||||
t.Prefix = task.Prefix
|
t.Prefix = task.Prefix
|
||||||
t.IgnoreError = task.IgnoreError
|
t.IgnoreError = task.IgnoreError
|
||||||
t.Run = task.Run
|
t.Run = task.Run
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type Taskfile struct {
|
|||||||
Silent bool
|
Silent bool
|
||||||
Dotenv []string
|
Dotenv []string
|
||||||
Run string
|
Run string
|
||||||
|
Interval string
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalYAML implements yaml.Unmarshaler interface
|
// UnmarshalYAML implements yaml.Unmarshaler interface
|
||||||
@@ -34,10 +35,13 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
Silent bool
|
Silent bool
|
||||||
Dotenv []string
|
Dotenv []string
|
||||||
Run string
|
Run string
|
||||||
|
Interval string
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := unmarshal(&taskfile); err != nil {
|
if err := unmarshal(&taskfile); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tf.Version = taskfile.Version
|
tf.Version = taskfile.Version
|
||||||
tf.Expansions = taskfile.Expansions
|
tf.Expansions = taskfile.Expansions
|
||||||
tf.Output = taskfile.Output
|
tf.Output = taskfile.Output
|
||||||
@@ -49,6 +53,8 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
tf.Silent = taskfile.Silent
|
tf.Silent = taskfile.Silent
|
||||||
tf.Dotenv = taskfile.Dotenv
|
tf.Dotenv = taskfile.Dotenv
|
||||||
tf.Run = taskfile.Run
|
tf.Run = taskfile.Run
|
||||||
|
tf.Interval = taskfile.Interval
|
||||||
|
|
||||||
if tf.Expansions <= 0 {
|
if tf.Expansions <= 0 {
|
||||||
tf.Expansions = 2
|
tf.Expansions = 2
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package taskfile
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,6 +35,18 @@ func (vs *Vars) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
return nil
|
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
|
// Merge merges the given Vars into the caller one
|
||||||
func (vs *Vars) Merge(other *Vars) {
|
func (vs *Vars) Merge(other *Vars) {
|
||||||
_ = other.Range(func(key string, value Var) error {
|
_ = other.Range(func(key string, value Var) error {
|
||||||
@@ -47,7 +60,7 @@ func (vs *Vars) Set(key string, value Var) {
|
|||||||
if vs.Mapping == nil {
|
if vs.Mapping == nil {
|
||||||
vs.Mapping = make(map[string]Var, 1)
|
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.Keys = append(vs.Keys, key)
|
||||||
}
|
}
|
||||||
vs.Mapping[key] = value
|
vs.Mapping[key] = value
|
||||||
|
|||||||
19
testdata/alias/Taskfile.yml
vendored
Normal 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
@@ -0,0 +1,7 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
qux:
|
||||||
|
aliases: [q, x]
|
||||||
|
cmds:
|
||||||
|
- echo "qux"
|
||||||
1
testdata/alias/alias-duplicate.txt
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
task: No tasks with description available. Try --list-all to list all tasks
|
||||||
11
testdata/alias/alias-summary.txt
vendored
Normal 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
@@ -0,0 +1,6 @@
|
|||||||
|
task: [foo] echo "foo"
|
||||||
|
foo
|
||||||
|
task: [bar] echo "bar"
|
||||||
|
bar
|
||||||
|
task: [included:qux] echo "qux"
|
||||||
|
qux
|
||||||
17
testdata/evaluate_symlinks_in_paths/Taskfile.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
sources:
|
||||||
|
- src/**/*
|
||||||
|
cmds:
|
||||||
|
- echo "some job"
|
||||||
|
|
||||||
|
test-sym:
|
||||||
|
cmds:
|
||||||
|
- echo "shared file source changed" > src/shared/b
|
||||||
|
|
||||||
|
reset:
|
||||||
|
cmds:
|
||||||
|
- echo "shared file source" > src/shared/b
|
||||||
|
- echo "file source" > src/a
|
||||||
1
testdata/evaluate_symlinks_in_paths/shared/b
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
shared file source
|
||||||
1
testdata/evaluate_symlinks_in_paths/shared/inner_shared/c
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
inner shared file source
|
||||||
1
testdata/evaluate_symlinks_in_paths/src/a
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
file source
|
||||||
1
testdata/evaluate_symlinks_in_paths/src/shared
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../shared
|
||||||
4
testdata/includes_incorrect/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
included: incomplete.yml
|
||||||
4
testdata/includes_incorrect/incomplete.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
name:
|
||||||
|
'test
|
||||||
15
testdata/includes_internal/Taskfile.yml
vendored
Normal 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
|
||||||
6
testdata/includes_internal/Taskfile2.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
task-3:
|
||||||
|
cmds:
|
||||||
|
- echo "Hello, World!"
|
||||||
10
testdata/includes_rel_path/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
included:
|
||||||
|
taskfile: ./included
|
||||||
|
dir: ./included
|
||||||
|
|
||||||
|
common:
|
||||||
|
taskfile: ./common
|
||||||
|
dir: ./common
|
||||||
4
testdata/includes_rel_path/common/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
pwd: pwd
|
||||||
6
testdata/includes_rel_path/included/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
common:
|
||||||
|
taskfile: ../common
|
||||||
|
dir: ../common
|
||||||
15
testdata/internal_task/Taskfile.yml
vendored
Normal 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!"
|
||||||
18
testdata/special_vars/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
included:
|
||||||
|
taskfile: ./included
|
||||||
|
dir: ./included
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- task: print
|
||||||
|
- task: included:print
|
||||||
|
|
||||||
|
print:
|
||||||
|
cmds:
|
||||||
|
- echo root/TASK={{.TASK}}
|
||||||
|
- echo root/ROOT_DIR={{.ROOT_DIR}}
|
||||||
|
- echo root/TASKFILE_DIR={{.TASKFILE_DIR}}
|
||||||
8
testdata/special_vars/included/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
print:
|
||||||
|
cmds:
|
||||||
|
- echo included/TASK={{.TASK}}
|
||||||
|
- echo included/ROOT_DIR={{.ROOT_DIR}}
|
||||||
|
- echo included/TASKFILE_DIR={{.TASKFILE_DIR}}
|
||||||
1
testdata/watcher_interval/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
src/*
|
||||||
16
testdata/watcher_interval/Taskfile.yaml
vendored
Normal 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
|
||||||
15
variables.go
@@ -1,10 +1,10 @@
|
|||||||
package task
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/internal/status"
|
"github.com/go-task/task/v3/internal/status"
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
@@ -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) {
|
func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskfile.Task, error) {
|
||||||
origTask, ok := e.Taskfile.Tasks[call.Task]
|
origTask, err := e.GetTask(call)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return nil, &taskNotFoundError{call.Task}
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var vars *taskfile.Vars
|
var vars *taskfile.Vars
|
||||||
var err error
|
|
||||||
if evaluateShVars {
|
if evaluateShVars {
|
||||||
vars, err = e.Compiler.GetVariables(origTask, call)
|
vars, err = e.Compiler.GetVariables(origTask, call)
|
||||||
} else {
|
} else {
|
||||||
@@ -50,6 +49,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
Label: r.Replace(origTask.Label),
|
Label: r.Replace(origTask.Label),
|
||||||
Desc: r.Replace(origTask.Desc),
|
Desc: r.Replace(origTask.Desc),
|
||||||
Summary: r.Replace(origTask.Summary),
|
Summary: r.Replace(origTask.Summary),
|
||||||
|
Aliases: origTask.Aliases,
|
||||||
Sources: r.ReplaceSlice(origTask.Sources),
|
Sources: r.ReplaceSlice(origTask.Sources),
|
||||||
Generates: r.ReplaceSlice(origTask.Generates),
|
Generates: r.ReplaceSlice(origTask.Generates),
|
||||||
Dir: r.Replace(origTask.Dir),
|
Dir: r.Replace(origTask.Dir),
|
||||||
@@ -57,6 +57,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
Env: nil,
|
Env: nil,
|
||||||
Silent: origTask.Silent,
|
Silent: origTask.Silent,
|
||||||
Interactive: origTask.Interactive,
|
Interactive: origTask.Interactive,
|
||||||
|
Internal: origTask.Internal,
|
||||||
Method: r.Replace(origTask.Method),
|
Method: r.Replace(origTask.Method),
|
||||||
Prefix: r.Replace(origTask.Prefix),
|
Prefix: r.Replace(origTask.Prefix),
|
||||||
IgnoreError: origTask.IgnoreError,
|
IgnoreError: origTask.IgnoreError,
|
||||||
@@ -68,8 +69,8 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if e.Dir != "" && !filepath.IsAbs(new.Dir) {
|
if e.Dir != "" {
|
||||||
new.Dir = filepath.Join(e.Dir, new.Dir)
|
new.Dir = filepathext.SmartJoin(e.Dir, new.Dir)
|
||||||
}
|
}
|
||||||
if new.Prefix == "" {
|
if new.Prefix == "" {
|
||||||
new.Prefix = new.Task
|
new.Prefix = new.Task
|
||||||
|
|||||||