Compare commits

..

93 Commits

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

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

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

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

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

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

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

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

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

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

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

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

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

Closes #780
2022-06-15 10:06:38 -03:00
76 changed files with 1558 additions and 517 deletions

View File

@@ -14,7 +14,7 @@ jobs:
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.18
go-version: 1.18.x
- uses: actions/checkout@v3

View File

@@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18.x
go-version: 1.19.x
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2

View File

@@ -13,7 +13,7 @@ jobs:
name: Test
strategy:
matrix:
go-version: [1.17.x, 1.18.x]
go-version: [1.18.x, 1.19.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{matrix.platform}}
steps:

1
.gitignore vendored
View File

@@ -30,3 +30,4 @@ tags
!/bin/.keep
/testdata/vars/v1
/tmp
node_modules

View File

@@ -17,6 +17,9 @@ build:
goarch: 386
env:
- CGO_ENABLED=0
mod_timestamp: '{{ .CommitTimestamp }}'
flags:
- -trimpath
ldflags:
- -s -w # Don't set main.version.

View File

@@ -1,12 +1,68 @@
# Changelog
## v3.16.0 - 2022-09-29
- Add `npm` as new installation method: `npm i -g @go-task/cli`
([#870](https://github.com/go-task/task/issues/870), [#871](https://github.com/go-task/task/pull/871), [npm package](https://www.npmjs.com/package/@go-task/cli)).
- Add support to marking tasks and includes as internal, which will hide them
from `--list` and `--list-all`
([#818](https://github.com/go-task/task/pull/818)).
## v3.15.2 - 2022-09-08
- Fix error when using variable in `env:` introduced in the previous release
([#858](https://github.com/go-task/task/issues/858), [#866](https://github.com/go-task/task/pull/866)).
- Fix handling of `CLI_ARGS` (`--`) in Bash completion
([#863](https://github.com/go-task/task/pull/863)).
- On zsh completion, add ability to replace `--list-all` with `--list` as
already possible on the Bash completion
([#861](https://github.com/go-task/task/pull/861)).
## v3.15.0 - 2022-09-03
- Add new special variables `ROOT_DIR` and `TASKFILE_DIR`. This was a highly
requested feature
([#215](https://github.com/go-task/task/issues/215), [#857](https://github.com/go-task/task/pull/857), [Documentation](https://taskfile.dev/api/#special-variables)).
- Follow symlinks on `sources`
([#826](https://github.com/go-task/task/issues/826), [#831](https://github.com/go-task/task/pull/831)).
- Improvements and fixes to Bash completion
([#835](https://github.com/go-task/task/pull/835), [#844](https://github.com/go-task/task/pull/844)).
## v3.14.1 - 2022-08-03
- Always resolve relative include paths relative to the including Taskfile
([#822](https://github.com/go-task/task/issues/822), [#823](https://github.com/go-task/task/pull/823)).
- Fix ZSH and PowerShell completions to consider all tasks instead of just the
public ones (those with descriptions)
([#803](https://github.com/go-task/task/pull/803)).
## v3.14.0 - 2022-07-08
- Add ability to override the `.task` directory location with the
`TASK_TEMP_DIR` environment variable.
- Allow to override Task colors using environment variables:
`TASK_COLOR_RESET`, `TASK_COLOR_BLUE`, `TASK_COLOR_GREEN`,
`TASK_COLOR_CYAN`, `TASK_COLOR_YELLOW`, `TASK_COLOR_MAGENTA`
and `TASK_COLOR_RED`
([#568](https://github.com/go-task/task/pull/568), [#792](https://github.com/go-task/task/pull/792)).
- Fixed bug when using the `output: group` mode where STDOUT and STDERR were
being print in separated blocks instead of in the right order
([#779](https://github.com/go-task/task/issues/779)).
- Starting on this release, ARM architecture binaries are been released to Snap
as well
([#795](https://github.com/go-task/task/issues/795)).
- i386 binaries won't be available anymore on Snap because Ubuntu removed the support
for this architecture.
- Upgrade mvdan.cc/sh, which fixes a bug with associative arrays
([#785](https://github.com/go-task/task/issues/785), [mvdan/sh#884](https://github.com/mvdan/sh/issues/884), [mvdan/sh#893](https://github.com/mvdan/sh/pull/893)).
## v3.13.0 - 2022-06-13
- Added `-n` as an alias to `--dry`
([#776](https://github.com/go-task/task/issues/776), [#777](https://github.com/go-task/task/pull/777)).
- Fix behavior of interrupt (SIGINT, SIGTERM) signals. Task will now give time
for the processes running to do cleanup work
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728)).
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728), [#769](https://github.com/go-task/task/pull/769)).
- Add new `--exit-code` (`-x`) flag that will pass-through the exit form the
command being ran
([#755](https://github.com/go-task/task/pull/755)).

View File

@@ -10,6 +10,6 @@
</p>
<p>
See <a href="https://taskfile.dev">taskfile.dev</a> for the documentation.
<a href="https://taskfile.dev/installation/">Installation</a> | <a href="https://taskfile.dev/usage/">Documentation</a> | <a href="https://twitter.com/taskfiledev">Twitter</a> | <a href="https://discord.gg/6TY36E39UK">Discord</a>
</p>
</div>

View File

@@ -65,15 +65,39 @@ tasks:
test:
desc: Runs test suite
deps: [install, sleepit:build]
deps: [install]
cmds:
- go test {{catLines .GO_PACKAGES}}
test:signals:
desc: Runs test suite with signals tests included
deps: [install, sleepit:build]
cmds:
- go test {{catLines .GO_PACKAGES}} -tags signals
test-release:
desc: Tests release process without publishing
cmds:
- goreleaser --snapshot --rm-dist
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:
cmds:
- echo '{{.GO_PACKAGES}}'

View File

@@ -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
_get_comp_words_by_ref -n : cur
local cur prev words cword
_init_completion -n : || return
case "$cur" in
--*)
local options
options="$(_parse_help task)"
mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur")
# Check for `--` within command-line and quit or strip suffix.
local i
for i in "${!words[@]}"; do
if [ "${words[$i]}" == "--" ]; then
# Do not complete words following `--` passed to CLI_ARGS.
[ $cword -gt $i ] && return
# Remove the words following `--` to not put --list in CLI_ARGS.
words=( "${words[@]:0:$i}" )
break
fi
done
# Handle special arguments of options.
case "$prev" in
-d|--dir)
_filedir -d
return $?
;;
*)
local tasks
tasks="$($GO_TASK_PROGNAME --list-all 2> /dev/null | awk 'NR>1 { sub(/:$/,"",$2); print $2 }')"
mapfile -t COMPREPLY < <(compgen -W "$tasks" -- "$cur")
-t|--taskfile)
_filedir yaml || return $?
_filedir yml
return $?
;;
-o|--output)
COMPREPLY=( $( compgen -W "interleaved group prefixed" -- $cur ) )
return 0
;;
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"
}
complete -F _go_task_completion $GO_TASK_PROGNAME
complete -F _task task

View File

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

View File

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

View File

@@ -9,9 +9,10 @@ tasks:
start:
desc: Start website
vars:
HOST: '{{default "localhost" .HOST}}'
PORT: '{{default "3001" .PORT}}'
cmds:
- npx docusaurus start --no-open --port={{.PORT}}
- npx docusaurus start --no-open --host={{.HOST}} --port={{.PORT}}
build:
desc: Build website

View File

@@ -15,7 +15,7 @@ task [--flags] [tasks...] [-- CLI_ARGS...]
:::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
:::
@@ -44,6 +44,34 @@ variable
| | `--version` | `bool` | `false` | Show Task version. |
| `-w` | `--watch` | `bool` | `false` | Enables watch of the given task. |
## Special Variables
There are some special variables that is available on the templating system:
| Var | Description |
| - | - |
| `CLI_ARGS` | Contain all extra arguments passed after `--` when calling Task through the CLI. |
| `TASK` | The name of the current task. |
| `ROOT_DIR` | The absolute path of the root Taskfile. |
| `TASKFILE_DIR` | The absolute path of the included Taskfile. |
| `CHECKSUM` | The checksum of the files listed in `sources`. Only available within the `status` prop and if method is set to `checksum`. |
| `TIMESTAMP` | The date object of the greatest timestamp of the files listes in `sources`. Only available within the `status` prop and if method is set to `timestamp`. |
## ENV
Some environment variables can be overriden to adjust Task behavior.
| ENV | Default | Description |
| - | - | - |
| `TASK_TEMP_DIR` | `.task` | Location of the temp dir. Can relative to the project like `tmp/task` or absolute like `/tmp/.task` or `~/.task`. |
| `TASK_COLOR_RESET` | `0` | Color used for white. |
| `TASK_COLOR_BLUE` | `34` | Color used for blue. |
| `TASK_COLOR_GREEN` | `32` | Color used for green. |
| `TASK_COLOR_CYAN` | `36` | Color used for cyan. |
| `TASK_COLOR_YELLOW` | `33` | Color used for yellow. |
| `TASK_COLOR_MAGENTA` | `35` | Color used for magenta. |
| `TASK_COLOR_RED` | `31` | Color used for red. |
## Schema
### Taskfile
@@ -66,9 +94,10 @@ variable
| 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. |
| `optional` | `bool` | `false` | If `true`, no errors will be thrown if the specified file does not exist. |
| `internal` | `bool` | `false` | If `true`, tasks will be omitted from both `--list` and `--list-all`. |
:::info
@@ -91,6 +120,7 @@ includes:
| `dir` | `string` | | The current directory which this task should run. |
| `method` | `string` | `checksum` | Method used by this task. Default to the one declared globally or `checksum`. Available options: `checksum`, `timestamp` and `none` |
| `silent` | `bool` | `false` | Skips some output for this task. Note that STDOUT and STDERR of the commands will still be redirected. |
| `internal` | `bool` | `false` | If `true`, omit this task from both `--list` and `--list-all`. |
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
| `prefix` | `string` | | Allows to override the prefix print before the STDOUT. Only relevant when using the `prefixed` output mode. |
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the commands. |
@@ -114,6 +144,9 @@ tasks:
foobar:
- echo "foo"
- echo "bar"
baz:
cmd: echo "baz"
```
:::

View File

@@ -5,13 +5,69 @@ sidebar_position: 6
# Changelog
## v3.16.0 - 2022-09-29
- Add `npm` as new installation method: `npm i -g @go-task/cli`
([#870](https://github.com/go-task/task/issues/870), [#871](https://github.com/go-task/task/pull/871), [npm package](https://www.npmjs.com/package/@go-task/cli)).
- Add support to marking tasks and includes as internal, which will hide them
from `--list` and `--list-all`
([#818](https://github.com/go-task/task/pull/818)).
## v3.15.2 - 2022-09-08
- Fix error when using variable in `env:` introduced in the previous release
([#858](https://github.com/go-task/task/issues/858), [#866](https://github.com/go-task/task/pull/866)).
- Fix handling of `CLI_ARGS` (`--`) in Bash completion
([#863](https://github.com/go-task/task/pull/863)).
- On zsh completion, add ability to replace `--list-all` with `--list` as
already possible on the Bash completion
([#861](https://github.com/go-task/task/pull/861)).
## v3.15.0 - 2022-09-03
- Add new special variables `ROOT_DIR` and `TASKFILE_DIR`. This was a highly
requested feature
([#215](https://github.com/go-task/task/issues/215), [#857](https://github.com/go-task/task/pull/857), [Documentation](https://taskfile.dev/api/#special-variables)).
- Follow symlinks on `sources`
([#826](https://github.com/go-task/task/issues/826), [#831](https://github.com/go-task/task/pull/831)).
- Improvements and fixes to Bash completion
([#835](https://github.com/go-task/task/pull/835), [#844](https://github.com/go-task/task/pull/844)).
## v3.14.1 - 2022-08-03
- Always resolve relative include paths relative to the including Taskfile
([#822](https://github.com/go-task/task/issues/822), [#823](https://github.com/go-task/task/pull/823)).
- Fix ZSH and PowerShell completions to consider all tasks instead of just the
public ones (those with descriptions)
([#803](https://github.com/go-task/task/pull/803)).
## v3.14.0 - 2022-07-08
- Add ability to override the `.task` directory location with the
`TASK_TEMP_DIR` environment variable.
- Allow to override Task colors using environment variables:
`TASK_COLOR_RESET`, `TASK_COLOR_BLUE`, `TASK_COLOR_GREEN`,
`TASK_COLOR_CYAN`, `TASK_COLOR_YELLOW`, `TASK_COLOR_MAGENTA`
and `TASK_COLOR_RED`
([#568](https://github.com/go-task/task/pull/568), [#792](https://github.com/go-task/task/pull/792)).
- Fixed bug when using the `output: group` mode where STDOUT and STDERR were
being print in separated blocks instead of in the right order
([#779](https://github.com/go-task/task/issues/779)).
- Starting on this release, ARM architecture binaries are been released to Snap
as well
([#795](https://github.com/go-task/task/issues/795)).
- i386 binaries won't be available anymore on Snap because Ubuntu removed the support
for this architecture.
- Upgrade mvdan.cc/sh, which fixes a bug with associative arrays
([#785](https://github.com/go-task/task/issues/785), [mvdan/sh#884](https://github.com/mvdan/sh/issues/884), [mvdan/sh#893](https://github.com/mvdan/sh/pull/893)).
## v3.13.0 - 2022-06-13
- Added `-n` as an alias to `--dry`
([#776](https://github.com/go-task/task/issues/776), [#777](https://github.com/go-task/task/pull/777)).
- Fix behavior of interrupt (SIGINT, SIGTERM) signals. Task will now give time
for the processes running to do cleanup work
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728)).
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728), [#769](https://github.com/go-task/task/pull/769)).
- Add new `--exit-code` (`-x`) flag that will pass-through the exit form the
command being ran
([#755](https://github.com/go-task/task/pull/755)).

View File

@@ -46,9 +46,11 @@ Some installation methods are maintained by third party:
- [GitHub Actions](https://github.com/arduino/setup-task)
by [@arduino](https://github.com/arduino)
- [AUR](https://aur.archlinux.org/packages/taskfile-git)
by [@kovetskiy](https://github.com/kovetskiy)
- [AUR](https://aur.archlinux.org/packages/go-task-bin)
by [@carlsmedstad](https://github.com/carlsmedstad)
- [Scoop](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json)
- [Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/)
- [NixOS](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/tools/go-task/default.nix)
## More

View File

@@ -42,11 +42,10 @@ This installation method is community owned.
### Scoop
If you're on Windows and have [Scoop][scoop] installed, use `extras` bucket
to install Task like:
If you're on Windows and have [Scoop][scoop] installed, getting
Task is as simple as running:
```cmd
scoop bucket add extras
scoop install task
```
@@ -56,15 +55,23 @@ may take some time until it's available on Scoop.
### AUR
If you're on Arch Linux you can install Task from
[AUR](https://aur.archlinux.org/packages/taskfile-git) using your favorite
[AUR](https://aur.archlinux.org/packages/go-task-bin) using your favorite
package manager such as `yay`, `pacaur` or `yaourt`:
```cmd
yay -S taskfile-git
yay -S go-task-bin
```
This installation method is community owned, but since it's `-git` version of
the package, it's always latest available version based on the Git repository.
Alternatively, there's
[this package](https://aur.archlinux.org/packages/go-task) which installs from
the source code instead of downloading the binary from the
[releases page](https://github.com/go-task/task/releases):
```cmd
yay -S go-task
```
This installation method is community owned.
### Fedora
@@ -90,6 +97,15 @@ nix-env -iA nixpkgs.go-task
This installation method is community owned. After a new release of Task, it
may take some time until it's available in [nixpkgs](https://github.com/NixOS/nixpkgs).
### npm
You can also use Node and npm to install Task by installing
[this package](https://www.npmjs.com/package/@go-task/cli).
```bash
npm install -g @go-task/cli
```
## Get The Binary
### Binary
@@ -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
easy generation of this script.
By default, it installs on the `./bin` directory relative to the working
directory:
```bash
# For Default Installation to ./bin with debug logging
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
# For Installation To /usr/local/bin for userwide access with debug logging
# May require sudo sh
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
```
:::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.
:::

View File

@@ -38,12 +38,12 @@ guide to check the full schema documentation and Task features.
## Features
- [Easy installation](installation.md): just download a single binary, add to
$PATH and you're done! Or you can also install using [Homebrew][homebrew],
[Snapcraft][snapcraft], or [Scoop][scoop] if you want;
`$PATH` and you're done! Or you can also install using [Homebrew][homebrew],
[Snapcraft][snapcraft], or [Scoop][scoop] if you want.
- Available on CIs: by adding [this simple command](installation.md#install-script)
to install on your CI script and you're done to use Task as part of your CI pipeline;
to install on your CI script and you're ready to use Task as part of your CI pipeline;
- Truly cross-platform: while most build tools only work well on Linux or macOS,
Task also supports Windows thanks to [this 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)
if a given set of files haven't changed since last run (based either on its
timestamp or content).
@@ -54,4 +54,4 @@ guide to check the full schema documentation and Task features.
[homebrew]: https://brew.sh/
[snapcraft]: https://snapcraft.io/
[scoop]: https://scoop.sh/
[sh]: https://mvdan.cc/sh
[sh]: https://github.com/mvdan/sh

View File

@@ -13,27 +13,35 @@ the `test-release` task of the Taskfile.
artifacts automatically when a new Git tag is pushed to master
(raw executables and DEB and RPM packages).
Since v3.15.0, raw executables can also be reproduced and verified locally by
checking out a specific tag and calling `goreleaser build`, using the Go version
defined in the above GitHub Actions.
# Homebrew
To release a new version on the [Homebrew tap][homebrewtap] edit the
[Formula/go-task.rb][gotaskrb] file, updating with the new version, download
URL and sha256.
Goreleaser will automatically push a new commit to the
[Formula/go-task.rb][gotaskrb] file in the [Homebrew tap][homebrewtap]
repository to release the new version.
# npm
To release to npm update the version in the [`package.json`][packagejson] file
and then run `task npm:publish` to push it.
# Snapcraft
The exception is the publishing of a new version of the
[snap package][snappackage]. This current require two steps after publishing
the binaries:
The [snap package][snappackage] requires to manual steps to release a new
version:
* Updating the current version on [snapcraft.yaml][snapcraftyaml];
* Moving both `i386` and `amd64` new artifacts to the stable channel on
the [Snapcraft dashboard][snapcraftdashboard]
* Updating the current version on [snapcraft.yaml][snapcraftyaml].
* Moving both `amd64`, `armhf` and `arm64` new artifacts to the stable channel on
the [Snapcraft dashboard][snapcraftdashboard].
# Scoop
Scoop is a community owned installation method. Scoop owners usually take care
of updating versions there by editing
[this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json).
Scoop is a command-line package manager for the Windows operating system.
Scoop package manifests are maintained by the community.
Scoop owners usually take care of updating versions there by editing [this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json).
If you think its Task version is outdated, open an issue to let us know.
# Nix
@@ -46,6 +54,7 @@ If you think its Task version is outdated, open an issue to let us know.
[goreleaser]: https://goreleaser.com/
[homebrewtap]: https://github.com/go-task/homebrew-tap
[gotaskrb]: https://github.com/go-task/homebrew-tap/blob/master/Formula/go-task.rb
[packagejson]: https://github.com/go-task/task/blob/master/package.json#L3
[snappackage]: https://github.com/go-task/snap
[snapcraftyaml]: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml#L2
[snapcraftdashboard]: https://snapcraft.io/task/releases

View File

@@ -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
from the `DockerTasks.yml` file.
Relative paths are resolved relative to the directory containing the including Taskfile.
### OS-specific Taskfiles
With `version: '2'`, task automatically includes any `Taskfile_{{OS}}.yml`
@@ -192,6 +194,22 @@ tasks:
- echo "This command can still be successfully executed if ./tests/Taskfile.yml does not exist"
```
### Internal includes
Includes marked as internal will set all the tasks of the included file to be
internal as well (see the [Internal tasks](#internal-tasks) section below).
This is useful when including utility tasks that are not intended to be used
directly by the user.
```yaml
version: '3'
includes:
tests:
taskfile: ./taskfiles/Utils.yml
internal: true
```
### Vars of included Taskfiles
You can also specify variables when including a Taskfile. This may be useful
@@ -215,12 +233,35 @@ includes:
:::info
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):
`MY_VAR: '{{.MY_VAR | default "my-default-value"}}'`.
:::
## Internal tasks
Internal tasks are tasks that cannot be called directly by the user. They will
not appear in the output when running `task --list|--list-all`. Other tasks may
call internal tasks in the usual way. This is useful for creating reusable,
function-like tasks that have no useful purpose on the command line.
```yaml
version: '3'
tasks:
build-image-1:
cmds:
- task: build-image
vars:
DOCKER_IMAGE: image-1
build-image:
internal: true
cmds:
- docker build -t {{.DOCKER_IMAGE}} .
```
## Task directory
By default, tasks will be executed in the directory where the Taskfile is
@@ -406,8 +447,6 @@ tasks:
Task will compare the checksum of the source files to determine if it's
necessary to run the task. If not, it will just print a message like
`Task "js" is up to date`.
You will probably want to ignore the `.task` folder in your `.gitignore` file
(It is there that Task stores the last checksum).
If you prefer this check to be made by the modification timestamp of the files,
instead of its checksum (content), just set the `method` property to `timestamp`.
@@ -423,11 +462,31 @@ tasks:
- ./*.go
generates:
- app{{exeExt}}
method: checksum
method: timestamp
```
:::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
Each task has only one checksum stored for its `sources`. If you want
to distinguish a task by any of its input variables, you can add those
variables as part of the task's label, and it will be considered a different
@@ -1058,7 +1117,7 @@ version: '3'
output:
group:
begin: '::begin::{{.TASK}}'
begin: '::group::{{.TASK}}'
end: '::endgroup::'
tasks:
@@ -1070,7 +1129,7 @@ tasks:
```bash
$ task default
::begin::default
::group::default
Hello, World!
::endgroup::
```

View File

@@ -1,10 +1,11 @@
// @ts-check
// Note: type annotations allow type checking and IDEs autocompletion
const lightCodeTheme = require('prism-react-renderer/themes/github');
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
const lightCodeTheme = require('./src/themes/prismLight');
const darkCodeTheme = require('./src/themes/prismDark');
const GITHUB_URL = 'https://github.com/go-task/task';
const TWITTER_URL = 'https://twitter.com/taskfiledev';
const DISCORD_URL = 'https://discord.gg/6TY36E39UK';
/** @type {import('@docusaurus/types').Config} */
@@ -100,6 +101,11 @@ const config = {
label: 'GitHub',
position: 'right'
},
{
href: TWITTER_URL,
label: 'Twitter',
position: 'right'
},
{
href: DISCORD_URL,
label: 'Discord',
@@ -134,6 +140,10 @@ const config = {
label: 'GitHub',
href: GITHUB_URL
},
{
label: 'Twitter',
href: TWITTER_URL
},
{
label: 'Discord',
href: DISCORD_URL

View File

@@ -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 {
--ifm-font-family-base: Roboto, system-ui, -apple-system, Segoe UI, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
--ifm-font-family-monospace: 'Roboto Mono', SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
--ifm-color-primary: #43ABA2 ;
--ifm-color-primary-dark: #3AB2A6;
--ifm-color-primary-darker: #32B8AB;
@@ -11,13 +16,6 @@
}
[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);
}

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,5 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 375 375" width="500" height="500">
<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>
<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>

Before

Width:  |  Height:  |  Size: 446 B

After

Width:  |  Height:  |  Size: 435 B

View File

@@ -1,3 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 375 375" width="500" height="500">
<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>
<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>

Before

Width:  |  Height:  |  Size: 365 B

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

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

View File

@@ -1577,33 +1577,46 @@
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/gen-mapping@^0.3.0":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9"
integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==
version "0.3.2"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
dependencies:
"@jridgewell/set-array" "^1.0.0"
"@jridgewell/set-array" "^1.0.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@^3.0.3":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe"
integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==
version "3.1.0"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
"@jridgewell/set-array@^1.0.0":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea"
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":
version "1.4.13"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c"
integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==
version "1.4.14"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/trace-mapping@^0.3.9":
version "0.3.13"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea"
integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==
version "0.3.14"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed"
integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==
dependencies:
"@jridgewell/resolve-uri" "^3.0.3"
"@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"
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:
version "4.5.0"
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"
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:
version "1.1.5"
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.10.0, terser@^5.7.2:
version "5.13.1"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.13.1.tgz#66332cdc5a01b04a224c9fad449fc1a18eaa1799"
integrity sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA==
version "5.14.2"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10"
integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==
dependencies:
"@jridgewell/source-map" "^0.3.2"
acorn "^8.5.0"
commander "^2.20.0"
source-map "~0.8.0-beta.0"
source-map-support "~0.5.20"
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"
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:
version "0.0.3"
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"
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:
version "4.5.0"
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"
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:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"

View File

@@ -17,7 +17,15 @@ type taskNotFoundError struct {
}
func (err *taskNotFoundError) Error() string {
return fmt.Sprintf(`task: Task "%s" not found`, err.taskName)
return fmt.Sprintf(`task: Task %q not found`, err.taskName)
}
type taskInternalError struct {
taskName string
}
func (err *taskInternalError) Error() string {
return fmt.Sprintf(`task: Task "%s" is internal`, err.taskName)
}
type TaskRunError struct {
@@ -26,7 +34,7 @@ type TaskRunError struct {
}
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 {
@@ -46,7 +54,7 @@ type MaximumTaskCallExceededError struct {
func (e *MaximumTaskCallExceededError) Error() string {
return fmt.Sprintf(
`task: maximum task call exceeded (%d) for task "%s": probably an cyclic dep or infinite loop`,
`task: maximum task call exceeded (%d) for task %q: probably an cyclic dep or infinite loop`,
MaximumTaskCall,
e.task,
)

9
go.mod
View File

@@ -8,10 +8,10 @@ require (
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/radovskyb/watcher v1.0.7
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.2
github.com/stretchr/testify v1.8.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
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 (
@@ -19,8 +19,9 @@ require (
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
go 1.17
go 1.18

37
go.sum
View File

@@ -1,30 +1,17 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/frankban/quicktest v1.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/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@@ -34,41 +21,35 @@ github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To=
github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/xerrors v0.0.0-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 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
mvdan.cc/sh/v3 v3.5.1 h1:hmP3UOw4f+EYexsJjFxvU38+kn+V/s2CclXHanIBkmQ=
mvdan.cc/sh/v3 v3.5.1/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899 h1:nwm4t5PtLlFd/H342GP50CtYf7vyMCOZkPx3g9shO0c=
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=

View File

@@ -52,7 +52,9 @@ func (e *Executor) printTasks(listAll bool) {
func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
for _, task := range e.Taskfile.Tasks {
tasks = append(tasks, task)
if !task.Internal {
tasks = append(tasks, task)
}
}
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
return
@@ -61,7 +63,7 @@ func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
for _, task := range e.Taskfile.Tasks {
if task.Desc != "" {
if !task.Internal && task.Desc != "" {
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
if err == nil {
task = compiledTask
@@ -92,7 +94,7 @@ func (e *Executor) ListTaskNames(allTasks bool) {
// create a string slice from all map values (*taskfile.Task)
s := make([]string, 0, len(e.Taskfile.Tasks))
for _, t := range e.Taskfile.Tasks {
if allTasks || t.Desc != "" {
if (allTasks || t.Desc != "") && !t.Internal {
s = append(s, strings.TrimRight(t.Task, ":"))
}
}

View File

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

View File

@@ -4,12 +4,12 @@ import (
"bytes"
"context"
"fmt"
"path/filepath"
"strings"
"sync"
"github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
@@ -44,7 +44,13 @@ func (c *CompilerV3) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*ta
func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
result := compiler.GetEnviron()
if t != nil {
result.Set("TASK", taskfile.Var{Static: t.Task})
specialVars, err := c.getSpecialVars(t)
if err != nil {
return nil, err
}
for k, v := range specialVars {
result.Set(k, taskfile.Var{Static: v})
}
}
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
@@ -83,9 +89,7 @@ func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluat
if err := tr.Err(); err != nil {
return nil, err
}
if !filepath.IsAbs(dir) {
dir = filepath.Join(c.Dir, dir)
}
dir = filepathext.SmartJoin(c.Dir, dir)
taskRangeFunc = getRangeFunc(dir)
}
@@ -167,3 +171,23 @@ func (c *CompilerV3) ResetCache() {
c.dynamicCache = nil
}
func (c *CompilerV3) getSpecialVars(t *taskfile.Task) (map[string]string, error) {
taskfileDir, err := c.getTaskfileDir(t)
if err != nil {
return nil, err
}
return map[string]string{
"TASK": t.Task,
"ROOT_DIR": c.Dir,
"TASKFILE_DIR": taskfileDir,
}, nil
}
func (c *CompilerV3) getTaskfileDir(t *taskfile.Task) (string, error) {
if t.IncludedTaskfile != nil {
return t.IncludedTaskfile.FullDirPath()
}
return c.Dir, nil
}

View File

@@ -73,7 +73,7 @@ func IsExitError(err error) bool {
// if available.
func Expand(s string) (string, error) {
s = filepath.ToSlash(s)
s = strings.Replace(s, " ", `\ `, -1)
s = strings.ReplaceAll(s, " ", `\ `)
fields, err := shell.Fields(s, nil)
if err != nil {
return "", err

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,9 +15,11 @@ type Templater 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.
func BuildFor(o *taskfile.Output) (Output, error) {
switch o.Name {

View File

@@ -16,7 +16,7 @@ import (
func TestInterleaved(t *testing.T) {
var b bytes.Buffer
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")
assert.Equal(t, "foo\nbar\n", b.String())
@@ -27,14 +27,19 @@ func TestInterleaved(t *testing.T) {
func TestGroup(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Group{}
var w = o.WrapWriter(&b, "", 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())
fmt.Fprintln(w, "baz")
fmt.Fprintln(stdErr, "err\nerr")
assert.Equal(t, "", b.String())
assert.NoError(t, w.Close())
assert.Equal(t, "foo\nbar\nbaz\n", b.String())
fmt.Fprintln(stdOut, "out")
assert.Equal(t, "", b.String())
fmt.Fprintln(stdErr, "err")
assert.Equal(t, "", b.String())
assert.NoError(t, cleanup())
assert.Equal(t, "out\nout\nerr\nerr\nout\nerr\n", b.String())
}
func TestGroupWithBeginEnd(t *testing.T) {
@@ -53,19 +58,19 @@ func TestGroupWithBeginEnd(t *testing.T) {
}
t.Run("simple", func(t *testing.T) {
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")
assert.Equal(t, "", b.String())
fmt.Fprintln(w, "baz")
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())
})
t.Run("no output", func(t *testing.T) {
var b bytes.Buffer
var w = o.WrapWriter(&b, "", &tmpl).(io.WriteCloser)
assert.NoError(t, w.Close())
var _, _, cleanup = o.WrapWriter(&b, io.Discard, "", &tmpl)
assert.NoError(t, cleanup())
assert.Equal(t, "", b.String())
})
}
@@ -73,7 +78,7 @@ func TestGroupWithBeginEnd(t *testing.T) {
func TestPrefixed(t *testing.T) {
var b bytes.Buffer
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) {
b.Reset()
@@ -82,6 +87,7 @@ func TestPrefixed(t *testing.T) {
assert.Equal(t, "[prefix] foo\n[prefix] bar\n", b.String())
fmt.Fprintln(w, "baz")
assert.Equal(t, "[prefix] foo\n[prefix] bar\n[prefix] baz\n", b.String())
assert.NoError(t, cleanup())
})
t.Run("multiple writes for a single line", func(t *testing.T) {
@@ -92,7 +98,7 @@ func TestPrefixed(t *testing.T) {
assert.Equal(t, "", b.String())
}
assert.NoError(t, w.Close())
assert.NoError(t, cleanup())
assert.Equal(t, "[prefix] Test!\n", b.String())
})
}

View File

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

View File

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

View File

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

View File

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

32
package-lock.json generated Normal file
View File

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

34
package.json Normal file
View File

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

299
setup.go Normal file
View File

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

View File

@@ -9,7 +9,7 @@ import (
)
// 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.
func (e *Executor) InterceptInterruptSignals() {
ch := make(chan os.Signal, 3)

View File

@@ -1,5 +1,5 @@
//go:build !windows
// +build !windows
//go:build signals
// +build signals
// This file contains tests for signal handling on Unix.
// Based on code from https://github.com/marco-m/timeit

View File

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

226
task.go
View File

@@ -2,7 +2,6 @@ package task
import (
"context"
"errors"
"fmt"
"io"
"os"
@@ -10,15 +9,12 @@ import (
"sync/atomic"
"github.com/go-task/task/v3/internal/compiler"
compilerv2 "github.com/go-task/task/v3/internal/compiler/v2"
compilerv3 "github.com/go-task/task/v3/internal/compiler/v3"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/internal/summary"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
"github.com/go-task/task/v3/taskfile/read"
"golang.org/x/sync/errgroup"
)
@@ -34,6 +30,7 @@ type Executor struct {
Taskfile *taskfile.Taskfile
Dir string
TempDir string
Entrypoint string
Force bool
Watch bool
@@ -67,11 +64,16 @@ type Executor struct {
func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
// check if given tasks exist
for _, c := range calls {
if _, ok := e.Taskfile.Tasks[c.Task]; !ok {
t, ok := e.Taskfile.Tasks[c.Task]
if !ok {
// FIXME: move to the main package
e.ListTasksWithDesc()
return &taskNotFoundError{taskName: c.Task}
}
if t.Internal {
e.ListTasksWithDesc()
return &taskInternalError{taskName: c.Task}
}
}
if e.Summary {
@@ -104,203 +106,6 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
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
func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
t, err := e.CompiledTask(call)
@@ -381,7 +186,7 @@ func (e *Executor) mkdir(t *taskfile.Task) error {
defer mutex.Unlock()
if _, err := os.Stat(t.Dir); os.IsNotExist(err) {
if err := os.MkdirAll(t.Dir, 0755); err != nil {
if err := os.MkdirAll(t.Dir, 0o755); err != nil {
return err
}
}
@@ -449,19 +254,10 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
if err != nil {
return fmt.Errorf("task: failed to get variables: %w", err)
}
stdOut := outputWrapper.WrapWriter(e.Stdout, t.Prefix, outputTemplater)
stdErr := outputWrapper.WrapWriter(e.Stderr, t.Prefix, outputTemplater)
stdOut, stdErr, close := outputWrapper.WrapWriter(e.Stdout, e.Stderr, t.Prefix, outputTemplater)
defer func() {
if _, ok := stdOut.(*os.File); !ok {
if closer, ok := stdOut.(io.Closer); ok {
closer.Close()
}
}
if _, ok := stdErr.(*os.File); !ok {
if closer, ok := stdErr.(io.Closer); ok {
closer.Close()
}
if err := close(); err != nil {
e.Logger.Errf(logger.Red, "task: unable to close writter: %v", err)
}
}()

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,7 @@ import (
"gopkg.in/yaml.v3"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
)
@@ -44,7 +44,8 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
}
readerNode.Dir = d
}
path, err := exists(filepath.Join(readerNode.Dir, readerNode.Entrypoint))
path, err := exists(filepathext.SmartJoin(readerNode.Dir, readerNode.Entrypoint))
if err != nil {
return nil, err
}
@@ -60,6 +61,16 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
return nil, err
}
// Annotate any included Taskfile reference with a base directory for resolving relative paths
_ = t.Includes.Range(func(key string, includedFile taskfile.IncludedTaskfile) error {
// Set the base directory for resolving relative paths, but only if not already set
if includedFile.BaseDir == "" {
includedFile.BaseDir = readerNode.Dir
t.Includes.Set(key, includedFile)
}
return nil
})
err = t.Includes.Range(func(namespace string, includedTask taskfile.IncludedTaskfile) error {
if v >= 3.0 {
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
@@ -67,21 +78,21 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
Taskfile: tr.Replace(includedTask.Taskfile),
Dir: tr.Replace(includedTask.Dir),
Optional: includedTask.Optional,
Internal: includedTask.Internal,
AdvancedImport: includedTask.AdvancedImport,
Vars: includedTask.Vars,
BaseDir: includedTask.BaseDir,
}
if err := tr.Err(); err != nil {
return err
}
}
path, err := execext.Expand(includedTask.Taskfile)
path, err := includedTask.FullTaskfilePath()
if err != nil {
return err
}
if !filepath.IsAbs(path) {
path = filepath.Join(readerNode.Dir, path)
}
path, err = exists(path)
if err != nil {
if includedTask.Optional {
@@ -114,27 +125,31 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
}
if includedTask.AdvancedImport {
dir, err := includedTask.FullDirPath()
if err != nil {
return err
}
for k, v := range includedTaskfile.Vars.Mapping {
o := v
o.Dir = filepath.Join(readerNode.Dir, includedTask.Dir)
o.Dir = dir
includedTaskfile.Vars.Mapping[k] = o
}
for k, v := range includedTaskfile.Env.Mapping {
o := v
o.Dir = filepath.Join(readerNode.Dir, includedTask.Dir)
o.Dir = dir
includedTaskfile.Env.Mapping[k] = o
}
for _, task := range includedTaskfile.Tasks {
if !filepath.IsAbs(task.Dir) {
task.Dir = filepath.Join(includedTask.Dir, task.Dir)
}
task.Dir = filepathext.SmartJoin(dir, task.Dir)
task.IncludeVars = includedTask.Vars
task.IncludedTaskfileVars = includedTaskfile.Vars
task.IncludedTaskfile = &includedTask
}
}
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
if err = taskfile.Merge(t, includedTaskfile, includedTask.Internal, namespace); err != nil {
return err
}
return nil
@@ -144,13 +159,13 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
}
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 {
osTaskfile, err := readTaskfile(path)
if err != nil {
return nil, err
}
if err = taskfile.Merge(t, osTaskfile); err != nil {
if err = taskfile.Merge(t, osTaskfile, false); err != nil {
return nil, err
}
}
@@ -186,7 +201,7 @@ func exists(path string) (string, error) {
}
for _, n := range defaultTaskfiles {
fpath := filepath.Join(path, n)
fpath := filepathext.SmartJoin(path, n)
if _, err := os.Stat(fpath); err == nil {
return fpath, nil
}
@@ -203,14 +218,14 @@ func checkCircularIncludes(node *ReaderNode) error {
return errors.New("task: failed to check for include cycle: node.Parent was nil")
}
var curNode = node
var basePath = filepath.Join(node.Dir, node.Entrypoint)
var basePath = filepathext.SmartJoin(node.Dir, node.Entrypoint)
for curNode.Parent != nil {
curNode = curNode.Parent
curPath := filepath.Join(curNode.Dir, curNode.Entrypoint)
curPath := filepathext.SmartJoin(curNode.Dir, curNode.Entrypoint)
if curPath == basePath {
return fmt.Errorf("task: include cycle detected between %s <--> %s",
curPath,
filepath.Join(node.Parent.Dir, node.Parent.Entrypoint),
filepathext.SmartJoin(node.Parent.Dir, node.Parent.Entrypoint),
)
}
}

View File

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

View File

@@ -20,12 +20,14 @@ type Task struct {
Env *Vars
Silent bool
Interactive bool
Internal bool
Method string
Prefix string
IgnoreError bool
Run string
IncludeVars *Vars
IncludedTaskfileVars *Vars
IncludedTaskfile *IncludedTaskfile
}
func (t *Task) Name() string {
@@ -63,6 +65,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
Env *Vars
Silent bool
Interactive bool
Internal bool
Method string
Prefix string
IgnoreError bool `yaml:"ignore_error"`
@@ -85,6 +88,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
t.Env = task.Env
t.Silent = task.Silent
t.Interactive = task.Interactive
t.Internal = task.Internal
t.Method = task.Method
t.Prefix = task.Prefix
t.IgnoreError = task.IgnoreError

View File

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

View File

@@ -0,0 +1 @@
shared file source

View File

@@ -0,0 +1 @@
inner shared file source

View File

@@ -0,0 +1 @@
file source

View File

@@ -0,0 +1 @@
../shared

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

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

View File

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

10
testdata/includes_rel_path/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
version: '3'
includes:
included:
taskfile: ./included
dir: ./included
common:
taskfile: ./common
dir: ./common

View File

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

View File

@@ -0,0 +1,6 @@
version: '3'
includes:
common:
taskfile: ../common
dir: ../common

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

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

18
testdata/special_vars/Taskfile.yml vendored Normal file
View 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}}

View 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}}

View File

@@ -1,10 +1,10 @@
package task
import (
"path/filepath"
"strings"
"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/templater"
"github.com/go-task/task/v3/taskfile"
@@ -57,6 +57,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
Env: nil,
Silent: origTask.Silent,
Interactive: origTask.Interactive,
Internal: origTask.Internal,
Method: r.Replace(origTask.Method),
Prefix: r.Replace(origTask.Prefix),
IgnoreError: origTask.IgnoreError,
@@ -68,8 +69,8 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
if err != nil {
return nil, err
}
if e.Dir != "" && !filepath.IsAbs(new.Dir) {
new.Dir = filepath.Join(e.Dir, new.Dir)
if e.Dir != "" {
new.Dir = filepathext.SmartJoin(e.Dir, new.Dir)
}
if new.Prefix == "" {
new.Prefix = new.Task