mirror of
https://github.com/go-task/task.git
synced 2026-05-18 13:15:41 +02:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5395921acc | ||
|
|
8a73411803 | ||
|
|
6c21568447 | ||
|
|
330722335d | ||
|
|
99397dfe98 | ||
|
|
1157b213de | ||
|
|
fa40e8a762 | ||
|
|
98e0cea469 | ||
|
|
508ff717c9 | ||
|
|
c7ba42b81a | ||
|
|
bb9d582255 | ||
|
|
38a06dad8e | ||
|
|
beb9f42215 | ||
|
|
df251de33e | ||
|
|
9a3d2bc3aa | ||
|
|
1ef5cf71d0 | ||
|
|
65fdb618aa | ||
|
|
3b44da323b | ||
|
|
2c20407e1b | ||
|
|
27455fc4c8 | ||
|
|
971c3e3a01 | ||
|
|
67b94798b7 | ||
|
|
c465234aa9 | ||
|
|
07a0b8938f | ||
|
|
ba81181eb7 | ||
|
|
e2b0789b0c | ||
|
|
2c6969d572 | ||
|
|
8d0754af4d | ||
|
|
81148c312e |
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
@@ -10,11 +10,15 @@ on:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.21.x, 1.22.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
go-version: ${{matrix.go-version}}
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -3,7 +3,7 @@ name: goreleaser
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Test
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.20.x, 1.21.x]
|
||||
go-version: [1.21.x, 1.22.x]
|
||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{matrix.platform}}
|
||||
steps:
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
## v3.35.0 - 2024-02-28
|
||||
|
||||
- Added support for
|
||||
[wildcards in task names](https://taskfile.dev/usage/#wildcard-arguments)
|
||||
(#836, #1489 by @pd93).
|
||||
- Added the ability to
|
||||
[run Taskfiles via stdin](https://taskfile.dev/usage/#reading-a-taskfile-from-stdin)
|
||||
(#655, #1483 by @pd93).
|
||||
- Bumped minimum Go version to 1.21 (#1500 by @pd93).
|
||||
- Fixed bug related to the `--list` flag (#1509, #1512 by @pd93, #1514, #1520 by
|
||||
@pd93).
|
||||
- Add mention on the documentation to the fact that the variable declaration
|
||||
order is respected (#1510 by @kirkrodrigues).
|
||||
- Improved style guide docs (#1495 by @iwittkau).
|
||||
- Removed duplicated entry for `requires` on the API docs (#1491 by
|
||||
@teatimeguest).
|
||||
|
||||
## v3.34.1 - 2024-01-27
|
||||
|
||||
- Fixed prompt regression on
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
)
|
||||
|
||||
// Parse parses command line argument: tasks and global variables
|
||||
func Parse(args ...string) ([]ast.Call, *ast.Vars) {
|
||||
calls := []ast.Call{}
|
||||
func Parse(args ...string) ([]*ast.Call, *ast.Vars) {
|
||||
calls := []*ast.Call{}
|
||||
globals := &ast.Vars{}
|
||||
|
||||
for _, arg := range args {
|
||||
if !strings.Contains(arg, "=") {
|
||||
calls = append(calls, ast.Call{Task: arg})
|
||||
calls = append(calls, &ast.Call{Task: arg})
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ import (
|
||||
func TestArgs(t *testing.T) {
|
||||
tests := []struct {
|
||||
Args []string
|
||||
ExpectedCalls []ast.Call
|
||||
ExpectedCalls []*ast.Call
|
||||
ExpectedGlobals *ast.Vars
|
||||
}{
|
||||
{
|
||||
Args: []string{"task-a", "task-b", "task-c"},
|
||||
ExpectedCalls: []ast.Call{
|
||||
ExpectedCalls: []*ast.Call{
|
||||
{Task: "task-a"},
|
||||
{Task: "task-b"},
|
||||
{Task: "task-c"},
|
||||
@@ -27,7 +27,7 @@ func TestArgs(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
|
||||
ExpectedCalls: []ast.Call{
|
||||
ExpectedCalls: []*ast.Call{
|
||||
{Task: "task-a"},
|
||||
{Task: "task-b"},
|
||||
{Task: "task-c"},
|
||||
@@ -45,7 +45,7 @@ func TestArgs(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Args: []string{"task-a", "CONTENT=with some spaces"},
|
||||
ExpectedCalls: []ast.Call{
|
||||
ExpectedCalls: []*ast.Call{
|
||||
{Task: "task-a"},
|
||||
},
|
||||
ExpectedGlobals: &ast.Vars{
|
||||
@@ -59,7 +59,7 @@ func TestArgs(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Args: []string{"FOO=bar", "task-a", "task-b"},
|
||||
ExpectedCalls: []ast.Call{
|
||||
ExpectedCalls: []*ast.Call{
|
||||
{Task: "task-a"},
|
||||
{Task: "task-b"},
|
||||
},
|
||||
@@ -74,15 +74,15 @@ func TestArgs(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Args: nil,
|
||||
ExpectedCalls: []ast.Call{},
|
||||
ExpectedCalls: []*ast.Call{},
|
||||
},
|
||||
{
|
||||
Args: []string{},
|
||||
ExpectedCalls: []ast.Call{},
|
||||
ExpectedCalls: []*ast.Call{},
|
||||
},
|
||||
{
|
||||
Args: []string{"FOO=bar", "BAR=baz"},
|
||||
ExpectedCalls: []ast.Call{},
|
||||
ExpectedCalls: []*ast.Call{},
|
||||
ExpectedGlobals: &ast.Vars{
|
||||
OrderedMap: omap.FromMapWithOrder(
|
||||
map[string]ast.Var{
|
||||
|
||||
@@ -264,11 +264,6 @@ func run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if (listOptions.ShouldListTasks()) && flags.silent {
|
||||
e.ListTaskNames(flags.listAll)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := e.Setup(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -279,6 +274,10 @@ func run() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if (listOptions.ShouldListTasks()) && flags.silent {
|
||||
return e.ListTaskNames(flags.listAll)
|
||||
}
|
||||
|
||||
if listOptions.ShouldListTasks() {
|
||||
foundTasks, err := e.ListTasks(listOptions)
|
||||
if err != nil {
|
||||
@@ -291,7 +290,7 @@ func run() error {
|
||||
}
|
||||
|
||||
var (
|
||||
calls []ast.Call
|
||||
calls []*ast.Call
|
||||
globals *ast.Vars
|
||||
)
|
||||
|
||||
@@ -304,7 +303,7 @@ func run() error {
|
||||
|
||||
// If there are no calls, run the default task instead
|
||||
if len(calls) == 0 {
|
||||
calls = append(calls, ast.Call{Task: "default"})
|
||||
calls = append(calls, &ast.Call{Task: "default"})
|
||||
}
|
||||
|
||||
globals.Set("CLI_ARGS", ast.Var{Value: cliArgs})
|
||||
|
||||
@@ -11,7 +11,7 @@ toc_max_heading_level: 5
|
||||
|
||||
Task command line tool has the following syntax:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
task [--flags] [tasks...] [-- CLI_ARGS...]
|
||||
```
|
||||
|
||||
@@ -214,6 +214,19 @@ vars:
|
||||
|
||||
:::
|
||||
|
||||
:::info
|
||||
|
||||
In a variables map, variables defined later may reference variables defined
|
||||
earlier (declaration order is respected):
|
||||
|
||||
```yaml
|
||||
vars:
|
||||
FIRST_VAR: "hello"
|
||||
SECOND_VAR: "{{.FIRST_VAR}} world"
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Task
|
||||
|
||||
| Attribute | Type | Default | Description |
|
||||
@@ -228,7 +241,6 @@ vars:
|
||||
| `sources` | `[]string` | | A list of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs. |
|
||||
| `generates` | `[]string` | | A list of files meant to be generated by this task. Relevant for `timestamp` method. Can be file paths or star globs. |
|
||||
| `status` | `[]string` | | A list of commands to check if this task should run. The task is skipped otherwise. This overrides `method`, `sources` and `generates`. |
|
||||
| `requires` | `[]string` | | A list of variables which should be set if this task is to run, if any of these variables are unset the task will error and not run. |
|
||||
| `preconditions` | [`[]Precondition`](#precondition) | | A list of commands to check if this task should run. If a condition is not met, the task will error. |
|
||||
| `requires` | [`Requires`](#requires) | | A list of required variables which should be set if this task is to run, if any variables listed are unset the task will error and not run. |
|
||||
| `dir` | `string` | | The directory in which this task should run. Defaults to the current working directory. |
|
||||
|
||||
@@ -5,6 +5,23 @@ sidebar_position: 14
|
||||
|
||||
# Changelog
|
||||
|
||||
## v3.35.0 - 2024-02-28
|
||||
|
||||
- Added support for
|
||||
[wildcards in task names](https://taskfile.dev/usage/#wildcard-arguments)
|
||||
(#836, #1489 by @pd93).
|
||||
- Added the ability to
|
||||
[run Taskfiles via stdin](https://taskfile.dev/usage/#reading-a-taskfile-from-stdin)
|
||||
(#655, #1483 by @pd93).
|
||||
- Bumped minimum Go version to 1.21 (#1500 by @pd93).
|
||||
- Fixed bug related to the `--list` flag (#1509, #1512 by @pd93, #1514, #1520 by
|
||||
@pd93).
|
||||
- Add mention on the documentation to the fact that the variable declaration
|
||||
order is respected (#1510 by @kirkrodrigues).
|
||||
- Improved style guide docs (#1495 by @iwittkau).
|
||||
- Removed duplicated entry for `requires` on the API docs (#1491 by
|
||||
@teatimeguest).
|
||||
|
||||
## v3.34.1 - 2024-01-27
|
||||
|
||||
- Fixed prompt regression on
|
||||
|
||||
@@ -69,7 +69,7 @@ tasks:
|
||||
- ./foo-printer.bash
|
||||
```
|
||||
|
||||
```bash
|
||||
```shell
|
||||
#!/bin/bash
|
||||
a=foo
|
||||
echo $a
|
||||
|
||||
@@ -14,7 +14,7 @@ Task offers many installation methods. Check out the available methods below.
|
||||
If you're on macOS or Linux and have [Homebrew][homebrew] installed, getting
|
||||
Task is as simple as running:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
brew install go-task/tap/go-task
|
||||
```
|
||||
|
||||
@@ -25,24 +25,25 @@ Recently, Task was also made available
|
||||
[on the official Homebrew repository](https://formulae.brew.sh/formula/go-task),
|
||||
so you also have that option if you prefer:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
brew install go-task
|
||||
```
|
||||
|
||||
### Tea
|
||||
|
||||
If you're on macOS or Linux and have [tea][tea] installed, getting
|
||||
Task is as simple as running:
|
||||
If you're on macOS or Linux and have [tea][tea] installed, getting Task is as
|
||||
simple as running:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
tea task
|
||||
```
|
||||
|
||||
or, if you have tea’s magic enabled:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
task
|
||||
```
|
||||
|
||||
This installation method is community owned. After a new release of Task, they
|
||||
are automatically released by tea in a minimum of time.
|
||||
|
||||
@@ -51,7 +52,7 @@ are automatically released by tea in a minimum of time.
|
||||
Task is available in [Snapcraft][snapcraft], but keep in mind that your Linux
|
||||
distribution should allow classic confinement for Snaps to Task work right:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sudo snap install task --classic
|
||||
```
|
||||
|
||||
@@ -60,7 +61,7 @@ sudo snap install task --classic
|
||||
If you're on Windows and have [Chocolatey][choco] installed, getting Task is as
|
||||
simple as running:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
choco install go-task
|
||||
```
|
||||
|
||||
@@ -71,7 +72,7 @@ This installation method is community owned.
|
||||
If you're on Windows and have [Scoop][scoop] installed, getting Task is as
|
||||
simple as running:
|
||||
|
||||
```cmd
|
||||
```shell
|
||||
scoop install task
|
||||
```
|
||||
|
||||
@@ -84,7 +85,7 @@ If you're on Arch Linux you can install Task from
|
||||
[AUR](https://aur.archlinux.org/packages/go-task-bin) using your favorite
|
||||
package manager such as `yay`, `pacaur` or `yaourt`:
|
||||
|
||||
```cmd
|
||||
```shell
|
||||
yay -S go-task-bin
|
||||
```
|
||||
|
||||
@@ -93,7 +94,7 @@ Alternatively, there's
|
||||
the source code instead of downloading the binary from the
|
||||
[releases page](https://github.com/go-task/task/releases):
|
||||
|
||||
```cmd
|
||||
```shell
|
||||
yay -S go-task
|
||||
```
|
||||
|
||||
@@ -105,7 +106,7 @@ If you're on Fedora Linux you can install Task from the official
|
||||
[Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/)
|
||||
repository using `dnf`:
|
||||
|
||||
```cmd
|
||||
```shell
|
||||
sudo dnf install go-task
|
||||
```
|
||||
|
||||
@@ -118,7 +119,7 @@ take some time until it's available in
|
||||
If you're on NixOS or have Nix installed you can install Task from
|
||||
[nixpkgs](https://github.com/NixOS/nixpkgs):
|
||||
|
||||
```cmd
|
||||
```shell
|
||||
nix-env -iA nixpkgs.go-task
|
||||
```
|
||||
|
||||
@@ -131,7 +132,7 @@ take some time until it's available in
|
||||
You can also use Node and npm to install Task by installing
|
||||
[this package](https://www.npmjs.com/package/@go-task/cli).
|
||||
|
||||
```bash
|
||||
```shell
|
||||
npm install -g @go-task/cli
|
||||
```
|
||||
|
||||
@@ -141,7 +142,7 @@ If you are using Windows and installed the
|
||||
[winget](https://github.com/microsoft/winget-cli) package management tool, you
|
||||
can install Task from [winget-pkgs](https://github.com/microsoft/winget-pkgs).
|
||||
|
||||
```bash
|
||||
```shell
|
||||
winget install Task.Task
|
||||
```
|
||||
|
||||
@@ -165,7 +166,7 @@ easy generation of this script.
|
||||
By default, it installs on the `./bin` directory relative to the working
|
||||
directory:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
|
||||
```
|
||||
|
||||
@@ -173,7 +174,7 @@ It is possible to override the installation directory with the `-b` parameter.
|
||||
On Linux, common choices are `~/.local/bin` and `~/bin` to install for the
|
||||
current user or `/usr/local/bin` to install for all users:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
|
||||
```
|
||||
|
||||
@@ -209,13 +210,13 @@ setup. You can find the minimum required version of Go in the
|
||||
|
||||
You can then install the latest release globally by running:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
go install github.com/go-task/task/v3/cmd/task@latest
|
||||
```
|
||||
|
||||
Or you can install into another directory:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
env GOBIN=/bin go install github.com/go-task/task/v3/cmd/task@latest
|
||||
```
|
||||
|
||||
@@ -239,7 +240,7 @@ First, ensure that you installed bash-completion using your package manager.
|
||||
|
||||
Make the completion file executable:
|
||||
|
||||
```
|
||||
```shell
|
||||
chmod +x path/to/task.bash
|
||||
```
|
||||
|
||||
@@ -278,7 +279,7 @@ mv path/to/task.fish ~/.config/fish/completions/task.fish
|
||||
|
||||
Open your profile script with:
|
||||
|
||||
```
|
||||
```powershell
|
||||
mkdir -Path (Split-Path -Parent $profile) -ErrorAction SilentlyContinue
|
||||
notepad $profile
|
||||
```
|
||||
|
||||
@@ -3,27 +3,28 @@ slug: /styleguide/
|
||||
sidebar_position: 10
|
||||
---
|
||||
|
||||
# Styleguide
|
||||
# Style guide
|
||||
|
||||
This is the official Task styleguide for `Taskfile.yml` files. This guide
|
||||
contains some basic instructions to keep your Taskfile clean and familiar to
|
||||
other users.
|
||||
This is the official style guide for `Taskfile.yml` files. It provides basic
|
||||
instructions for keeping your Taskfiles clean and familiar to other users.
|
||||
|
||||
This contains general guidelines, but they don't necessarily need to be strictly
|
||||
followed. Feel free to disagree and proceed differently at some point if you
|
||||
need or want to. Also, feel free to open issues or pull requests with
|
||||
improvements to this guide.
|
||||
This guide contains general guidelines, but they do not necessarily need to be
|
||||
followed strictly. Feel free to disagree and do things differently if you need
|
||||
or want to. Any improvements to this guide are welcome! Please open an issue or
|
||||
create a pull request to contribute.
|
||||
|
||||
## Use the correct order of keywords
|
||||
## Use the suggested ordering of the main sections
|
||||
|
||||
- `version:`
|
||||
- `includes:`
|
||||
- Configuration ones, like `output:`, `silent:`, `method:` and `run:`
|
||||
- `vars:`
|
||||
- `env:`, `dotenv:`
|
||||
- `tasks:`
|
||||
```yaml
|
||||
version:
|
||||
includes:
|
||||
# optional configurations (output, silent, method, run, etc.)
|
||||
vars:
|
||||
env: # followed or replaced by dotenv
|
||||
tasks:
|
||||
```
|
||||
|
||||
## Use 2 spaces for indentation
|
||||
## Use two spaces for indentation
|
||||
|
||||
This is the most common convention for YAML files, and Task follows it.
|
||||
|
||||
@@ -42,7 +43,7 @@ tasks:
|
||||
- echo 'foo'
|
||||
```
|
||||
|
||||
## Separate with spaces the mains sections
|
||||
## Separate the main sections with empty lines
|
||||
|
||||
```yaml
|
||||
# bad
|
||||
@@ -76,7 +77,7 @@ tasks:
|
||||
# ...
|
||||
```
|
||||
|
||||
## Add spaces between tasks
|
||||
## Separate tasks with empty lines
|
||||
|
||||
```yaml
|
||||
# bad
|
||||
@@ -111,7 +112,7 @@ tasks:
|
||||
- echo 'baz'
|
||||
```
|
||||
|
||||
## Use upper-case variable names
|
||||
## Use only uppercase letters for variable names
|
||||
|
||||
```yaml
|
||||
# bad
|
||||
@@ -138,7 +139,7 @@ tasks:
|
||||
- go build -o {{.BINARY_NAME}} .
|
||||
```
|
||||
|
||||
## Don't wrap vars in spaces when templating
|
||||
## Avoid using whitespace when templating variables
|
||||
|
||||
```yaml
|
||||
# bad
|
||||
@@ -159,9 +160,10 @@ tasks:
|
||||
- echo '{{.MESSAGE}}'
|
||||
```
|
||||
|
||||
This convention is also used by most people for any Go templating.
|
||||
This convention is also commonly used in templates for the Go programming
|
||||
language.
|
||||
|
||||
## Separate task name words with a dash
|
||||
## Use kebab case for task names
|
||||
|
||||
```yaml
|
||||
# bad
|
||||
@@ -182,7 +184,7 @@ tasks:
|
||||
- echo 'Do something'
|
||||
```
|
||||
|
||||
## Use colon for task namespacing
|
||||
## Use a colon to separate the task namespace and name
|
||||
|
||||
```yaml
|
||||
# good
|
||||
@@ -200,7 +202,7 @@ tasks:
|
||||
|
||||
This is also done automatically when using included Taskfiles.
|
||||
|
||||
## Prefer external scripts over complex multi-line commands
|
||||
## Prefer using external scripts instead of multi-line commands
|
||||
|
||||
```yaml
|
||||
# bad
|
||||
|
||||
@@ -27,7 +27,7 @@ tasks:
|
||||
|
||||
Running the tasks is as simple as running:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
task assets build
|
||||
```
|
||||
|
||||
@@ -118,6 +118,18 @@ tasks:
|
||||
|
||||
:::
|
||||
|
||||
### Reading a Taskfile from stdin
|
||||
|
||||
Taskfile also supports reading from stdin. This is useful if you are generating
|
||||
Taskfiles dynamically and don't want write them to disk. This works just like
|
||||
any other program that supports stdin. For example:
|
||||
|
||||
```shell
|
||||
task < <(cat ./Taskfile.yml)
|
||||
# OR
|
||||
cat ./Taskfile.yml | task
|
||||
```
|
||||
|
||||
## Environment variables
|
||||
|
||||
### Task
|
||||
@@ -162,11 +174,11 @@ variables, as you can see in the [Variables](#variables) section.
|
||||
You can also ask Task to include `.env` like files by using the `dotenv:`
|
||||
setting:
|
||||
|
||||
```bash title=".env"
|
||||
```shell title=".env"
|
||||
KEYNAME=VALUE
|
||||
```
|
||||
|
||||
```bash title="testing/.env"
|
||||
```shell title="testing/.env"
|
||||
ENDPOINT=testing.com
|
||||
```
|
||||
|
||||
@@ -699,7 +711,7 @@ path like `tmp/task` that will be interpreted as relative to the project
|
||||
directory, or an absolute or home path like `/tmp/.task` or `~/.task`
|
||||
(subdirectories will be created for each project).
|
||||
|
||||
```bash
|
||||
```shell
|
||||
export TASK_TEMP_DIR='~/.task'
|
||||
```
|
||||
|
||||
@@ -950,7 +962,7 @@ listed below in order of importance (i.e. most important first):
|
||||
|
||||
Example of sending parameters with environment variables:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ TASK_VARIABLE=a-value task do-something
|
||||
```
|
||||
|
||||
@@ -964,7 +976,7 @@ Since some shells do not support the above syntax to set environment variables
|
||||
(Windows) tasks also accept a similar style when not at the beginning of the
|
||||
command.
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ task write-file FILE=file.txt "CONTENT=Hello, World!" print "MESSAGE=All done!"
|
||||
```
|
||||
|
||||
@@ -1192,7 +1204,7 @@ If `--` is given in the CLI, all following parameters are added to a special
|
||||
|
||||
The below example will run `yarn install`.
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ task yarn -- install
|
||||
```
|
||||
|
||||
@@ -1205,6 +1217,53 @@ tasks:
|
||||
- yarn {{.CLI_ARGS}}
|
||||
```
|
||||
|
||||
## Wildcard arguments
|
||||
|
||||
Another way to parse arguments into a task is to use a wildcard in your task's
|
||||
name. Wildcards are denoted by an asterisk (`*`) and can be used multiple times
|
||||
in a task's name to pass in multiple arguments.
|
||||
|
||||
Matching arguments will be captured and stored in the `.MATCH` variable and can
|
||||
then be used in your task's commands like any other variable. This variable is
|
||||
an array of strings and so will need to be indexed to access the individual
|
||||
arguments. We suggest creating a named variable for each argument to make it
|
||||
clear what they contain:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
echo-*:
|
||||
vars:
|
||||
TEXT: '{{index .MATCH 0}}'
|
||||
cmds:
|
||||
- echo {{.TEXT}}
|
||||
|
||||
run-*-*:
|
||||
vars:
|
||||
ARG_1: '{{index .MATCH 0}}'
|
||||
ARG_2: '{{index .MATCH 1}}'
|
||||
cmds:
|
||||
- echo {{.ARG_1}} {{.ARG_2}}
|
||||
```
|
||||
|
||||
```shell
|
||||
# This call matches the "echo-*" task and the string "hello" is captured by the
|
||||
# wildcard and stored in the .MATCH variable. We then index the .MATCH array and
|
||||
# store the result in the .TEXT variable which is then echoed out in the cmds.
|
||||
$ task echo-hello
|
||||
hello
|
||||
# You can use whitespace in your arguments as long as you quote the task name
|
||||
$ task "echo-hello world"
|
||||
hello world
|
||||
# And you can pass multiple arguments
|
||||
$ task run-foo-bar
|
||||
foo bar
|
||||
```
|
||||
|
||||
If multiple matching tasks are found, an error occurs. If you are using included
|
||||
Taskfiles, tasks in parent files will be considered first.
|
||||
|
||||
## Doing task cleanup with `defer`
|
||||
|
||||
With the `defer` keyword, it's possible to schedule cleanup to be run once the
|
||||
@@ -1353,7 +1412,7 @@ tasks:
|
||||
|
||||
would print the following output:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
* build: Build the go binary.
|
||||
* test: Run all the go tests.
|
||||
```
|
||||
@@ -1486,7 +1545,7 @@ tasks:
|
||||
- echo 'dangerous command'
|
||||
```
|
||||
|
||||
```bash
|
||||
```shell
|
||||
❯ task dangerous
|
||||
task: "This is a dangerous command... Do you want to continue?" [y/N]
|
||||
```
|
||||
@@ -1495,7 +1554,7 @@ Warning prompts are called before executing a task. If a prompt is denied Task
|
||||
will exit with [exit code](/api#exit-codes) 205. If approved, Task will continue
|
||||
as normal.
|
||||
|
||||
```bash
|
||||
```shell
|
||||
❯ task example
|
||||
not dangerous command
|
||||
task: "This is a dangerous command. Do you want to continue?" [y/N]
|
||||
@@ -1532,14 +1591,14 @@ tasks:
|
||||
|
||||
Normally this will be printed:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
echo "Print something"
|
||||
Print something
|
||||
```
|
||||
|
||||
With silent mode on, the below will be printed instead:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
Print something
|
||||
```
|
||||
|
||||
@@ -1686,7 +1745,7 @@ tasks:
|
||||
silent: true
|
||||
```
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ task default
|
||||
::group::default
|
||||
Hello, World!
|
||||
@@ -1711,7 +1770,7 @@ tasks:
|
||||
errors: echo 'output-of-errors' && exit 1
|
||||
```
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ task passes
|
||||
$ task errors
|
||||
output-of-errors
|
||||
@@ -1744,7 +1803,7 @@ tasks:
|
||||
silent: true
|
||||
```
|
||||
|
||||
```bash
|
||||
```shell
|
||||
$ task default
|
||||
[print-foo] foo
|
||||
[print-bar] bar
|
||||
@@ -1829,8 +1888,8 @@ task again. This requires the `sources` attribute to be given, so task knows
|
||||
which files to watch.
|
||||
|
||||
The default watch interval is 5 seconds, but it's possible to change it by
|
||||
either setting `interval: '500ms'` in the root of the Taskfile passing it as an
|
||||
argument like `--interval=500ms`.
|
||||
either setting `interval: '500ms'` in the root of the Taskfile or by passing it
|
||||
as an argument like `--interval=500ms`.
|
||||
|
||||
Also, it's possible to set `watch: true` in a given task and it'll automatically
|
||||
run in watch mode:
|
||||
|
||||
@@ -254,7 +254,12 @@ const config: Config = {
|
||||
},
|
||||
prism: {
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme
|
||||
darkTheme: darkCodeTheme,
|
||||
additionalLanguages: [
|
||||
"bash", // aka. shell
|
||||
"json",
|
||||
"powershell"
|
||||
]
|
||||
},
|
||||
// NOTE(@andreynering): Don't worry, these keys are meant to be public =)
|
||||
algolia: {
|
||||
|
||||
@@ -65,15 +65,15 @@ func (err *TaskInternalError) Code() int {
|
||||
return CodeTaskInternal
|
||||
}
|
||||
|
||||
// TaskNameConflictError is returned when multiple tasks with the same name or
|
||||
// TaskNameConflictError is returned when multiple tasks with a matching name or
|
||||
// alias are found.
|
||||
type TaskNameConflictError struct {
|
||||
AliasName string
|
||||
Call string
|
||||
TaskNames []string
|
||||
}
|
||||
|
||||
func (err *TaskNameConflictError) Error() string {
|
||||
return fmt.Sprintf(`task: Multiple tasks (%s) with alias %q found`, strings.Join(err.TaskNames, ", "), err.AliasName)
|
||||
return fmt.Sprintf(`task: Found multiple tasks (%s) that match %q`, strings.Join(err.TaskNames, ", "), err.Call)
|
||||
}
|
||||
|
||||
func (err *TaskNameConflictError) Code() int {
|
||||
|
||||
9
go.mod
9
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/go-task/task/v3
|
||||
|
||||
go 1.20
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
@@ -15,11 +15,10 @@ require (
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/zeebo/xxh3 v1.0.2
|
||||
golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/term v0.16.0
|
||||
golang.org/x/term v0.17.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/sh/v3 v3.7.0
|
||||
mvdan.cc/sh/v3 v3.8.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -28,6 +27,6 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
|
||||
29
go.sum
29
go.sum
@@ -1,21 +1,26 @@
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
@@ -29,7 +34,8 @@ 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/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
|
||||
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
||||
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY=
|
||||
github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -43,23 +49,22 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e h1:723BNChdd0c2Wk6WOE320qGBiPtYx0F0Bbm1kriShfE=
|
||||
golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg=
|
||||
mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8=
|
||||
mvdan.cc/sh/v3 v3.8.0 h1:ZxuJipLZwr/HLbASonmXtcvvC9HXY9d2lXZHnKGjFc8=
|
||||
mvdan.cc/sh/v3 v3.8.0/go.mod h1:w04623xkgBVo7/IUK89E0g8hBykgEpN0vgOj3RJr6MY=
|
||||
|
||||
11
help.go
11
help.go
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
@@ -121,14 +120,7 @@ func (e *Executor) ListTasks(o ListOptions) (bool, error) {
|
||||
// ListTaskNames prints only the task names in a Taskfile.
|
||||
// Only tasks with a non-empty description are printed if allTasks is false.
|
||||
// Otherwise, all task names are printed.
|
||||
func (e *Executor) ListTaskNames(allTasks bool) {
|
||||
// if called from cmd/task.go, e.Taskfile has not yet been parsed
|
||||
if e.Taskfile == nil {
|
||||
if err := e.readTaskfile(); err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
func (e *Executor) ListTaskNames(allTasks bool) error {
|
||||
// use stdout if no output defined
|
||||
var w io.Writer = os.Stdout
|
||||
if e.Stdout != nil {
|
||||
@@ -157,6 +149,7 @@ func (e *Executor) ListTaskNames(allTasks bool) {
|
||||
for _, t := range taskNames {
|
||||
fmt.Fprintln(w, t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool) (*editors.Taskfile, error) {
|
||||
|
||||
@@ -36,12 +36,12 @@ func (c *Compiler) GetTaskfileVariables() (*ast.Vars, error) {
|
||||
return c.getVariables(nil, nil, true)
|
||||
}
|
||||
|
||||
func (c *Compiler) GetVariables(t *ast.Task, call ast.Call) (*ast.Vars, error) {
|
||||
return c.getVariables(t, &call, true)
|
||||
func (c *Compiler) GetVariables(t *ast.Task, call *ast.Call) (*ast.Vars, error) {
|
||||
return c.getVariables(t, call, true)
|
||||
}
|
||||
|
||||
func (c *Compiler) FastGetVariables(t *ast.Task, call ast.Call) (*ast.Vars, error) {
|
||||
return c.getVariables(t, &call, false)
|
||||
func (c *Compiler) FastGetVariables(t *ast.Task, call *ast.Call) (*ast.Vars, error) {
|
||||
return c.getVariables(t, call, false)
|
||||
}
|
||||
|
||||
func (c *Compiler) getVariables(t *ast.Task, call *ast.Call, evaluateShVars bool) (*ast.Vars, error) {
|
||||
|
||||
26
internal/exp/maps.go
Normal file
26
internal/exp/maps.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// This package is intended as a place to copy functions from the
|
||||
// golang.org/x/exp package. Copying these functions allows us to rely on our
|
||||
// own code instead of an external package that may change unpredictably in the
|
||||
// future.
|
||||
//
|
||||
// It also prevents problems with transitive dependencies whereby a
|
||||
// package that imports Task (and therefore our version of golang.org/x/exp)
|
||||
// cannot import a different version of golang.org/x/exp.
|
||||
//
|
||||
// Finally, it serves as a place to track functions that may be able to be
|
||||
// removed in the future if they are added to the standard library. This is also
|
||||
// why this package is under the internal directory since these functions are
|
||||
// not intended to be used outside of Task.
|
||||
package exp
|
||||
|
||||
import "cmp"
|
||||
|
||||
// Keys is a copy of https://pkg.go.dev/golang.org/x/exp@v0.0.0-20240103183307-be819d1f06fc/maps#Keys.
|
||||
// This is not yet included in the standard library. See https://github.com/golang/go/issues/61538.
|
||||
func Keys[K cmp.Ordered, V any](m map[K]V) []K {
|
||||
var keys []K
|
||||
for key := range m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/spf13/pflag"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/go-task/task/v3/internal/logger"
|
||||
)
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
"github.com/go-task/task/v3/internal/term"
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
package omap
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/internal/deepcopy"
|
||||
"github.com/go-task/task/v3/internal/exp"
|
||||
)
|
||||
|
||||
// An OrderedMap is a wrapper around a regular map that maintains an ordered
|
||||
// list of the map's keys. This allows you to run deterministic and ordered
|
||||
// operations on the map such as printing/serializing/iterating.
|
||||
type OrderedMap[K constraints.Ordered, V any] struct {
|
||||
type OrderedMap[K cmp.Ordered, V any] struct {
|
||||
s []K
|
||||
m map[K]V
|
||||
}
|
||||
|
||||
// New will create a new OrderedMap of the given type and return it.
|
||||
func New[K constraints.Ordered, V any]() OrderedMap[K, V] {
|
||||
func New[K cmp.Ordered, V any]() OrderedMap[K, V] {
|
||||
return OrderedMap[K, V]{
|
||||
s: make([]K, 0),
|
||||
m: make(map[K]V),
|
||||
@@ -29,14 +29,14 @@ func New[K constraints.Ordered, V any]() OrderedMap[K, V] {
|
||||
|
||||
// FromMap will create a new OrderedMap from the given map. Since Golang maps
|
||||
// are unordered, the order of the created OrderedMap will be random.
|
||||
func FromMap[K constraints.Ordered, V any](m map[K]V) OrderedMap[K, V] {
|
||||
func FromMap[K cmp.Ordered, V any](m map[K]V) OrderedMap[K, V] {
|
||||
om := New[K, V]()
|
||||
om.m = m
|
||||
om.s = maps.Keys(m)
|
||||
om.s = exp.Keys(m)
|
||||
return om
|
||||
}
|
||||
|
||||
func FromMapWithOrder[K constraints.Ordered, V any](m map[K]V, order []K) OrderedMap[K, V] {
|
||||
func FromMapWithOrder[K cmp.Ordered, V any](m map[K]V, order []K) OrderedMap[K, V] {
|
||||
om := New[K, V]()
|
||||
if len(m) != len(order) {
|
||||
panic("length of map and order must be equal")
|
||||
|
||||
@@ -43,13 +43,7 @@ func TestSortFunc(t *testing.T) {
|
||||
om.Set(1, "one")
|
||||
om.Set(2, "two")
|
||||
om.SortFunc(func(a, b int) int {
|
||||
if a < b {
|
||||
return 1
|
||||
}
|
||||
if a > b {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
return b - a
|
||||
})
|
||||
assert.Equal(t, []int{3, 2, 1}, om.s)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package slicesext
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/constraints"
|
||||
"golang.org/x/exp/slices"
|
||||
"cmp"
|
||||
"slices"
|
||||
)
|
||||
|
||||
func UniqueJoin[T constraints.Ordered](ss ...[]T) []T {
|
||||
func UniqueJoin[T cmp.Ordered](ss ...[]T) []T {
|
||||
var length int
|
||||
for _, s := range ss {
|
||||
length += len(s)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/go-task/task/v3/taskfile/ast"
|
||||
)
|
||||
|
||||
func PrintTasks(l *logger.Logger, t *ast.Taskfile, c []ast.Call) {
|
||||
func PrintTasks(l *logger.Logger, t *ast.Taskfile, c []*ast.Call) {
|
||||
for i, call := range c {
|
||||
PrintSpaceBetweenSummaries(l, i)
|
||||
PrintTask(l, t.Tasks.Get(call.Task))
|
||||
|
||||
@@ -163,7 +163,7 @@ func TestPrintAllWithSpaces(t *testing.T) {
|
||||
|
||||
summary.PrintTasks(&l,
|
||||
&ast.Taskfile{Tasks: tasks},
|
||||
[]ast.Call{{Task: "t1"}, {Task: "t2"}, {Task: "t3"}})
|
||||
[]*ast.Call{{Task: "t1"}, {Task: "t2"}, {Task: "t3"}})
|
||||
|
||||
assert.True(t, strings.HasPrefix(buffer.String(), "task: t1"))
|
||||
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n\n\ntask: t2")
|
||||
|
||||
@@ -2,11 +2,10 @@ package templater
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"maps"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/go-task/task/v3/taskfile/ast"
|
||||
)
|
||||
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@go-task/cli",
|
||||
"version": "3.34.1",
|
||||
"version": "3.35.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@go-task/cli",
|
||||
"version": "3.34.1",
|
||||
"version": "3.35.0",
|
||||
"description": "A task runner / simpler Make alternative written in Go",
|
||||
"scripts": {
|
||||
"postinstall": "go-npm install",
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/go-task/task/v3/taskfile/ast"
|
||||
)
|
||||
|
||||
func (e *Executor) areTaskRequiredVarsSet(ctx context.Context, t *ast.Task, call ast.Call) error {
|
||||
func (e *Executor) areTaskRequiredVarsSet(ctx context.Context, t *ast.Task, call *ast.Call) error {
|
||||
if t.Requires == nil || len(t.Requires.Vars) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
17
setup.go
17
setup.go
@@ -67,22 +67,19 @@ func (e *Executor) setCurrentDir() error {
|
||||
return err
|
||||
}
|
||||
e.Dir = wd
|
||||
} else {
|
||||
var err error
|
||||
e.Dir, err = filepath.Abs(e.Dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Search for a taskfile
|
||||
root, err := taskfile.ExistsWalk(e.Dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.Dir = filepath.Dir(root)
|
||||
e.Entrypoint = filepath.Base(root)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Executor) readTaskfile() error {
|
||||
uri := filepath.Join(e.Dir, e.Entrypoint)
|
||||
node, err := taskfile.NewNode(uri, e.Insecure)
|
||||
node, err := taskfile.NewRootNode(e.Dir, e.Entrypoint, e.Insecure)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
// Status returns an error if any the of given tasks is not up-to-date
|
||||
func (e *Executor) Status(ctx context.Context, calls ...ast.Call) error {
|
||||
func (e *Executor) Status(ctx context.Context, calls ...*ast.Call) error {
|
||||
for _, call := range calls {
|
||||
|
||||
// Compile the task
|
||||
|
||||
44
task.go
44
task.go
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -24,7 +25,6 @@ import (
|
||||
"github.com/go-task/task/v3/taskfile/ast"
|
||||
|
||||
"github.com/sajari/fuzzy"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@@ -80,7 +80,7 @@ type Executor struct {
|
||||
}
|
||||
|
||||
// Run runs Task
|
||||
func (e *Executor) Run(ctx context.Context, calls ...ast.Call) error {
|
||||
func (e *Executor) Run(ctx context.Context, calls ...*ast.Call) error {
|
||||
// check if given tasks exist
|
||||
for _, call := range calls {
|
||||
task, err := e.GetTask(call)
|
||||
@@ -142,7 +142,7 @@ func (e *Executor) Run(ctx context.Context, calls ...ast.Call) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Executor) splitRegularAndWatchCalls(calls ...ast.Call) (regularCalls []ast.Call, watchCalls []ast.Call, err error) {
|
||||
func (e *Executor) splitRegularAndWatchCalls(calls ...*ast.Call) (regularCalls []*ast.Call, watchCalls []*ast.Call, err error) {
|
||||
for _, c := range calls {
|
||||
t, err := e.GetTask(c)
|
||||
if err != nil {
|
||||
@@ -159,7 +159,7 @@ func (e *Executor) splitRegularAndWatchCalls(calls ...ast.Call) (regularCalls []
|
||||
}
|
||||
|
||||
// RunTask runs a task by its name
|
||||
func (e *Executor) RunTask(ctx context.Context, call ast.Call) error {
|
||||
func (e *Executor) RunTask(ctx context.Context, call *ast.Call) error {
|
||||
t, err := e.FastCompiledTask(call)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -296,7 +296,7 @@ func (e *Executor) runDeps(ctx context.Context, t *ast.Task) error {
|
||||
for _, d := range t.Deps {
|
||||
d := d
|
||||
g.Go(func() error {
|
||||
err := e.RunTask(ctx, ast.Call{Task: d.Task, Vars: d.Vars, Silent: d.Silent, Indirect: true})
|
||||
err := e.RunTask(ctx, &ast.Call{Task: d.Task, Vars: d.Vars, Silent: d.Silent, Indirect: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -307,7 +307,7 @@ func (e *Executor) runDeps(ctx context.Context, t *ast.Task) error {
|
||||
return g.Wait()
|
||||
}
|
||||
|
||||
func (e *Executor) runDeferred(t *ast.Task, call ast.Call, i int) {
|
||||
func (e *Executor) runDeferred(t *ast.Task, call *ast.Call, i int) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -316,7 +316,7 @@ func (e *Executor) runDeferred(t *ast.Task, call ast.Call, i int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call ast.Call, i int) error {
|
||||
func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *ast.Call, i int) error {
|
||||
cmd := t.Cmds[i]
|
||||
|
||||
switch {
|
||||
@@ -324,7 +324,7 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call ast.Call, i
|
||||
reacquire := e.releaseConcurrencyLimit()
|
||||
defer reacquire()
|
||||
|
||||
err := e.RunTask(ctx, ast.Call{Task: cmd.Task, Vars: cmd.Vars, Silent: cmd.Silent, Indirect: true})
|
||||
err := e.RunTask(ctx, &ast.Call{Task: cmd.Task, Vars: cmd.Vars, Silent: cmd.Silent, Indirect: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -413,14 +413,30 @@ func (e *Executor) startExecution(ctx context.Context, t *ast.Task, execute func
|
||||
// GetTask will return the task with the name matching the given call from the taskfile.
|
||||
// If no task is found, it will search for tasks with a matching alias.
|
||||
// If multiple tasks contain the same alias or no matches are found an error is returned.
|
||||
func (e *Executor) GetTask(call ast.Call) (*ast.Task, error) {
|
||||
func (e *Executor) GetTask(call *ast.Call) (*ast.Task, error) {
|
||||
// Search for a matching task
|
||||
matchingTask := e.Taskfile.Tasks.Get(call.Task)
|
||||
if matchingTask != nil {
|
||||
return matchingTask, nil
|
||||
matchingTasks := e.Taskfile.Tasks.FindMatchingTasks(call)
|
||||
switch len(matchingTasks) {
|
||||
case 0: // Carry on
|
||||
case 1:
|
||||
if call.Vars == nil {
|
||||
call.Vars = &ast.Vars{}
|
||||
}
|
||||
call.Vars.Set("MATCH", ast.Var{Value: matchingTasks[0].Wildcards})
|
||||
return matchingTasks[0].Task, nil
|
||||
default:
|
||||
taskNames := make([]string, len(matchingTasks))
|
||||
for i, matchingTask := range matchingTasks {
|
||||
taskNames[i] = matchingTask.Task.Task
|
||||
}
|
||||
return nil, &errors.TaskNameConflictError{
|
||||
Call: call.Task,
|
||||
TaskNames: taskNames,
|
||||
}
|
||||
}
|
||||
|
||||
// If didn't find one, search for a task with a matching alias
|
||||
var matchingTask *ast.Task
|
||||
var aliasedTasks []string
|
||||
for _, task := range e.Taskfile.Tasks.Values() {
|
||||
if slices.Contains(task.Aliases, call.Task) {
|
||||
@@ -431,7 +447,7 @@ func (e *Executor) GetTask(call ast.Call) (*ast.Task, error) {
|
||||
// If we found multiple tasks
|
||||
if len(aliasedTasks) > 1 {
|
||||
return nil, &errors.TaskNameConflictError{
|
||||
AliasName: call.Task,
|
||||
Call: call.Task,
|
||||
TaskNames: aliasedTasks,
|
||||
}
|
||||
}
|
||||
@@ -476,7 +492,7 @@ func (e *Executor) GetTaskList(filters ...FilterFunc) ([]*ast.Task, error) {
|
||||
idx := i
|
||||
task := tasks[idx]
|
||||
g.Go(func() error {
|
||||
compiledTask, err := e.FastCompiledTask(ast.Call{Task: task.Task})
|
||||
compiledTask, err := e.FastCompiledTask(&ast.Call{Task: task.Task})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
246
task_test.go
246
task_test.go
@@ -53,7 +53,7 @@ func (fct fileContentTest) Run(t *testing.T) {
|
||||
Stderr: io.Discard,
|
||||
}
|
||||
require.NoError(t, e.Setup(), "e.Setup()")
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: fct.Target}), "e.Run(target)")
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: fct.Target}), "e.Run(target)")
|
||||
|
||||
for name, expectContent := range fct.Files {
|
||||
t.Run(fct.name(name), func(t *testing.T) {
|
||||
@@ -76,7 +76,7 @@ func TestEmptyTask(t *testing.T) {
|
||||
Stderr: io.Discard,
|
||||
}
|
||||
require.NoError(t, e.Setup(), "e.Setup()")
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "default"}))
|
||||
}
|
||||
|
||||
func TestEnv(t *testing.T) {
|
||||
@@ -141,7 +141,7 @@ func TestSpecialVars(t *testing.T) {
|
||||
Silent: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: test.target}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: test.target}))
|
||||
assert.Equal(t, test.expected+"\n", buff.String())
|
||||
})
|
||||
}
|
||||
@@ -160,7 +160,7 @@ func TestConcurrency(t *testing.T) {
|
||||
Concurrency: 1,
|
||||
}
|
||||
require.NoError(t, e.Setup(), "e.Setup()")
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: target}), "e.Run(target)")
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: target}), "e.Run(target)")
|
||||
}
|
||||
|
||||
func TestParams(t *testing.T) {
|
||||
@@ -212,7 +212,7 @@ func TestDeps(t *testing.T) {
|
||||
Stderr: io.Discard,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "default"}))
|
||||
|
||||
for _, f := range files {
|
||||
f = filepathext.SmartJoin(dir, f)
|
||||
@@ -249,15 +249,15 @@ func TestStatus(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
// gen-foo creates foo.txt, and will always fail it's status check.
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-foo"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-foo"}))
|
||||
// gen-foo creates bar.txt, and will pass its status-check the 3. time it
|
||||
// is run. It creates bar.txt, but also lists it as its source. So, the checksum
|
||||
// for the file won't match before after the second run as we the file
|
||||
// only exists after the first run.
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-bar"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-bar"}))
|
||||
// gen-silent-baz is marked as being silent, and should only produce output
|
||||
// if e.Verbose is set to true.
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-silent-baz"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-silent-baz"}))
|
||||
|
||||
for _, f := range files {
|
||||
if _, err := os.Stat(filepathext.SmartJoin(dir, f)); err != nil {
|
||||
@@ -266,10 +266,10 @@ func TestStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
// Run gen-bar a second time to produce a checksum file that matches bar.txt
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-bar"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-bar"}))
|
||||
|
||||
// Run gen-bar a third time, to make sure we've triggered the status check.
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-bar"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-bar"}))
|
||||
|
||||
// We're silent, so no output should have been produced.
|
||||
assert.Empty(t, buff.String())
|
||||
@@ -278,7 +278,7 @@ func TestStatus(t *testing.T) {
|
||||
// for the next test.
|
||||
err := os.Remove(filepathext.SmartJoin(dir, "bar.txt"))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-bar"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-bar"}))
|
||||
buff.Reset()
|
||||
|
||||
// Global silence switched of, so we should see output unless the task itself
|
||||
@@ -286,34 +286,34 @@ func TestStatus(t *testing.T) {
|
||||
e.Silent = false
|
||||
|
||||
// all: not up-to-date
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-foo"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-foo"}))
|
||||
assert.Equal(t, "task: [gen-foo] touch foo.txt", strings.TrimSpace(buff.String()))
|
||||
buff.Reset()
|
||||
// status: not up-to-date
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-foo"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-foo"}))
|
||||
assert.Equal(t, "task: [gen-foo] touch foo.txt", strings.TrimSpace(buff.String()))
|
||||
buff.Reset()
|
||||
|
||||
// sources: not up-to-date
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-bar"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-bar"}))
|
||||
assert.Equal(t, "task: [gen-bar] touch bar.txt", strings.TrimSpace(buff.String()))
|
||||
buff.Reset()
|
||||
// all: up-to-date
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-bar"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-bar"}))
|
||||
assert.Equal(t, `task: Task "gen-bar" is up to date`, strings.TrimSpace(buff.String()))
|
||||
buff.Reset()
|
||||
|
||||
// sources: not up-to-date, no output produced.
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-silent-baz"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-silent-baz"}))
|
||||
assert.Empty(t, buff.String())
|
||||
|
||||
// up-to-date, no output produced
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-silent-baz"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-silent-baz"}))
|
||||
assert.Empty(t, buff.String())
|
||||
|
||||
e.Verbose = true
|
||||
// up-to-date, output produced due to Verbose mode.
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "gen-silent-baz"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "gen-silent-baz"}))
|
||||
assert.Equal(t, `task: Task "gen-silent-baz" is up to date`, strings.TrimSpace(buff.String()))
|
||||
buff.Reset()
|
||||
}
|
||||
@@ -330,13 +330,13 @@ func TestPrecondition(t *testing.T) {
|
||||
|
||||
// A precondition that has been met
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "foo"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "foo"}))
|
||||
if buff.String() != "" {
|
||||
t.Errorf("Got Output when none was expected: %s", buff.String())
|
||||
}
|
||||
|
||||
// A precondition that was not met
|
||||
require.Error(t, e.Run(context.Background(), ast.Call{Task: "impossible"}))
|
||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: "impossible"}))
|
||||
|
||||
if buff.String() != "task: 1 != 0 obviously!\n" {
|
||||
t.Errorf("Wrong output message: %s", buff.String())
|
||||
@@ -344,7 +344,7 @@ func TestPrecondition(t *testing.T) {
|
||||
buff.Reset()
|
||||
|
||||
// Calling a task with a precondition in a dependency fails the task
|
||||
require.Error(t, e.Run(context.Background(), ast.Call{Task: "depends_on_impossible"}))
|
||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: "depends_on_impossible"}))
|
||||
|
||||
if buff.String() != "task: 1 != 0 obviously!\n" {
|
||||
t.Errorf("Wrong output message: %s", buff.String())
|
||||
@@ -352,7 +352,7 @@ func TestPrecondition(t *testing.T) {
|
||||
buff.Reset()
|
||||
|
||||
// Calling a task with a precondition in a cmd fails the task
|
||||
require.Error(t, e.Run(context.Background(), ast.Call{Task: "executes_failing_task_as_cmd"}))
|
||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: "executes_failing_task_as_cmd"}))
|
||||
if buff.String() != "task: 1 != 0 obviously!\n" {
|
||||
t.Errorf("Wrong output message: %s", buff.String())
|
||||
}
|
||||
@@ -393,7 +393,7 @@ func TestGenerates(t *testing.T) {
|
||||
fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask)
|
||||
|
||||
// Run task for the first time.
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: theTask}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: theTask}))
|
||||
|
||||
if _, err := os.Stat(srcFile); err != nil {
|
||||
t.Errorf("File should exist: %v", err)
|
||||
@@ -408,7 +408,7 @@ func TestGenerates(t *testing.T) {
|
||||
buff.Reset()
|
||||
|
||||
// Re-run task to ensure it's now found to be up-to-date.
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: theTask}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: theTask}))
|
||||
if buff.String() != upToDate {
|
||||
t.Errorf("Wrong output message: %s", buff.String())
|
||||
}
|
||||
@@ -446,7 +446,7 @@ func TestStatusChecksum(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: test.task}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: test.task}))
|
||||
for _, f := range test.files {
|
||||
_, err := os.Stat(filepathext.SmartJoin(dir, f))
|
||||
require.NoError(t, err)
|
||||
@@ -459,7 +459,7 @@ func TestStatusChecksum(t *testing.T) {
|
||||
time := s.ModTime()
|
||||
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: test.task}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: test.task}))
|
||||
assert.Equal(t, `task: Task "`+test.task+`" is up to date`+"\n", buff.String())
|
||||
|
||||
s, err = os.Stat(filepathext.SmartJoin(tempdir, "checksum/"+test.task))
|
||||
@@ -482,7 +482,7 @@ func TestAlias(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "f"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "f"}))
|
||||
assert.Equal(t, string(data), buff.String())
|
||||
}
|
||||
|
||||
@@ -496,7 +496,7 @@ func TestDuplicateAlias(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.Error(t, e.Run(context.Background(), ast.Call{Task: "x"}))
|
||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: "x"}))
|
||||
assert.Equal(t, "", buff.String())
|
||||
}
|
||||
|
||||
@@ -514,7 +514,7 @@ func TestAliasSummary(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "f"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "f"}))
|
||||
assert.Equal(t, string(data), buff.String())
|
||||
}
|
||||
|
||||
@@ -528,7 +528,7 @@ func TestLabelUpToDate(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "foo"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "foo"}))
|
||||
assert.Contains(t, buff.String(), "foobar")
|
||||
}
|
||||
|
||||
@@ -543,7 +543,7 @@ func TestLabelSummary(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "foo"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "foo"}))
|
||||
assert.Contains(t, buff.String(), "foobar")
|
||||
}
|
||||
|
||||
@@ -554,7 +554,7 @@ func TestLabelInStatus(t *testing.T) {
|
||||
Dir: dir,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
err := e.Status(context.Background(), ast.Call{Task: "foo"})
|
||||
err := e.Status(context.Background(), &ast.Call{Task: "foo"})
|
||||
assert.ErrorContains(t, err, "foobar")
|
||||
}
|
||||
|
||||
@@ -568,7 +568,7 @@ func TestLabelWithVariableExpansion(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "foo"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "foo"}))
|
||||
assert.Contains(t, buff.String(), "foobaz")
|
||||
}
|
||||
|
||||
@@ -582,7 +582,7 @@ func TestLabelInSummary(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "foo"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "foo"}))
|
||||
assert.Contains(t, buff.String(), "foobar")
|
||||
}
|
||||
|
||||
@@ -618,7 +618,7 @@ func TestPromptInSummary(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: "foo"})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: "foo"})
|
||||
|
||||
if test.wantError {
|
||||
require.Error(t, err)
|
||||
@@ -646,7 +646,7 @@ func TestPromptWithIndirectTask(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: "bar"})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: "bar"})
|
||||
assert.Contains(t, outBuff.String(), "show-prompt")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -679,7 +679,7 @@ func TestPromptAssumeYes(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: "foo"})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: "foo"})
|
||||
|
||||
if !test.assumeYes {
|
||||
require.Error(t, err)
|
||||
@@ -791,7 +791,7 @@ func TestStatusVariables(t *testing.T) {
|
||||
Verbose: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "build"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "build"}))
|
||||
|
||||
assert.Contains(t, buff.String(), "3e464c4b03f4b65d740e1e130d4d108a")
|
||||
|
||||
@@ -832,7 +832,7 @@ func TestCyclicDep(t *testing.T) {
|
||||
Stderr: io.Discard,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
assert.IsType(t, &errors.TaskCalledTooManyTimesError{}, e.Run(context.Background(), ast.Call{Task: "task-1"}))
|
||||
assert.IsType(t, &errors.TaskCalledTooManyTimesError{}, e.Run(context.Background(), &ast.Call{Task: "task-1"}))
|
||||
}
|
||||
|
||||
func TestTaskVersion(t *testing.T) {
|
||||
@@ -875,10 +875,10 @@ func TestTaskIgnoreErrors(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "task-should-pass"}))
|
||||
require.Error(t, e.Run(context.Background(), ast.Call{Task: "task-should-fail"}))
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "cmd-should-pass"}))
|
||||
require.Error(t, e.Run(context.Background(), ast.Call{Task: "cmd-should-fail"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "task-should-pass"}))
|
||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: "task-should-fail"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "cmd-should-pass"}))
|
||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: "cmd-should-fail"}))
|
||||
}
|
||||
|
||||
func TestExpand(t *testing.T) {
|
||||
@@ -896,7 +896,7 @@ func TestExpand(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "pwd"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "pwd"}))
|
||||
assert.Equal(t, home, strings.TrimSpace(buff.String()))
|
||||
}
|
||||
|
||||
@@ -915,7 +915,7 @@ func TestDry(t *testing.T) {
|
||||
Dry: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "build"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "build"}))
|
||||
|
||||
assert.Equal(t, "task: [build] touch file.txt", strings.TrimSpace(buff.String()))
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
@@ -939,13 +939,13 @@ func TestDryChecksum(t *testing.T) {
|
||||
Dry: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "default"}))
|
||||
|
||||
_, err := os.Stat(checksumFile)
|
||||
require.Error(t, err, "checksum file should not exist")
|
||||
|
||||
e.Dry = false
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "default"}))
|
||||
_, err = os.Stat(checksumFile)
|
||||
require.NoError(t, err, "checksum file should exist")
|
||||
}
|
||||
@@ -1127,11 +1127,11 @@ func TestIncludesRelativePath(t *testing.T) {
|
||||
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "common:pwd"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "common:pwd"}))
|
||||
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
|
||||
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "included:common:pwd"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "included:common:pwd"}))
|
||||
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
|
||||
}
|
||||
|
||||
@@ -1159,7 +1159,7 @@ func TestIncludesInternal(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: test.task})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: test.task})
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
@@ -1193,7 +1193,7 @@ func TestIncludesInterpolation(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: test.task})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: test.task})
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
@@ -1228,7 +1228,7 @@ func TestInternalTask(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: test.task})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: test.task})
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
@@ -1297,7 +1297,7 @@ func TestSummary(t *testing.T) {
|
||||
Silent: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "task-with-summary"}, ast.Call{Task: "other-task-with-summary"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "task-with-summary"}, &ast.Call{Task: "other-task-with-summary"}))
|
||||
|
||||
data, err := os.ReadFile(filepathext.SmartJoin(dir, "task-with-summary.txt"))
|
||||
require.NoError(t, err)
|
||||
@@ -1321,7 +1321,7 @@ func TestWhenNoDirAttributeItRunsInSameDirAsTaskfile(t *testing.T) {
|
||||
}
|
||||
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "whereami"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "whereami"}))
|
||||
|
||||
// got should be the "dir" part of "testdata/dir"
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
@@ -1339,7 +1339,7 @@ func TestWhenDirAttributeAndDirExistsItRunsInThatDir(t *testing.T) {
|
||||
}
|
||||
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "whereami"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "whereami"}))
|
||||
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
assert.Equal(t, expected, got, "Mismatch in the working directory")
|
||||
@@ -1363,7 +1363,7 @@ func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) {
|
||||
t.Errorf("Directory should not exist: %v", err)
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: target}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: target}))
|
||||
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
assert.Equal(t, expected, got, "Mismatch in the working directory")
|
||||
@@ -1390,7 +1390,7 @@ func TestDynamicVariablesRunOnTheNewCreatedDir(t *testing.T) {
|
||||
t.Errorf("Directory should not exist: %v", err)
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: target}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: target}))
|
||||
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
assert.Equal(t, expected, got, "Mismatch in the working directory")
|
||||
@@ -1448,7 +1448,7 @@ func TestShortTaskNotation(t *testing.T) {
|
||||
Silent: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "default"}))
|
||||
assert.Equal(t, "string-slice-1\nstring-slice-2\nstring\n", buff.String())
|
||||
}
|
||||
|
||||
@@ -1592,7 +1592,7 @@ func TestExitImmediately(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.Error(t, e.Run(context.Background(), ast.Call{Task: "default"}))
|
||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: "default"}))
|
||||
assert.Contains(t, buff.String(), `"this_should_fail": executable file not found in $PATH`)
|
||||
}
|
||||
|
||||
@@ -1628,7 +1628,7 @@ echo ran
|
||||
task: [task-1] echo 'task-1 ran successfully'
|
||||
task-1 ran successfully
|
||||
`)
|
||||
require.Error(t, e.Run(context.Background(), ast.Call{Task: "task-2"}))
|
||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: "task-2"}))
|
||||
assert.Contains(t, buff.String(), expectedOutputOrder)
|
||||
}
|
||||
|
||||
@@ -1653,7 +1653,7 @@ func TestIgnoreNilElements(t *testing.T) {
|
||||
Silent: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "default"}))
|
||||
assert.Equal(t, "string-slice-1\n", buff.String())
|
||||
})
|
||||
}
|
||||
@@ -1679,7 +1679,7 @@ task: [bye] echo 'Bye!'
|
||||
Bye!
|
||||
::endgroup::
|
||||
`)
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "bye"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "bye"}))
|
||||
t.Log(buff.String())
|
||||
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
|
||||
}
|
||||
@@ -1694,7 +1694,7 @@ func TestOutputGroupErrorOnlySwallowsOutputOnSuccess(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "passing"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "passing"}))
|
||||
t.Log(buff.String())
|
||||
assert.Empty(t, buff.String())
|
||||
}
|
||||
@@ -1709,7 +1709,7 @@ func TestOutputGroupErrorOnlyShowsOutputOnFailure(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.Error(t, e.Run(context.Background(), ast.Call{Task: "failing"}))
|
||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: "failing"}))
|
||||
t.Log(buff.String())
|
||||
assert.Contains(t, "failing-output", strings.TrimSpace(buff.String()))
|
||||
assert.NotContains(t, "passing", strings.TrimSpace(buff.String()))
|
||||
@@ -1739,7 +1739,7 @@ VAR_1 is included-default-var1
|
||||
task: [included3:task1] echo "VAR_2 is included-default-var2"
|
||||
VAR_2 is included-default-var2
|
||||
`)
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "task1"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "task1"}))
|
||||
t.Log(buff.String())
|
||||
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
|
||||
}
|
||||
@@ -1762,7 +1762,7 @@ Hello foo
|
||||
task: [bar:lib:greet] echo 'Hello bar'
|
||||
Hello bar
|
||||
`)
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "default"}))
|
||||
t.Log(buff.String())
|
||||
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
|
||||
}
|
||||
@@ -1795,7 +1795,7 @@ func TestErrorCode(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: test.task})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: test.task})
|
||||
require.Error(t, err)
|
||||
taskRunErr, ok := err.(*errors.TaskRunError)
|
||||
assert.True(t, ok, "cannot cast returned error to *task.TaskRunError")
|
||||
@@ -1847,7 +1847,7 @@ func TestEvaluateSymlinksInPaths(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require.NoError(t, e.Setup())
|
||||
err := e.Run(context.Background(), ast.Call{Task: test.task})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: test.task})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expected, strings.TrimSpace(buff.String()))
|
||||
buff.Reset()
|
||||
@@ -1886,7 +1886,7 @@ func TestTaskfileWalk(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "default"}))
|
||||
assert.Equal(t, test.expected, buff.String())
|
||||
})
|
||||
}
|
||||
@@ -1902,7 +1902,7 @@ func TestUserWorkingDirectory(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "default"}))
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", wd), buff.String())
|
||||
}
|
||||
|
||||
@@ -1922,7 +1922,7 @@ func TestUserWorkingDirectoryWithIncluded(t *testing.T) {
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "included:echo"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "included:echo"}))
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", wd), buff.String())
|
||||
}
|
||||
|
||||
@@ -1934,7 +1934,7 @@ func TestPlatforms(t *testing.T) {
|
||||
Stderr: &buff,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "build-" + runtime.GOOS}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "build-" + runtime.GOOS}))
|
||||
assert.Equal(t, fmt.Sprintf("task: [build-%s] echo 'Running task on %s'\nRunning task on %s\n", runtime.GOOS, runtime.GOOS, runtime.GOOS), buff.String())
|
||||
}
|
||||
|
||||
@@ -1947,7 +1947,7 @@ func TestPOSIXShellOptsGlobalLevel(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: "pipefail"})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: "pipefail"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||
}
|
||||
@@ -1961,7 +1961,7 @@ func TestPOSIXShellOptsTaskLevel(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: "pipefail"})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: "pipefail"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||
}
|
||||
@@ -1975,7 +1975,7 @@ func TestPOSIXShellOptsCommandLevel(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: "pipefail"})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: "pipefail"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||
}
|
||||
@@ -1989,7 +1989,7 @@ func TestBashShellOptsGlobalLevel(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: "globstar"})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: "globstar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||
}
|
||||
@@ -2003,7 +2003,7 @@ func TestBashShellOptsTaskLevel(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: "globstar"})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: "globstar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||
}
|
||||
@@ -2017,7 +2017,7 @@ func TestBashShellOptsCommandLevel(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: "globstar"})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: "globstar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||
}
|
||||
@@ -2035,7 +2035,7 @@ func TestSplitArgs(t *testing.T) {
|
||||
vars := &ast.Vars{}
|
||||
vars.Set("CLI_ARGS", ast.Var{Value: "foo bar 'foo bar baz'"})
|
||||
|
||||
err := e.Run(context.Background(), ast.Call{Task: "default", Vars: vars})
|
||||
err := e.Run(context.Background(), &ast.Call{Task: "default", Vars: vars})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "3\n", buff.String())
|
||||
}
|
||||
@@ -2063,20 +2063,20 @@ func TestSilence(t *testing.T) {
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
// First verify that the silent flag is in place.
|
||||
task, err := e.GetTask(ast.Call{Task: "task-test-silent-calls-chatty-silenced"})
|
||||
task, err := e.GetTask(&ast.Call{Task: "task-test-silent-calls-chatty-silenced"})
|
||||
require.NoError(t, err, "Unable to look up task task-test-silent-calls-chatty-silenced")
|
||||
require.True(t, task.Cmds[0].Silent, "The task task-test-silent-calls-chatty-silenced should have a silent call to chatty")
|
||||
|
||||
// Then test the two basic cases where the task is silent or not.
|
||||
// A silenced task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "silent"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "silent"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "siWhile running lent: Expected not see output, because the task is silent")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty (not silent) task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "chatty"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "chatty"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "chWhile running atty: Expected to see output, because the task is not silent")
|
||||
|
||||
@@ -2084,42 +2084,42 @@ func TestSilence(t *testing.T) {
|
||||
|
||||
// Then test invoking the two task from other tasks.
|
||||
// A silenced task that calls a chatty task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "task-test-silent-calls-chatty-non-silenced"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "task-test-silent-calls-chatty-non-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-silent-calls-chatty-non-silenced: Expected to see output. The task is silenced, but the called task is not. Silence does not propagate to called tasks.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A silent task that does a silent call to a chatty task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "task-test-silent-calls-chatty-silenced"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "task-test-silent-calls-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-silent-calls-chatty-silenced: Expected not to see output. The task calls chatty task, but the call is silenced.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that does a call to a chatty task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "task-test-chatty-calls-chatty-non-silenced"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "task-test-chatty-calls-chatty-non-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-chatty-calls-chatty-non-silenced: Expected to see output. Both caller and callee are chatty and not silenced.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that does a silenced call to a chatty task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "task-test-chatty-calls-chatty-silenced"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "task-test-chatty-calls-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-chatty-calls-chatty-silenced: Expected to see output. Call to a chatty task is silenced, but the parent task is not.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task with no cmd's of its own that does a silenced call to a chatty task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "task-test-no-cmds-calls-chatty-silenced"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "task-test-no-cmds-calls-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-no-cmds-calls-chatty-silenced: Expected not to see output. While the task itself is not silenced, it does not have any cmds and only does an invocation of a silenced task.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that does a silenced invocation of a task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "task-test-chatty-calls-silenced-cmd"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "task-test-chatty-calls-silenced-cmd"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-chatty-calls-silenced-cmd: Expected not to see output. While the task itself is not silenced, its call to the chatty task is silent.")
|
||||
|
||||
@@ -2127,21 +2127,21 @@ func TestSilence(t *testing.T) {
|
||||
|
||||
// Then test calls via dependencies.
|
||||
// A silent task that depends on a chatty task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "task-test-is-silent-depends-on-chatty-non-silenced"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "task-test-is-silent-depends-on-chatty-non-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-is-silent-depends-on-chatty-non-silenced: Expected to see output. The task is silent and depends on a chatty task. Dependencies does not inherit silence.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A silent task that depends on a silenced chatty task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "task-test-is-silent-depends-on-chatty-silenced"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "task-test-is-silent-depends-on-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-is-silent-depends-on-chatty-silenced: Expected not to see output. The task is silent and has a silenced dependency on a chatty task.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that, depends on a silenced chatty task.
|
||||
err = e.Run(context.Background(), ast.Call{Task: "task-test-is-chatty-depends-on-chatty-silenced"})
|
||||
err = e.Run(context.Background(), &ast.Call{Task: "task-test-is-chatty-depends-on-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-is-chatty-depends-on-chatty-silenced: Expected not to see output. The task is chatty but does not have commands and has a silenced dependency on a chatty task.")
|
||||
|
||||
@@ -2189,7 +2189,7 @@ func TestForce(t *testing.T) {
|
||||
ForceAll: tt.forceAll,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: "task-with-dep"}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "task-with-dep"}))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2244,7 +2244,67 @@ func TestFor(t *testing.T) {
|
||||
Force: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), ast.Call{Task: test.name}))
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: test.name}))
|
||||
assert.Equal(t, test.expectedOutput, buff.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWildcard(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
call string
|
||||
expectedOutput string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "basic wildcard",
|
||||
call: "wildcard-foo",
|
||||
expectedOutput: "Hello foo\n",
|
||||
},
|
||||
{
|
||||
name: "double wildcard",
|
||||
call: "foo-wildcard-bar",
|
||||
expectedOutput: "Hello foo bar\n",
|
||||
},
|
||||
{
|
||||
name: "store wildcard",
|
||||
call: "start-foo",
|
||||
expectedOutput: "Starting foo\n",
|
||||
},
|
||||
{
|
||||
name: "matches exactly",
|
||||
call: "matches-exactly-*",
|
||||
expectedOutput: "I don't consume matches: []\n",
|
||||
},
|
||||
{
|
||||
name: "no matches",
|
||||
call: "no-match",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "multiple matches",
|
||||
call: "wildcard-foo-bar",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.call, func(t *testing.T) {
|
||||
var buff bytes.Buffer
|
||||
e := task.Executor{
|
||||
Dir: "testdata/wildcards",
|
||||
Stdout: &buff,
|
||||
Stderr: &buff,
|
||||
Silent: true,
|
||||
Force: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
if test.wantErr {
|
||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: test.call}))
|
||||
return
|
||||
}
|
||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: test.call}))
|
||||
assert.Equal(t, test.expectedOutput, buff.String())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/internal/execext"
|
||||
"github.com/go-task/task/v3/internal/filepathext"
|
||||
omap "github.com/go-task/task/v3/internal/omap"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Include represents information about included taskfiles
|
||||
|
||||
@@ -2,6 +2,8 @@ package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
@@ -51,6 +53,30 @@ func (t *Task) Name() string {
|
||||
return t.Task
|
||||
}
|
||||
|
||||
// WildcardMatch will check if the given string matches the name of the Task and returns any wildcard values.
|
||||
func (t *Task) WildcardMatch(name string) (bool, []string) {
|
||||
// Convert the name into a regex string
|
||||
regexStr := fmt.Sprintf("^%s$", strings.ReplaceAll(t.Task, "*", "(.*)"))
|
||||
regex := regexp.MustCompile(regexStr)
|
||||
wildcards := regex.FindStringSubmatch(name)
|
||||
wildcardCount := strings.Count(t.Task, "*")
|
||||
|
||||
// If there are no wildcards, return false
|
||||
if len(wildcards) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Remove the first match, which is the full string
|
||||
wildcards = wildcards[1:]
|
||||
|
||||
// If there are more/less wildcards than matches, return false
|
||||
if len(wildcards) != wildcardCount {
|
||||
return false, wildcards
|
||||
}
|
||||
|
||||
return true, wildcards
|
||||
}
|
||||
|
||||
func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
||||
switch node.Kind {
|
||||
|
||||
|
||||
@@ -14,6 +14,36 @@ type Tasks struct {
|
||||
omap.OrderedMap[string, *Task]
|
||||
}
|
||||
|
||||
type MatchingTask struct {
|
||||
Task *Task
|
||||
Wildcards []string
|
||||
}
|
||||
|
||||
func (t *Tasks) FindMatchingTasks(call *Call) []*MatchingTask {
|
||||
if call == nil {
|
||||
return nil
|
||||
}
|
||||
var task *Task
|
||||
var matchingTasks []*MatchingTask
|
||||
// If there is a direct match, return it
|
||||
if task = t.OrderedMap.Get(call.Task); task != nil {
|
||||
matchingTasks = append(matchingTasks, &MatchingTask{Task: task, Wildcards: nil})
|
||||
return matchingTasks
|
||||
}
|
||||
// Attempt a wildcard match
|
||||
// For now, we can just nil check the task before each loop
|
||||
_ = t.Range(func(key string, value *Task) error {
|
||||
if match, wildcards := value.WildcardMatch(call.Task); match {
|
||||
matchingTasks = append(matchingTasks, &MatchingTask{
|
||||
Task: value,
|
||||
Wildcards: wildcards,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return matchingTasks
|
||||
}
|
||||
|
||||
func (t1 *Tasks) Merge(t2 Tasks, include *Include) {
|
||||
_ = t2.Range(func(k string, v *Task) error {
|
||||
// We do a deep copy of the task struct here to ensure that no data can
|
||||
|
||||
@@ -2,6 +2,8 @@ package taskfile
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
@@ -14,6 +16,30 @@ type Node interface {
|
||||
Location() string
|
||||
Optional() bool
|
||||
Remote() bool
|
||||
BaseDir() string
|
||||
}
|
||||
|
||||
func NewRootNode(
|
||||
dir string,
|
||||
entrypoint string,
|
||||
insecure bool,
|
||||
) (Node, error) {
|
||||
// Check if there is something to read on STDIN
|
||||
stat, _ := os.Stdin.Stat()
|
||||
if (stat.Mode()&os.ModeCharDevice) == 0 && stat.Size() > 0 {
|
||||
return NewStdinNode(dir)
|
||||
}
|
||||
// If no entrypoint is specified, search for a taskfile
|
||||
if entrypoint == "" {
|
||||
root, err := ExistsWalk(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewNode(root, insecure)
|
||||
}
|
||||
// Use the specified entrypoint
|
||||
uri := filepath.Join(dir, entrypoint)
|
||||
return NewNode(uri, insecure)
|
||||
}
|
||||
|
||||
func NewNode(
|
||||
|
||||
@@ -52,3 +52,7 @@ func (node *FileNode) Read(ctx context.Context) ([]byte, error) {
|
||||
defer f.Close()
|
||||
return io.ReadAll(f)
|
||||
}
|
||||
|
||||
func (node *FileNode) BaseDir() string {
|
||||
return node.Dir
|
||||
}
|
||||
|
||||
@@ -65,3 +65,7 @@ func (node *HTTPNode) Read(ctx context.Context) ([]byte, error) {
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (node *HTTPNode) BaseDir() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
46
taskfile/node_stdin.go
Normal file
46
taskfile/node_stdin.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package taskfile
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// A StdinNode is a node that reads a taskfile from the standard input stream.
|
||||
type StdinNode struct {
|
||||
*BaseNode
|
||||
Dir string
|
||||
}
|
||||
|
||||
func NewStdinNode(dir string) (*StdinNode, error) {
|
||||
base := NewBaseNode()
|
||||
return &StdinNode{
|
||||
BaseNode: base,
|
||||
Dir: dir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (node *StdinNode) Location() string {
|
||||
return "__stdin__"
|
||||
}
|
||||
|
||||
func (node *StdinNode) Remote() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (node *StdinNode) Read(ctx context.Context) ([]byte, error) {
|
||||
var stdin []byte
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
stdin = fmt.Appendln(stdin, scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stdin, nil
|
||||
}
|
||||
|
||||
func (node *StdinNode) BaseDir() string {
|
||||
return node.Dir
|
||||
}
|
||||
@@ -48,12 +48,11 @@ func Read(
|
||||
return nil, &errors.TaskfileVersionCheckError{URI: node.Location()}
|
||||
}
|
||||
|
||||
// Annotate any included Taskfile reference with a base directory for resolving relative paths
|
||||
if node, isFileNode := node.(*FileNode); isFileNode {
|
||||
if dir := node.BaseDir(); dir != "" {
|
||||
_ = tf.Includes.Range(func(namespace string, include ast.Include) error {
|
||||
// Set the base directory for resolving relative paths, but only if not already set
|
||||
if include.BaseDir == "" {
|
||||
include.BaseDir = node.Dir
|
||||
include.BaseDir = dir
|
||||
tf.Includes.Set(namespace, include)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -35,7 +35,10 @@ func Exists(path string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if fi.Mode().IsRegular() {
|
||||
if fi.Mode().IsRegular() ||
|
||||
fi.Mode()&os.ModeDevice != 0 ||
|
||||
fi.Mode()&os.ModeSymlink != 0 ||
|
||||
fi.Mode()&os.ModeNamedPipe != 0 {
|
||||
return filepath.Abs(path)
|
||||
}
|
||||
|
||||
|
||||
25
testdata/wildcards/Taskfile.yml
vendored
Normal file
25
testdata/wildcards/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
version: 3
|
||||
|
||||
tasks:
|
||||
wildcard-*:
|
||||
cmds:
|
||||
- echo "Hello {{index .MATCH 0}}"
|
||||
|
||||
wildcard-*-*:
|
||||
cmds:
|
||||
- echo "Hello {{index .MATCH 0}}"
|
||||
|
||||
'*-wildcard-*':
|
||||
cmds:
|
||||
- echo "Hello {{index .MATCH 0}} {{index .MATCH 1}}"
|
||||
|
||||
# Matches is empty when you call the task name exactly (i.e. `task matches-exactly-*`)
|
||||
matches-exactly-*:
|
||||
cmds:
|
||||
- "echo \"I don't consume matches: {{.MATCH}}\""
|
||||
|
||||
start-*:
|
||||
vars:
|
||||
SERVICE: "{{index .MATCH 0}}"
|
||||
cmds:
|
||||
- echo "Starting {{.SERVICE}}"
|
||||
@@ -17,16 +17,16 @@ import (
|
||||
|
||||
// CompiledTask returns a copy of a task, but replacing variables in almost all
|
||||
// properties using the Go template package.
|
||||
func (e *Executor) CompiledTask(call ast.Call) (*ast.Task, error) {
|
||||
func (e *Executor) CompiledTask(call *ast.Call) (*ast.Task, error) {
|
||||
return e.compiledTask(call, true)
|
||||
}
|
||||
|
||||
// FastCompiledTask is like CompiledTask, but it skippes dynamic variables.
|
||||
func (e *Executor) FastCompiledTask(call ast.Call) (*ast.Task, error) {
|
||||
func (e *Executor) FastCompiledTask(call *ast.Call) (*ast.Task, error) {
|
||||
return e.compiledTask(call, false)
|
||||
}
|
||||
|
||||
func (e *Executor) compiledTask(call ast.Call, evaluateShVars bool) (*ast.Task, error) {
|
||||
func (e *Executor) compiledTask(call *ast.Call, evaluateShVars bool) (*ast.Task, error) {
|
||||
origTask, err := e.GetTask(call)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
12
watch.go
12
watch.go
@@ -21,7 +21,7 @@ import (
|
||||
const defaultWatchInterval = 5 * time.Second
|
||||
|
||||
// watchTasks start watching the given tasks
|
||||
func (e *Executor) watchTasks(calls ...ast.Call) error {
|
||||
func (e *Executor) watchTasks(calls ...*ast.Call) error {
|
||||
tasks := make([]string, len(calls))
|
||||
for i, c := range calls {
|
||||
tasks[i] = c.Task
|
||||
@@ -119,24 +119,24 @@ func closeOnInterrupt(w *watcher.Watcher) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...ast.Call) error {
|
||||
func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...*ast.Call) error {
|
||||
watchedFiles := w.WatchedFiles()
|
||||
|
||||
var registerTaskFiles func(ast.Call) error
|
||||
registerTaskFiles = func(c ast.Call) error {
|
||||
var registerTaskFiles func(*ast.Call) error
|
||||
registerTaskFiles = func(c *ast.Call) error {
|
||||
task, err := e.CompiledTask(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, d := range task.Deps {
|
||||
if err := registerTaskFiles(ast.Call{Task: d.Task, Vars: d.Vars}); err != nil {
|
||||
if err := registerTaskFiles(&ast.Call{Task: d.Task, Vars: d.Vars}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, c := range task.Cmds {
|
||||
if c.Task != "" {
|
||||
if err := registerTaskFiles(ast.Call{Task: c.Task, Vars: c.Vars}); err != nil {
|
||||
if err := registerTaskFiles(&ast.Call{Task: c.Task, Vars: c.Vars}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user