mirror of
https://github.com/go-task/task.git
synced 2026-06-28 15:14:18 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73aba36309 | ||
|
|
cb393ccd3a | ||
|
|
347fcf9f67 | ||
|
|
fce7575b03 | ||
|
|
2da7ddc399 | ||
|
|
1c1be683ab | ||
|
|
4be1050234 | ||
|
|
2efb3533ec | ||
|
|
aa6c7e4b94 | ||
|
|
63c50d13ee | ||
|
|
c1e127e42f | ||
|
|
9e38e8a4db |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,22 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v3.20.0 - 2023-01-14
|
||||||
|
|
||||||
|
- Improve behavior and performance of status checking when using the
|
||||||
|
`timestamp` mode
|
||||||
|
([#976](https://github.com/go-task/task/issues/976), [#977](https://github.com/go-task/task/pull/977) by @aminya).
|
||||||
|
- Performance optimizations were made for large Taskfiles
|
||||||
|
([#982](https://github.com/go-task/task/pull/982) by @pd93).
|
||||||
|
- Add ability to configure options for the [`set`](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
|
||||||
|
and [`shopt`](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) builtins
|
||||||
|
([#908](https://github.com/go-task/task/issues/908), [#929](https://github.com/go-task/task/pull/929) by @pd93, [Documentation](http://taskfile.dev/usage/#set-and-shopt)).
|
||||||
|
- Add new `platforms:` attribute to `task` and `cmd`, so it's now possible to
|
||||||
|
choose in which platforms that given task or command will be run on. Possible
|
||||||
|
values are operating system (GOOS), architecture (GOARCH) or a combination of
|
||||||
|
the two. Example: `platforms: [linux]`, `platforms: [amd64]` or
|
||||||
|
`platforms: [linux/amd64]`. Other platforms will be skipped
|
||||||
|
([#978](https://github.com/go-task/task/issues/978), [#980](https://github.com/go-task/task/pull/980) by @leaanthony).
|
||||||
|
|
||||||
## v3.19.1 - 2022-12-31
|
## v3.19.1 - 2022-12-31
|
||||||
|
|
||||||
- Small bug fix: closing `Taskfile.yml` once we're done reading it
|
- Small bug fix: closing `Taskfile.yml` once we're done reading it
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="https://taskfile.dev/installation/">Installation</a> | <a href="https://taskfile.dev/usage/">Documentation</a> | <a href="https://twitter.com/taskfiledev">Twitter</a> | <a href="https://discord.gg/6TY36E39UK">Discord</a>
|
<a href="https://taskfile.dev/installation/">Installation</a> | <a href="https://taskfile.dev/usage/">Documentation</a> | <a href="https://twitter.com/taskfiledev">Twitter</a> | <a href="https://fosstodon.org/@task">Mastodon</a> | <a href="https://discord.gg/6TY36E39UK">Discord</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
19
Taskfile.yml
19
Taskfile.yml
@@ -6,13 +6,6 @@ includes:
|
|||||||
taskfile: ./docs
|
taskfile: ./docs
|
||||||
dir: ./docs
|
dir: ./docs
|
||||||
|
|
||||||
vars:
|
|
||||||
GIT_COMMIT:
|
|
||||||
sh: git log -n 1 --format=%h
|
|
||||||
|
|
||||||
GO_PACKAGES:
|
|
||||||
sh: go list ./...
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: '0'
|
CGO_ENABLED: '0'
|
||||||
|
|
||||||
@@ -29,6 +22,9 @@ tasks:
|
|||||||
- './**/*.go'
|
- './**/*.go'
|
||||||
cmds:
|
cmds:
|
||||||
- go install -v -ldflags="-w -s -X main.version={{.GIT_COMMIT}}" ./cmd/task
|
- go install -v -ldflags="-w -s -X main.version={{.GIT_COMMIT}}" ./cmd/task
|
||||||
|
vars:
|
||||||
|
GIT_COMMIT:
|
||||||
|
sh: git log -n 1 --format=%h
|
||||||
|
|
||||||
mod:
|
mod:
|
||||||
desc: Downloads and tidy Go modules
|
desc: Downloads and tidy Go modules
|
||||||
@@ -73,12 +69,18 @@ tasks:
|
|||||||
deps: [install]
|
deps: [install]
|
||||||
cmds:
|
cmds:
|
||||||
- go test {{catLines .GO_PACKAGES}}
|
- go test {{catLines .GO_PACKAGES}}
|
||||||
|
vars:
|
||||||
|
GO_PACKAGES:
|
||||||
|
sh: go list ./...
|
||||||
|
|
||||||
test:all:
|
test:all:
|
||||||
desc: Runs test suite with signals and watch tests included
|
desc: Runs test suite with signals and watch tests included
|
||||||
deps: [install, sleepit:build]
|
deps: [install, sleepit:build]
|
||||||
cmds:
|
cmds:
|
||||||
- go test {{catLines .GO_PACKAGES}} -tags 'signals watch'
|
- go test {{catLines .GO_PACKAGES}} -tags 'signals watch'
|
||||||
|
vars:
|
||||||
|
GO_PACKAGES:
|
||||||
|
sh: go list ./...
|
||||||
|
|
||||||
test-release:
|
test-release:
|
||||||
desc: Tests release process without publishing
|
desc: Tests release process without publishing
|
||||||
@@ -106,4 +108,7 @@ tasks:
|
|||||||
packages:
|
packages:
|
||||||
cmds:
|
cmds:
|
||||||
- echo '{{.GO_PACKAGES}}'
|
- echo '{{.GO_PACKAGES}}'
|
||||||
|
vars:
|
||||||
|
GO_PACKAGES:
|
||||||
|
sh: go list ./...
|
||||||
silent: true
|
silent: true
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
const GITHUB_URL = 'https://github.com/go-task/task';
|
const GITHUB_URL = 'https://github.com/go-task/task';
|
||||||
const TWITTER_URL = 'https://twitter.com/taskfiledev';
|
const TWITTER_URL = 'https://twitter.com/taskfiledev';
|
||||||
|
const MASTODON_URL = 'https://fosstodon.org/@task';
|
||||||
const DISCORD_URL = 'https://discord.gg/6TY36E39UK';
|
const DISCORD_URL = 'https://discord.gg/6TY36E39UK';
|
||||||
const CHINESE_URL = 'https://task-zh.readthedocs.io/zh_CN/latest/';
|
const CHINESE_URL = 'https://task-zh.readthedocs.io/zh_CN/latest/';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
GITHUB_URL,
|
CHINESE_URL,
|
||||||
TWITTER_URL,
|
|
||||||
DISCORD_URL,
|
DISCORD_URL,
|
||||||
CHINESE_URL
|
GITHUB_URL,
|
||||||
|
MASTODON_URL,
|
||||||
|
TWITTER_URL
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ Some environment variables can be overriden to adjust Task behavior.
|
|||||||
| `dotenv` | `[]string` | | A list of `.env` file paths to be parsed. |
|
| `dotenv` | `[]string` | | A list of `.env` file paths to be parsed. |
|
||||||
| `run` | `string` | `always` | Default 'run' option for this Taskfile. Available options: `always`, `once` and `when_changed`. |
|
| `run` | `string` | `always` | Default 'run' option for this Taskfile. Available options: `always`, `once` and `when_changed`. |
|
||||||
| `interval` | `string` | `5s` | Sets a different watch interval when using `--watch`, the default being 5 seconds. This string should be a valid [Go Duration](https://pkg.go.dev/time#ParseDuration). |
|
| `interval` | `string` | `5s` | Sets a different watch interval when using `--watch`, the default being 5 seconds. This string should be a valid [Go Duration](https://pkg.go.dev/time#ParseDuration). |
|
||||||
|
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
|
||||||
|
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
|
||||||
|
|
||||||
### Include
|
### Include
|
||||||
|
|
||||||
@@ -139,6 +141,9 @@ includes:
|
|||||||
| `prefix` | `string` | | Defines a string to prefix the output of tasks running in parallel. Only used when the output mode is `prefixed`. |
|
| `prefix` | `string` | | Defines a string to prefix the output of tasks running in parallel. Only used when the output mode is `prefixed`. |
|
||||||
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing commands. |
|
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing commands. |
|
||||||
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
|
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
|
||||||
|
| `platforms` | `[]string` | All platforms | Specifies which platforms the task should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/master/src/go/build/syslist.go). Task will be skipped otherwise. |
|
||||||
|
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
|
||||||
|
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
@@ -189,6 +194,9 @@ tasks:
|
|||||||
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the referenced task. Only relevant when setting `task` instead of `cmd`. |
|
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the referenced task. Only relevant when setting `task` instead of `cmd`. |
|
||||||
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
|
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
|
||||||
| `defer` | `string` | | Alternative to `cmd`, but schedules the command to be executed at the end of this task instead of immediately. This cannot be used together with `cmd`. |
|
| `defer` | `string` | | Alternative to `cmd`, but schedules the command to be executed at the end of this task instead of immediately. This cannot be used together with `cmd`. |
|
||||||
|
| `platforms` | `[]string` | All platforms | Specifies which platforms the command should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/master/src/go/build/syslist.go). Command will be skipped otherwise. |
|
||||||
|
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
|
||||||
|
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,23 @@ sidebar_position: 7
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v3.20.0 - 2023-01-14
|
||||||
|
|
||||||
|
- Improve behavior and performance of status checking when using the
|
||||||
|
`timestamp` mode
|
||||||
|
([#976](https://github.com/go-task/task/issues/976), [#977](https://github.com/go-task/task/pull/977) by @aminya).
|
||||||
|
- Performance optimizations were made for large Taskfiles
|
||||||
|
([#982](https://github.com/go-task/task/pull/982) by @pd93).
|
||||||
|
- Add ability to configure options for the [`set`](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
|
||||||
|
and [`shopt`](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) builtins
|
||||||
|
([#908](https://github.com/go-task/task/issues/908), [#929](https://github.com/go-task/task/pull/929) by @pd93, [Documentation](http://taskfile.dev/usage/#set-and-shopt)).
|
||||||
|
- Add new `platforms:` attribute to `task` and `cmd`, so it's now possible to
|
||||||
|
choose in which platforms that given task or command will be run on. Possible
|
||||||
|
values are operating system (GOOS), architecture (GOARCH) or a combination of
|
||||||
|
the two. Example: `platforms: [linux]`, `platforms: [amd64]` or
|
||||||
|
`platforms: [linux/amd64]`. Other platforms will be skipped
|
||||||
|
([#978](https://github.com/go-task/task/issues/978), [#980](https://github.com/go-task/task/pull/980) by @leaanthony).
|
||||||
|
|
||||||
## v3.19.1 - 2022-12-31
|
## v3.19.1 - 2022-12-31
|
||||||
|
|
||||||
- Small bug fix: closing `Taskfile.yml` once we're done reading it
|
- Small bug fix: closing `Taskfile.yml` once we're done reading it
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ the [Snapcraft dashboard][snapcraftdashboard].
|
|||||||
|
|
||||||
Scoop is a command-line package manager for the Windows operating system.
|
Scoop is a command-line package manager for the Windows operating system.
|
||||||
Scoop package manifests are maintained by the community.
|
Scoop package manifests are maintained by the community.
|
||||||
Scoop owners usually take care of updating versions there by editing [this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json).
|
Scoop owners usually take care of updating versions there by editing [this file](https://github.com/ScoopInstaller/Main/blob/master/bucket/task.json).
|
||||||
If you think its Task version is outdated, open an issue to let us know.
|
If you think its Task version is outdated, open an issue to let us know.
|
||||||
|
|
||||||
# Nix
|
# Nix
|
||||||
|
|||||||
@@ -439,6 +439,78 @@ tasks:
|
|||||||
- echo {{.TEXT}}
|
- echo {{.TEXT}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Platform specific tasks and commands
|
||||||
|
|
||||||
|
If you want to restrict the running of tasks to explicit platforms, this can be achieved
|
||||||
|
using the `platforms:` key. Tasks can be restricted to a specific OS, architecture or a
|
||||||
|
combination of both.
|
||||||
|
On a mismatch, the task or command will be skipped, and no error will be thrown.
|
||||||
|
|
||||||
|
The values allowed as OS or Arch are valid `GOOS` and `GOARCH` values, as
|
||||||
|
defined by the Go language
|
||||||
|
[here](https://github.com/golang/go/blob/master/src/go/build/syslist.go).
|
||||||
|
|
||||||
|
The `build-windows` task below will run only on Windows, and on any architecture:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-windows:
|
||||||
|
platforms: [windows]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running command on Windows'
|
||||||
|
```
|
||||||
|
|
||||||
|
This can be restricted to a specific architecture as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-windows-amd64:
|
||||||
|
platforms: [windows/amd64]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running command on Windows (amd64)'
|
||||||
|
```
|
||||||
|
|
||||||
|
It is also possible to restrict the task to specific architectures:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-amd64:
|
||||||
|
platforms: [amd64]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running command on amd64'
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple platforms can be specified as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build:
|
||||||
|
platforms: [windows/amd64, darwin]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running command on Windows (amd64) and macOS'
|
||||||
|
```
|
||||||
|
|
||||||
|
Individual commands can also be restricted to specific platforms:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build:
|
||||||
|
cmds:
|
||||||
|
- cmd: echo 'Running command on Windows (amd64) and macOS'
|
||||||
|
platforms: [windows/amd64, darwin]
|
||||||
|
- cmd: echo 'Running on all platforms'
|
||||||
|
```
|
||||||
|
|
||||||
## Calling another task
|
## Calling another task
|
||||||
|
|
||||||
When a task has many dependencies, they are executed concurrently. This will
|
When a task has many dependencies, they are executed concurrently. This will
|
||||||
@@ -595,9 +667,9 @@ The method `none` skips any validation and always run the task.
|
|||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
For the `checksum` (default) method to work, it is only necessary to
|
For the `checksum` (default) or `timestamp` method to work, it is only necessary to
|
||||||
inform the source files, but if you want to use the `timestamp` method, you
|
inform the source files.
|
||||||
also need to inform the generated files with `generates`.
|
When the `timestamp` method is used, the last time of the running the task is considered as a generate.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@@ -1348,6 +1420,31 @@ tasks:
|
|||||||
- ./app{{exeExt}} -h localhost -p 8080
|
- ./app{{exeExt}} -h localhost -p 8080
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `set` and `shopt`
|
||||||
|
|
||||||
|
It's possible to specify options to the
|
||||||
|
[`set`](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
|
||||||
|
and [`shopt`](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html)
|
||||||
|
builtins. This can be added at global, task or command level.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
set: [pipefail]
|
||||||
|
shopt: [globstar]
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
# `globstar` required for double star globs to work
|
||||||
|
default: echo **/*.go
|
||||||
|
```
|
||||||
|
|
||||||
|
:::info
|
||||||
|
|
||||||
|
Keep in mind that not all options are available in the
|
||||||
|
[shell interpreter library](https://github.com/mvdan/sh) that Task uses.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
## Watch tasks
|
## Watch tasks
|
||||||
|
|
||||||
With the flags `--watch` or `-w` task will watch for file changes
|
With the flags `--watch` or `-w` task will watch for file changes
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
// Note: type annotations allow type checking and IDEs autocompletion
|
// Note: type annotations allow type checking and IDEs autocompletion
|
||||||
|
|
||||||
const {
|
const {
|
||||||
GITHUB_URL,
|
CHINESE_URL,
|
||||||
TWITTER_URL,
|
|
||||||
DISCORD_URL,
|
DISCORD_URL,
|
||||||
CHINESE_URL
|
GITHUB_URL,
|
||||||
|
MASTODON_URL,
|
||||||
|
TWITTER_URL
|
||||||
} = require('./constants');
|
} = require('./constants');
|
||||||
const lightCodeTheme = require('./src/themes/prismLight');
|
const lightCodeTheme = require('./src/themes/prismLight');
|
||||||
const darkCodeTheme = require('./src/themes/prismDark');
|
const darkCodeTheme = require('./src/themes/prismDark');
|
||||||
@@ -108,6 +109,11 @@ const config = {
|
|||||||
label: 'Twitter',
|
label: 'Twitter',
|
||||||
position: 'right'
|
position: 'right'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
href: MASTODON_URL,
|
||||||
|
label: 'Mastodon',
|
||||||
|
position: 'right'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
href: DISCORD_URL,
|
href: DISCORD_URL,
|
||||||
label: 'Discord',
|
label: 'Discord',
|
||||||
@@ -146,6 +152,10 @@ const config = {
|
|||||||
label: 'Twitter',
|
label: 'Twitter',
|
||||||
href: TWITTER_URL
|
href: TWITTER_URL
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Mastodon',
|
||||||
|
href: MASTODON_URL
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Discord',
|
label: 'Discord',
|
||||||
href: DISCORD_URL
|
href: DISCORD_URL
|
||||||
|
|||||||
64
docs/static/schema.json
vendored
64
docs/static/schema.json
vendored
@@ -108,6 +108,20 @@
|
|||||||
"description": "The directory in which this task should run. Defaults to the current working directory.",
|
"description": "The directory in which this task should run. Defaults to the current working directory.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"set": {
|
||||||
|
"description": "Enables POSIX shell options for all of a task's commands. See https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/3/set"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shopt": {
|
||||||
|
"description": "Enables Bash shell options for all of a task's commands. See https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/3/shopt"
|
||||||
|
}
|
||||||
|
},
|
||||||
"vars": {
|
"vars": {
|
||||||
"description": "A set of variables that can be used in the task.",
|
"description": "A set of variables that can be used in the task.",
|
||||||
"$ref": "#/definitions/3/vars"
|
"$ref": "#/definitions/3/vars"
|
||||||
@@ -155,6 +169,13 @@
|
|||||||
"run": {
|
"run": {
|
||||||
"description": "Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`.",
|
"description": "Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`.",
|
||||||
"$ref": "#/definitions/3/run"
|
"$ref": "#/definitions/3/run"
|
||||||
|
},
|
||||||
|
"platforms": {
|
||||||
|
"description": "Specifies which platforms the task should be run on.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -177,6 +198,14 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"set": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["allexport", "a", "errexit", "e", "noexec", "n", "noglob", "f", "nounset", "u", "xtrace", "x", "pipefail"]
|
||||||
|
},
|
||||||
|
"shopt": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["expand_aliases", "globstar", "nullglob"]
|
||||||
|
},
|
||||||
"vars": {
|
"vars": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"patternProperties": {
|
"patternProperties": {
|
||||||
@@ -226,6 +255,20 @@
|
|||||||
"description": "Silent mode disables echoing of command before Task runs it",
|
"description": "Silent mode disables echoing of command before Task runs it",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"set": {
|
||||||
|
"description": "Enables POSIX shell options for this command. See https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/3/set"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shopt": {
|
||||||
|
"description": "Enables Bash shell options for this command. See https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/3/shopt"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ignore_error": {
|
"ignore_error": {
|
||||||
"description": "Prevent command from aborting the execution of task even after receiving a status code of 1",
|
"description": "Prevent command from aborting the execution of task even after receiving a status code of 1",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
@@ -233,6 +276,13 @@
|
|||||||
"defer": {
|
"defer": {
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"platforms": {
|
||||||
|
"description": "Specifies which platforms the command should be run on.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@@ -357,6 +407,20 @@
|
|||||||
"description": "Default 'silent' options for this Taskfile. If `false`, can be overidden with `true` in a task by task basis.",
|
"description": "Default 'silent' options for this Taskfile. If `false`, can be overidden with `true` in a task by task basis.",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"set": {
|
||||||
|
"description": "Enables POSIX shell options for all commands in the Taskfile. See https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/3/set"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shopt": {
|
||||||
|
"description": "Enables Bash shell options for all commands in the Taskfile. See https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/3/shopt"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dotenv": {
|
"dotenv": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "A list of `.env` file paths to be parsed.",
|
"description": "A list of `.env` file paths to be parsed.",
|
||||||
|
|||||||
@@ -5877,9 +5877,9 @@ json-schema-traverse@^1.0.0:
|
|||||||
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
|
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
|
||||||
|
|
||||||
json5@^2.1.2, json5@^2.2.1:
|
json5@^2.1.2, json5@^2.2.1:
|
||||||
version "2.2.1"
|
version "2.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
|
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab"
|
||||||
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
|
integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==
|
||||||
|
|
||||||
jsonfile@^6.0.1:
|
jsonfile@^6.0.1:
|
||||||
version "6.1.0"
|
version "6.1.0"
|
||||||
|
|||||||
9
help.go
9
help.go
@@ -51,10 +51,10 @@ func (o ListOptions) Validate() error {
|
|||||||
// Filters returns the slice of FilterFunc which filters a list
|
// Filters returns the slice of FilterFunc which filters a list
|
||||||
// of taskfile.Task according to the given ListOptions
|
// of taskfile.Task according to the given ListOptions
|
||||||
func (o ListOptions) Filters() []FilterFunc {
|
func (o ListOptions) Filters() []FilterFunc {
|
||||||
filters := []FilterFunc{FilterOutInternal()}
|
filters := []FilterFunc{FilterOutInternal}
|
||||||
|
|
||||||
if o.ListOnlyTasksWithDescriptions {
|
if o.ListOnlyTasksWithDescriptions {
|
||||||
filters = append(filters, FilterOutNoDesc())
|
filters = append(filters, FilterOutNoDesc)
|
||||||
}
|
}
|
||||||
|
|
||||||
return filters
|
return filters
|
||||||
@@ -65,7 +65,10 @@ func (o ListOptions) Filters() []FilterFunc {
|
|||||||
// The function returns a boolean indicating whether tasks were found
|
// The function returns a boolean indicating whether tasks were found
|
||||||
// and an error if one was encountered while preparing the output.
|
// and an error if one was encountered while preparing the output.
|
||||||
func (e *Executor) ListTasks(o ListOptions) (bool, error) {
|
func (e *Executor) ListTasks(o ListOptions) (bool, error) {
|
||||||
tasks := e.GetTaskList(o.Filters()...)
|
tasks, err := e.GetTaskList(o.Filters()...)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
if o.FormatTaskListAsJSON {
|
if o.FormatTaskListAsJSON {
|
||||||
output, err := e.ToEditorOutput(tasks)
|
output, err := e.ToEditorOutput(tasks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package execext
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -17,12 +18,14 @@ import (
|
|||||||
|
|
||||||
// RunCommandOptions is the options for the RunCommand func
|
// RunCommandOptions is the options for the RunCommand func
|
||||||
type RunCommandOptions struct {
|
type RunCommandOptions struct {
|
||||||
Command string
|
Command string
|
||||||
Dir string
|
Dir string
|
||||||
Env []string
|
Env []string
|
||||||
Stdin io.Reader
|
PosixOpts []string
|
||||||
Stdout io.Writer
|
BashOpts []string
|
||||||
Stderr io.Writer
|
Stdin io.Reader
|
||||||
|
Stdout io.Writer
|
||||||
|
Stderr io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -36,9 +39,18 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
|||||||
return ErrNilOptions
|
return ErrNilOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := syntax.NewParser().Parse(strings.NewReader(opts.Command), "")
|
// Set "-e" or "errexit" by default
|
||||||
if err != nil {
|
opts.PosixOpts = append(opts.PosixOpts, "e")
|
||||||
return err
|
|
||||||
|
// Format POSIX options into a slice that mvdan/sh understands
|
||||||
|
var params []string
|
||||||
|
for _, opt := range opts.PosixOpts {
|
||||||
|
if len(opt) == 1 {
|
||||||
|
params = append(params, fmt.Sprintf("-%s", opt))
|
||||||
|
} else {
|
||||||
|
params = append(params, "-o")
|
||||||
|
params = append(params, opt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
environ := opts.Env
|
environ := opts.Env
|
||||||
@@ -47,7 +59,7 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r, err := interp.New(
|
r, err := interp.New(
|
||||||
interp.Params("-e"),
|
interp.Params(params...),
|
||||||
interp.Env(expand.ListEnviron(environ...)),
|
interp.Env(expand.ListEnviron(environ...)),
|
||||||
interp.ExecHandler(interp.DefaultExecHandler(15*time.Second)),
|
interp.ExecHandler(interp.DefaultExecHandler(15*time.Second)),
|
||||||
interp.OpenHandler(openHandler),
|
interp.OpenHandler(openHandler),
|
||||||
@@ -58,6 +70,25 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parser := syntax.NewParser()
|
||||||
|
|
||||||
|
// Run any shopt commands
|
||||||
|
if len(opts.BashOpts) > 0 {
|
||||||
|
shoptCmdStr := fmt.Sprintf("shopt -s %s", strings.Join(opts.BashOpts, " "))
|
||||||
|
shoptCmd, err := parser.Parse(strings.NewReader(shoptCmdStr), "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := r.Run(ctx, shoptCmd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the user-defined command
|
||||||
|
p, err := parser.Parse(strings.NewReader(opts.Command), "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return r.Run(ctx, p)
|
return r.Run(ctx, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
62
internal/goext/meta.go
Normal file
62
internal/goext/meta.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package goext
|
||||||
|
|
||||||
|
// NOTE(@andreynering): The lists in this file were copied from:
|
||||||
|
//
|
||||||
|
// https://github.com/golang/go/blob/master/src/go/build/syslist.go
|
||||||
|
|
||||||
|
func IsKnownOS(str string) bool {
|
||||||
|
_, known := knownOS[str]
|
||||||
|
return known
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsKnownArch(str string) bool {
|
||||||
|
_, known := knownArch[str]
|
||||||
|
return known
|
||||||
|
}
|
||||||
|
|
||||||
|
var knownOS = map[string]struct{}{
|
||||||
|
"aix": {},
|
||||||
|
"android": {},
|
||||||
|
"darwin": {},
|
||||||
|
"dragonfly": {},
|
||||||
|
"freebsd": {},
|
||||||
|
"hurd": {},
|
||||||
|
"illumos": {},
|
||||||
|
"ios": {},
|
||||||
|
"js": {},
|
||||||
|
"linux": {},
|
||||||
|
"nacl": {},
|
||||||
|
"netbsd": {},
|
||||||
|
"openbsd": {},
|
||||||
|
"plan9": {},
|
||||||
|
"solaris": {},
|
||||||
|
"windows": {},
|
||||||
|
"zos": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
var knownArch = map[string]struct{}{
|
||||||
|
"386": {},
|
||||||
|
"amd64": {},
|
||||||
|
"amd64p32": {},
|
||||||
|
"arm": {},
|
||||||
|
"armbe": {},
|
||||||
|
"arm64": {},
|
||||||
|
"arm64be": {},
|
||||||
|
"loong64": {},
|
||||||
|
"mips": {},
|
||||||
|
"mipsle": {},
|
||||||
|
"mips64": {},
|
||||||
|
"mips64le": {},
|
||||||
|
"mips64p32": {},
|
||||||
|
"mips64p32le": {},
|
||||||
|
"ppc": {},
|
||||||
|
"ppc64": {},
|
||||||
|
"ppc64le": {},
|
||||||
|
"riscv": {},
|
||||||
|
"riscv64": {},
|
||||||
|
"s390": {},
|
||||||
|
"s390x": {},
|
||||||
|
"sparc": {},
|
||||||
|
"sparc64": {},
|
||||||
|
"wasm": {},
|
||||||
|
}
|
||||||
20
internal/slicesext/slicesext.go
Normal file
20
internal/slicesext/slicesext.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package slicesext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UniqueJoin[T constraints.Ordered](ss ...[]T) []T {
|
||||||
|
var length int
|
||||||
|
for _, s := range ss {
|
||||||
|
length += len(s)
|
||||||
|
}
|
||||||
|
r := make([]T, length)
|
||||||
|
var i int
|
||||||
|
for _, s := range ss {
|
||||||
|
i += copy(r[i:], s)
|
||||||
|
}
|
||||||
|
slices.Sort(r)
|
||||||
|
return slices.Compact(r)
|
||||||
|
}
|
||||||
@@ -85,6 +85,7 @@ func (c *Checksum) checksum(files ...string) (string, error) {
|
|||||||
if _, err = io.Copy(h, f); err != nil {
|
if _, err = io.Copy(h, f); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||||
@@ -109,12 +110,12 @@ func (*Checksum) Kind() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checksum) checksumFilePath() string {
|
func (c *Checksum) checksumFilePath() string {
|
||||||
return filepath.Join(c.TempDir, "checksum", c.normalizeFilename(c.Task))
|
return filepath.Join(c.TempDir, "checksum", normalizeFilename(c.Task))
|
||||||
}
|
}
|
||||||
|
|
||||||
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")
|
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")
|
||||||
|
|
||||||
// replaces invalid caracters on filenames with "-"
|
// replaces invalid caracters on filenames with "-"
|
||||||
func (*Checksum) normalizeFilename(f string) string {
|
func normalizeFilename(f string) string {
|
||||||
return checksumFilenameRegexp.ReplaceAllString(f, "-")
|
return checksumFilenameRegexp.ReplaceAllString(f, "-")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ func TestNormalizeFilename(t *testing.T) {
|
|||||||
{"foo1bar2baz3", "foo1bar2baz3"},
|
{"foo1bar2baz3", "foo1bar2baz3"},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
assert.Equal(t, test.Out, (&Checksum{}).normalizeFilename(test.In))
|
assert.Equal(t, test.Out, normalizeFilename(test.In))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,24 @@ package status
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Timestamp checks if any source change compared with the generated files,
|
// Timestamp checks if any source change compared with the generated files,
|
||||||
// using file modifications timestamps.
|
// using file modifications timestamps.
|
||||||
type Timestamp struct {
|
type Timestamp struct {
|
||||||
|
TempDir string
|
||||||
|
Task string
|
||||||
Dir string
|
Dir string
|
||||||
Sources []string
|
Sources []string
|
||||||
Generates []string
|
Generates []string
|
||||||
|
Dry bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUpToDate implements the Checker interface
|
// IsUpToDate implements the Checker interface
|
||||||
func (t *Timestamp) IsUpToDate() (bool, error) {
|
func (t *Timestamp) IsUpToDate() (bool, error) {
|
||||||
if len(t.Sources) == 0 || len(t.Generates) == 0 {
|
if len(t.Sources) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,17 +32,51 @@ func (t *Timestamp) IsUpToDate() (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sourcesMaxTime, err := getMaxTime(sources...)
|
timestampFile := t.timestampFilePath()
|
||||||
if err != nil || sourcesMaxTime.IsZero() {
|
|
||||||
|
// If the file exists, add the file path to the generates.
|
||||||
|
// If the generate file is old, the task will be executed.
|
||||||
|
_, err = os.Stat(timestampFile)
|
||||||
|
if err == nil {
|
||||||
|
generates = append(generates, timestampFile)
|
||||||
|
} else {
|
||||||
|
// Create the timestamp file for the next execution when the file does not exist.
|
||||||
|
if !t.Dry {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(timestampFile), 0o755); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
f, err := os.Create(timestampFile)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
taskTime := time.Now()
|
||||||
|
|
||||||
|
// Compare the time of the generates and sources. If the generates are old, the task will be executed.
|
||||||
|
|
||||||
|
// Get the max time of the generates.
|
||||||
|
generateMaxTime, err := getMaxTime(generates...)
|
||||||
|
if err != nil || generateMaxTime.IsZero() {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
generatesMinTime, err := getMinTime(generates...)
|
// Check if any of the source files is newer than the max time of the generates.
|
||||||
if err != nil || generatesMinTime.IsZero() {
|
shouldUpdate, err := anyFileNewerThan(sources, generateMaxTime)
|
||||||
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return !generatesMinTime.Before(sourcesMaxTime), nil
|
// Modify the metadata of the file to the the current time.
|
||||||
|
if !t.Dry {
|
||||||
|
if err := os.Chtimes(timestampFile, taskTime, taskTime); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !shouldUpdate, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Timestamp) Kind() string {
|
func (t *Timestamp) Kind() string {
|
||||||
@@ -64,18 +102,6 @@ func (t *Timestamp) Value() (interface{}, error) {
|
|||||||
return sourcesMaxTime, nil
|
return sourcesMaxTime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMinTime(files ...string) (time.Time, error) {
|
|
||||||
var t time.Time
|
|
||||||
for _, f := range files {
|
|
||||||
info, err := os.Stat(f)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
t = minTime(t, info.ModTime())
|
|
||||||
}
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMaxTime(files ...string) (time.Time, error) {
|
func getMaxTime(files ...string) (time.Time, error) {
|
||||||
var t time.Time
|
var t time.Time
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
@@ -88,13 +114,6 @@ func getMaxTime(files ...string) (time.Time, error) {
|
|||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func minTime(a, b time.Time) time.Time {
|
|
||||||
if !a.IsZero() && a.Before(b) {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func maxTime(a, b time.Time) time.Time {
|
func maxTime(a, b time.Time) time.Time {
|
||||||
if a.After(b) {
|
if a.After(b) {
|
||||||
return a
|
return a
|
||||||
@@ -102,7 +121,26 @@ func maxTime(a, b time.Time) time.Time {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the modification time of any of the files is newer than the the given time, returns true.
|
||||||
|
// This function is lazy, as it stops when it finds a file newer than the given time.
|
||||||
|
func anyFileNewerThan(files []string, givenTime time.Time) (bool, error) {
|
||||||
|
for _, f := range files {
|
||||||
|
info, err := os.Stat(f)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if info.ModTime().After(givenTime) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// OnError implements the Checker interface
|
// OnError implements the Checker interface
|
||||||
func (*Timestamp) OnError() error {
|
func (*Timestamp) OnError() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Timestamp) timestampFilePath() string {
|
||||||
|
return filepath.Join(t.TempDir, "timestamp", normalizeFilename(t.Task))
|
||||||
|
}
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@go-task/cli",
|
"name": "@go-task/cli",
|
||||||
"version": "3.19.1",
|
"version": "3.20.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@go-task/cli",
|
"name": "@go-task/cli",
|
||||||
"version": "3.19.1",
|
"version": "3.20.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@go-task/cli",
|
"name": "@go-task/cli",
|
||||||
"version": "3.19.1",
|
"version": "3.20.0",
|
||||||
"description": "A task runner / simpler Make alternative written in Go",
|
"description": "A task runner / simpler Make alternative written in Go",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "go-npm install",
|
"postinstall": "go-npm install",
|
||||||
|
|||||||
@@ -87,9 +87,12 @@ func (e *Executor) getStatusChecker(t *taskfile.Task) (status.Checker, error) {
|
|||||||
|
|
||||||
func (e *Executor) timestampChecker(t *taskfile.Task) status.Checker {
|
func (e *Executor) timestampChecker(t *taskfile.Task) status.Checker {
|
||||||
return &status.Timestamp{
|
return &status.Timestamp{
|
||||||
|
TempDir: e.TempDir,
|
||||||
|
Task: t.Name(),
|
||||||
Dir: t.Dir,
|
Dir: t.Dir,
|
||||||
Sources: t.Sources,
|
Sources: t.Sources,
|
||||||
Generates: t.Generates,
|
Generates: t.Generates,
|
||||||
|
Dry: e.Dry,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
111
task.go
111
task.go
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -15,6 +16,7 @@ import (
|
|||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/output"
|
"github.com/go-task/task/v3/internal/output"
|
||||||
|
"github.com/go-task/task/v3/internal/slicesext"
|
||||||
"github.com/go-task/task/v3/internal/summary"
|
"github.com/go-task/task/v3/internal/summary"
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
@@ -135,6 +137,11 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
return e.startExecution(ctx, t, func(ctx context.Context) error {
|
return e.startExecution(ctx, t, func(ctx context.Context) error {
|
||||||
|
if !shouldRunOnCurrentPlatform(t.Platforms) {
|
||||||
|
e.Logger.VerboseOutf(logger.Yellow, `task: "%s" not for current platform - ignored`, call.Task)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" started`, call.Task)
|
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" started`, call.Task)
|
||||||
if err := e.runDeps(ctx, t); err != nil {
|
if err := e.runDeps(ctx, t); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -252,6 +259,11 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case cmd.Cmd != "":
|
case cmd.Cmd != "":
|
||||||
|
if !shouldRunOnCurrentPlatform(cmd.Platforms) {
|
||||||
|
e.Logger.VerboseOutf(logger.Yellow, `task: [%s] %s not for current platform - ignored`, t.Name(), cmd.Cmd)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Taskfile.Silent && !e.Silent) {
|
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Taskfile.Silent && !e.Silent) {
|
||||||
e.Logger.Errf(logger.Green, "task: [%s] %s", t.Name(), cmd.Cmd)
|
e.Logger.Errf(logger.Green, "task: [%s] %s", t.Name(), cmd.Cmd)
|
||||||
}
|
}
|
||||||
@@ -272,17 +284,19 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
|
|||||||
stdOut, stdErr, close := outputWrapper.WrapWriter(e.Stdout, e.Stderr, t.Prefix, outputTemplater)
|
stdOut, stdErr, close := outputWrapper.WrapWriter(e.Stdout, e.Stderr, t.Prefix, outputTemplater)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := close(); err != nil {
|
if err := close(); err != nil {
|
||||||
e.Logger.Errf(logger.Red, "task: unable to close writter: %v", err)
|
e.Logger.Errf(logger.Red, "task: unable to close writer: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = execext.RunCommand(ctx, &execext.RunCommandOptions{
|
err = execext.RunCommand(ctx, &execext.RunCommandOptions{
|
||||||
Command: cmd.Cmd,
|
Command: cmd.Cmd,
|
||||||
Dir: t.Dir,
|
Dir: t.Dir,
|
||||||
Env: getEnviron(t),
|
Env: getEnviron(t),
|
||||||
Stdin: e.Stdin,
|
PosixOpts: slicesext.UniqueJoin(e.Taskfile.Set, t.Set, cmd.Set),
|
||||||
Stdout: stdOut,
|
BashOpts: slicesext.UniqueJoin(e.Taskfile.Shopt, t.Shopt, cmd.Shopt),
|
||||||
Stderr: stdErr,
|
Stdin: e.Stdin,
|
||||||
|
Stdout: stdOut,
|
||||||
|
Stderr: stdErr,
|
||||||
})
|
})
|
||||||
if execext.IsExitError(err) && cmd.IgnoreError {
|
if execext.IsExitError(err) && cmd.IgnoreError {
|
||||||
e.Logger.VerboseErrf(logger.Yellow, "task: [%s] command error ignored: %v", t.Name(), err)
|
e.Logger.VerboseErrf(logger.Yellow, "task: [%s] command error ignored: %v", t.Name(), err)
|
||||||
@@ -386,23 +400,39 @@ func (e *Executor) GetTask(call taskfile.Call) (*taskfile.Task, error) {
|
|||||||
return matchingTask, nil
|
return matchingTask, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilterFunc func(tasks []*taskfile.Task) []*taskfile.Task
|
type FilterFunc func(task *taskfile.Task) bool
|
||||||
|
|
||||||
func (e *Executor) GetTaskList(filters ...FilterFunc) []*taskfile.Task {
|
func (e *Executor) GetTaskList(filters ...FilterFunc) ([]*taskfile.Task, error) {
|
||||||
tasks := make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
tasks := make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
||||||
|
|
||||||
|
// Create an error group to wait for each task to be compiled
|
||||||
|
var g errgroup.Group
|
||||||
|
|
||||||
// Fetch and compile the list of tasks
|
// Fetch and compile the list of tasks
|
||||||
for _, task := range e.Taskfile.Tasks {
|
for key := range e.Taskfile.Tasks {
|
||||||
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
|
task := e.Taskfile.Tasks[key]
|
||||||
if err == nil {
|
g.Go(func() error {
|
||||||
task = compiledTask
|
|
||||||
}
|
// Check if we should filter the task
|
||||||
tasks = append(tasks, task)
|
for _, filter := range filters {
|
||||||
|
if filter(task) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile the task
|
||||||
|
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
|
||||||
|
if err == nil {
|
||||||
|
task = compiledTask
|
||||||
|
}
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter the tasks
|
// Wait for all the go routines to finish
|
||||||
for _, filter := range filters {
|
if err := g.Wait(); err != nil {
|
||||||
tasks = filter(tasks)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the tasks.
|
// Sort the tasks.
|
||||||
@@ -420,38 +450,27 @@ func (e *Executor) GetTaskList(filters ...FilterFunc) []*taskfile.Task {
|
|||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
return tasks
|
return tasks, nil
|
||||||
}
|
|
||||||
|
|
||||||
// Filter is a generic task filtering function. It will remove each task in the
|
|
||||||
// slice where the result of the given function is true.
|
|
||||||
func Filter(f func(task *taskfile.Task) bool) FilterFunc {
|
|
||||||
return func(tasks []*taskfile.Task) []*taskfile.Task {
|
|
||||||
shift := 0
|
|
||||||
for _, task := range tasks {
|
|
||||||
if !f(task) {
|
|
||||||
tasks[shift] = task
|
|
||||||
shift++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This loop stops any memory leaks
|
|
||||||
for j := shift; j < len(tasks); j++ {
|
|
||||||
tasks[j] = nil
|
|
||||||
}
|
|
||||||
return slices.Clip(tasks[:shift])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterOutNoDesc removes all tasks that do not contain a description.
|
// FilterOutNoDesc removes all tasks that do not contain a description.
|
||||||
func FilterOutNoDesc() FilterFunc {
|
func FilterOutNoDesc(task *taskfile.Task) bool {
|
||||||
return Filter(func(task *taskfile.Task) bool {
|
return task.Desc == ""
|
||||||
return task.Desc == ""
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterOutInternal removes all tasks that are marked as internal.
|
// FilterOutInternal removes all tasks that are marked as internal.
|
||||||
func FilterOutInternal() FilterFunc {
|
func FilterOutInternal(task *taskfile.Task) bool {
|
||||||
return Filter(func(task *taskfile.Task) bool {
|
return task.Internal
|
||||||
return task.Internal
|
}
|
||||||
})
|
|
||||||
|
func shouldRunOnCurrentPlatform(platforms []*taskfile.Platform) bool {
|
||||||
|
if len(platforms) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, p := range platforms {
|
||||||
|
if (p.OS == "" || p.OS == runtime.GOOS) && (p.Arch == "" || p.Arch == runtime.GOARCH) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
96
task_test.go
96
task_test.go
@@ -1696,3 +1696,99 @@ func TestUserWorkingDirectory(t *testing.T) {
|
|||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
||||||
assert.Equal(t, fmt.Sprintf("%s\n", wd), buff.String())
|
assert.Equal(t, fmt.Sprintf("%s\n", wd), buff.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPlatforms(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: "testdata/platforms",
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.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())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPOSIXShellOptsGlobalLevel(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: "testdata/shopts/global_level",
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "pipefail"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPOSIXShellOptsTaskLevel(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: "testdata/shopts/task_level",
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "pipefail"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPOSIXShellOptsCommandLevel(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: "testdata/shopts/command_level",
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "pipefail"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBashShellOptsGlobalLevel(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: "testdata/shopts/global_level",
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "globstar"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBashShellOptsTaskLevel(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: "testdata/shopts/task_level",
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "globstar"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBashShellOptsCommandLevel(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: "testdata/shopts/command_level",
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "globstar"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,9 +11,12 @@ type Cmd struct {
|
|||||||
Cmd string
|
Cmd string
|
||||||
Silent bool
|
Silent bool
|
||||||
Task string
|
Task string
|
||||||
|
Set []string
|
||||||
|
Shopt []string
|
||||||
Vars *Vars
|
Vars *Vars
|
||||||
IgnoreError bool
|
IgnoreError bool
|
||||||
Defer bool
|
Defer bool
|
||||||
|
Platforms []*Platform
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dep is a task dependency
|
// Dep is a task dependency
|
||||||
@@ -39,12 +42,18 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
var cmdStruct struct {
|
var cmdStruct struct {
|
||||||
Cmd string
|
Cmd string
|
||||||
Silent bool
|
Silent bool
|
||||||
|
Set []string
|
||||||
|
Shopt []string
|
||||||
IgnoreError bool `yaml:"ignore_error"`
|
IgnoreError bool `yaml:"ignore_error"`
|
||||||
|
Platforms []*Platform
|
||||||
}
|
}
|
||||||
if err := node.Decode(&cmdStruct); err == nil && cmdStruct.Cmd != "" {
|
if err := node.Decode(&cmdStruct); err == nil && cmdStruct.Cmd != "" {
|
||||||
c.Cmd = cmdStruct.Cmd
|
c.Cmd = cmdStruct.Cmd
|
||||||
c.Silent = cmdStruct.Silent
|
c.Silent = cmdStruct.Silent
|
||||||
|
c.Set = cmdStruct.Set
|
||||||
|
c.Shopt = cmdStruct.Shopt
|
||||||
c.IgnoreError = cmdStruct.IgnoreError
|
c.IgnoreError = cmdStruct.IgnoreError
|
||||||
|
c.Platforms = cmdStruct.Platforms
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
90
taskfile/platforms.go
Normal file
90
taskfile/platforms.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package taskfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/goext"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Platform represents GOOS and GOARCH values
|
||||||
|
type Platform struct {
|
||||||
|
OS string
|
||||||
|
Arch string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrInvalidPlatform struct {
|
||||||
|
Platform string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *ErrInvalidPlatform) Error() string {
|
||||||
|
return fmt.Sprintf(`task: Invalid platform "%s"`, err.Platform)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML implements yaml.Unmarshaler interface.
|
||||||
|
func (p *Platform) UnmarshalYAML(node *yaml.Node) error {
|
||||||
|
switch node.Kind {
|
||||||
|
case yaml.ScalarNode:
|
||||||
|
var platform string
|
||||||
|
if err := node.Decode(&platform); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.parsePlatform(platform); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into platform", node.Line, node.ShortTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePlatform takes a string representing an OS/Arch combination (or either on their own)
|
||||||
|
// and parses it into the Platform struct. It returns an error if the input string is invalid.
|
||||||
|
// Valid combinations for input: OS, Arch, OS/Arch
|
||||||
|
func (p *Platform) parsePlatform(input string) error {
|
||||||
|
splitValues := strings.Split(input, "/")
|
||||||
|
if len(splitValues) > 2 {
|
||||||
|
return &ErrInvalidPlatform{Platform: input}
|
||||||
|
}
|
||||||
|
if err := p.parseOsOrArch(splitValues[0]); err != nil {
|
||||||
|
return &ErrInvalidPlatform{Platform: input}
|
||||||
|
}
|
||||||
|
if len(splitValues) == 2 {
|
||||||
|
if err := p.parseArch(splitValues[1]); err != nil {
|
||||||
|
return &ErrInvalidPlatform{Platform: input}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseOsOrArch will check if the given input is a valid OS or Arch value.
|
||||||
|
// If so, it will store it. If not, an error is returned
|
||||||
|
func (p *Platform) parseOsOrArch(osOrArch string) error {
|
||||||
|
if osOrArch == "" {
|
||||||
|
return fmt.Errorf("task: Blank OS/Arch value provided")
|
||||||
|
}
|
||||||
|
if goext.IsKnownOS(osOrArch) {
|
||||||
|
p.OS = osOrArch
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if goext.IsKnownArch(osOrArch) {
|
||||||
|
p.Arch = osOrArch
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("task: Invalid OS/Arch value provided (%s)", osOrArch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Platform) parseArch(arch string) error {
|
||||||
|
if arch == "" {
|
||||||
|
return fmt.Errorf("task: Blank Arch value provided")
|
||||||
|
}
|
||||||
|
if p.Arch != "" {
|
||||||
|
return fmt.Errorf("task: Multiple Arch values provided")
|
||||||
|
}
|
||||||
|
if goext.IsKnownArch(arch) {
|
||||||
|
p.Arch = arch
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("task: Invalid Arch value provided (%s)", arch)
|
||||||
|
}
|
||||||
49
taskfile/platforms_test.go
Normal file
49
taskfile/platforms_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package taskfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPlatformParsing(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Input string
|
||||||
|
ExpectedOS string
|
||||||
|
ExpectedArch string
|
||||||
|
Error string
|
||||||
|
}{
|
||||||
|
{Input: "windows", ExpectedOS: "windows", ExpectedArch: ""},
|
||||||
|
{Input: "linux", ExpectedOS: "linux", ExpectedArch: ""},
|
||||||
|
{Input: "darwin", ExpectedOS: "darwin", ExpectedArch: ""},
|
||||||
|
|
||||||
|
{Input: "386", ExpectedOS: "", ExpectedArch: "386"},
|
||||||
|
{Input: "amd64", ExpectedOS: "", ExpectedArch: "amd64"},
|
||||||
|
{Input: "arm64", ExpectedOS: "", ExpectedArch: "arm64"},
|
||||||
|
|
||||||
|
{Input: "windows/386", ExpectedOS: "windows", ExpectedArch: "386"},
|
||||||
|
{Input: "windows/amd64", ExpectedOS: "windows", ExpectedArch: "amd64"},
|
||||||
|
{Input: "windows/arm64", ExpectedOS: "windows", ExpectedArch: "arm64"},
|
||||||
|
|
||||||
|
{Input: "invalid", Error: `task: Invalid platform "invalid"`},
|
||||||
|
{Input: "invalid/invalid", Error: `task: Invalid platform "invalid/invalid"`},
|
||||||
|
{Input: "windows/invalid", Error: `task: Invalid platform "windows/invalid"`},
|
||||||
|
{Input: "invalid/amd64", Error: `task: Invalid platform "invalid/amd64"`},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.Input, func(t *testing.T) {
|
||||||
|
var p Platform
|
||||||
|
err := p.parsePlatform(test.Input)
|
||||||
|
|
||||||
|
if test.Error != "" {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, test.Error, err.Error())
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, test.ExpectedOS, p.OS)
|
||||||
|
assert.Equal(t, test.ExpectedArch, p.Arch)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,8 @@ type Task struct {
|
|||||||
Status []string
|
Status []string
|
||||||
Preconditions []*Precondition
|
Preconditions []*Precondition
|
||||||
Dir string
|
Dir string
|
||||||
|
Set []string
|
||||||
|
Shopt []string
|
||||||
Vars *Vars
|
Vars *Vars
|
||||||
Env *Vars
|
Env *Vars
|
||||||
Dotenv []string
|
Dotenv []string
|
||||||
@@ -36,6 +38,7 @@ type Task struct {
|
|||||||
IncludeVars *Vars
|
IncludeVars *Vars
|
||||||
IncludedTaskfileVars *Vars
|
IncludedTaskfileVars *Vars
|
||||||
IncludedTaskfile *IncludedTaskfile
|
IncludedTaskfile *IncludedTaskfile
|
||||||
|
Platforms []*Platform
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) Name() string {
|
func (t *Task) Name() string {
|
||||||
@@ -80,6 +83,8 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
Status []string
|
Status []string
|
||||||
Preconditions []*Precondition
|
Preconditions []*Precondition
|
||||||
Dir string
|
Dir string
|
||||||
|
Set []string
|
||||||
|
Shopt []string
|
||||||
Vars *Vars
|
Vars *Vars
|
||||||
Env *Vars
|
Env *Vars
|
||||||
Dotenv []string
|
Dotenv []string
|
||||||
@@ -90,6 +95,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
Prefix string
|
Prefix string
|
||||||
IgnoreError bool `yaml:"ignore_error"`
|
IgnoreError bool `yaml:"ignore_error"`
|
||||||
Run string
|
Run string
|
||||||
|
Platforms []*Platform
|
||||||
}
|
}
|
||||||
if err := node.Decode(&task); err != nil {
|
if err := node.Decode(&task); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -105,6 +111,8 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
t.Status = task.Status
|
t.Status = task.Status
|
||||||
t.Preconditions = task.Preconditions
|
t.Preconditions = task.Preconditions
|
||||||
t.Dir = task.Dir
|
t.Dir = task.Dir
|
||||||
|
t.Set = task.Set
|
||||||
|
t.Shopt = task.Shopt
|
||||||
t.Vars = task.Vars
|
t.Vars = task.Vars
|
||||||
t.Env = task.Env
|
t.Env = task.Env
|
||||||
t.Dotenv = task.Dotenv
|
t.Dotenv = task.Dotenv
|
||||||
@@ -115,6 +123,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
t.Prefix = task.Prefix
|
t.Prefix = task.Prefix
|
||||||
t.IgnoreError = task.IgnoreError
|
t.IgnoreError = task.IgnoreError
|
||||||
t.Run = task.Run
|
t.Run = task.Run
|
||||||
|
t.Platforms = task.Platforms
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +146,8 @@ func (t *Task) DeepCopy() *Task {
|
|||||||
Status: deepCopySlice(t.Status),
|
Status: deepCopySlice(t.Status),
|
||||||
Preconditions: deepCopySlice(t.Preconditions),
|
Preconditions: deepCopySlice(t.Preconditions),
|
||||||
Dir: t.Dir,
|
Dir: t.Dir,
|
||||||
|
Set: deepCopySlice(t.Set),
|
||||||
|
Shopt: deepCopySlice(t.Shopt),
|
||||||
Vars: t.Vars.DeepCopy(),
|
Vars: t.Vars.DeepCopy(),
|
||||||
Env: t.Env.DeepCopy(),
|
Env: t.Env.DeepCopy(),
|
||||||
Dotenv: deepCopySlice(t.Dotenv),
|
Dotenv: deepCopySlice(t.Dotenv),
|
||||||
@@ -150,6 +161,7 @@ func (t *Task) DeepCopy() *Task {
|
|||||||
IncludeVars: t.IncludeVars.DeepCopy(),
|
IncludeVars: t.IncludeVars.DeepCopy(),
|
||||||
IncludedTaskfileVars: t.IncludedTaskfileVars.DeepCopy(),
|
IncludedTaskfileVars: t.IncludedTaskfileVars.DeepCopy(),
|
||||||
IncludedTaskfile: t.IncludedTaskfile.DeepCopy(),
|
IncludedTaskfile: t.IncludedTaskfile.DeepCopy(),
|
||||||
|
Platforms: deepCopySlice(t.Platforms),
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ type Taskfile struct {
|
|||||||
Output Output
|
Output Output
|
||||||
Method string
|
Method string
|
||||||
Includes *IncludedTaskfiles
|
Includes *IncludedTaskfiles
|
||||||
|
Set []string
|
||||||
|
Shopt []string
|
||||||
Vars *Vars
|
Vars *Vars
|
||||||
Env *Vars
|
Env *Vars
|
||||||
Tasks Tasks
|
Tasks Tasks
|
||||||
@@ -34,6 +36,8 @@ func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
Output Output
|
Output Output
|
||||||
Method string
|
Method string
|
||||||
Includes *IncludedTaskfiles
|
Includes *IncludedTaskfiles
|
||||||
|
Set []string
|
||||||
|
Shopt []string
|
||||||
Vars *Vars
|
Vars *Vars
|
||||||
Env *Vars
|
Env *Vars
|
||||||
Tasks Tasks
|
Tasks Tasks
|
||||||
@@ -50,6 +54,8 @@ func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
tf.Output = taskfile.Output
|
tf.Output = taskfile.Output
|
||||||
tf.Method = taskfile.Method
|
tf.Method = taskfile.Method
|
||||||
tf.Includes = taskfile.Includes
|
tf.Includes = taskfile.Includes
|
||||||
|
tf.Set = taskfile.Set
|
||||||
|
tf.Shopt = taskfile.Shopt
|
||||||
tf.Vars = taskfile.Vars
|
tf.Vars = taskfile.Vars
|
||||||
tf.Env = taskfile.Env
|
tf.Env = taskfile.Env
|
||||||
tf.Tasks = taskfile.Tasks
|
tf.Tasks = taskfile.Tasks
|
||||||
|
|||||||
55
testdata/platforms/Taskfile.yml
vendored
Normal file
55
testdata/platforms/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-windows:
|
||||||
|
platforms: [windows]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running task on windows'
|
||||||
|
|
||||||
|
build-darwin:
|
||||||
|
platforms: [darwin]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running task on darwin'
|
||||||
|
|
||||||
|
build-linux:
|
||||||
|
platforms: [linux]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running task on linux'
|
||||||
|
|
||||||
|
build-freebsd:
|
||||||
|
platforms: [freebsd]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running task on freebsd'
|
||||||
|
|
||||||
|
build-blank-os:
|
||||||
|
platforms: []
|
||||||
|
cmds:
|
||||||
|
- echo 'Running command'
|
||||||
|
|
||||||
|
build-multiple:
|
||||||
|
platforms: []
|
||||||
|
cmds:
|
||||||
|
- cmd: echo 'Running command'
|
||||||
|
- cmd: echo 'Running on Windows'
|
||||||
|
platforms: [windows]
|
||||||
|
- cmd: echo 'Running on Darwin'
|
||||||
|
platforms: [darwin]
|
||||||
|
|
||||||
|
build-amd64:
|
||||||
|
platforms: [amd64]
|
||||||
|
cmds:
|
||||||
|
- echo "Running command on amd64"
|
||||||
|
|
||||||
|
build-arm64:
|
||||||
|
platforms: [arm64]
|
||||||
|
cmds:
|
||||||
|
- echo "Running command on arm64"
|
||||||
|
|
||||||
|
build-mixed:
|
||||||
|
cmds:
|
||||||
|
- cmd: echo 'building on windows/arm64'
|
||||||
|
platforms: [windows/arm64]
|
||||||
|
- cmd: echo 'building on linux/amd64'
|
||||||
|
platforms: [linux/amd64]
|
||||||
|
- cmd: echo 'building on darwin'
|
||||||
|
platforms: [darwin]
|
||||||
14
testdata/shopts/command_level/Taskfile.yml
vendored
Normal file
14
testdata/shopts/command_level/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
silent: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
pipefail:
|
||||||
|
cmds:
|
||||||
|
- cmd: set -o | grep pipefail
|
||||||
|
set: [pipefail]
|
||||||
|
|
||||||
|
globstar:
|
||||||
|
cmds:
|
||||||
|
- cmd: shopt | grep globstar
|
||||||
|
shopt: [globstar]
|
||||||
14
testdata/shopts/global_level/Taskfile.yml
vendored
Normal file
14
testdata/shopts/global_level/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
silent: true
|
||||||
|
set: [pipefail]
|
||||||
|
shopt: [globstar]
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
pipefail:
|
||||||
|
cmds:
|
||||||
|
- set -o | grep pipefail
|
||||||
|
|
||||||
|
globstar:
|
||||||
|
cmds:
|
||||||
|
- shopt | grep globstar
|
||||||
14
testdata/shopts/task_level/Taskfile.yml
vendored
Normal file
14
testdata/shopts/task_level/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
silent: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
pipefail:
|
||||||
|
set: [pipefail]
|
||||||
|
cmds:
|
||||||
|
- set -o | grep pipefail
|
||||||
|
|
||||||
|
globstar:
|
||||||
|
shopt: [globstar]
|
||||||
|
cmds:
|
||||||
|
- shopt | grep globstar
|
||||||
10
variables.go
10
variables.go
@@ -56,6 +56,8 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
Sources: r.ReplaceSlice(origTask.Sources),
|
Sources: r.ReplaceSlice(origTask.Sources),
|
||||||
Generates: r.ReplaceSlice(origTask.Generates),
|
Generates: r.ReplaceSlice(origTask.Generates),
|
||||||
Dir: r.Replace(origTask.Dir),
|
Dir: r.Replace(origTask.Dir),
|
||||||
|
Set: origTask.Set,
|
||||||
|
Shopt: origTask.Shopt,
|
||||||
Vars: nil,
|
Vars: nil,
|
||||||
Env: nil,
|
Env: nil,
|
||||||
Dotenv: r.ReplaceSlice(origTask.Dotenv),
|
Dotenv: r.ReplaceSlice(origTask.Dotenv),
|
||||||
@@ -68,6 +70,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
Run: r.Replace(origTask.Run),
|
Run: r.Replace(origTask.Run),
|
||||||
IncludeVars: origTask.IncludeVars,
|
IncludeVars: origTask.IncludeVars,
|
||||||
IncludedTaskfileVars: origTask.IncludedTaskfileVars,
|
IncludedTaskfileVars: origTask.IncludedTaskfileVars,
|
||||||
|
Platforms: origTask.Platforms,
|
||||||
}
|
}
|
||||||
new.Dir, err = execext.Expand(new.Dir)
|
new.Dir, err = execext.Expand(new.Dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -124,12 +127,15 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
new.Cmds = append(new.Cmds, &taskfile.Cmd{
|
new.Cmds = append(new.Cmds, &taskfile.Cmd{
|
||||||
Task: r.Replace(cmd.Task),
|
|
||||||
Silent: cmd.Silent,
|
|
||||||
Cmd: r.Replace(cmd.Cmd),
|
Cmd: r.Replace(cmd.Cmd),
|
||||||
|
Silent: cmd.Silent,
|
||||||
|
Task: r.Replace(cmd.Task),
|
||||||
|
Set: cmd.Set,
|
||||||
|
Shopt: cmd.Shopt,
|
||||||
Vars: r.ReplaceVars(cmd.Vars),
|
Vars: r.ReplaceVars(cmd.Vars),
|
||||||
IgnoreError: cmd.IgnoreError,
|
IgnoreError: cmd.IgnoreError,
|
||||||
Defer: cmd.Defer,
|
Defer: cmd.Defer,
|
||||||
|
Platforms: cmd.Platforms,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user