Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
200ba4ed04 | ||
|
|
1e8939dd58 | ||
|
|
f45dd11e53 | ||
|
|
1a0cc1d64d | ||
|
|
421cb522d9 | ||
|
|
1b18b041d6 | ||
|
|
8788703ac6 | ||
|
|
b6c25e3ad9 | ||
|
|
73eaa68cd1 | ||
|
|
beb927f7b4 | ||
|
|
cdc969cd4e | ||
|
|
2a67499f12 | ||
|
|
6a3cc79daa | ||
|
|
97d4a947ee | ||
|
|
e0e47ad9a0 | ||
|
|
b08eac58e9 | ||
|
|
c2148a359d | ||
|
|
c172185a24 | ||
|
|
1140a5c4ae | ||
|
|
3cc378c960 | ||
|
|
9b3a961303 | ||
|
|
d048555149 | ||
|
|
7533858a52 | ||
|
|
c4e10ef0aa | ||
|
|
c20842e7cd | ||
|
|
6cfdb21313 | ||
|
|
e396f4d06f | ||
|
|
47c1bb6a5b | ||
|
|
adfb0b513e | ||
|
|
98d78b9d8a | ||
|
|
a1c32a56ea | ||
|
|
6584bcf87f | ||
|
|
7ac75af622 | ||
|
|
b3c283b282 | ||
|
|
c2615dd746 | ||
|
|
789518f70d | ||
|
|
ad3008d855 | ||
|
|
3da426603f | ||
|
|
8d26e34b0a | ||
|
|
110d1d7245 | ||
|
|
f7384623df | ||
|
|
5d24e166ab | ||
|
|
f787937a30 |
2
.github/workflows/lint.yml
vendored
@@ -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
|
||||
|
||||
|
||||
2
.github/workflows/release.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/test.yml
vendored
@@ -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:
|
||||
|
||||
@@ -17,6 +17,9 @@ build:
|
||||
goarch: 386
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w # Don't set main.version.
|
||||
|
||||
|
||||
18
CHANGELOG.md
@@ -1,5 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## 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), [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
|
||||
|
||||
@@ -1,26 +1,43 @@
|
||||
#!/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")
|
||||
# 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=( $( "${COMP_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
|
||||
|
||||
@@ -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 + " " }
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ function __task_list() {
|
||||
(( enabled )) || return 0
|
||||
|
||||
scripts=()
|
||||
for item in "${(@)${(f)$("${cmd[@]}" --list)}[2,-1]#\* }"; do
|
||||
for item in "${(@)${(f)$("${cmd[@]}" --list-all)}[2,-1]#\* }"; do
|
||||
task="${item%%:[[:space:]]*}"
|
||||
desc="${item##[^[:space:]]##[[:space:]]##}"
|
||||
scripts+=( "${task//:/\\:}:$desc" )
|
||||
|
||||
@@ -44,6 +44,19 @@ 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.
|
||||
@@ -81,7 +94,7 @@ Some environment variables can be overriden to adjust Task behavior.
|
||||
|
||||
| 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. |
|
||||
|
||||
@@ -129,6 +142,9 @@ tasks:
|
||||
foobar:
|
||||
- echo "foo"
|
||||
- echo "bar"
|
||||
|
||||
baz:
|
||||
cmd: echo "baz"
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
@@ -5,6 +5,24 @@ sidebar_position: 6
|
||||
|
||||
# Changelog
|
||||
|
||||
## 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), [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
|
||||
|
||||
@@ -106,19 +106,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.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ guide to check the full schema documentation and Task features.
|
||||
`$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].
|
||||
- Great for code generation: you can easily [prevent a task from running](/usage#prevent-unnecessary-work)
|
||||
|
||||
@@ -31,9 +31,9 @@ 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
|
||||
|
||||
@@ -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`
|
||||
@@ -215,7 +217,7 @@ 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"}}'`.
|
||||
|
||||
|
||||
BIN
docs/static/img/favicon.ico
vendored
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 170 KiB |
BIN
docs/static/img/logo.png
vendored
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 13 KiB |
6
docs/static/img/logo.svg
vendored
@@ -1,5 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 375 375" width="500" height="500">
|
||||
<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 |
4
docs/static/img/logo_mono.svg
vendored
@@ -1,3 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 375 375" width="500" height="500">
|
||||
<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 |
BIN
docs/static/img/og-image.png
vendored
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 19 KiB |
BIN
docs/static/img/pix.png
vendored
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -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"
|
||||
|
||||
@@ -17,7 +17,7 @@ type taskNotFoundError struct {
|
||||
}
|
||||
|
||||
func (err *taskNotFoundError) Error() string {
|
||||
return fmt.Sprintf(`task: Task "%s" not found`, err.taskName)
|
||||
return fmt.Sprintf(`task: Task %q not found`, err.taskName)
|
||||
}
|
||||
|
||||
type TaskRunError struct {
|
||||
@@ -26,7 +26,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 +46,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,
|
||||
)
|
||||
|
||||
4
go.mod
@@ -19,9 +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
|
||||
|
||||
25
go.sum
@@ -1,30 +1,17 @@
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||
github.com/creack/pty v1.1.17/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,14 +21,11 @@ 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=
|
||||
@@ -54,23 +38,18 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cO
|
||||
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/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.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=
|
||||
|
||||
7
init.go
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
14
internal/filepathext/filepathext.go
Normal 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)
|
||||
}
|
||||
@@ -8,6 +8,8 @@ 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
|
||||
@@ -43,8 +45,8 @@ func (c *Checksum) IsUpToDate() (bool, error) {
|
||||
}
|
||||
|
||||
if !c.Dry {
|
||||
_ = os.MkdirAll(filepath.Join(c.TempDir, "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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
28
setup.go
@@ -12,6 +12,7 @@ import (
|
||||
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"
|
||||
@@ -19,6 +20,10 @@ import (
|
||||
)
|
||||
|
||||
func (e *Executor) Setup() error {
|
||||
if err := e.setCurrentDir(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := e.readTaskfile(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -52,6 +57,23 @@ func (e *Executor) Setup() error {
|
||||
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{
|
||||
@@ -69,7 +91,7 @@ func (e *Executor) setupTempDir() error {
|
||||
}
|
||||
|
||||
if os.Getenv("TASK_TEMP_DIR") == "" {
|
||||
e.TempDir = filepath.Join(e.Dir, ".task")
|
||||
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 {
|
||||
@@ -77,9 +99,9 @@ func (e *Executor) setupTempDir() error {
|
||||
}
|
||||
projectDir, _ := filepath.Abs(e.Dir)
|
||||
projectName := filepath.Base(projectDir)
|
||||
e.TempDir = filepath.Join(tempDir, projectName)
|
||||
e.TempDir = filepathext.SmartJoin(tempDir, projectName)
|
||||
} else {
|
||||
e.TempDir = filepath.Join(e.Dir, os.Getenv("TASK_TEMP_DIR"))
|
||||
e.TempDir = filepathext.SmartJoin(e.Dir, os.Getenv("TASK_TEMP_DIR"))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -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)
|
||||
|
||||
2
task.go
@@ -181,7 +181,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
|
||||
}
|
||||
}
|
||||
|
||||
165
task_test.go
@@ -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,12 +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: filepath.Join(fct.Dir, ".task"),
|
||||
TempDir: filepathext.SmartJoin(fct.Dir, ".task"),
|
||||
Entrypoint: fct.Entrypoint,
|
||||
Stdout: io.Discard,
|
||||
Stderr: io.Discard,
|
||||
@@ -52,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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -162,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"
|
||||
@@ -234,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{
|
||||
@@ -246,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)
|
||||
}
|
||||
@@ -262,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)
|
||||
@@ -272,7 +307,7 @@ func TestStatus(t *testing.T) {
|
||||
var buff bytes.Buffer
|
||||
e := &task.Executor{
|
||||
Dir: dir,
|
||||
TempDir: filepath.Join(dir, ".task"),
|
||||
TempDir: filepathext.SmartJoin(dir, ".task"),
|
||||
Stdout: &buff,
|
||||
Stderr: &buff,
|
||||
Silent: true,
|
||||
@@ -282,7 +317,7 @@ func TestStatus(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -359,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)
|
||||
@@ -378,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)
|
||||
|
||||
@@ -415,16 +450,16 @@ 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,
|
||||
TempDir: filepath.Join(dir, ".task"),
|
||||
TempDir: filepathext.SmartJoin(dir, ".task"),
|
||||
Stdout: &buff,
|
||||
Stderr: &buff,
|
||||
}
|
||||
@@ -432,7 +467,7 @@ func TestStatusChecksum(t *testing.T) {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -576,13 +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: filepath.Join(dir, ".task"),
|
||||
TempDir: filepathext.SmartJoin(dir, ".task"),
|
||||
Stdout: &buff,
|
||||
Stderr: &buff,
|
||||
Silent: false,
|
||||
@@ -593,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()
|
||||
@@ -604,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 {
|
||||
@@ -693,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
|
||||
@@ -718,12 +753,12 @@ 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,
|
||||
TempDir: filepath.Join(dir, ".task"),
|
||||
TempDir: filepathext.SmartJoin(dir, ".task"),
|
||||
Stdout: io.Discard,
|
||||
Stderr: io.Discard,
|
||||
Dry: true,
|
||||
@@ -774,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{
|
||||
@@ -784,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) {
|
||||
@@ -852,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) {
|
||||
@@ -890,6 +938,26 @@ 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 TestSupportedFileNames(t *testing.T) {
|
||||
fileNames := []string{
|
||||
"Taskfile.yml",
|
||||
@@ -926,12 +994,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())
|
||||
@@ -1299,3 +1367,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)
|
||||
}
|
||||
|
||||
@@ -2,17 +2,23 @@ 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
|
||||
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
|
||||
@@ -107,3 +113,31 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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,8 @@ func Dotenv(c compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.V
|
||||
|
||||
for _, dotEnvPath := range tf.Dotenv {
|
||||
dotEnvPath = tr.Replace(dotEnvPath)
|
||||
dotEnvPath = filepathext.SmartJoin(dir, dotEnvPath)
|
||||
|
||||
if !filepath.IsAbs(dotEnvPath) {
|
||||
dotEnvPath = filepath.Join(dir, dotEnvPath)
|
||||
}
|
||||
if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
@@ -69,19 +80,18 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
|
||||
Optional: includedTask.Optional,
|
||||
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,23 +124,27 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +158,7 @@ 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 {
|
||||
@@ -186,7 +200,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 +217,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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -26,6 +26,7 @@ type Task struct {
|
||||
Run string
|
||||
IncludeVars *Vars
|
||||
IncludedTaskfileVars *Vars
|
||||
IncludedTaskfile *IncludedTaskfile
|
||||
}
|
||||
|
||||
func (t *Task) Name() string {
|
||||
|
||||
17
testdata/evaluate_symlinks_in_paths/Taskfile.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
sources:
|
||||
- src/**/*
|
||||
cmds:
|
||||
- echo "some job"
|
||||
|
||||
test-sym:
|
||||
cmds:
|
||||
- echo "shared file source changed" > src/shared/b
|
||||
|
||||
reset:
|
||||
cmds:
|
||||
- echo "shared file source" > src/shared/b
|
||||
- echo "file source" > src/a
|
||||
1
testdata/evaluate_symlinks_in_paths/shared/b
vendored
Normal file
@@ -0,0 +1 @@
|
||||
shared file source
|
||||
1
testdata/evaluate_symlinks_in_paths/shared/inner_shared/c
vendored
Normal file
@@ -0,0 +1 @@
|
||||
inner shared file source
|
||||
1
testdata/evaluate_symlinks_in_paths/src/a
vendored
Normal file
@@ -0,0 +1 @@
|
||||
file source
|
||||
1
testdata/evaluate_symlinks_in_paths/src/shared
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../shared
|
||||
10
testdata/includes_rel_path/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
version: '3'
|
||||
|
||||
includes:
|
||||
included:
|
||||
taskfile: ./included
|
||||
dir: ./included
|
||||
|
||||
common:
|
||||
taskfile: ./common
|
||||
dir: ./common
|
||||
4
testdata/includes_rel_path/common/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
pwd: pwd
|
||||
6
testdata/includes_rel_path/included/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
version: '3'
|
||||
|
||||
includes:
|
||||
common:
|
||||
taskfile: ../common
|
||||
dir: ../common
|
||||
18
testdata/special_vars/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
version: '3'
|
||||
|
||||
includes:
|
||||
included:
|
||||
taskfile: ./included
|
||||
dir: ./included
|
||||
|
||||
tasks:
|
||||
default:
|
||||
cmds:
|
||||
- task: print
|
||||
- task: included:print
|
||||
|
||||
print:
|
||||
cmds:
|
||||
- echo root/TASK={{.TASK}}
|
||||
- echo root/ROOT_DIR={{.ROOT_DIR}}
|
||||
- echo root/TASKFILE_DIR={{.TASKFILE_DIR}}
|
||||
8
testdata/special_vars/included/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
print:
|
||||
cmds:
|
||||
- echo included/TASK={{.TASK}}
|
||||
- echo included/ROOT_DIR={{.ROOT_DIR}}
|
||||
- echo included/TASKFILE_DIR={{.TASKFILE_DIR}}
|
||||
@@ -1,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"
|
||||
@@ -68,8 +68,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
|
||||
|
||||