mirror of
https://github.com/go-task/task.git
synced 2026-06-24 21:26:04 +00:00
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79f595d8d1 | ||
|
|
db2865fb17 | ||
|
|
f945fa60d9 | ||
|
|
454988f657 | ||
|
|
7e0346d6eb | ||
|
|
00a90d1fe6 | ||
|
|
d6c185580a | ||
|
|
fd9132c15d | ||
|
|
42702e81b3 | ||
|
|
09c9d55695 | ||
|
|
69e9effc88 | ||
|
|
1c782c599f | ||
|
|
ed37071fd6 | ||
|
|
d73cf106b1 | ||
|
|
1d7982e80a | ||
|
|
d5d1984116 | ||
|
|
9eda1629bb | ||
|
|
9a5d49774e | ||
|
|
b2efebce96 | ||
|
|
85232bd704 | ||
|
|
df4e3aea79 | ||
|
|
290d45fd05 | ||
|
|
168e8c925c | ||
|
|
d9859b18fe | ||
|
|
784847f35b | ||
|
|
97287377d1 | ||
|
|
a15b66e003 | ||
|
|
a441b4b90d | ||
|
|
0dcc1390a6 | ||
|
|
01c86636e9 | ||
|
|
846c27d579 | ||
|
|
db05059b42 | ||
|
|
b824328850 | ||
|
|
a8767a2b1a | ||
|
|
5e14e7fb70 | ||
|
|
fbaa7be52e | ||
|
|
b6016b244e | ||
|
|
e339a64261 | ||
|
|
17e18442ab | ||
|
|
e8aa3a17a6 | ||
|
|
bdb97eab86 | ||
|
|
690000254c | ||
|
|
6a0b778978 | ||
|
|
549d141053 | ||
|
|
c31ecdb8de | ||
|
|
8a09d044c7 | ||
|
|
a3b5b89930 | ||
|
|
ad6f100f6a | ||
|
|
3cfe21af58 | ||
|
|
b70a660975 | ||
|
|
04c1d1389f | ||
|
|
f12156bf81 | ||
|
|
0177ac660b | ||
|
|
361b9b4ce4 | ||
|
|
78792bd11c | ||
|
|
8b38ddfcd9 | ||
|
|
78ddf50d2d | ||
|
|
93dcb20e12 | ||
|
|
41a71e1dee | ||
|
|
a5ed8ad58c | ||
|
|
e45ed85b55 | ||
|
|
52474f9103 | ||
|
|
c2587da27d | ||
|
|
26036877b2 | ||
|
|
906cdd9050 | ||
|
|
762662d056 | ||
|
|
1de4b38766 | ||
|
|
6c73ab823b | ||
|
|
5ef1651151 | ||
|
|
8d695bc8d7 | ||
|
|
c892d055ed | ||
|
|
b327e54be1 | ||
|
|
989045489c | ||
|
|
2dc181c75e | ||
|
|
0a6833e9d8 | ||
|
|
8f80fc4e2c | ||
|
|
347c796662 | ||
|
|
9bed7f7a9b |
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,7 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Bug Report
|
name: Bug Report
|
||||||
about: Use the template to report bugs and issues
|
about: Use the template to report bugs and issues
|
||||||
labels: bug
|
|
||||||
---
|
---
|
||||||
|
|
||||||
- Task version:
|
- Task version:
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,7 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Feature Request
|
name: Feature Request
|
||||||
about: Use the template to make feature requests
|
about: Use the template to make feature requests
|
||||||
labels: feature
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Describe in detail what feature do you want to see in Task.
|
Describe in detail what feature do you want to see in Task.
|
||||||
|
|||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -10,10 +10,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.17.x
|
go-version: 1.17.x
|
||||||
|
|
||||||
@@ -23,4 +23,4 @@ jobs:
|
|||||||
version: latest
|
version: latest
|
||||||
args: release --rm-dist
|
args: release --rm-dist
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||||
|
|||||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -10,13 +10,13 @@ jobs:
|
|||||||
runs-on: ${{matrix.platform}}
|
runs-on: ${{matrix.platform}}
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{matrix.go-version}}
|
- name: Set up Go ${{matrix.go-version}}
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: ${{matrix.go-version}}
|
go-version: ${{matrix.go-version}}
|
||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Download Go modules
|
- name: Download Go modules
|
||||||
run: go mod download
|
run: go mod download
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ gomod:
|
|||||||
|
|
||||||
archives:
|
archives:
|
||||||
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
|
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
|
||||||
|
files:
|
||||||
|
- README.md
|
||||||
|
- LICENSE
|
||||||
|
- completion/**/*
|
||||||
format_overrides:
|
format_overrides:
|
||||||
- goos: windows
|
- goos: windows
|
||||||
format: zip
|
format: zip
|
||||||
@@ -62,6 +66,8 @@ brews:
|
|||||||
name: homebrew-tap
|
name: homebrew-tap
|
||||||
test:
|
test:
|
||||||
system "#{bin}/task", "--help"
|
system "#{bin}/task", "--help"
|
||||||
url_template: https://github.com/go-task/task/releases/download/{{.Tag}}/{{.ArtifactName}}
|
install: |-
|
||||||
commit_msg_template: Bump {{.Tag}}
|
bin.install "task"
|
||||||
token: "{{.Env.GORELEASER_TOKEN}}"
|
bash_completion.install "completion/bash/task.bash" => "task"
|
||||||
|
zsh_completion.install "completion/zsh/_task" => "_task"
|
||||||
|
fish_completion.install "completion/fish/task.fish"
|
||||||
|
|||||||
72
CHANGELOG.md
72
CHANGELOG.md
@@ -1,6 +1,76 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 3.7.2 - 2021-09-04
|
## v3.10.0 - 2022-01-04
|
||||||
|
|
||||||
|
- A new `--list-all` (alias `-a`) flag is now available. It's similar to the
|
||||||
|
exiting `--list` (`-l`) but prints all tasks, even those without a
|
||||||
|
description
|
||||||
|
([#383](https://github.com/go-task/task/issues/383), [#401](https://github.com/go-task/task/pull/401)).
|
||||||
|
- It's now possible to schedule cleanup commands to run once a task finishes
|
||||||
|
with the `defer:` keyword
|
||||||
|
([Documentation](https://taskfile.dev/#/usage?id=doing-task-cleanup-with-defer), [#475](https://github.com/go-task/task/issues/475), [#626](https://github.com/go-task/task/pull/626)).
|
||||||
|
- Remove long deprecated and undocumented `$` variable prefix and `^` command
|
||||||
|
prefix
|
||||||
|
([#642](https://github.com/go-task/task/issues/642), [#644](https://github.com/go-task/task/issues/644), [#645](https://github.com/go-task/task/pull/645)).
|
||||||
|
- Add support for `.yaml` extension (as an alternative to `.yml`).
|
||||||
|
This was requested multiple times throughout the years. Enjoy!
|
||||||
|
([#183](https://github.com/go-task/task/issues/183), [#184](https://github.com/go-task/task/pull/184), [#369](https://github.com/go-task/task/issues/369), [#584](https://github.com/go-task/task/issues/584), [#621](https://github.com/go-task/task/pull/621)).
|
||||||
|
- Fixed error when computing a variable when the task directory do not exist
|
||||||
|
yet
|
||||||
|
([#481](https://github.com/go-task/task/issues/481), [#579](https://github.com/go-task/task/pull/579)).
|
||||||
|
|
||||||
|
## v3.9.2 - 2021-12-02
|
||||||
|
|
||||||
|
- Upgrade [mvdan/sh](https://github.com/mvdan/sh) which contains a fix a for
|
||||||
|
a important regression on Windows
|
||||||
|
([#619](https://github.com/go-task/task/issues/619), [mvdan/sh#768](https://github.com/mvdan/sh/issues/768), [mvdan/sh#769](https://github.com/mvdan/sh/pull/769)).
|
||||||
|
|
||||||
|
## v3.9.1 - 2021-11-28
|
||||||
|
|
||||||
|
- Add logging in verbose mode for when a task starts and finishes
|
||||||
|
([#533](https://github.com/go-task/task/issues/533), [#588](https://github.com/go-task/task/pull/588)).
|
||||||
|
- Fix an issue with preconditions and context errors
|
||||||
|
([#597](https://github.com/go-task/task/issues/597), [#598](https://github.com/go-task/task/pull/598)).
|
||||||
|
- Quote each `{{.CLI_ARGS}}` argument to prevent one with spaces to become many
|
||||||
|
([#613](https://github.com/go-task/task/pull/613)).
|
||||||
|
- Fix nil pointer when `cmd:` was left empty
|
||||||
|
([#612](https://github.com/go-task/task/issues/612), [#614](https://github.com/go-task/task/pull/614)).
|
||||||
|
- Upgrade [mvdan/sh](https://github.com/mvdan/sh) which contains two
|
||||||
|
relevant fixes:
|
||||||
|
- Fix quote of empty strings in `shellQuote`
|
||||||
|
([#609](https://github.com/go-task/task/issues/609), [mvdan/sh#763](https://github.com/mvdan/sh/issues/763)).
|
||||||
|
- Fix issue of wrong environment variable being picked when there's another
|
||||||
|
very similar one
|
||||||
|
([#586](https://github.com/go-task/task/issues/586), [mvdan/sh#745](https://github.com/mvdan/sh/pull/745)).
|
||||||
|
- Install shell completions automatically when installing via Homebrew
|
||||||
|
([#264](https://github.com/go-task/task/issues/264), [#592](https://github.com/go-task/task/pull/592), [go-task/homebrew-tap#2](https://github.com/go-task/homebrew-tap/pull/2)).
|
||||||
|
|
||||||
|
## v3.9.0 - 2021-10-02
|
||||||
|
|
||||||
|
- A new `shellQuote` function was added to the template system
|
||||||
|
(`{{shellQuote "a string"}}`) to ensure a string is safe for use in shell
|
||||||
|
([mvdan/sh#727](https://github.com/mvdan/sh/pull/727), [mvdan/sh#737](https://github.com/mvdan/sh/pull/737), [Documentation](https://pkg.go.dev/mvdan.cc/sh/v3@v3.4.0/syntax#Quote))
|
||||||
|
- In this version [mvdan.cc/sh](https://github.com/mvdan/sh) was upgraded
|
||||||
|
with some small fixes and features
|
||||||
|
- The `read -p` flag is now supported
|
||||||
|
([#314](https://github.com/go-task/task/issues/314), [mvdan/sh#551](https://github.com/mvdan/sh/issues/551), [mvdan/sh#772](https://github.com/mvdan/sh/pull/722))
|
||||||
|
- The `pwd -P` and `pwd -L` flags are now supported
|
||||||
|
([#553](https://github.com/go-task/task/issues/553), [mvdan/sh#724](https://github.com/mvdan/sh/issues/724), [mvdan/sh#728](https://github.com/mvdan/sh/pull/728))
|
||||||
|
- The `$GID` environment variable is now correctly being set
|
||||||
|
([#561](https://github.com/go-task/task/issues/561), [mvdan/sh#723](https://github.com/mvdan/sh/pull/723))
|
||||||
|
|
||||||
|
## v3.8.0 - 2021-09-26
|
||||||
|
|
||||||
|
- Add `interactive: true` setting to improve support for interactive CLI apps
|
||||||
|
([#217](https://github.com/go-task/task/issues/217), [#563](https://github.com/go-task/task/pull/563)).
|
||||||
|
- Fix some `nil` errors
|
||||||
|
([#534](https://github.com/go-task/task/issues/534), [#573](https://github.com/go-task/task/pull/573)).
|
||||||
|
- Add ability to declare an included Taskfile as optional
|
||||||
|
([#519](https://github.com/go-task/task/issues/519), [#552](https://github.com/go-task/task/pull/552)).
|
||||||
|
- Add support for including Taskfiles in the home directory by using `~`
|
||||||
|
([#539](https://github.com/go-task/task/issues/539), [#557](https://github.com/go-task/task/pull/557)).
|
||||||
|
|
||||||
|
## v3.7.3 - 2021-09-04
|
||||||
|
|
||||||
- Add official support to Apple M1 ([#564](https://github.com/go-task/task/pull/564), [#567](https://github.com/go-task/task/pull/567)).
|
- Add official support to Apple M1 ([#564](https://github.com/go-task/task/pull/564), [#567](https://github.com/go-task/task/pull/567)).
|
||||||
- Our [official Homebrew tap](https://github.com/go-task/homebrew-tap) will
|
- Our [official Homebrew tap](https://github.com/go-task/homebrew-tap) will
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
"mvdan.cc/sh/v3/syntax"
|
||||||
|
|
||||||
"github.com/go-task/task/v3"
|
"github.com/go-task/task/v3"
|
||||||
"github.com/go-task/task/v3/args"
|
"github.com/go-task/task/v3/args"
|
||||||
@@ -59,6 +60,7 @@ func main() {
|
|||||||
helpFlag bool
|
helpFlag bool
|
||||||
init bool
|
init bool
|
||||||
list bool
|
list bool
|
||||||
|
listAll bool
|
||||||
status bool
|
status bool
|
||||||
force bool
|
force bool
|
||||||
watch bool
|
watch bool
|
||||||
@@ -76,8 +78,9 @@ func main() {
|
|||||||
|
|
||||||
pflag.BoolVar(&versionFlag, "version", false, "show Task version")
|
pflag.BoolVar(&versionFlag, "version", false, "show Task version")
|
||||||
pflag.BoolVarP(&helpFlag, "help", "h", false, "shows Task usage")
|
pflag.BoolVarP(&helpFlag, "help", "h", false, "shows Task usage")
|
||||||
pflag.BoolVarP(&init, "init", "i", false, "creates a new Taskfile.yml in the current folder")
|
pflag.BoolVarP(&init, "init", "i", false, "creates a new Taskfile.yaml in the current folder")
|
||||||
pflag.BoolVarP(&list, "list", "l", false, "lists tasks with description of current Taskfile")
|
pflag.BoolVarP(&list, "list", "l", false, "lists tasks with description of current Taskfile")
|
||||||
|
pflag.BoolVarP(&listAll, "list-all", "a", false, "lists tasks with or without a description")
|
||||||
pflag.BoolVar(&status, "status", false, "exits with non-zero exit code if any of the given tasks is not up-to-date")
|
pflag.BoolVar(&status, "status", false, "exits with non-zero exit code if any of the given tasks is not up-to-date")
|
||||||
pflag.BoolVarP(&force, "force", "f", false, "forces execution even when the task is up-to-date")
|
pflag.BoolVarP(&force, "force", "f", false, "forces execution even when the task is up-to-date")
|
||||||
pflag.BoolVarP(&watch, "watch", "w", false, "enables watch of the given task")
|
pflag.BoolVarP(&watch, "watch", "w", false, "enables watch of the given task")
|
||||||
@@ -121,8 +124,6 @@ func main() {
|
|||||||
if entrypoint != "" {
|
if entrypoint != "" {
|
||||||
dir = filepath.Dir(entrypoint)
|
dir = filepath.Dir(entrypoint)
|
||||||
entrypoint = filepath.Base(entrypoint)
|
entrypoint = filepath.Base(entrypoint)
|
||||||
} else {
|
|
||||||
entrypoint = "Taskfile.yml"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
@@ -154,23 +155,32 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if list {
|
if list {
|
||||||
e.PrintTasksHelp()
|
e.ListTasksWithDesc()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if listAll {
|
||||||
|
e.ListAllTasks()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
calls []taskfile.Call
|
calls []taskfile.Call
|
||||||
globals *taskfile.Vars
|
globals *taskfile.Vars
|
||||||
tasksAndVars, cliArgs = getArgs()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
tasksAndVars, cliArgs, err := getArgs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if v >= 3.0 {
|
if v >= 3.0 {
|
||||||
calls, globals = args.ParseV3(tasksAndVars...)
|
calls, globals = args.ParseV3(tasksAndVars...)
|
||||||
} else {
|
} else {
|
||||||
calls, globals = args.ParseV2(tasksAndVars...)
|
calls, globals = args.ParseV2(tasksAndVars...)
|
||||||
}
|
}
|
||||||
|
|
||||||
globals.Set("CLI_ARGS", taskfile.Var{Static: strings.Join(cliArgs, " ")})
|
globals.Set("CLI_ARGS", taskfile.Var{Static: cliArgs})
|
||||||
e.Taskfile.Vars.Merge(globals)
|
e.Taskfile.Vars.Merge(globals)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -191,20 +201,25 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getArgs() (tasksAndVars, cliArgs []string) {
|
func getArgs() ([]string, string, error) {
|
||||||
var (
|
var (
|
||||||
args = pflag.Args()
|
args = pflag.Args()
|
||||||
doubleDashPos = pflag.CommandLine.ArgsLenAtDash()
|
doubleDashPos = pflag.CommandLine.ArgsLenAtDash()
|
||||||
)
|
)
|
||||||
|
|
||||||
if doubleDashPos != -1 {
|
if doubleDashPos == -1 {
|
||||||
tasksAndVars = args[:doubleDashPos]
|
return args, "", nil
|
||||||
cliArgs = args[doubleDashPos:]
|
|
||||||
} else {
|
|
||||||
tasksAndVars = args
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
var quotedCliArgs []string
|
||||||
|
for _, arg := range args[doubleDashPos:] {
|
||||||
|
quotedCliArg, err := syntax.Quote(arg, syntax.LangBash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
quotedCliArgs = append(quotedCliArgs, quotedCliArg)
|
||||||
|
}
|
||||||
|
return args[:doubleDashPos], strings.Join(quotedCliArgs, " "), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSignalContext() context.Context {
|
func getSignalContext() context.Context {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
function __list() {
|
function __list() {
|
||||||
local -a scripts
|
local -a scripts
|
||||||
|
|
||||||
if [ -f Taskfile.yml ]; then
|
if [ -f Taskfile.yml ] || [ -f Taskfile.yaml ]; then
|
||||||
scripts=($(task -l | sed '1d' | sed 's/^\* //' | awk '{ print $1 }' | sed 's/:$//' | sed 's/:/\\:/g'))
|
scripts=($(task -l | sed '1d' | sed 's/^\* //' | awk '{ print $1 }' | sed 's/:$//' | sed 's/:/\\:/g'))
|
||||||
_describe 'script' scripts
|
_describe 'script' scripts
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ officially supported. On Linux, only `Taskfile.yml` will work, though.
|
|||||||
|
|
||||||
- `version:`
|
- `version:`
|
||||||
- `includes:`
|
- `includes:`
|
||||||
- Configuration ones, like `output:`, `expansions:` or `silent:`
|
- Configuration ones, like `output:` or `silent:`
|
||||||
- `vars:`
|
- `vars:`
|
||||||
- `env:`
|
- `env:`
|
||||||
- `tasks:`
|
- `tasks:`
|
||||||
@@ -60,7 +60,6 @@ version: '3'
|
|||||||
includes:
|
includes:
|
||||||
docker: ./docker/Taskfile.yml
|
docker: ./docker/Taskfile.yml
|
||||||
output: prefixed
|
output: prefixed
|
||||||
expansions: 3
|
|
||||||
vars:
|
vars:
|
||||||
FOO: bar
|
FOO: bar
|
||||||
env:
|
env:
|
||||||
@@ -76,7 +75,6 @@ includes:
|
|||||||
docker: ./docker/Taskfile.yml
|
docker: ./docker/Taskfile.yml
|
||||||
|
|
||||||
output: prefixed
|
output: prefixed
|
||||||
expansions: 3
|
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
FOO: bar
|
FOO: bar
|
||||||
|
|||||||
@@ -154,6 +154,25 @@ includes:
|
|||||||
> This was a deliberate decision to keep use and implementation simple.
|
> This was a deliberate decision to keep use and implementation simple.
|
||||||
> If you disagree, open an GitHub issue and explain your use case. =)
|
> If you disagree, open an GitHub issue and explain your use case. =)
|
||||||
|
|
||||||
|
### Optional includes
|
||||||
|
|
||||||
|
Includes marked as optional will allow Task to continue execution as normal if
|
||||||
|
the included file is missing.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
tests:
|
||||||
|
taskfile: ./tests/Taskfile.yml
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
greet:
|
||||||
|
cmds:
|
||||||
|
- echo "This command can still be successfully executed if ./tests/Taskfile.yml does not exist"
|
||||||
|
```
|
||||||
|
|
||||||
## 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
|
||||||
@@ -570,7 +589,7 @@ This works for all types of variables.
|
|||||||
|
|
||||||
## Forwarding CLI arguments to commands
|
## Forwarding CLI arguments to commands
|
||||||
|
|
||||||
If `--` is given in the CLI, all following paramaters are added to a
|
If `--` is given in the CLI, all following parameters are added to a
|
||||||
special `.CLI_ARGS` variable. This is useful to forward arguments to another
|
special `.CLI_ARGS` variable. This is useful to forward arguments to another
|
||||||
command.
|
command.
|
||||||
|
|
||||||
@@ -589,6 +608,45 @@ tasks:
|
|||||||
- yarn {{.CLI_ARGS}}
|
- yarn {{.CLI_ARGS}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Doing task cleanup with `defer`
|
||||||
|
|
||||||
|
With the `defer` keyword, it's possible to schedule cleanup to be run once
|
||||||
|
the task finishes. The difference with just putting it as the last command is
|
||||||
|
that this command will run even when the task fails.
|
||||||
|
|
||||||
|
In the example below `rm -rf tmpdir/` will run even if the third command fails:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- mkdir -p tmpdir/
|
||||||
|
- defer: rm -rf tmpdir/
|
||||||
|
- echo 'Do work on tmpdir/'
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to move the cleanup command into another task, that's possible as
|
||||||
|
well:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- mkdir -p tmpdir/
|
||||||
|
- defer: { task: cleanup }
|
||||||
|
- echo 'Do work on tmpdir/'
|
||||||
|
|
||||||
|
cleanup: rm -rf tmpdir/
|
||||||
|
```
|
||||||
|
|
||||||
|
> NOTE: Due to the nature of how the
|
||||||
|
[Go's own `defer` work](https://go.dev/tour/flowcontrol/13), the deferred
|
||||||
|
commands are executed in the reverse order if you schedule multiple of them.
|
||||||
|
|
||||||
## Go's template engine
|
## Go's template engine
|
||||||
|
|
||||||
Task parse commands as [Go's template engine][gotemplate] before executing
|
Task parse commands as [Go's template engine][gotemplate] before executing
|
||||||
@@ -620,6 +678,9 @@ Task also adds the following functions:
|
|||||||
converts a string from `/` path format to `\`.
|
converts a string from `/` path format to `\`.
|
||||||
- `exeExt`: Returns the right executable extension for the current OS
|
- `exeExt`: Returns the right executable extension for the current OS
|
||||||
(`".exe"` for Windows, `""` for others).
|
(`".exe"` for Windows, `""` for others).
|
||||||
|
- `shellQuote`: Quotes a string to make it safe for use in shell scripts.
|
||||||
|
Task uses [this Go function](https://pkg.go.dev/mvdan.cc/sh/v3@v3.4.0/syntax#Quote)
|
||||||
|
for this. The Bash dialect is assumed.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -681,6 +742,8 @@ would print the following output:
|
|||||||
* test: Run all the go tests.
|
* test: Run all the go tests.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you want to see all tasks, there's a `--list-all` (alias `-a`) flag as well.
|
||||||
|
|
||||||
## Display summary of task
|
## Display summary of task
|
||||||
|
|
||||||
Running `task --summary task-name` will show a summary of a task.
|
Running `task --summary task-name` will show a summary of a task.
|
||||||
@@ -931,6 +994,28 @@ $ task default
|
|||||||
|
|
||||||
> The `output` option can also be specified by the `--output` or `-o` flags.
|
> The `output` option can also be specified by the `--output` or `-o` flags.
|
||||||
|
|
||||||
|
## Interactive CLI application
|
||||||
|
|
||||||
|
When running interactive CLI applications inside Task they can sometimes behave
|
||||||
|
weirdly, specially when the [output mode](#output-syntax) is set to something
|
||||||
|
other than `interleaved` (the default), or when interactive apps are ran in
|
||||||
|
parallel with other tasks.
|
||||||
|
|
||||||
|
The `interactive: true` tells Task this is an interactive application, and Task
|
||||||
|
will try to optimize for it:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
cmds:
|
||||||
|
- vim my-file.txt
|
||||||
|
interactive: true
|
||||||
|
```
|
||||||
|
|
||||||
|
If you still have problem running an interactive app through Task, please open
|
||||||
|
an issue about it.
|
||||||
|
|
||||||
## Short task syntax
|
## Short task syntax
|
||||||
|
|
||||||
Starting on Task v3, you can now write tasks with a shorter syntax if they
|
Starting on Task v3, you can now write tasks with a shorter syntax if they
|
||||||
|
|||||||
6
go.mod
6
go.mod
@@ -1,9 +1,9 @@
|
|||||||
module github.com/go-task/task/v3
|
module github.com/go-task/task/v3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.12.0
|
github.com/fatih/color v1.13.0
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.4.0
|
||||||
github.com/mattn/go-zglob v0.0.3
|
github.com/mattn/go-zglob v0.0.3
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/radovskyb/watcher v1.0.7
|
github.com/radovskyb/watcher v1.0.7
|
||||||
@@ -11,7 +11,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||||
mvdan.cc/sh/v3 v3.3.1
|
mvdan.cc/sh/v3 v3.4.2
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|||||||
44
go.sum
44
go.sum
@@ -1,30 +1,35 @@
|
|||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/creack/pty v1.1.13 h1:rTPnd/xocYRjutMfqide2zle1u96upp1gm6eUHKi7us=
|
github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
|
||||||
github.com/creack/pty v1.1.13/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
|
github.com/frankban/quicktest v1.13.1 h1:xVm/f9seEhZFL9+n5kv5XLrGwy6elc4V9v/XFY2vmd8=
|
||||||
|
github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
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/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.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To=
|
github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To=
|
||||||
github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||||
@@ -34,7 +39,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
|
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
|
||||||
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@@ -46,10 +53,13 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
|
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665 h1:QOQNt6vCjMpXE7JSK5VvAzJC1byuN3FgTNSBwf+CJgI=
|
||||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=
|
||||||
|
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -58,5 +68,5 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
|
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
|
||||||
mvdan.cc/sh/v3 v3.3.1 h1:aA0i7NZOc1oV5jfAH20FCz+QsmI/TX7FiAquC5Rdo5o=
|
mvdan.cc/sh/v3 v3.4.2 h1:d3TKODXfZ1bjWU/StENN+GDg5xOzNu5+C8AEu405E5U=
|
||||||
mvdan.cc/sh/v3 v3.3.1/go.mod h1:DpbFT2B4fXpKiq69fEoMe+71JrmUn5aUekYy9fNKnQw=
|
mvdan.cc/sh/v3 v3.4.2/go.mod h1:p/tqPPI4Epfk2rICAe2RoaNd8HBSJ8t9Y2DA9yQlbzY=
|
||||||
|
|||||||
38
help.go
38
help.go
@@ -9,11 +9,32 @@ import (
|
|||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrintTasksHelp prints help os tasks that have a description
|
// ListTasksWithDesc reports tasks that have a description spec.
|
||||||
func (e *Executor) PrintTasksHelp() {
|
func (e *Executor) ListTasksWithDesc() {
|
||||||
tasks := e.tasksWithDesc()
|
e.printTasks(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAllTasks reports all tasks, with or without a description spec.
|
||||||
|
func (e *Executor) ListAllTasks() {
|
||||||
|
e.printTasks(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) printTasks(listAll bool) {
|
||||||
|
var tasks []*taskfile.Task
|
||||||
|
if listAll {
|
||||||
|
tasks = e.allTaskNames()
|
||||||
|
} else {
|
||||||
|
tasks = e.tasksWithDesc()
|
||||||
|
}
|
||||||
|
|
||||||
if len(tasks) == 0 {
|
if len(tasks) == 0 {
|
||||||
e.Logger.Outf(logger.Yellow, "task: No tasks with description available")
|
if listAll {
|
||||||
|
e.Logger.Outf(logger.Yellow, "task: No tasks available")
|
||||||
|
} else {
|
||||||
|
e.Logger.Outf(logger.Yellow, "task: No tasks with description available. Try --list-all to list all tasks")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
|
e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
|
||||||
@@ -26,6 +47,15 @@ func (e *Executor) PrintTasksHelp() {
|
|||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
|
||||||
|
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
||||||
|
for _, task := range e.Taskfile.Tasks {
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
}
|
||||||
|
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
|
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 {
|
||||||
|
|||||||
7
init.go
7
init.go
@@ -3,7 +3,6 @@ package task
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
@@ -24,15 +23,15 @@ 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.yml")
|
f := filepath.Join(dir, "Taskfile.yaml")
|
||||||
|
|
||||||
if _, err := os.Stat(f); err == nil {
|
if _, err := os.Stat(f); err == nil {
|
||||||
return ErrTaskfileAlreadyExists
|
return ErrTaskfileAlreadyExists
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ioutil.WriteFile(f, []byte(defaultTaskfile), 0644); err != nil {
|
if err := os.WriteFile(f, []byte(defaultTaskfile), 0644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "Taskfile.yml created in the current directory\n")
|
fmt.Fprintf(w, "Taskfile.yaml created in the current directory\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
|||||||
|
|
||||||
r, err := interp.New(
|
r, err := interp.New(
|
||||||
interp.Params("-e"),
|
interp.Params("-e"),
|
||||||
interp.Dir(opts.Dir),
|
|
||||||
interp.Env(expand.ListEnviron(environ...)),
|
interp.Env(expand.ListEnviron(environ...)),
|
||||||
interp.OpenHandler(openHandler),
|
interp.OpenHandler(openHandler),
|
||||||
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
|
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
|
||||||
|
dirOption(opts.Dir),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -87,3 +87,24 @@ func openHandler(ctx context.Context, path string, flag int, perm os.FileMode) (
|
|||||||
}
|
}
|
||||||
return interp.DefaultOpenHandler()(ctx, path, flag, perm)
|
return interp.DefaultOpenHandler()(ctx, path, flag, perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dirOption(path string) interp.RunnerOption {
|
||||||
|
return func(r *interp.Runner) error {
|
||||||
|
err := interp.Dir(path)(r)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the specified directory doesn't exist, it will be created later.
|
||||||
|
// Therefore, even if `interp.Dir` method returns an error, the
|
||||||
|
// directory path should be set only when the directory cannot be found.
|
||||||
|
if absPath, _ := filepath.Abs(path); absPath != "" {
|
||||||
|
if _, err := os.Stat(absPath); os.IsNotExist(err) {
|
||||||
|
r.Dir = absPath
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -30,7 +29,7 @@ func (c *Checksum) IsUpToDate() (bool, error) {
|
|||||||
|
|
||||||
checksumFile := c.checksumFilePath()
|
checksumFile := c.checksumFilePath()
|
||||||
|
|
||||||
data, _ := ioutil.ReadFile(checksumFile)
|
data, _ := os.ReadFile(checksumFile)
|
||||||
oldMd5 := strings.TrimSpace(string(data))
|
oldMd5 := strings.TrimSpace(string(data))
|
||||||
|
|
||||||
sources, err := globs(c.TaskDir, c.Sources)
|
sources, err := globs(c.TaskDir, c.Sources)
|
||||||
@@ -45,7 +44,7 @@ func (c *Checksum) IsUpToDate() (bool, error) {
|
|||||||
|
|
||||||
if !c.Dry {
|
if !c.Dry {
|
||||||
_ = os.MkdirAll(filepath.Join(c.BaseDir, ".task", "checksum"), 0755)
|
_ = os.MkdirAll(filepath.Join(c.BaseDir, ".task", "checksum"), 0755)
|
||||||
if err = ioutil.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
|
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/go-task/slim-sprig"
|
sprig "github.com/go-task/slim-sprig"
|
||||||
|
"mvdan.cc/sh/v3/syntax"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -37,6 +38,9 @@ func init() {
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
|
"shellQuote": func(str string) (string, error) {
|
||||||
|
return syntax.Quote(str, syntax.LangBash)
|
||||||
|
},
|
||||||
// IsSH is deprecated.
|
// IsSH is deprecated.
|
||||||
"IsSH": func() bool { return true },
|
"IsSH": func() bool { return true },
|
||||||
}
|
}
|
||||||
|
|||||||
35
task.go
35
task.go
@@ -68,7 +68,7 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
|||||||
for _, c := range calls {
|
for _, c := range calls {
|
||||||
if _, ok := e.Taskfile.Tasks[c.Task]; !ok {
|
if _, ok := e.Taskfile.Tasks[c.Task]; !ok {
|
||||||
// FIXME: move to the main package
|
// FIXME: move to the main package
|
||||||
e.PrintTasksHelp()
|
e.ListTasksWithDesc()
|
||||||
return &taskNotFoundError{taskName: c.Task}
|
return &taskNotFoundError{taskName: c.Task}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,10 +105,6 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
|||||||
|
|
||||||
// Setup setups Executor's internal state
|
// Setup setups Executor's internal state
|
||||||
func (e *Executor) Setup() error {
|
func (e *Executor) Setup() error {
|
||||||
if e.Entrypoint == "" {
|
|
||||||
e.Entrypoint = "Taskfile.yml"
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
e.Taskfile, err = read.Taskfile(e.Dir, e.Entrypoint)
|
e.Taskfile, err = read.Taskfile(e.Dir, e.Entrypoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -310,11 +306,16 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
return e.startExecution(ctx, t, func(ctx context.Context) error {
|
return e.startExecution(ctx, t, func(ctx context.Context) error {
|
||||||
|
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" started`, call.Task)
|
||||||
if err := e.runDeps(ctx, t); err != nil {
|
if err := e.runDeps(ctx, t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !e.Force {
|
if !e.Force {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
|
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -338,6 +339,11 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := range t.Cmds {
|
for i := range t.Cmds {
|
||||||
|
if t.Cmds[i].Defer {
|
||||||
|
defer e.runDeferred(t, call, i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if err := e.runCommand(ctx, t, call, i); err != nil {
|
if err := e.runCommand(ctx, t, call, i); err != nil {
|
||||||
if err2 := e.statusOnError(t); err2 != nil {
|
if err2 := e.statusOnError(t); err2 != nil {
|
||||||
e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v", err2)
|
e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v", err2)
|
||||||
@@ -351,6 +357,7 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
return &taskRunError{t.Task, err}
|
return &taskRunError{t.Task, err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" finished`, call.Task)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -393,6 +400,15 @@ func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error {
|
|||||||
return g.Wait()
|
return g.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Executor) runDeferred(t *taskfile.Task, call taskfile.Call, i int) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := e.runCommand(ctx, t, call, i); err != nil {
|
||||||
|
e.Logger.VerboseErrf(logger.Yellow, `task: ignored error in deferred cmd: %s`, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfile.Call, i int) error {
|
func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfile.Call, i int) error {
|
||||||
cmd := t.Cmds[i]
|
cmd := t.Cmds[i]
|
||||||
|
|
||||||
@@ -415,8 +431,13 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
stdOut := e.Output.WrapWriter(e.Stdout, t.Prefix)
|
outputWrapper := e.Output
|
||||||
stdErr := e.Output.WrapWriter(e.Stderr, t.Prefix)
|
if t.Interactive {
|
||||||
|
outputWrapper = output.Interleaved{}
|
||||||
|
}
|
||||||
|
stdOut := outputWrapper.WrapWriter(e.Stdout, t.Prefix)
|
||||||
|
stdErr := outputWrapper.WrapWriter(e.Stderr, t.Prefix)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if _, ok := stdOut.(*os.File); !ok {
|
if _, ok := stdOut.(*os.File); !ok {
|
||||||
if closer, ok := stdOut.(io.Closer); ok {
|
if closer, ok := stdOut.(io.Closer); ok {
|
||||||
|
|||||||
247
task_test.go
247
task_test.go
@@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -24,10 +24,11 @@ func init() {
|
|||||||
// fileContentTest provides a basic reusable test-case for running a Taskfile
|
// fileContentTest provides a basic reusable test-case for running a Taskfile
|
||||||
// and inspect generated files.
|
// and inspect generated files.
|
||||||
type fileContentTest struct {
|
type fileContentTest struct {
|
||||||
Dir string
|
Dir string
|
||||||
Target string
|
Entrypoint string
|
||||||
TrimSpace bool
|
Target string
|
||||||
Files map[string]string
|
TrimSpace bool
|
||||||
|
Files map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fct fileContentTest) name(file string) string {
|
func (fct fileContentTest) name(file string) string {
|
||||||
@@ -40,16 +41,17 @@ func (fct fileContentTest) Run(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
e := &task.Executor{
|
e := &task.Executor{
|
||||||
Dir: fct.Dir,
|
Dir: fct.Dir,
|
||||||
Stdout: ioutil.Discard,
|
Entrypoint: fct.Entrypoint,
|
||||||
Stderr: ioutil.Discard,
|
Stdout: io.Discard,
|
||||||
|
Stderr: io.Discard,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup(), "e.Setup()")
|
assert.NoError(t, e.Setup(), "e.Setup()")
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: fct.Target}), "e.Run(target)")
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: fct.Target}), "e.Run(target)")
|
||||||
|
|
||||||
for name, expectContent := range fct.Files {
|
for name, expectContent := range fct.Files {
|
||||||
t.Run(fct.name(name), func(t *testing.T) {
|
t.Run(fct.name(name), func(t *testing.T) {
|
||||||
b, err := ioutil.ReadFile(filepath.Join(fct.Dir, name))
|
b, err := os.ReadFile(filepath.Join(fct.Dir, name))
|
||||||
assert.NoError(t, err, "Error reading file")
|
assert.NoError(t, err, "Error reading file")
|
||||||
s := string(b)
|
s := string(b)
|
||||||
if fct.TrimSpace {
|
if fct.TrimSpace {
|
||||||
@@ -63,8 +65,8 @@ func (fct fileContentTest) Run(t *testing.T) {
|
|||||||
func TestEmptyTask(t *testing.T) {
|
func TestEmptyTask(t *testing.T) {
|
||||||
e := &task.Executor{
|
e := &task.Executor{
|
||||||
Dir: "testdata/empty_task",
|
Dir: "testdata/empty_task",
|
||||||
Stdout: ioutil.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: ioutil.Discard,
|
Stderr: io.Discard,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup(), "e.Setup()")
|
assert.NoError(t, e.Setup(), "e.Setup()")
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
||||||
@@ -168,8 +170,8 @@ func TestVarsInvalidTmpl(t *testing.T) {
|
|||||||
|
|
||||||
e := &task.Executor{
|
e := &task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
Stdout: ioutil.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: ioutil.Discard,
|
Stderr: io.Discard,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup(), "e.Setup()")
|
assert.NoError(t, e.Setup(), "e.Setup()")
|
||||||
assert.EqualError(t, e.Run(context.Background(), taskfile.Call{Task: target}), expectError, "e.Run(target)")
|
assert.EqualError(t, e.Run(context.Background(), taskfile.Call{Task: target}), expectError, "e.Run(target)")
|
||||||
@@ -183,8 +185,8 @@ func TestConcurrency(t *testing.T) {
|
|||||||
|
|
||||||
e := &task.Executor{
|
e := &task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
Stdout: ioutil.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: ioutil.Discard,
|
Stderr: io.Discard,
|
||||||
Concurrency: 1,
|
Concurrency: 1,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup(), "e.Setup()")
|
assert.NoError(t, e.Setup(), "e.Setup()")
|
||||||
@@ -236,8 +238,8 @@ func TestDeps(t *testing.T) {
|
|||||||
|
|
||||||
e := &task.Executor{
|
e := &task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
Stdout: ioutil.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: ioutil.Discard,
|
Stderr: io.Discard,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
||||||
@@ -516,10 +518,58 @@ func TestLabelInList(t *testing.T) {
|
|||||||
Stderr: &buff,
|
Stderr: &buff,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
e.PrintTasksHelp()
|
e.ListTasksWithDesc()
|
||||||
assert.Contains(t, buff.String(), "foobar")
|
assert.Contains(t, buff.String(), "foobar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// task -al case 1: listAll list all tasks
|
||||||
|
func TestListAllShowsNoDesc(t *testing.T) {
|
||||||
|
const dir = "testdata/list_mixed_desc"
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
var title string
|
||||||
|
e.ListAllTasks()
|
||||||
|
for _, title = range []string{
|
||||||
|
"foo",
|
||||||
|
"voo",
|
||||||
|
"doo",
|
||||||
|
} {
|
||||||
|
assert.Contains(t, buff.String(), title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// task -al case 2: !listAll list some tasks (only those with desc)
|
||||||
|
func TestListCanListDescOnly(t *testing.T) {
|
||||||
|
const dir = "testdata/list_mixed_desc"
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
e.ListTasksWithDesc()
|
||||||
|
|
||||||
|
var title string
|
||||||
|
assert.Contains(t, buff.String(), "foo")
|
||||||
|
for _, title = range []string{
|
||||||
|
"voo",
|
||||||
|
"doo",
|
||||||
|
} {
|
||||||
|
assert.NotContains(t, buff.String(), title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStatusVariables(t *testing.T) {
|
func TestStatusVariables(t *testing.T) {
|
||||||
const dir = "testdata/status_vars"
|
const dir = "testdata/status_vars"
|
||||||
|
|
||||||
@@ -550,20 +600,21 @@ 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.yml")
|
var file = filepath.Join(dir, "Taskfile.yaml")
|
||||||
|
|
||||||
_ = os.Remove(file)
|
_ = os.Remove(file)
|
||||||
if _, err := os.Stat(file); err == nil {
|
if _, err := os.Stat(file); err == nil {
|
||||||
t.Errorf("Taskfile.yml should not exist")
|
t.Errorf("Taskfile.yaml should not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := task.InitTaskfile(ioutil.Discard, dir); err != nil {
|
if err := task.InitTaskfile(io.Discard, dir); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(file); err != nil {
|
if _, err := os.Stat(file); err != nil {
|
||||||
t.Errorf("Taskfile.yml should exist")
|
t.Errorf("Taskfile.yaml should exist")
|
||||||
}
|
}
|
||||||
|
_ = os.Remove(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCyclicDep(t *testing.T) {
|
func TestCyclicDep(t *testing.T) {
|
||||||
@@ -571,8 +622,8 @@ func TestCyclicDep(t *testing.T) {
|
|||||||
|
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
Stdout: ioutil.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: ioutil.Discard,
|
Stderr: io.Discard,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
assert.IsType(t, &task.MaximumTaskCallExceededError{}, e.Run(context.Background(), taskfile.Call{Task: "task-1"}))
|
assert.IsType(t, &task.MaximumTaskCallExceededError{}, e.Run(context.Background(), taskfile.Call{Task: "task-1"}))
|
||||||
@@ -590,8 +641,8 @@ func TestTaskVersion(t *testing.T) {
|
|||||||
t.Run(test.Dir, func(t *testing.T) {
|
t.Run(test.Dir, func(t *testing.T) {
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: test.Dir,
|
Dir: test.Dir,
|
||||||
Stdout: ioutil.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: ioutil.Discard,
|
Stderr: io.Discard,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
assert.Equal(t, test.Version, e.Taskfile.Version)
|
assert.Equal(t, test.Version, e.Taskfile.Version)
|
||||||
@@ -605,8 +656,8 @@ func TestTaskIgnoreErrors(t *testing.T) {
|
|||||||
|
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
Stdout: ioutil.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: ioutil.Discard,
|
Stderr: io.Discard,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
@@ -668,8 +719,8 @@ func TestDryChecksum(t *testing.T) {
|
|||||||
|
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
Stdout: ioutil.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: ioutil.Discard,
|
Stderr: io.Discard,
|
||||||
Dry: true,
|
Dry: true,
|
||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
@@ -755,6 +806,56 @@ func TestIncludesCallingRoot(t *testing.T) {
|
|||||||
tt.Run(t)
|
tt.Run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIncludesOptional(t *testing.T) {
|
||||||
|
tt := fileContentTest{
|
||||||
|
Dir: "testdata/includes_optional",
|
||||||
|
Target: "default",
|
||||||
|
TrimSpace: true,
|
||||||
|
Files: map[string]string{
|
||||||
|
"called_dep.txt": "called_dep",
|
||||||
|
}}
|
||||||
|
tt.Run(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIncludesOptionalImplicitFalse(t *testing.T) {
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: "testdata/includes_optional_implicit_false",
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIncludesOptionalExplicitFalse(t *testing.T) {
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: "testdata/includes_optional_explicit_false",
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIncludesFromCustomTaskfile(t *testing.T) {
|
||||||
|
tt := fileContentTest{
|
||||||
|
Dir: "testdata/includes_yaml",
|
||||||
|
Entrypoint: "Custom.ext",
|
||||||
|
Target: "default",
|
||||||
|
TrimSpace: true,
|
||||||
|
Files: map[string]string{
|
||||||
|
"main.txt": "main",
|
||||||
|
"included_with_yaml_extension.txt": "included_with_yaml_extension",
|
||||||
|
"included_with_custom_file.txt": "included_with_custom_file",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tt.Run(t)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSummary(t *testing.T) {
|
func TestSummary(t *testing.T) {
|
||||||
const dir = "testdata/summary"
|
const dir = "testdata/summary"
|
||||||
|
|
||||||
@@ -769,7 +870,7 @@ 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 := ioutil.ReadFile(filepath.Join(dir, "task-with-summary.txt"))
|
data, err := os.ReadFile(filepath.Join(dir, "task-with-summary.txt"))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
expectedOutput := string(data)
|
expectedOutput := string(data)
|
||||||
@@ -842,6 +943,33 @@ func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) {
|
|||||||
_ = os.RemoveAll(toBeCreated)
|
_ = os.RemoveAll(toBeCreated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDynamicVariablesRunOnTheNewCreatedDir(t *testing.T) {
|
||||||
|
const expected = "created"
|
||||||
|
const dir = "testdata/dir/dynamic_var_on_created_dir/"
|
||||||
|
const toBeCreated = dir + expected
|
||||||
|
const target = "default"
|
||||||
|
var out bytes.Buffer
|
||||||
|
e := &task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &out,
|
||||||
|
Stderr: &out,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the directory to be created doesn't actually exist.
|
||||||
|
_ = os.RemoveAll(toBeCreated)
|
||||||
|
if _, err := os.Stat(toBeCreated); err == nil {
|
||||||
|
t.Errorf("Directory should not exist: %v", err)
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: target}))
|
||||||
|
|
||||||
|
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||||
|
assert.Equal(t, expected, got, "Mismatch in the working directory")
|
||||||
|
|
||||||
|
// Clean-up after ourselves only if no error.
|
||||||
|
_ = os.RemoveAll(toBeCreated)
|
||||||
|
}
|
||||||
|
|
||||||
func TestDynamicVariablesShouldRunOnTheTaskDir(t *testing.T) {
|
func TestDynamicVariablesShouldRunOnTheTaskDir(t *testing.T) {
|
||||||
tt := fileContentTest{
|
tt := fileContentTest{
|
||||||
Dir: "testdata/dir/dynamic_var",
|
Dir: "testdata/dir/dynamic_var",
|
||||||
@@ -860,8 +988,8 @@ func TestDynamicVariablesShouldRunOnTheTaskDir(t *testing.T) {
|
|||||||
func TestDisplaysErrorOnUnsupportedVersion(t *testing.T) {
|
func TestDisplaysErrorOnUnsupportedVersion(t *testing.T) {
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: "testdata/version/v1",
|
Dir: "testdata/version/v1",
|
||||||
Stdout: ioutil.Discard,
|
Stdout: io.Discard,
|
||||||
Stderr: ioutil.Discard,
|
Stderr: io.Discard,
|
||||||
}
|
}
|
||||||
err := e.Setup()
|
err := e.Setup()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
@@ -990,3 +1118,56 @@ func TestRunOnlyRunsJobsHashOnce(t *testing.T) {
|
|||||||
}
|
}
|
||||||
tt.Run(t)
|
tt.Run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeferredCmds(t *testing.T) {
|
||||||
|
const dir = "testdata/deferred"
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
expectedOutputOrder := strings.TrimSpace(`
|
||||||
|
task: [task-2] echo 'cmd ran'
|
||||||
|
cmd ran
|
||||||
|
task: [task-2] exit 1
|
||||||
|
task: [task-2] echo 'failing' && exit 2
|
||||||
|
failing
|
||||||
|
task: [task-2] echo 'echo ran'
|
||||||
|
echo ran
|
||||||
|
task: [task-1] echo 'task-1 ran successfully'
|
||||||
|
task-1 ran successfully
|
||||||
|
`)
|
||||||
|
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "task-2"}))
|
||||||
|
fmt.Println(buff.String())
|
||||||
|
assert.Contains(t, buff.String(), expectedOutputOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIgnoreNilElements(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
dir string
|
||||||
|
}{
|
||||||
|
{"nil cmd", "testdata/ignore_nil_elements/cmds"},
|
||||||
|
{"nil dep", "testdata/ignore_nil_elements/deps"},
|
||||||
|
{"nil include", "testdata/ignore_nil_elements/includes"},
|
||||||
|
{"nil precondition", "testdata/ignore_nil_elements/preconditions"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: test.dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
Silent: true,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
||||||
|
assert.Equal(t, "string-slice-1\n", buff.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package taskfile
|
package taskfile
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Cmd is a task command
|
// Cmd is a task command
|
||||||
type Cmd struct {
|
type Cmd struct {
|
||||||
Cmd string
|
Cmd string
|
||||||
@@ -11,6 +7,7 @@ type Cmd struct {
|
|||||||
Task string
|
Task string
|
||||||
Vars *Vars
|
Vars *Vars
|
||||||
IgnoreError bool
|
IgnoreError bool
|
||||||
|
Defer bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dep is a task dependency
|
// Dep is a task dependency
|
||||||
@@ -23,11 +20,7 @@ type Dep struct {
|
|||||||
func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
var cmd string
|
var cmd string
|
||||||
if err := unmarshal(&cmd); err == nil {
|
if err := unmarshal(&cmd); err == nil {
|
||||||
if strings.HasPrefix(cmd, "^") {
|
c.Cmd = cmd
|
||||||
c.Task = strings.TrimPrefix(cmd, "^")
|
|
||||||
} else {
|
|
||||||
c.Cmd = cmd
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var cmdStruct struct {
|
var cmdStruct struct {
|
||||||
@@ -41,6 +34,23 @@ func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
c.IgnoreError = cmdStruct.IgnoreError
|
c.IgnoreError = cmdStruct.IgnoreError
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
var deferredCmd struct {
|
||||||
|
Defer string
|
||||||
|
}
|
||||||
|
if err := unmarshal(&deferredCmd); err == nil && deferredCmd.Defer != "" {
|
||||||
|
c.Defer = true
|
||||||
|
c.Cmd = deferredCmd.Defer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var deferredCall struct {
|
||||||
|
Defer Call
|
||||||
|
}
|
||||||
|
if err := unmarshal(&deferredCall); err == nil && deferredCall.Defer.Task != "" {
|
||||||
|
c.Defer = true
|
||||||
|
c.Task = deferredCall.Defer.Task
|
||||||
|
c.Vars = deferredCall.Defer.Vars
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var taskCall struct {
|
var taskCall struct {
|
||||||
Task string
|
Task string
|
||||||
Vars *Vars
|
Vars *Vars
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
type IncludedTaskfile struct {
|
type IncludedTaskfile struct {
|
||||||
Taskfile string
|
Taskfile string
|
||||||
Dir string
|
Dir string
|
||||||
|
Optional bool
|
||||||
AdvancedImport bool
|
AdvancedImport bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,12 +93,14 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
|
|||||||
var includedTaskfile struct {
|
var includedTaskfile struct {
|
||||||
Taskfile string
|
Taskfile string
|
||||||
Dir string
|
Dir string
|
||||||
|
Optional bool
|
||||||
}
|
}
|
||||||
if err := unmarshal(&includedTaskfile); err != nil {
|
if err := unmarshal(&includedTaskfile); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
it.Dir = includedTaskfile.Dir
|
|
||||||
it.Taskfile = includedTaskfile.Taskfile
|
it.Taskfile = includedTaskfile.Taskfile
|
||||||
|
it.Dir = includedTaskfile.Dir
|
||||||
|
it.Optional = includedTaskfile.Optional
|
||||||
it.AdvancedImport = true
|
it.AdvancedImport = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
|
|||||||
dep.Task = taskNameWithNamespace(dep.Task, namespaces...)
|
dep.Task = taskNameWithNamespace(dep.Task, namespaces...)
|
||||||
}
|
}
|
||||||
for _, cmd := range v.Cmds {
|
for _, cmd := range v.Cmds {
|
||||||
if cmd.Task != "" {
|
if cmd != nil && cmd.Task != "" {
|
||||||
cmd.Task = taskNameWithNamespace(cmd.Task, namespaces...)
|
cmd.Task = taskNameWithNamespace(cmd.Task, namespaces...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
@@ -18,14 +19,26 @@ var (
|
|||||||
ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
|
ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
|
||||||
// ErrIncludedTaskfilesCantHaveDotenvs is returned when a included Taskfile contains dotenvs
|
// ErrIncludedTaskfilesCantHaveDotenvs is returned when a included Taskfile contains dotenvs
|
||||||
ErrIncludedTaskfilesCantHaveDotenvs = errors.New("task: Included Taskfiles can't have dotenv declarations. Please, move the dotenv declaration to the main Taskfile")
|
ErrIncludedTaskfilesCantHaveDotenvs = errors.New("task: Included Taskfiles can't have dotenv declarations. Please, move the dotenv declaration to the main Taskfile")
|
||||||
|
|
||||||
|
defaultTaskfiles = []string{"Taskfile.yml", "Taskfile.yaml"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Taskfile reads a Taskfile for a given directory
|
// Taskfile reads a Taskfile for a given directory
|
||||||
|
// Uses current dir when dir is left empty. Uses Taskfile.yml
|
||||||
|
// or Taskfile.yaml when entrypoint is left empty
|
||||||
func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
|
func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
|
||||||
path := filepath.Join(dir, entrypoint)
|
if dir == "" {
|
||||||
if _, err := os.Stat(path); err != nil {
|
d, err := os.Getwd()
|
||||||
return nil, fmt.Errorf(`task: No Taskfile found on "%s". Use "task --init" to create a new one`, path)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dir = d
|
||||||
}
|
}
|
||||||
|
path, err := exists(filepath.Join(dir, entrypoint))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
t, err := readTaskfile(path)
|
t, err := readTaskfile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -42,6 +55,7 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
|
|||||||
includedTask = taskfile.IncludedTaskfile{
|
includedTask = taskfile.IncludedTaskfile{
|
||||||
Taskfile: tr.Replace(includedTask.Taskfile),
|
Taskfile: tr.Replace(includedTask.Taskfile),
|
||||||
Dir: tr.Replace(includedTask.Dir),
|
Dir: tr.Replace(includedTask.Dir),
|
||||||
|
Optional: includedTask.Optional,
|
||||||
AdvancedImport: includedTask.AdvancedImport,
|
AdvancedImport: includedTask.AdvancedImport,
|
||||||
}
|
}
|
||||||
if err := tr.Err(); err != nil {
|
if err := tr.Err(); err != nil {
|
||||||
@@ -49,19 +63,22 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if filepath.IsAbs(includedTask.Taskfile) {
|
path, err := execext.Expand(includedTask.Taskfile)
|
||||||
path = includedTask.Taskfile
|
|
||||||
} else {
|
|
||||||
path = filepath.Join(dir, includedTask.Taskfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if info.IsDir() {
|
if !filepath.IsAbs(path) {
|
||||||
path = filepath.Join(path, "Taskfile.yml")
|
path = filepath.Join(dir, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path, err = exists(path)
|
||||||
|
if err != nil {
|
||||||
|
if includedTask.Optional {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
includedTaskfile, err := readTaskfile(path)
|
includedTaskfile, err := readTaskfile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -134,3 +151,22 @@ func readTaskfile(file string) (*taskfile.Taskfile, error) {
|
|||||||
var t taskfile.Taskfile
|
var t taskfile.Taskfile
|
||||||
return &t, yaml.NewDecoder(f).Decode(&t)
|
return &t, yaml.NewDecoder(f).Decode(&t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func exists(path string) (string, error) {
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if fi.Mode().IsRegular() {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range defaultTaskfiles {
|
||||||
|
fpath := filepath.Join(path, n)
|
||||||
|
if _, err := os.Stat(fpath); err == nil {
|
||||||
|
return fpath, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf(`task: No Taskfile found in "%s". Use "task --init" to create a new one`, path)
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type Task struct {
|
|||||||
Vars *Vars
|
Vars *Vars
|
||||||
Env *Vars
|
Env *Vars
|
||||||
Silent bool
|
Silent bool
|
||||||
|
Interactive bool
|
||||||
Method string
|
Method string
|
||||||
Prefix string
|
Prefix string
|
||||||
IgnoreError bool
|
IgnoreError bool
|
||||||
@@ -59,6 +60,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
Vars *Vars
|
Vars *Vars
|
||||||
Env *Vars
|
Env *Vars
|
||||||
Silent bool
|
Silent bool
|
||||||
|
Interactive bool
|
||||||
Method string
|
Method string
|
||||||
Prefix string
|
Prefix string
|
||||||
IgnoreError bool `yaml:"ignore_error"`
|
IgnoreError bool `yaml:"ignore_error"`
|
||||||
@@ -80,6 +82,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
t.Vars = task.Vars
|
t.Vars = task.Vars
|
||||||
t.Env = task.Env
|
t.Env = task.Env
|
||||||
t.Silent = task.Silent
|
t.Silent = task.Silent
|
||||||
|
t.Interactive = task.Interactive
|
||||||
t.Method = task.Method
|
t.Method = task.Method
|
||||||
t.Prefix = task.Prefix
|
t.Prefix = task.Prefix
|
||||||
t.IgnoreError = task.IgnoreError
|
t.IgnoreError = task.IgnoreError
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ vars:
|
|||||||
PARAM1: VALUE1
|
PARAM1: VALUE1
|
||||||
PARAM2: VALUE2
|
PARAM2: VALUE2
|
||||||
`
|
`
|
||||||
|
yamlDeferredCall = `defer: { task: some_task, vars: { PARAM1: "var" } }`
|
||||||
|
yamlDeferredCmd = `defer: echo 'test'`
|
||||||
)
|
)
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
content string
|
content string
|
||||||
@@ -41,6 +43,21 @@ vars:
|
|||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
yamlDeferredCmd,
|
||||||
|
&taskfile.Cmd{},
|
||||||
|
&taskfile.Cmd{Cmd: "echo 'test'", Defer: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
yamlDeferredCall,
|
||||||
|
&taskfile.Cmd{},
|
||||||
|
&taskfile.Cmd{Task: "some_task", Vars: &taskfile.Vars{
|
||||||
|
Keys: []string{"PARAM1"},
|
||||||
|
Mapping: map[string]taskfile.Var{
|
||||||
|
"PARAM1": taskfile.Var{Static: "var"},
|
||||||
|
},
|
||||||
|
}, Defer: true},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
yamlDep,
|
yamlDep,
|
||||||
&taskfile.Dep{},
|
&taskfile.Dep{},
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package taskfile
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
@@ -108,11 +107,7 @@ type Var struct {
|
|||||||
func (v *Var) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
func (v *Var) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
var str string
|
var str string
|
||||||
if err := unmarshal(&str); err == nil {
|
if err := unmarshal(&str); err == nil {
|
||||||
if strings.HasPrefix(str, "$") {
|
v.Static = str
|
||||||
v.Sh = strings.TrimPrefix(str, "$")
|
|
||||||
} else {
|
|
||||||
v.Static = str
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
testdata/deferred/Taskfile.yml
vendored
Normal file
12
testdata/deferred/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
task-1:
|
||||||
|
- echo 'task-1 ran {{.PARAM}}'
|
||||||
|
|
||||||
|
task-2:
|
||||||
|
- defer: { task: "task-1", vars: { PARAM: "successfully" } }
|
||||||
|
- defer: echo 'echo ran'
|
||||||
|
- defer: echo 'failing' && exit 2
|
||||||
|
- echo 'cmd ran'
|
||||||
|
- exit 1
|
||||||
10
testdata/dir/dynamic_var_on_created_dir/Taskfile.yml
vendored
Normal file
10
testdata/dir/dynamic_var_on_created_dir/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
dir: created
|
||||||
|
cmds:
|
||||||
|
- echo {{.TASK_DIR}}
|
||||||
|
vars:
|
||||||
|
TASK_DIR:
|
||||||
|
sh: echo $(pwd)
|
||||||
7
testdata/ignore_nil_elements/cmds/Taskfile.yml
vendored
Normal file
7
testdata/ignore_nil_elements/cmds/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- echo "string-slice-1"
|
||||||
|
-
|
||||||
11
testdata/ignore_nil_elements/deps/Taskfile.yml
vendored
Normal file
11
testdata/ignore_nil_elements/deps/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
deps:
|
||||||
|
-
|
||||||
|
- task: dep
|
||||||
|
|
||||||
|
dep:
|
||||||
|
cmds:
|
||||||
|
- echo "string-slice-1"
|
||||||
9
testdata/ignore_nil_elements/includes/Taskfile.yml
vendored
Normal file
9
testdata/ignore_nil_elements/includes/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
inc: inc.yml
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- task: inc:default
|
||||||
7
testdata/ignore_nil_elements/includes/inc.yml
vendored
Normal file
7
testdata/ignore_nil_elements/includes/inc.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
-
|
||||||
|
- echo "string-slice-1"
|
||||||
9
testdata/ignore_nil_elements/preconditions/Taskfile.yml
vendored
Normal file
9
testdata/ignore_nil_elements/preconditions/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
preconditions:
|
||||||
|
-
|
||||||
|
- sh: "[ 1 = 1 ]"
|
||||||
|
cmds:
|
||||||
|
- echo "string-slice-1"
|
||||||
1
testdata/includes_optional/.gitignore
vendored
Normal file
1
testdata/includes_optional/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.txt
|
||||||
11
testdata/includes_optional/Taskfile.yml
vendored
Normal file
11
testdata/includes_optional/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
included:
|
||||||
|
taskfile: TaskfileOptional.yml
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- echo "called_dep" > called_dep.txt
|
||||||
11
testdata/includes_optional_explicit_false/Taskfile.yml
vendored
Normal file
11
testdata/includes_optional_explicit_false/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
included:
|
||||||
|
taskfile: TaskfileOptional.yml
|
||||||
|
optional: false
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- echo "Hello, world!"
|
||||||
9
testdata/includes_optional_implicit_false/Taskfile.yml
vendored
Normal file
9
testdata/includes_optional_implicit_false/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
included: TaskfileOptional.yml
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- echo "Hello, world!"
|
||||||
1
testdata/includes_yaml/.gitignore
vendored
Normal file
1
testdata/includes_yaml/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.txt
|
||||||
16
testdata/includes_yaml/Custom.ext
vendored
Normal file
16
testdata/includes_yaml/Custom.ext
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
included: ./included
|
||||||
|
custom: ./included/custom.yaml
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- task: gen
|
||||||
|
- task: included:gen
|
||||||
|
- task: custom:gen
|
||||||
|
|
||||||
|
gen:
|
||||||
|
cmds:
|
||||||
|
- echo main > main.txt
|
||||||
6
testdata/includes_yaml/included/Taskfile.yaml
vendored
Normal file
6
testdata/includes_yaml/included/Taskfile.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
gen:
|
||||||
|
cmds:
|
||||||
|
- echo included_with_yaml_extension > included_with_yaml_extension.txt
|
||||||
6
testdata/includes_yaml/included/custom.yaml
vendored
Normal file
6
testdata/includes_yaml/included/custom.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
gen:
|
||||||
|
cmds:
|
||||||
|
- echo included_with_custom_file > included_with_custom_file.txt
|
||||||
12
testdata/list_mixed_desc/Taskfile.yml
vendored
Normal file
12
testdata/list_mixed_desc/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
foo:
|
||||||
|
label: "foobar"
|
||||||
|
desc: "foo has desc and label"
|
||||||
|
|
||||||
|
voo:
|
||||||
|
label: "voo has no desc"
|
||||||
|
|
||||||
|
doo:
|
||||||
|
label: "doo has desc, no label"
|
||||||
3
testdata/vars/v2/Taskfile.yml
vendored
3
testdata/vars/v2/Taskfile.yml
vendored
@@ -35,7 +35,8 @@ tasks:
|
|||||||
- echo '{{.TASK}}' > task_name.txt
|
- echo '{{.TASK}}' > task_name.txt
|
||||||
vars:
|
vars:
|
||||||
FOO: foo
|
FOO: foo
|
||||||
BAR: $echo bar
|
BAR:
|
||||||
|
sh: echo bar
|
||||||
BAZ:
|
BAZ:
|
||||||
sh: echo baz
|
sh: echo baz
|
||||||
TMPL_FOO: "{{.FOO}}"
|
TMPL_FOO: "{{.FOO}}"
|
||||||
|
|||||||
3
testdata/vars/v2/Taskvars.yml
vendored
3
testdata/vars/v2/Taskvars.yml
vendored
@@ -1,5 +1,6 @@
|
|||||||
FOO2: foo2
|
FOO2: foo2
|
||||||
BAR2: $echo bar2
|
BAR2:
|
||||||
|
sh: echo bar2
|
||||||
BAZ2:
|
BAZ2:
|
||||||
sh: echo baz2
|
sh: echo baz2
|
||||||
TMPL2_FOO: "{{.FOO}}"
|
TMPL2_FOO: "{{.FOO}}"
|
||||||
|
|||||||
35
variables.go
35
variables.go
@@ -56,6 +56,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
Vars: nil,
|
Vars: nil,
|
||||||
Env: nil,
|
Env: nil,
|
||||||
Silent: origTask.Silent,
|
Silent: origTask.Silent,
|
||||||
|
Interactive: origTask.Interactive,
|
||||||
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,
|
||||||
@@ -90,34 +91,44 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(origTask.Cmds) > 0 {
|
if len(origTask.Cmds) > 0 {
|
||||||
new.Cmds = make([]*taskfile.Cmd, len(origTask.Cmds))
|
new.Cmds = make([]*taskfile.Cmd, 0, len(origTask.Cmds))
|
||||||
for i, cmd := range origTask.Cmds {
|
for _, cmd := range origTask.Cmds {
|
||||||
new.Cmds[i] = &taskfile.Cmd{
|
if cmd == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
new.Cmds = append(new.Cmds, &taskfile.Cmd{
|
||||||
Task: r.Replace(cmd.Task),
|
Task: r.Replace(cmd.Task),
|
||||||
Silent: cmd.Silent,
|
Silent: cmd.Silent,
|
||||||
Cmd: r.Replace(cmd.Cmd),
|
Cmd: r.Replace(cmd.Cmd),
|
||||||
Vars: r.ReplaceVars(cmd.Vars),
|
Vars: r.ReplaceVars(cmd.Vars),
|
||||||
IgnoreError: cmd.IgnoreError,
|
IgnoreError: cmd.IgnoreError,
|
||||||
}
|
Defer: cmd.Defer,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(origTask.Deps) > 0 {
|
if len(origTask.Deps) > 0 {
|
||||||
new.Deps = make([]*taskfile.Dep, len(origTask.Deps))
|
new.Deps = make([]*taskfile.Dep, 0, len(origTask.Deps))
|
||||||
for i, dep := range origTask.Deps {
|
for _, dep := range origTask.Deps {
|
||||||
new.Deps[i] = &taskfile.Dep{
|
if dep == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
new.Deps = append(new.Deps, &taskfile.Dep{
|
||||||
Task: r.Replace(dep.Task),
|
Task: r.Replace(dep.Task),
|
||||||
Vars: r.ReplaceVars(dep.Vars),
|
Vars: r.ReplaceVars(dep.Vars),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(origTask.Preconditions) > 0 {
|
if len(origTask.Preconditions) > 0 {
|
||||||
new.Preconditions = make([]*taskfile.Precondition, len(origTask.Preconditions))
|
new.Preconditions = make([]*taskfile.Precondition, 0, len(origTask.Preconditions))
|
||||||
for i, precond := range origTask.Preconditions {
|
for _, precond := range origTask.Preconditions {
|
||||||
new.Preconditions[i] = &taskfile.Precondition{
|
if precond == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
new.Preconditions = append(new.Preconditions, &taskfile.Precondition{
|
||||||
Sh: r.Replace(precond.Sh),
|
Sh: r.Replace(precond.Sh),
|
||||||
Msg: r.Replace(precond.Msg),
|
Msg: r.Replace(precond.Msg),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user