Compare commits

...

53 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
54 changed files with 900 additions and 147 deletions

1
.gitignore vendored
View File

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

View File

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

View File

@@ -1,5 +1,33 @@
# Changelog # 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 ## v3.14.1 - 2022-08-03
- Always resolve relative include paths relative to the including Taskfile - Always resolve relative include paths relative to the including Taskfile

View File

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

View File

@@ -80,6 +80,24 @@ tasks:
cmds: cmds:
- goreleaser --snapshot --rm-dist - goreleaser --snapshot --rm-dist
docs:changelog:
desc: Copy CHANGELOG.md to the documentation website
vars:
FILE: docs/docs/changelog.md
cmds:
- rm {{.FILE}}
- 'echo "---" >> {{.FILE}}'
- 'echo "slug: /changelog/" >> {{.FILE}}'
- 'echo "sidebar_position: 6" >> {{.FILE}}'
- 'echo "---" >> {{.FILE}}'
- 'echo "" >> {{.FILE}}'
- 'cat CHANGELOG.md >> {{.FILE}}'
npm:publish:
desc: Publish release to npm
cmds:
- npm publish --access=public
packages: packages:
cmds: cmds:
- echo '{{.GO_PACKAGES}}' - echo '{{.GO_PACKAGES}}'

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

View File

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

View File

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

View File

@@ -44,6 +44,19 @@ variable
| | `--version` | `bool` | `false` | Show Task version. | | | `--version` | `bool` | `false` | Show Task version. |
| `-w` | `--watch` | `bool` | `false` | Enables watch of the given task. | | `-w` | `--watch` | `bool` | `false` | Enables watch of the given task. |
## Special Variables
There are some special variables that is available on the templating system:
| Var | Description |
| - | - |
| `CLI_ARGS` | Contain all extra arguments passed after `--` when calling Task through the CLI. |
| `TASK` | The name of the current task. |
| `ROOT_DIR` | The absolute path of the root Taskfile. |
| `TASKFILE_DIR` | The absolute path of the included Taskfile. |
| `CHECKSUM` | The checksum of the files listed in `sources`. Only available within the `status` prop and if method is set to `checksum`. |
| `TIMESTAMP` | The date object of the greatest timestamp of the files listes in `sources`. Only available within the `status` prop and if method is set to `timestamp`. |
## ENV ## ENV
Some environment variables can be overriden to adjust Task behavior. Some environment variables can be overriden to adjust Task behavior.
@@ -84,6 +97,7 @@ Some environment variables can be overriden to adjust Task behavior.
| `taskfile` | `string` | | The path for the Taskfile or directory to be included. If a directory, Task will look for files named `Taskfile.yml` or `Taskfile.yaml` inside that directory. If a relative path, resolved relative to the directory containing the including Taskfile. | | `taskfile` | `string` | | The path for the Taskfile or directory to be included. If a directory, Task will look for files named `Taskfile.yml` or `Taskfile.yaml` inside that directory. If a relative path, resolved relative to the directory containing the including Taskfile. |
| `dir` | `string` | The parent Taskfile directory | The working directory of the included tasks when run. | | `dir` | `string` | The parent Taskfile directory | The working directory of the included tasks when run. |
| `optional` | `bool` | `false` | If `true`, no errors will be thrown if the specified file does not exist. | | `optional` | `bool` | `false` | If `true`, no errors will be thrown if the specified file does not exist. |
| `internal` | `bool` | `false` | If `true`, tasks will be omitted from both `--list` and `--list-all`. |
:::info :::info
@@ -106,6 +120,7 @@ includes:
| `dir` | `string` | | The current directory which this task should run. | | `dir` | `string` | | The current directory which this task should run. |
| `method` | `string` | `checksum` | Method used by this task. Default to the one declared globally or `checksum`. Available options: `checksum`, `timestamp` and `none` | | `method` | `string` | `checksum` | Method used by this task. Default to the one declared globally or `checksum`. Available options: `checksum`, `timestamp` and `none` |
| `silent` | `bool` | `false` | Skips some output for this task. Note that STDOUT and STDERR of the commands will still be redirected. | | `silent` | `bool` | `false` | Skips some output for this task. Note that STDOUT and STDERR of the commands will still be redirected. |
| `internal` | `bool` | `false` | If `true`, 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`. | | `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
| `prefix` | `string` | | Allows to override the prefix print before the STDOUT. Only relevant when using the `prefixed` output mode. | | `prefix` | `string` | | Allows to override the prefix print before the STDOUT. Only relevant when using the `prefixed` output mode. |
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the commands. | | `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the commands. |

View File

@@ -5,6 +5,42 @@ sidebar_position: 6
# Changelog # 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 ## v3.14.0 - 2022-07-08
- Add ability to override the `.task` directory location with the - Add ability to override the `.task` directory location with the

View File

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

View File

@@ -55,15 +55,23 @@ may take some time until it's available on Scoop.
### AUR ### AUR
If you're on Arch Linux you can install Task from If you're on Arch Linux you can install Task from
[AUR](https://aur.archlinux.org/packages/taskfile-git) using your favorite [AUR](https://aur.archlinux.org/packages/go-task-bin) using your favorite
package manager such as `yay`, `pacaur` or `yaourt`: package manager such as `yay`, `pacaur` or `yaourt`:
```cmd ```cmd
yay -S taskfile-git yay -S go-task-bin
``` ```
This installation method is community owned, but since it's `-git` version of Alternatively, there's
the package, it's always latest available version based on the Git repository. [this package](https://aur.archlinux.org/packages/go-task) which installs from
the source code instead of downloading the binary from the
[releases page](https://github.com/go-task/task/releases):
```cmd
yay -S go-task
```
This installation method is community owned.
### Fedora ### Fedora
@@ -89,6 +97,15 @@ nix-env -iA nixpkgs.go-task
This installation method is community owned. After a new release of Task, it This installation method is community owned. After a new release of Task, it
may take some time until it's available in [nixpkgs](https://github.com/NixOS/nixpkgs). may take some time until it's available in [nixpkgs](https://github.com/NixOS/nixpkgs).
### npm
You can also use Node and npm to install Task by installing
[this package](https://www.npmjs.com/package/@go-task/cli).
```bash
npm install -g @go-task/cli
```
## Get The Binary ## Get The Binary
### Binary ### Binary

View File

@@ -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], `$PATH` and you're done! Or you can also install using [Homebrew][homebrew],
[Snapcraft][snapcraft], or [Scoop][scoop] if you want. [Snapcraft][snapcraft], or [Scoop][scoop] if you want.
- Available on CIs: by adding [this simple command](installation.md#install-script) - Available on CIs: by adding [this simple command](installation.md#install-script)
to install on your CI script and you're done to use Task as part of your CI pipeline; to install on your CI script and you're ready to use Task as part of your CI pipeline;
- Truly cross-platform: while most build tools only work well on Linux or macOS, - Truly cross-platform: while most build tools only work well on Linux or macOS,
Task also supports Windows thanks to [this shell interpreter for Go][sh]. Task also supports Windows thanks to [this shell interpreter for Go][sh].
- Great for code generation: you can easily [prevent a task from running](/usage#prevent-unnecessary-work) - Great for code generation: you can easily [prevent a task from running](/usage#prevent-unnecessary-work)

View File

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

View File

@@ -194,6 +194,22 @@ tasks:
- echo "This command can still be successfully executed if ./tests/Taskfile.yml does not exist" - echo "This command can still be successfully executed if ./tests/Taskfile.yml does not exist"
``` ```
### Internal includes
Includes marked as internal will set all the tasks of the included file to be
internal as well (see the [Internal tasks](#internal-tasks) section below).
This is useful when including utility tasks that are not intended to be used
directly by the user.
```yaml
version: '3'
includes:
tests:
taskfile: ./taskfiles/Utils.yml
internal: true
```
### Vars of included Taskfiles ### Vars of included Taskfiles
You can also specify variables when including a Taskfile. This may be useful You can also specify variables when including a Taskfile. This may be useful
@@ -223,6 +239,29 @@ use the [default function](https://go-task.github.io/slim-sprig/defaults.html):
::: :::
## Internal tasks
Internal tasks are tasks that cannot be called directly by the user. They will
not appear in the output when running `task --list|--list-all`. Other tasks may
call internal tasks in the usual way. This is useful for creating reusable,
function-like tasks that have no useful purpose on the command line.
```yaml
version: '3'
tasks:
build-image-1:
cmds:
- task: build-image
vars:
DOCKER_IMAGE: image-1
build-image:
internal: true
cmds:
- docker build -t {{.DOCKER_IMAGE}} .
```
## Task directory ## Task directory
By default, tasks will be executed in the directory where the Taskfile is By default, tasks will be executed in the directory where the Taskfile is

View File

@@ -1,8 +1,8 @@
// @ts-check // @ts-check
// Note: type annotations allow type checking and IDEs autocompletion // Note: type annotations allow type checking and IDEs autocompletion
const lightCodeTheme = require('prism-react-renderer/themes/github'); const lightCodeTheme = require('./src/themes/prismLight');
const darkCodeTheme = require('prism-react-renderer/themes/dracula'); const darkCodeTheme = require('./src/themes/prismDark');
const GITHUB_URL = 'https://github.com/go-task/task'; const GITHUB_URL = 'https://github.com/go-task/task';
const TWITTER_URL = 'https://twitter.com/taskfiledev'; const TWITTER_URL = 'https://twitter.com/taskfiledev';

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 { :root {
--ifm-font-family-base: Roboto, system-ui, -apple-system, Segoe UI, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
--ifm-font-family-monospace: 'Roboto Mono', SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
--ifm-color-primary: #43ABA2 ; --ifm-color-primary: #43ABA2 ;
--ifm-color-primary-dark: #3AB2A6; --ifm-color-primary-dark: #3AB2A6;
--ifm-color-primary-darker: #32B8AB; --ifm-color-primary-darker: #32B8AB;
@@ -11,13 +16,6 @@
} }
[data-theme='dark'] { [data-theme='dark'] {
--ifm-color-primary: #43ABA2 ;
--ifm-color-primary-dark: #3AB2A6;
--ifm-color-primary-darker: #32B8AB;
--ifm-color-primary-darkest: #29BEB0;
--ifm-color-primary-light: #4CA59D;
--ifm-color-primary-lighter: #559F98;
--ifm-color-primary-lightest: #5D9993;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
} }

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: 19 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

@@ -8,6 +8,8 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"github.com/go-task/task/v3/internal/filepathext"
) )
// Checksum validades if a task is up to date by calculating its source // Checksum validades if a task is up to date by calculating its source
@@ -43,8 +45,8 @@ func (c *Checksum) IsUpToDate() (bool, error) {
} }
if !c.Dry { if !c.Dry {
_ = os.MkdirAll(filepath.Join(c.TempDir, "checksum"), 0755) _ = os.MkdirAll(filepathext.SmartJoin(c.TempDir, "checksum"), 0o755)
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil { if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0o644); err != nil {
return false, err return false, err
} }
} }

View File

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

View File

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

32
package-lock.json generated Normal file
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"
}
}

View File

@@ -12,6 +12,7 @@ import (
compilerv2 "github.com/go-task/task/v3/internal/compiler/v2" compilerv2 "github.com/go-task/task/v3/internal/compiler/v2"
compilerv3 "github.com/go-task/task/v3/internal/compiler/v3" compilerv3 "github.com/go-task/task/v3/internal/compiler/v3"
"github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/logger" "github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/output" "github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/taskfile" "github.com/go-task/task/v3/taskfile"
@@ -19,6 +20,10 @@ import (
) )
func (e *Executor) Setup() error { func (e *Executor) Setup() error {
if err := e.setCurrentDir(); err != nil {
return err
}
if err := e.readTaskfile(); err != nil { if err := e.readTaskfile(); err != nil {
return err return err
} }
@@ -52,6 +57,23 @@ func (e *Executor) Setup() error {
return nil 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 { func (e *Executor) readTaskfile() error {
var err error var err error
e.Taskfile, err = read.Taskfile(&read.ReaderNode{ e.Taskfile, err = read.Taskfile(&read.ReaderNode{
@@ -69,7 +91,7 @@ func (e *Executor) setupTempDir() error {
} }
if os.Getenv("TASK_TEMP_DIR") == "" { 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"), "~") { } 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")) tempDir, err := execext.Expand(os.Getenv("TASK_TEMP_DIR"))
if err != nil { if err != nil {
@@ -77,9 +99,9 @@ func (e *Executor) setupTempDir() error {
} }
projectDir, _ := filepath.Abs(e.Dir) projectDir, _ := filepath.Abs(e.Dir)
projectName := filepath.Base(projectDir) projectName := filepath.Base(projectDir)
e.TempDir = filepath.Join(tempDir, projectName) e.TempDir = filepathext.SmartJoin(tempDir, projectName)
} else { } 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 return nil

View File

@@ -9,7 +9,7 @@ import (
) )
// NOTE(@andreynering): This function intercepts SIGINT and SIGTERM signals // NOTE(@andreynering): This function intercepts SIGINT and SIGTERM signals
// so the Task process is not killed immediatelly and processes running have // so the Task process is not killed immediately and processes running have
// time to do cleanup work. // time to do cleanup work.
func (e *Executor) InterceptInterruptSignals() { func (e *Executor) InterceptInterruptSignals() {
ch := make(chan os.Signal, 3) ch := make(chan os.Signal, 3)

View File

@@ -64,11 +64,16 @@ type Executor struct {
func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error { func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
// check if given tasks exist // check if given tasks exist
for _, c := range calls { for _, 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 // FIXME: move to the main package
e.ListTasksWithDesc() e.ListTasksWithDesc()
return &taskNotFoundError{taskName: c.Task} return &taskNotFoundError{taskName: c.Task}
} }
if t.Internal {
e.ListTasksWithDesc()
return &taskInternalError{taskName: c.Task}
}
} }
if e.Summary { if e.Summary {
@@ -181,7 +186,7 @@ func (e *Executor) mkdir(t *taskfile.Task) error {
defer mutex.Unlock() defer mutex.Unlock()
if _, err := os.Stat(t.Dir); os.IsNotExist(err) { if _, err := os.Stat(t.Dir); os.IsNotExist(err) {
if err := os.MkdirAll(t.Dir, 0755); err != nil { if err := os.MkdirAll(t.Dir, 0o755); err != nil {
return err return err
} }
} }

View File

@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/go-task/task/v3" "github.com/go-task/task/v3"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/taskfile" "github.com/go-task/task/v3/taskfile"
) )
@@ -37,12 +38,12 @@ func (fct fileContentTest) name(file string) string {
func (fct fileContentTest) Run(t *testing.T) { func (fct fileContentTest) Run(t *testing.T) {
for f := range fct.Files { for f := range fct.Files {
_ = os.Remove(filepath.Join(fct.Dir, f)) _ = os.Remove(filepathext.SmartJoin(fct.Dir, f))
} }
e := &task.Executor{ e := &task.Executor{
Dir: fct.Dir, Dir: fct.Dir,
TempDir: filepath.Join(fct.Dir, ".task"), TempDir: filepathext.SmartJoin(fct.Dir, ".task"),
Entrypoint: fct.Entrypoint, Entrypoint: fct.Entrypoint,
Stdout: io.Discard, Stdout: io.Discard,
Stderr: io.Discard, Stderr: io.Discard,
@@ -52,7 +53,7 @@ func (fct fileContentTest) Run(t *testing.T) {
for name, expectContent := range fct.Files { for name, expectContent := range fct.Files {
t.Run(fct.name(name), func(t *testing.T) { t.Run(fct.name(name), func(t *testing.T) {
path := filepath.Join(fct.Dir, name) path := filepathext.SmartJoin(fct.Dir, name)
b, err := os.ReadFile(path) b, err := os.ReadFile(path)
assert.NoError(t, err, "Error reading file") assert.NoError(t, err, "Error reading file")
s := string(b) s := string(b)
@@ -163,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) { func TestVarsInvalidTmpl(t *testing.T) {
const ( const (
dir = "testdata/vars/v2" dir = "testdata/vars/v2"
@@ -235,7 +269,7 @@ func TestDeps(t *testing.T) {
} }
for _, f := range files { for _, f := range files {
_ = os.Remove(filepath.Join(dir, f)) _ = os.Remove(filepathext.SmartJoin(dir, f))
} }
e := &task.Executor{ e := &task.Executor{
@@ -247,7 +281,7 @@ func TestDeps(t *testing.T) {
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"})) assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
for _, f := range files { for _, f := range files {
f = filepath.Join(dir, f) f = filepathext.SmartJoin(dir, f)
if _, err := os.Stat(f); err != nil { if _, err := os.Stat(f); err != nil {
t.Errorf("File %s should exist", f) t.Errorf("File %s should exist", f)
} }
@@ -263,7 +297,7 @@ func TestStatus(t *testing.T) {
} }
for _, f := range files { for _, f := range files {
path := filepath.Join(dir, f) path := filepathext.SmartJoin(dir, f)
_ = os.Remove(path) _ = os.Remove(path)
if _, err := os.Stat(path); err == nil { if _, err := os.Stat(path); err == nil {
t.Errorf("File should not exist: %v", err) t.Errorf("File should not exist: %v", err)
@@ -273,7 +307,7 @@ func TestStatus(t *testing.T) {
var buff bytes.Buffer var buff bytes.Buffer
e := &task.Executor{ e := &task.Executor{
Dir: dir, Dir: dir,
TempDir: filepath.Join(dir, ".task"), TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: &buff, Stdout: &buff,
Stderr: &buff, Stderr: &buff,
Silent: true, Silent: true,
@@ -283,7 +317,7 @@ func TestStatus(t *testing.T) {
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"})) assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
for _, f := range files { for _, f := range files {
if _, err := os.Stat(filepath.Join(dir, f)); err != nil { if _, err := os.Stat(filepathext.SmartJoin(dir, f)); err != nil {
t.Errorf("File should exist: %v", err) t.Errorf("File should exist: %v", err)
} }
} }
@@ -360,10 +394,10 @@ func TestGenerates(t *testing.T) {
fileWithSpaces = "my text file.txt" fileWithSpaces = "my text file.txt"
) )
var srcFile = filepath.Join(dir, srcTask) var srcFile = filepathext.SmartJoin(dir, srcTask)
for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} { for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} {
path := filepath.Join(dir, task) path := filepathext.SmartJoin(dir, task)
_ = os.Remove(path) _ = os.Remove(path)
if _, err := os.Stat(path); err == nil { if _, err := os.Stat(path); err == nil {
t.Errorf("File should not exist: %v", err) t.Errorf("File should not exist: %v", err)
@@ -379,7 +413,7 @@ func TestGenerates(t *testing.T) {
assert.NoError(t, e.Setup()) assert.NoError(t, e.Setup())
for _, theTask := range []string{relTask, absTask, fileWithSpaces} { for _, theTask := range []string{relTask, absTask, fileWithSpaces} {
var destFile = filepath.Join(dir, theTask) var destFile = filepathext.SmartJoin(dir, theTask)
var upToDate = fmt.Sprintf("task: Task \"%s\" is up to date\n", srcTask) + var upToDate = fmt.Sprintf("task: Task \"%s\" is up to date\n", srcTask) +
fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask) fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask)
@@ -416,16 +450,16 @@ func TestStatusChecksum(t *testing.T) {
} }
for _, f := range files { for _, f := range files {
_ = os.Remove(filepath.Join(dir, f)) _ = os.Remove(filepathext.SmartJoin(dir, f))
_, err := os.Stat(filepath.Join(dir, f)) _, err := os.Stat(filepathext.SmartJoin(dir, f))
assert.Error(t, err) assert.Error(t, err)
} }
var buff bytes.Buffer var buff bytes.Buffer
e := task.Executor{ e := task.Executor{
Dir: dir, Dir: dir,
TempDir: filepath.Join(dir, ".task"), TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: &buff, Stdout: &buff,
Stderr: &buff, Stderr: &buff,
} }
@@ -433,7 +467,7 @@ func TestStatusChecksum(t *testing.T) {
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"})) assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
for _, f := range files { for _, f := range files {
_, err := os.Stat(filepath.Join(dir, f)) _, err := os.Stat(filepathext.SmartJoin(dir, f))
assert.NoError(t, err) assert.NoError(t, err)
} }
@@ -577,13 +611,13 @@ func TestListCanListDescOnly(t *testing.T) {
func TestStatusVariables(t *testing.T) { func TestStatusVariables(t *testing.T) {
const dir = "testdata/status_vars" const dir = "testdata/status_vars"
_ = os.RemoveAll(filepath.Join(dir, ".task")) _ = os.RemoveAll(filepathext.SmartJoin(dir, ".task"))
_ = os.Remove(filepath.Join(dir, "generated.txt")) _ = os.Remove(filepathext.SmartJoin(dir, "generated.txt"))
var buff bytes.Buffer var buff bytes.Buffer
e := task.Executor{ e := task.Executor{
Dir: dir, Dir: dir,
TempDir: filepath.Join(dir, ".task"), TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: &buff, Stdout: &buff,
Stderr: &buff, Stderr: &buff,
Silent: false, Silent: false,
@@ -594,7 +628,7 @@ func TestStatusVariables(t *testing.T) {
assert.Contains(t, buff.String(), "d41d8cd98f00b204e9800998ecf8427e") assert.Contains(t, buff.String(), "d41d8cd98f00b204e9800998ecf8427e")
inf, err := os.Stat(filepath.Join(dir, "source.txt")) inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt"))
assert.NoError(t, err) assert.NoError(t, err)
ts := fmt.Sprintf("%d", inf.ModTime().Unix()) ts := fmt.Sprintf("%d", inf.ModTime().Unix())
tf := inf.ModTime().String() tf := inf.ModTime().String()
@@ -605,7 +639,7 @@ func TestStatusVariables(t *testing.T) {
func TestInit(t *testing.T) { func TestInit(t *testing.T) {
const dir = "testdata/init" const dir = "testdata/init"
var file = filepath.Join(dir, "Taskfile.yaml") var file = filepathext.SmartJoin(dir, "Taskfile.yaml")
_ = os.Remove(file) _ = os.Remove(file)
if _, err := os.Stat(file); err == nil { if _, err := os.Stat(file); err == nil {
@@ -694,7 +728,7 @@ func TestExpand(t *testing.T) {
func TestDry(t *testing.T) { func TestDry(t *testing.T) {
const dir = "testdata/dry" const dir = "testdata/dry"
file := filepath.Join(dir, "file.txt") file := filepathext.SmartJoin(dir, "file.txt")
_ = os.Remove(file) _ = os.Remove(file)
var buff bytes.Buffer var buff bytes.Buffer
@@ -719,12 +753,12 @@ func TestDry(t *testing.T) {
func TestDryChecksum(t *testing.T) { func TestDryChecksum(t *testing.T) {
const dir = "testdata/dry_checksum" const dir = "testdata/dry_checksum"
checksumFile := filepath.Join(dir, ".task/checksum/default") checksumFile := filepathext.SmartJoin(dir, ".task/checksum/default")
_ = os.Remove(checksumFile) _ = os.Remove(checksumFile)
e := task.Executor{ e := task.Executor{
Dir: dir, Dir: dir,
TempDir: filepath.Join(dir, ".task"), TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: io.Discard, Stdout: io.Discard,
Stderr: io.Discard, Stderr: io.Discard,
Dry: true, Dry: true,
@@ -776,12 +810,6 @@ func TestIncludesMultiLevel(t *testing.T) {
func TestIncludeCycle(t *testing.T) { func TestIncludeCycle(t *testing.T) {
const dir = "testdata/includes_cycle" const dir = "testdata/includes_cycle"
wd, err := os.Getwd()
assert.Nil(t, err)
message := "task: include cycle detected between %s/%s/one/Taskfile.yml <--> %s/%s/Taskfile.yml"
expectedError := fmt.Sprintf(message, wd, dir, wd, dir)
var buff bytes.Buffer var buff bytes.Buffer
e := task.Executor{ e := task.Executor{
Dir: dir, Dir: dir,
@@ -790,7 +818,9 @@ func TestIncludeCycle(t *testing.T) {
Silent: true, Silent: true,
} }
assert.EqualError(t, e.Setup(), expectedError) err := e.Setup()
assert.Error(t, err)
assert.Contains(t, err.Error(), "task: include cycle detected between")
} }
func TestIncorrectVersionIncludes(t *testing.T) { func TestIncorrectVersionIncludes(t *testing.T) {
@@ -928,6 +958,86 @@ func TestIncludesRelativePath(t *testing.T) {
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common") 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) { func TestSupportedFileNames(t *testing.T) {
fileNames := []string{ fileNames := []string{
"Taskfile.yml", "Taskfile.yml",
@@ -964,12 +1074,12 @@ func TestSummary(t *testing.T) {
assert.NoError(t, e.Setup()) assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-summary"}, taskfile.Call{Task: "other-task-with-summary"})) assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-summary"}, taskfile.Call{Task: "other-task-with-summary"}))
data, err := os.ReadFile(filepath.Join(dir, "task-with-summary.txt")) data, err := os.ReadFile(filepathext.SmartJoin(dir, "task-with-summary.txt"))
assert.NoError(t, err) assert.NoError(t, err)
expectedOutput := string(data) expectedOutput := string(data)
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
expectedOutput = strings.Replace(expectedOutput, "\r\n", "\n", -1) expectedOutput = strings.ReplaceAll(expectedOutput, "\r\n", "\n")
} }
assert.Equal(t, expectedOutput, buff.String()) assert.Equal(t, expectedOutput, buff.String())
@@ -1337,3 +1447,36 @@ func TestErrorCode(t *testing.T) {
assert.True(t, ok, "cannot cast returned error to *task.TaskRunError") assert.True(t, ok, "cannot cast returned error to *task.TaskRunError")
assert.Equal(t, 42, casted.ExitCode(), "unexpected exit code from task") assert.Equal(t, 42, casted.ExitCode(), "unexpected exit code from task")
} }
func TestEvaluateSymlinksInPaths(t *testing.T) {
const dir = "testdata/evaluate_symlinks_in_paths"
var buff bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: false,
}
assert.NoError(t, e.Setup())
err := e.Run(context.Background(), taskfile.Call{Task: "default"})
assert.NoError(t, err)
assert.NotEqual(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "test-sym"})
assert.NoError(t, err)
assert.NotEqual(t, `task: Task "test-sym" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "default"})
assert.NoError(t, err)
assert.NotEqual(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "default"})
assert.NoError(t, err)
assert.Equal(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "reset"})
assert.NoError(t, err)
buff.Reset()
err = os.RemoveAll(dir + "/.task")
assert.NoError(t, err)
}

View File

@@ -6,6 +6,7 @@ import (
"path/filepath" "path/filepath"
"github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -15,6 +16,7 @@ type IncludedTaskfile struct {
Taskfile string Taskfile string
Dir string Dir string
Optional bool Optional bool
Internal bool
AdvancedImport bool AdvancedImport bool
Vars *Vars Vars *Vars
BaseDir string // The directory from which the including taskfile was loaded; used to resolve relative paths BaseDir string // The directory from which the including taskfile was loaded; used to resolve relative paths
@@ -100,6 +102,7 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
Taskfile string Taskfile string
Dir string Dir string
Optional bool Optional bool
Internal bool
Vars *Vars Vars *Vars
} }
if err := unmarshal(&includedTaskfile); err != nil { if err := unmarshal(&includedTaskfile); err != nil {
@@ -108,6 +111,7 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
it.Taskfile = includedTaskfile.Taskfile it.Taskfile = includedTaskfile.Taskfile
it.Dir = includedTaskfile.Dir it.Dir = includedTaskfile.Dir
it.Optional = includedTaskfile.Optional it.Optional = includedTaskfile.Optional
it.Internal = includedTaskfile.Internal
it.AdvancedImport = true it.AdvancedImport = true
it.Vars = includedTaskfile.Vars it.Vars = includedTaskfile.Vars
return nil return nil
@@ -133,7 +137,7 @@ func (it *IncludedTaskfile) resolvePath(path string) (string, error) {
return path, nil return path, nil
} }
result, err := filepath.Abs(filepath.Join(it.BaseDir, path)) result, err := filepath.Abs(filepathext.SmartJoin(it.BaseDir, path))
if err != nil { if err != nil {
return "", fmt.Errorf("task: error resolving path %s relative to %s: %w", path, it.BaseDir, err) return "", fmt.Errorf("task: error resolving path %s relative to %s: %w", path, it.BaseDir, err)
} }

View File

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

View File

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

View File

@@ -9,6 +9,7 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/templater" "github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile" "github.com/go-task/task/v3/taskfile"
) )
@@ -44,7 +45,7 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
readerNode.Dir = d readerNode.Dir = d
} }
path, err := exists(filepath.Join(readerNode.Dir, readerNode.Entrypoint)) path, err := exists(filepathext.SmartJoin(readerNode.Dir, readerNode.Entrypoint))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -77,6 +78,7 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
Taskfile: tr.Replace(includedTask.Taskfile), Taskfile: tr.Replace(includedTask.Taskfile),
Dir: tr.Replace(includedTask.Dir), Dir: tr.Replace(includedTask.Dir),
Optional: includedTask.Optional, Optional: includedTask.Optional,
Internal: includedTask.Internal,
AdvancedImport: includedTask.AdvancedImport, AdvancedImport: includedTask.AdvancedImport,
Vars: includedTask.Vars, Vars: includedTask.Vars,
BaseDir: includedTask.BaseDir, BaseDir: includedTask.BaseDir,
@@ -140,16 +142,14 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
} }
for _, task := range includedTaskfile.Tasks { for _, task := range includedTaskfile.Tasks {
if !filepath.IsAbs(task.Dir) { task.Dir = filepathext.SmartJoin(dir, task.Dir)
task.Dir = filepath.Join(dir, task.Dir)
}
task.IncludeVars = includedTask.Vars task.IncludeVars = includedTask.Vars
task.IncludedTaskfileVars = includedTaskfile.Vars task.IncludedTaskfileVars = includedTaskfile.Vars
task.IncludedTaskfile = &includedTask
} }
} }
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil { if err = taskfile.Merge(t, includedTaskfile, includedTask.Internal, namespace); err != nil {
return err return err
} }
return nil return nil
@@ -159,13 +159,13 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
} }
if v < 3.0 { if v < 3.0 {
path = filepath.Join(readerNode.Dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS)) path = filepathext.SmartJoin(readerNode.Dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
if _, err = os.Stat(path); err == nil { if _, err = os.Stat(path); err == nil {
osTaskfile, err := readTaskfile(path) osTaskfile, err := readTaskfile(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err = taskfile.Merge(t, osTaskfile); err != nil { if err = taskfile.Merge(t, osTaskfile, false); err != nil {
return nil, err return nil, err
} }
} }
@@ -191,29 +191,19 @@ func readTaskfile(file string) (*taskfile.Taskfile, error) {
return &t, yaml.NewDecoder(f).Decode(&t) return &t, yaml.NewDecoder(f).Decode(&t)
} }
// exists finds a Taskfile at the stated location, returning a fully qualified path to the file
func exists(path string) (string, error) { func exists(path string) (string, error) {
fi, err := os.Stat(path) fi, err := os.Stat(path)
if err != nil { if err != nil {
return "", err return "", err
} }
if fi.Mode().IsRegular() { if fi.Mode().IsRegular() {
// File exists, return a fully qualified path return path, nil
result, err := filepath.Abs(path)
if err != nil {
return "", err
}
return result, nil
} }
for _, n := range defaultTaskfiles { for _, n := range defaultTaskfiles {
fpath := filepath.Join(path, n) fpath := filepathext.SmartJoin(path, n)
if _, err := os.Stat(fpath); err == nil { if _, err := os.Stat(fpath); err == nil {
result, err := filepath.Abs(fpath) return fpath, nil
if err != nil {
return "", err
}
return result, nil
} }
} }
@@ -228,14 +218,14 @@ func checkCircularIncludes(node *ReaderNode) error {
return errors.New("task: failed to check for include cycle: node.Parent was nil") return errors.New("task: failed to check for include cycle: node.Parent was nil")
} }
var curNode = node var curNode = node
var basePath = filepath.Join(node.Dir, node.Entrypoint) var basePath = filepathext.SmartJoin(node.Dir, node.Entrypoint)
for curNode.Parent != nil { for curNode.Parent != nil {
curNode = curNode.Parent curNode = curNode.Parent
curPath := filepath.Join(curNode.Dir, curNode.Entrypoint) curPath := filepathext.SmartJoin(curNode.Dir, curNode.Entrypoint)
if curPath == basePath { if curPath == basePath {
return fmt.Errorf("task: include cycle detected between %s <--> %s", return fmt.Errorf("task: include cycle detected between %s <--> %s",
curPath, curPath,
filepath.Join(node.Parent.Dir, node.Parent.Entrypoint), filepathext.SmartJoin(node.Parent.Dir, node.Parent.Entrypoint),
) )
} }
} }

View File

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

View File

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

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!"

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