mirror of
https://github.com/go-task/task.git
synced 2026-06-16 04:11:40 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
888338c60e | ||
|
|
e6c6cc7811 | ||
|
|
fa0e72bd69 | ||
|
|
18decac44d | ||
|
|
1012a0cf2b | ||
|
|
7e4de945cf | ||
|
|
a468272726 | ||
|
|
039d8f000d | ||
|
|
d35f960a8a | ||
|
|
634f8ed574 | ||
|
|
d369451308 | ||
|
|
8f1202424d | ||
|
|
50e5813222 | ||
|
|
7bc268aeaa | ||
|
|
537b5b1e25 | ||
|
|
aae38f8ce7 | ||
|
|
ad05432bcf | ||
|
|
8aa983257d | ||
|
|
046a97d1e5 | ||
|
|
d28649b13d | ||
|
|
3e16ca37bc | ||
|
|
2da38a5bdc | ||
|
|
bbe1d8b52e | ||
|
|
97c85e39c3 | ||
|
|
a7b59e5b12 | ||
|
|
9eb1252ce9 | ||
|
|
0e01e13670 | ||
|
|
239e61e718 | ||
|
|
22549e9fd8 | ||
|
|
1f9fd24064 | ||
|
|
a7594740e3 | ||
|
|
945c72cf6c | ||
|
|
824b0c0132 | ||
|
|
51e9f2f579 | ||
|
|
e8ec33d9d0 | ||
|
|
3bbbaf12fd | ||
|
|
30ffacd879 | ||
|
|
75e9b7791c | ||
|
|
4b665ab19a | ||
|
|
08265ed1d7 | ||
|
|
cded9af90f | ||
|
|
4e1f2ad017 | ||
|
|
7f92b7072d | ||
|
|
4a589ba6a4 | ||
|
|
7f16325fcc | ||
|
|
b62e5bf34c | ||
|
|
bd6b348cc7 | ||
|
|
62f35fe8c8 | ||
|
|
cb2cb4659c | ||
|
|
a2c58415cf | ||
|
|
2cb9987c99 | ||
|
|
36584cfb7c | ||
|
|
b825ad6a12 | ||
|
|
58c69e36a1 |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,3 +1,2 @@
|
|||||||
open_collective: task
|
open_collective: task
|
||||||
patreon: andreynering
|
custom: 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A¤cy_code=USD&source=url'
|
||||||
custom: 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A¤cy_code=BRL&source=url'
|
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -15,10 +15,10 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.x
|
go-version: 1.17.x
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v1
|
uses: goreleaser/goreleaser-action@v2
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: release --rm-dist
|
args: release --rm-dist
|
||||||
|
|||||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -5,7 +5,7 @@ jobs:
|
|||||||
name: Test
|
name: Test
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.14.x, 1.15.x]
|
go-version: [1.16.x, 1.17.x]
|
||||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
runs-on: ${{matrix.platform}}
|
runs-on: ${{matrix.platform}}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
build:
|
build:
|
||||||
binary: task
|
binary: task
|
||||||
main: cmd/task
|
main: ./cmd/task
|
||||||
goos:
|
goos:
|
||||||
- windows
|
- windows
|
||||||
- darwin
|
- darwin
|
||||||
@@ -50,3 +50,18 @@ nfpms:
|
|||||||
- deb
|
- deb
|
||||||
- rpm
|
- rpm
|
||||||
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
|
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
|
||||||
|
|
||||||
|
brews:
|
||||||
|
- name: go-task
|
||||||
|
description: Task runner / simpler Make alternative written in Go
|
||||||
|
license: MIT
|
||||||
|
homepage: https://taskfile.dev
|
||||||
|
folder: Formula
|
||||||
|
tap:
|
||||||
|
owner: go-task
|
||||||
|
name: homebrew-tap
|
||||||
|
test:
|
||||||
|
system "#{bin}/task", "--help"
|
||||||
|
url_template: https://github.com/go-task/task/releases/download/{{.Tag}}/{{.ArtifactName}}
|
||||||
|
commit_msg_template: Bump {{.Tag}}
|
||||||
|
token: "{{.Env.GORELEASER_TOKEN}}"
|
||||||
|
|||||||
34
CHANGELOG.md
34
CHANGELOG.md
@@ -1,5 +1,39 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 3.7.2 - 2021-09-04
|
||||||
|
|
||||||
|
- Add official support to Apple M1 ([#564](https://github.com/go-task/task/pull/564), [#567](https://github.com/go-task/task/pull/567)).
|
||||||
|
- Our [official Homebrew tap](https://github.com/go-task/homebrew-tap) will
|
||||||
|
support more platforms, including Apple M1
|
||||||
|
|
||||||
|
## v3.7.0 - 2021-07-31
|
||||||
|
|
||||||
|
- Add `run:` setting to control if tasks should run multiple times or not.
|
||||||
|
Available options are `always` (the default), `when_changed` (if a variable
|
||||||
|
modified the task) and `once` (run only once no matter what).
|
||||||
|
This is a long time requested feature. Enjoy!
|
||||||
|
([#53](https://github.com/go-task/task/issues/53), [#359](https://github.com/go-task/task/pull/359)).
|
||||||
|
|
||||||
|
## v3.6.0 - 2021-07-10
|
||||||
|
|
||||||
|
- Allow using both `sources:` and `status:` in the same task
|
||||||
|
([#411](https://github.com/go-task/task/issues/411), [#427](https://github.com/go-task/task/issues/427), [#477](https://github.com/go-task/task/pull/477)).
|
||||||
|
- Small optimization and bug fix: don't compute variables if not needed for
|
||||||
|
`dotenv:` ([#517](https://github.com/go-task/task/issues/517)).
|
||||||
|
|
||||||
|
## v3.5.0 - 2021-07-04
|
||||||
|
|
||||||
|
- Add support for interpolation in `dotenv:`
|
||||||
|
([#433](https://github.com/go-task/task/discussions/433), [#434](https://github.com/go-task/task/issues/434), [#453](https://github.com/go-task/task/pull/453)).
|
||||||
|
|
||||||
|
## v3.4.3 - 2021-05-30
|
||||||
|
|
||||||
|
- Add support for the `NO_COLOR` environment variable.
|
||||||
|
([#459](https://github.com/go-task/task/issues/459), [fatih/color#137](https://github.com/fatih/color/pull/137)).
|
||||||
|
- Fix bug where sources were not considering the right directory
|
||||||
|
in `--watch` mode
|
||||||
|
([#484](https://github.com/go-task/task/issues/484), [#485](https://github.com/go-task/task/pull/485)).
|
||||||
|
|
||||||
## v3.4.2 - 2021-04-23
|
## v3.4.2 - 2021-04-23
|
||||||
|
|
||||||
- On watch, report which file failed to read
|
- On watch, report which file failed to read
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func main() {
|
|||||||
pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
|
pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
|
||||||
pflag.StringVarP(&entrypoint, "taskfile", "t", "", `choose which Taskfile to run. Defaults to "Taskfile.yml"`)
|
pflag.StringVarP(&entrypoint, "taskfile", "t", "", `choose which Taskfile to run. Defaults to "Taskfile.yml"`)
|
||||||
pflag.StringVarP(&output, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
|
pflag.StringVarP(&output, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
|
||||||
pflag.BoolVarP(&color, "color", "c", true, "colored output")
|
pflag.BoolVarP(&color, "color", "c", true, "colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable")
|
||||||
pflag.IntVarP(&concurrency, "concurrency", "C", 0, "limit number tasks to run concurrently")
|
pflag.IntVarP(&concurrency, "concurrency", "C", 0, "limit number tasks to run concurrently")
|
||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ _task_completion()
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
COMPREPLY=($(compgen -c | echo "$scripts" | grep $curr_arg));
|
COMPREPLY=($(compgen -c | echo "$scripts" | grep -- $curr_arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
complete -F _task_completion task
|
complete -F _task_completion task
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
function __task_get_tasks --description "Prints all available tasks with their description"
|
function __task_get_tasks --description "Prints all available tasks with their description"
|
||||||
task -l | sed '1d' | awk '{ $1=""; print $0 }' | tr ': ', '\t' | string trim
|
task -l | sed '1d' | awk '{ $1=""; print $0 }' | sed 's/:\ /\t/g' | string trim
|
||||||
end
|
end
|
||||||
|
|
||||||
complete -c task -d 'Runs the specified task(s). Falls back to the "default" task if no task name was specified, or lists all tasks if an unknown task name was
|
complete -c task -d 'Runs the specified task(s). Falls back to the "default" task if no task name was specified, or lists all tasks if an unknown task name was
|
||||||
|
|||||||
@@ -23,11 +23,17 @@ Additionally, there's also some work done by
|
|||||||
extension, which has its code [here](https://github.com/paulvarache/vscode-taskfile)
|
extension, which has its code [here](https://github.com/paulvarache/vscode-taskfile)
|
||||||
and is published [here](https://marketplace.visualstudio.com/items?itemName=paulvarache.vscode-taskfile).
|
and is published [here](https://marketplace.visualstudio.com/items?itemName=paulvarache.vscode-taskfile).
|
||||||
|
|
||||||
|
### Sublime Text 4 package
|
||||||
|
|
||||||
|
There is a convenience wrapper for initializing and running tasks from Sublime Text's command palette. The package is
|
||||||
|
developed by [@biozz](https://github.com/biozz), the source code is available [here](https://github.com/biozz/sublime-taskfile)
|
||||||
|
and it is published on Package Control [here](https://packagecontrol.io/packages/Taskfile).
|
||||||
|
|
||||||
## Installation methods
|
## Installation methods
|
||||||
|
|
||||||
Some installation methods are maintained by third party:
|
Some installation methods are maintained by third party:
|
||||||
|
|
||||||
- [GitHub Actions](https://github.com/arduino/actions/tree/master/setup-taskfile)
|
- [GitHub Actions](https://github.com/arduino/setup-task)
|
||||||
by [@arduino](https://github.com/arduino)
|
by [@arduino](https://github.com/arduino)
|
||||||
- [AUR](https://aur.archlinux.org/packages/taskfile-git)
|
- [AUR](https://aur.archlinux.org/packages/taskfile-git)
|
||||||
by [@kovetskiy](https://github.com/kovetskiy)
|
by [@kovetskiy](https://github.com/kovetskiy)
|
||||||
|
|||||||
@@ -14,22 +14,14 @@ these options to donate:
|
|||||||
- [$2 per month](https://opencollective.com/task/contribute/backer-4034/checkout)
|
- [$2 per month](https://opencollective.com/task/contribute/backer-4034/checkout)
|
||||||
- [$5 per month](https://opencollective.com/task/contribute/supporter-8404/checkout)
|
- [$5 per month](https://opencollective.com/task/contribute/supporter-8404/checkout)
|
||||||
- [$20 per month](https://opencollective.com/task/contribute/sponsor-4035/checkout)
|
- [$20 per month](https://opencollective.com/task/contribute/sponsor-4035/checkout)
|
||||||
|
- [$50 per month](https://opencollective.com/task/contribute/sponsor-28775/checkout)
|
||||||
- [Custom value - One-time donation option supported](https://opencollective.com/task/donate)
|
- [Custom value - One-time donation option supported](https://opencollective.com/task/donate)
|
||||||
|
|
||||||
## Patreon
|
|
||||||
|
|
||||||
I'm also on [Patreon](https://www.patreon.com/andreynering) if
|
|
||||||
you prefer:
|
|
||||||
|
|
||||||
- [$5 per month](https://www.patreon.com/join/andreynering/checkout?rid=4229277)
|
|
||||||
- [$10 per month](https://www.patreon.com/join/andreynering/checkout?rid=4229276)
|
|
||||||
- [$15 per month](https://www.patreon.com/join/andreynering/checkout?rid=4229275)
|
|
||||||
|
|
||||||
You can choose a custom value on any of the links above.
|
|
||||||
|
|
||||||
Patreon does not support one-time donation. As a workaround you can fire a
|
|
||||||
subscription and cancel it once the donation was succeded.
|
|
||||||
|
|
||||||
## PayPal
|
## PayPal
|
||||||
|
|
||||||
- [Any value - One-time donation](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A¤cy_code=BRL&source=url)
|
- [Any value - One-time donation](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A¤cy_code=USD&source=url)
|
||||||
|
|
||||||
|
## PIX (Brazil only)
|
||||||
|
|
||||||
|
If you're Brazilian, you can donate any value by
|
||||||
|
<a href="/pix.png" target="_blank">using this QR Code</a>.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
<meta name="description" content="A task runner / simpler Make alternative written in Go">
|
<meta name="description" content="A task runner / simpler Make alternative written in Go">
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple-dark.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple.css">
|
||||||
<meta name="google-site-verification" content="VGAYkbdmuaciIDGkBe-eAg9yfZg0C6ostgonbGxxOa0" />
|
<meta name="google-site-verification" content="VGAYkbdmuaciIDGkBe-eAg9yfZg0C6ostgonbGxxOa0" />
|
||||||
<style>
|
<style>
|
||||||
#logo {
|
#logo {
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify-themeable/dist/js/docsify-themeable.min.js"></script>
|
<script src="//cdn.jsdelivr.net/npm/docsify-themeable/dist/js/docsify-themeable.min.js"></script>
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify-tabs"></script>
|
<script src="//cdn.jsdelivr.net/npm/docsify-tabs"></script>
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||||
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-bash.min.js"></script>
|
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-bash.min.js"></script>
|
||||||
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-yaml.min.js"></script>
|
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-yaml.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -87,12 +87,12 @@ sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/b
|
|||||||
#### **GitHub Actions**
|
#### **GitHub Actions**
|
||||||
|
|
||||||
If you want to install Task in GitHub Actions you can try using
|
If you want to install Task in GitHub Actions you can try using
|
||||||
[this action](https://github.com/arduino/actions/tree/master/setup-taskfile)
|
[this action](https://github.com/arduino/setup-task)
|
||||||
by the Arduino team:
|
by the Arduino team:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Install Task
|
- name: Install Task
|
||||||
uses: Arduino/actions/setup-taskfile@master
|
uses: arduino/setup-task@v1
|
||||||
```
|
```
|
||||||
|
|
||||||
This installation method is community owned.
|
This installation method is community owned.
|
||||||
|
|||||||
BIN
docs/pix.png
Normal file
BIN
docs/pix.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -78,18 +78,25 @@ setting:
|
|||||||
KEYNAME=VALUE
|
KEYNAME=VALUE
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# testing/.env
|
||||||
|
ENDPOINT=testing.com
|
||||||
|
```
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# Taskfile.yml
|
# Taskfile.yml
|
||||||
|
|
||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
dotenv: ['.env']
|
env:
|
||||||
|
ENV: testing
|
||||||
|
|
||||||
|
dotenv: ['.env', '{{.ENV}}/.env.', '{{.HOME}}/.env']
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
greet:
|
greet:
|
||||||
cmds:
|
cmds:
|
||||||
- echo "Using $KEYNAME"
|
- echo "Using $KEYNAME and endpoint $ENDPOINT"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Including other Taskfiles
|
## Including other Taskfiles
|
||||||
@@ -446,6 +453,47 @@ tasks:
|
|||||||
- echo "I will not run"
|
- echo "I will not run"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Limiting when tasks run
|
||||||
|
|
||||||
|
If a task executed by multiple `cmds` or multiple `deps` you can control
|
||||||
|
when it is executed using `run`. `run` can also be set at the root
|
||||||
|
of the Taskfile to change the behavior of all the tasks unless explicitly
|
||||||
|
overridden.
|
||||||
|
|
||||||
|
Supported values for `run`:
|
||||||
|
|
||||||
|
* `always` (default) always attempt to invoke the task regardless of the
|
||||||
|
number of previous executions
|
||||||
|
* `once` only invoke this task once regardless of the number of references
|
||||||
|
* `when_changed` only invokes the task once for each unique set of variables
|
||||||
|
passed into the task
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- task: generate-file
|
||||||
|
vars: { CONTENT: '1' }
|
||||||
|
- task: generate-file
|
||||||
|
vars: { CONTENT: '2' }
|
||||||
|
- task: generate-file
|
||||||
|
vars: { CONTENT: '2' }
|
||||||
|
|
||||||
|
generate-file:
|
||||||
|
run: when_changed
|
||||||
|
deps:
|
||||||
|
- install-deps
|
||||||
|
cmds:
|
||||||
|
- echo {{.CONTENT}}
|
||||||
|
|
||||||
|
install-deps:
|
||||||
|
run: once
|
||||||
|
cmds:
|
||||||
|
- sleep 5 # long operation like installing packages
|
||||||
|
```
|
||||||
|
|
||||||
## Variables
|
## Variables
|
||||||
|
|
||||||
When doing interpolation of variables, Task will look for the below.
|
When doing interpolation of variables, Task will look for the below.
|
||||||
@@ -569,7 +617,7 @@ Task also adds the following functions:
|
|||||||
- `toSlash`: Does nothing on Unix, but on Windows converts a string from `\`
|
- `toSlash`: Does nothing on Unix, but on Windows converts a string from `\`
|
||||||
path format to `/`.
|
path format to `/`.
|
||||||
- `fromSlash`: Opposite of `toSlash`. Does nothing on Unix, but on Windows
|
- `fromSlash`: Opposite of `toSlash`. Does nothing on Unix, but on Windows
|
||||||
converts a string from `\` path format to `/`.
|
converts a string from `/` path format to `\`.
|
||||||
- `exeExt`: Returns the right executable extension for the current OS
|
- `exeExt`: Returns the right executable extension for the current OS
|
||||||
(`".exe"` for Windows, `""` for others).
|
(`".exe"` for Windows, `""` for others).
|
||||||
|
|
||||||
|
|||||||
9
go.mod
9
go.mod
@@ -1,16 +1,17 @@
|
|||||||
module github.com/go-task/task/v3
|
module github.com/go-task/task/v3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.10.0
|
github.com/fatih/color v1.12.0
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.3.0
|
||||||
github.com/mattn/go-zglob v0.0.3
|
github.com/mattn/go-zglob v0.0.3
|
||||||
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/radovskyb/watcher v1.0.7
|
github.com/radovskyb/watcher v1.0.7
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||||
mvdan.cc/sh/v3 v3.2.4
|
mvdan.cc/sh/v3 v3.3.1
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.13
|
go 1.16
|
||||||
|
|||||||
42
go.sum
42
go.sum
@@ -1,53 +1,55 @@
|
|||||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.13 h1:rTPnd/xocYRjutMfqide2zle1u96upp1gm6eUHKi7us=
|
||||||
|
github.com/creack/pty v1.1.13/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To=
|
github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To=
|
||||||
github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
||||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
|
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||||
|
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
|
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
|
||||||
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
||||||
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||||
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407 h1:5zh5atpUEdIc478E/ebrIaHLKcfVvG6dL/fGv7BcMoM=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
|
||||||
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -55,6 +57,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
mvdan.cc/editorconfig v0.1.1-0.20200121172147-e40951bde157/go.mod h1:Ge4atmRUYqueGppvJ7JNrtqpqokoJEFxYbP0Z+WeKS8=
|
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
|
||||||
mvdan.cc/sh/v3 v3.2.4 h1:+fZaWcXWRjYAvqzEKoDhDM3DkxdDUykU2iw0VMKFe9s=
|
mvdan.cc/sh/v3 v3.3.1 h1:aA0i7NZOc1oV5jfAH20FCz+QsmI/TX7FiAquC5Rdo5o=
|
||||||
mvdan.cc/sh/v3 v3.2.4/go.mod h1:fPQmabBpREM/XQ9YXSU5ZFZ/Sm+PmKP9/vkFHgYKJEI=
|
mvdan.cc/sh/v3 v3.3.1/go.mod h1:DpbFT2B4fXpKiq69fEoMe+71JrmUn5aUekYy9fNKnQw=
|
||||||
|
|||||||
28
hash.go
Normal file
28
hash.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/hash"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *Executor) GetHash(t *taskfile.Task) (string, error) {
|
||||||
|
r := t.Run
|
||||||
|
if r == "" {
|
||||||
|
r = e.Taskfile.Run
|
||||||
|
}
|
||||||
|
|
||||||
|
var h hash.HashFunc
|
||||||
|
switch r {
|
||||||
|
case "always":
|
||||||
|
h = hash.Empty
|
||||||
|
case "once":
|
||||||
|
h = hash.Name
|
||||||
|
case "when_changed":
|
||||||
|
h = hash.Hash
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf(`task: invalid run "%s"`, r)
|
||||||
|
}
|
||||||
|
return h(t)
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
// Compiler handles compilation of a task before its execution.
|
// Compiler handles compilation of a task before its execution.
|
||||||
// E.g. variable merger, template processing, etc.
|
// E.g. variable merger, template processing, etc.
|
||||||
type Compiler interface {
|
type Compiler interface {
|
||||||
|
GetTaskfileVariables() (*taskfile.Vars, error)
|
||||||
GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
|
GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
|
||||||
FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
|
FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
|
||||||
HandleDynamicVar(v taskfile.Var, dir string) (string, error)
|
HandleDynamicVar(v taskfile.Var, dir string) (string, error)
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ type CompilerV2 struct {
|
|||||||
muDynamicCache sync.Mutex
|
muDynamicCache sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CompilerV2) GetTaskfileVariables() (*taskfile.Vars, error) {
|
||||||
|
return &taskfile.Vars{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FastGetVariables is a no-op on v2
|
// FastGetVariables is a no-op on v2
|
||||||
func (c *CompilerV2) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
|
func (c *CompilerV2) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
|
||||||
return c.GetVariables(t, call)
|
return c.GetVariables(t, call)
|
||||||
@@ -105,7 +109,7 @@ func (c *CompilerV2) HandleDynamicVar(v taskfile.Var, _ string) (string, error)
|
|||||||
Stderr: c.Logger.Stderr,
|
Stderr: c.Logger.Stderr,
|
||||||
}
|
}
|
||||||
if err := execext.RunCommand(context.Background(), opts); err != nil {
|
if err := execext.RunCommand(context.Background(), opts); err != nil {
|
||||||
return "", fmt.Errorf(`task: Command "%s" in taskvars file failed: %s`, opts.Command, err)
|
return "", fmt.Errorf(`task: Command "%s" failed: %s`, opts.Command, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim a single trailing newline from the result to make most command
|
// Trim a single trailing newline from the result to make most command
|
||||||
|
|||||||
@@ -29,17 +29,23 @@ type CompilerV3 struct {
|
|||||||
muDynamicCache sync.Mutex
|
muDynamicCache sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CompilerV3) GetTaskfileVariables() (*taskfile.Vars, error) {
|
||||||
|
return c.getVariables(nil, nil, true)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CompilerV3) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
|
func (c *CompilerV3) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
|
||||||
return c.getVariables(t, call, true)
|
return c.getVariables(t, &call, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CompilerV3) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
|
func (c *CompilerV3) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
|
||||||
return c.getVariables(t, call, false)
|
return c.getVariables(t, &call, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CompilerV3) getVariables(t *taskfile.Task, call taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
|
func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
|
||||||
result := compiler.GetEnviron()
|
result := compiler.GetEnviron()
|
||||||
result.Set("TASK", taskfile.Var{Static: t.Task})
|
if t != nil {
|
||||||
|
result.Set("TASK", taskfile.Var{Static: t.Task})
|
||||||
|
}
|
||||||
|
|
||||||
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
|
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
|
||||||
return func(k string, v taskfile.Var) error {
|
return func(k string, v taskfile.Var) error {
|
||||||
@@ -74,6 +80,11 @@ func (c *CompilerV3) getVariables(t *taskfile.Task, call taskfile.Call, evaluate
|
|||||||
if err := c.TaskfileVars.Range(rangeFunc); err != nil {
|
if err := c.TaskfileVars.Range(rangeFunc); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t == nil || call == nil {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := call.Vars.Range(rangeFunc); err != nil {
|
if err := call.Vars.Range(rangeFunc); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -125,7 +136,7 @@ func (c *CompilerV3) HandleDynamicVar(v taskfile.Var, dir string) (string, error
|
|||||||
Stderr: c.Logger.Stderr,
|
Stderr: c.Logger.Stderr,
|
||||||
}
|
}
|
||||||
if err := execext.RunCommand(context.Background(), opts); err != nil {
|
if err := execext.RunCommand(context.Background(), opts); err != nil {
|
||||||
return "", fmt.Errorf(`task: Command "%s" in taskvars file failed: %s`, opts.Command, err)
|
return "", fmt.Errorf(`task: Command "%s" failed: %s`, opts.Command, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim a single trailing newline from the result to make most command
|
// Trim a single trailing newline from the result to make most command
|
||||||
|
|||||||
24
internal/hash/hash.go
Normal file
24
internal/hash/hash.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package hash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/mitchellh/hashstructure/v2"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HashFunc func(*taskfile.Task) (string, error)
|
||||||
|
|
||||||
|
func Empty(*taskfile.Task) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Name(t *taskfile.Task) (string, error) {
|
||||||
|
return t.Task, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Hash(t *taskfile.Task) (string, error) {
|
||||||
|
h, err := hashstructure.Hash(t, hashstructure.FormatV2, nil)
|
||||||
|
return fmt.Sprintf("%s:%d", t.Task, h), err
|
||||||
|
}
|
||||||
@@ -6,17 +6,16 @@ import (
|
|||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Color func() PrintFunc
|
||||||
type PrintFunc func(io.Writer, string, ...interface{})
|
type PrintFunc func(io.Writer, string, ...interface{})
|
||||||
|
|
||||||
var (
|
func Default() PrintFunc { return color.New(color.Reset).FprintfFunc() }
|
||||||
Default PrintFunc = color.New(color.Reset).FprintfFunc()
|
func Blue() PrintFunc { return color.New(color.FgBlue).FprintfFunc() }
|
||||||
Blue PrintFunc = color.New(color.FgBlue).FprintfFunc()
|
func Green() PrintFunc { return color.New(color.FgGreen).FprintfFunc() }
|
||||||
Green PrintFunc = color.New(color.FgGreen).FprintfFunc()
|
func Cyan() PrintFunc { return color.New(color.FgCyan).FprintfFunc() }
|
||||||
Cyan PrintFunc = color.New(color.FgCyan).FprintfFunc()
|
func Yellow() PrintFunc { return color.New(color.FgYellow).FprintfFunc() }
|
||||||
Yellow PrintFunc = color.New(color.FgYellow).FprintfFunc()
|
func Magenta() PrintFunc { return color.New(color.FgMagenta).FprintfFunc() }
|
||||||
Magenta PrintFunc = color.New(color.FgMagenta).FprintfFunc()
|
func Red() PrintFunc { return color.New(color.FgRed).FprintfFunc() }
|
||||||
Red PrintFunc = color.New(color.FgRed).FprintfFunc()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Logger is just a wrapper that prints stuff to STDOUT or STDERR,
|
// Logger is just a wrapper that prints stuff to STDOUT or STDERR,
|
||||||
// with optional color.
|
// with optional color.
|
||||||
@@ -28,37 +27,39 @@ type Logger struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Outf prints stuff to STDOUT.
|
// Outf prints stuff to STDOUT.
|
||||||
func (l *Logger) Outf(print PrintFunc, s string, args ...interface{}) {
|
func (l *Logger) Outf(color Color, s string, args ...interface{}) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
s, args = "%s", []interface{}{s}
|
s, args = "%s", []interface{}{s}
|
||||||
}
|
}
|
||||||
if !l.Color {
|
if !l.Color {
|
||||||
print = Default
|
color = Default
|
||||||
}
|
}
|
||||||
|
print := color()
|
||||||
print(l.Stdout, s+"\n", args...)
|
print(l.Stdout, s+"\n", args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerboseOutf prints stuff to STDOUT if verbose mode is enabled.
|
// VerboseOutf prints stuff to STDOUT if verbose mode is enabled.
|
||||||
func (l *Logger) VerboseOutf(print PrintFunc, s string, args ...interface{}) {
|
func (l *Logger) VerboseOutf(color Color, s string, args ...interface{}) {
|
||||||
if l.Verbose {
|
if l.Verbose {
|
||||||
l.Outf(print, s, args...)
|
l.Outf(color, s, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errf prints stuff to STDERR.
|
// Errf prints stuff to STDERR.
|
||||||
func (l *Logger) Errf(print PrintFunc, s string, args ...interface{}) {
|
func (l *Logger) Errf(color Color, s string, args ...interface{}) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
s, args = "%s", []interface{}{s}
|
s, args = "%s", []interface{}{s}
|
||||||
}
|
}
|
||||||
if !l.Color {
|
if !l.Color {
|
||||||
print = Default
|
color = Default
|
||||||
}
|
}
|
||||||
|
print := color()
|
||||||
print(l.Stderr, s+"\n", args...)
|
print(l.Stderr, s+"\n", args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerboseErrf prints stuff to STDERR if verbose mode is enabled.
|
// VerboseErrf prints stuff to STDERR if verbose mode is enabled.
|
||||||
func (l *Logger) VerboseErrf(print PrintFunc, s string, args ...interface{}) {
|
func (l *Logger) VerboseErrf(color Color, s string, args ...interface{}) {
|
||||||
if l.Verbose {
|
if l.Verbose {
|
||||||
l.Errf(print, s, args...)
|
l.Errf(color, s, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func (c *Checksum) IsUpToDate() (bool, error) {
|
|||||||
if len(c.Generates) > 0 {
|
if len(c.Generates) > 0 {
|
||||||
// For each specified 'generates' field, check whether the files actually exist
|
// For each specified 'generates' field, check whether the files actually exist
|
||||||
for _, g := range c.Generates {
|
for _, g := range c.Generates {
|
||||||
generates, err := glob(c.TaskDir, g)
|
generates, err := Glob(c.TaskDir, g)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
func globs(dir string, globs []string) ([]string, error) {
|
func globs(dir string, globs []string) ([]string, error) {
|
||||||
files := make([]string, 0)
|
files := make([]string, 0)
|
||||||
for _, g := range globs {
|
for _, g := range globs {
|
||||||
f, err := glob(dir, g)
|
f, err := Glob(dir, g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ func globs(dir string, globs []string) ([]string, error) {
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func glob(dir string, g string) ([]string, error) {
|
func Glob(dir string, g string) ([]string, error) {
|
||||||
files := make([]string, 0)
|
files := make([]string, 0)
|
||||||
if !filepath.IsAbs(g) {
|
if !filepath.IsAbs(g) {
|
||||||
g = filepath.Join(dir, g)
|
g = filepath.Join(dir, g)
|
||||||
|
|||||||
29
status.go
29
status.go
@@ -29,16 +29,35 @@ func (e *Executor) Status(ctx context.Context, calls ...taskfile.Call) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) isTaskUpToDate(ctx context.Context, t *taskfile.Task) (bool, error) {
|
func (e *Executor) isTaskUpToDate(ctx context.Context, t *taskfile.Task) (bool, error) {
|
||||||
|
if len(t.Status) == 0 && len(t.Sources) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
if len(t.Status) > 0 {
|
if len(t.Status) > 0 {
|
||||||
return e.isTaskUpToDateStatus(ctx, t)
|
isUpToDate, err := e.isTaskUpToDateStatus(ctx, t)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !isUpToDate {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checker, err := e.getStatusChecker(t)
|
if len(t.Sources) > 0 {
|
||||||
if err != nil {
|
checker, err := e.getStatusChecker(t)
|
||||||
return false, err
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
isUpToDate, err := checker.IsUpToDate()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !isUpToDate {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return checker.IsUpToDate()
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) statusOnError(t *taskfile.Task) error {
|
func (e *Executor) statusOnError(t *taskfile.Task) error {
|
||||||
|
|||||||
143
task.go
143
task.go
@@ -58,6 +58,8 @@ type Executor struct {
|
|||||||
concurrencySemaphore chan struct{}
|
concurrencySemaphore chan struct{}
|
||||||
taskCallCount map[string]*int32
|
taskCallCount map[string]*int32
|
||||||
mkdirMutexMap map[string]*sync.Mutex
|
mkdirMutexMap map[string]*sync.Mutex
|
||||||
|
executionHashes map[string]context.Context
|
||||||
|
executionHashesMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs Task
|
// Run runs Task
|
||||||
@@ -149,9 +151,12 @@ func (e *Executor) Setup() error {
|
|||||||
if v == 2.0 {
|
if v == 2.0 {
|
||||||
v = 2.6
|
v = 2.6
|
||||||
}
|
}
|
||||||
|
if v == 3.0 {
|
||||||
|
v = 3.7
|
||||||
|
}
|
||||||
|
|
||||||
if v > 3.0 {
|
if v > 3.7 {
|
||||||
return fmt.Errorf(`task: Taskfile versions greater than v3.0 not implemented in the version of Task`)
|
return fmt.Errorf(`task: Taskfile versions greater than v3.7 not implemented in the version of Task`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Color available only on v3
|
// Color available only on v3
|
||||||
@@ -176,6 +181,23 @@ func (e *Executor) Setup() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v >= 3.0 {
|
||||||
|
env, err := read.Dotenv(e.Compiler, e.Taskfile, e.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = env.Range(func(key string, value taskfile.Var) error {
|
||||||
|
if _, ok := e.Taskfile.Env.Mapping[key]; !ok {
|
||||||
|
e.Taskfile.Env.Set(key, value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if v < 2.1 && e.Taskfile.Output != "" {
|
if v < 2.1 && e.Taskfile.Output != "" {
|
||||||
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
|
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
|
||||||
}
|
}
|
||||||
@@ -243,6 +265,24 @@ func (e *Executor) Setup() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v < 3.7 {
|
||||||
|
if e.Taskfile.Run != "" {
|
||||||
|
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, task := range e.Taskfile.Tasks {
|
||||||
|
if task.Run != "" {
|
||||||
|
return errors.New(`task: Setting the "run" type is only available starting on Taskfile version v3.7`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Taskfile.Run == "" {
|
||||||
|
e.Taskfile.Run = "always"
|
||||||
|
}
|
||||||
|
|
||||||
|
e.executionHashes = make(map[string]context.Context)
|
||||||
|
|
||||||
e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks))
|
e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks))
|
||||||
e.mkdirMutexMap = make(map[string]*sync.Mutex, len(e.Taskfile.Tasks))
|
e.mkdirMutexMap = make(map[string]*sync.Mutex, len(e.Taskfile.Tasks))
|
||||||
for k := range e.Taskfile.Tasks {
|
for k := range e.Taskfile.Tasks {
|
||||||
@@ -269,48 +309,50 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
release := e.acquireConcurrencyLimit()
|
release := e.acquireConcurrencyLimit()
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
if err := e.runDeps(ctx, t); err != nil {
|
return e.startExecution(ctx, t, func(ctx context.Context) error {
|
||||||
return err
|
if err := e.runDeps(ctx, t); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
if !e.Force {
|
|
||||||
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
upToDate, err := e.isTaskUpToDate(ctx, t)
|
if !e.Force {
|
||||||
if err != nil {
|
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
|
||||||
if upToDate && preCondMet {
|
|
||||||
if !e.Silent {
|
|
||||||
e.Logger.Errf(logger.Magenta, `task: Task "%s" is up to date`, t.Name())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := e.mkdir(t); err != nil {
|
|
||||||
e.Logger.Errf(logger.Red, "task: cannot make directory %q: %v", t.Dir, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range t.Cmds {
|
|
||||||
if err := e.runCommand(ctx, t, call, i); err != nil {
|
|
||||||
if err2 := e.statusOnError(t); err2 != nil {
|
|
||||||
e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v", err2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if execext.IsExitError(err) && t.IgnoreError {
|
upToDate, err := e.isTaskUpToDate(ctx, t)
|
||||||
e.Logger.VerboseErrf(logger.Yellow, "task: task error ignored: %v", err)
|
if err != nil {
|
||||||
continue
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &taskRunError{t.Task, err}
|
if upToDate && preCondMet {
|
||||||
|
if !e.Silent {
|
||||||
|
e.Logger.Errf(logger.Magenta, `task: Task "%s" is up to date`, t.Name())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nil
|
if err := e.mkdir(t); err != nil {
|
||||||
|
e.Logger.Errf(logger.Red, "task: cannot make directory %q: %v", t.Dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range t.Cmds {
|
||||||
|
if err := e.runCommand(ctx, t, call, i); err != nil {
|
||||||
|
if err2 := e.statusOnError(t); err2 != nil {
|
||||||
|
e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v", err2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if execext.IsExitError(err) && t.IgnoreError {
|
||||||
|
e.Logger.VerboseErrf(logger.Yellow, "task: task error ignored: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return &taskRunError{t.Task, err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) mkdir(t *taskfile.Task) error {
|
func (e *Executor) mkdir(t *taskfile.Task) error {
|
||||||
@@ -428,3 +470,32 @@ func getEnviron(t *taskfile.Task) []string {
|
|||||||
|
|
||||||
return environ
|
return environ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Executor) startExecution(ctx context.Context, t *taskfile.Task, execute func(ctx context.Context) error) error {
|
||||||
|
h, err := e.GetHash(t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if h == "" {
|
||||||
|
return execute(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.executionHashesMutex.Lock()
|
||||||
|
otherExecutionCtx, ok := e.executionHashes[h]
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
e.executionHashesMutex.Unlock()
|
||||||
|
e.Logger.VerboseErrf(logger.Magenta, "task: skipping execution of task: %s", h)
|
||||||
|
<-otherExecutionCtx.Done()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
e.executionHashes[h] = ctx
|
||||||
|
e.executionHashesMutex.Unlock()
|
||||||
|
|
||||||
|
return execute(ctx)
|
||||||
|
}
|
||||||
|
|||||||
95
task_test.go
95
task_test.go
@@ -17,6 +17,10 @@ import (
|
|||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
_ = os.Setenv("NO_COLOR", "1")
|
||||||
|
}
|
||||||
|
|
||||||
// fileContentTest provides a basic reusable test-case for running a Taskfile
|
// fileContentTest provides a basic reusable test-case for running a Taskfile
|
||||||
// and inspect generated files.
|
// and inspect generated files.
|
||||||
type fileContentTest struct {
|
type fileContentTest struct {
|
||||||
@@ -248,12 +252,18 @@ func TestDeps(t *testing.T) {
|
|||||||
|
|
||||||
func TestStatus(t *testing.T) {
|
func TestStatus(t *testing.T) {
|
||||||
const dir = "testdata/status"
|
const dir = "testdata/status"
|
||||||
var file = filepath.Join(dir, "foo.txt")
|
|
||||||
|
|
||||||
_ = os.Remove(file)
|
files := []string{
|
||||||
|
"foo.txt",
|
||||||
|
"bar.txt",
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(file); err == nil {
|
for _, f := range files {
|
||||||
t.Errorf("File should not exist: %v", err)
|
path := filepath.Join(dir, f)
|
||||||
|
_ = os.Remove(path)
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
t.Errorf("File should not exist: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
@@ -265,17 +275,33 @@ func TestStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assert.NoError(t, e.Setup())
|
assert.NoError(t, e.Setup())
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
|
||||||
|
|
||||||
if _, err := os.Stat(file); err != nil {
|
for _, f := range files {
|
||||||
t.Errorf("File should exist: %v", err)
|
if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
|
||||||
|
t.Errorf("File should exist: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Silent = false
|
e.Silent = false
|
||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
|
|
||||||
|
|
||||||
if buff.String() != `task: Task "gen-foo" is up to date`+"\n" {
|
// all: not up-to-date
|
||||||
t.Errorf("Wrong output message: %s", buff.String())
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
|
||||||
}
|
assert.Equal(t, "task: [gen-foo] touch foo.txt", strings.TrimSpace(buff.String()))
|
||||||
|
buff.Reset()
|
||||||
|
// status: not up-to-date
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
|
||||||
|
assert.Equal(t, "task: [gen-foo] touch foo.txt", strings.TrimSpace(buff.String()))
|
||||||
|
buff.Reset()
|
||||||
|
|
||||||
|
// sources: not up-to-date
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
|
||||||
|
assert.Equal(t, "task: [gen-bar] touch bar.txt", strings.TrimSpace(buff.String()))
|
||||||
|
buff.Reset()
|
||||||
|
// all: up-to-date
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
|
||||||
|
assert.Equal(t, `task: Task "gen-bar" is up to date`, strings.TrimSpace(buff.String()))
|
||||||
|
buff.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrecondition(t *testing.T) {
|
func TestPrecondition(t *testing.T) {
|
||||||
@@ -900,6 +926,44 @@ func TestDotenvShouldAllowMissingEnv(t *testing.T) {
|
|||||||
tt.Run(t)
|
tt.Run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDotenvHasLocalEnvInPath(t *testing.T) {
|
||||||
|
tt := fileContentTest{
|
||||||
|
Dir: "testdata/dotenv/local_env_in_path",
|
||||||
|
Target: "default",
|
||||||
|
TrimSpace: false,
|
||||||
|
Files: map[string]string{
|
||||||
|
"var.txt": "VAR='var_in_dot_env_1'\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tt.Run(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDotenvHasLocalVarInPath(t *testing.T) {
|
||||||
|
tt := fileContentTest{
|
||||||
|
Dir: "testdata/dotenv/local_var_in_path",
|
||||||
|
Target: "default",
|
||||||
|
TrimSpace: false,
|
||||||
|
Files: map[string]string{
|
||||||
|
"var.txt": "VAR='var_in_dot_env_3'\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tt.Run(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDotenvHasEnvVarInPath(t *testing.T) {
|
||||||
|
os.Setenv("ENV_VAR", "testing")
|
||||||
|
|
||||||
|
tt := fileContentTest{
|
||||||
|
Dir: "testdata/dotenv/env_var_in_path",
|
||||||
|
Target: "default",
|
||||||
|
TrimSpace: false,
|
||||||
|
Files: map[string]string{
|
||||||
|
"var.txt": "VAR='var_in_dot_env_2'\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tt.Run(t)
|
||||||
|
}
|
||||||
|
|
||||||
func TestExitImmediately(t *testing.T) {
|
func TestExitImmediately(t *testing.T) {
|
||||||
const dir = "testdata/exit_immediately"
|
const dir = "testdata/exit_immediately"
|
||||||
|
|
||||||
@@ -915,3 +979,14 @@ func TestExitImmediately(t *testing.T) {
|
|||||||
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
||||||
assert.Contains(t, buff.String(), `"this_should_fail": executable file not found in $PATH`)
|
assert.Contains(t, buff.String(), `"this_should_fail": executable file not found in $PATH`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunOnlyRunsJobsHashOnce(t *testing.T) {
|
||||||
|
tt := fileContentTest{
|
||||||
|
Dir: "testdata/run",
|
||||||
|
Target: "generate-hash",
|
||||||
|
Files: map[string]string{
|
||||||
|
"hash.txt": "starting 1\n1\n2\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tt.Run(t)
|
||||||
|
}
|
||||||
|
|||||||
50
taskfile/read/dotenv.go
Normal file
50
taskfile/read/dotenv.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package read
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/compiler"
|
||||||
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Dotenv(c compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.Vars, error) {
|
||||||
|
if len(tf.Dotenv) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
vars, err := c.GetTaskfileVariables()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
env := &taskfile.Vars{}
|
||||||
|
|
||||||
|
tr := templater.Templater{Vars: vars, RemoveNoValue: true}
|
||||||
|
|
||||||
|
for _, dotEnvPath := range tf.Dotenv {
|
||||||
|
dotEnvPath = tr.Replace(dotEnvPath)
|
||||||
|
|
||||||
|
if !filepath.IsAbs(dotEnvPath) {
|
||||||
|
dotEnvPath = filepath.Join(dir, dotEnvPath)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
envs, err := godotenv.Read(dotEnvPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for key, value := range envs {
|
||||||
|
if _, ok := env.Mapping[key]; !ok {
|
||||||
|
env.Set(key, taskfile.Var{Static: value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return env, nil
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/joho/godotenv"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
@@ -37,27 +36,6 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v >= 3.0 {
|
|
||||||
for _, dotEnvPath := range t.Dotenv {
|
|
||||||
if !filepath.IsAbs(dotEnvPath) {
|
|
||||||
dotEnvPath = filepath.Join(dir, dotEnvPath)
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
envs, err := godotenv.Read(dotEnvPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for key, value := range envs {
|
|
||||||
if _, ok := t.Env.Mapping[key]; !ok {
|
|
||||||
t.Env.Set(key, taskfile.Var{Static: value})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = t.Includes.Range(func(namespace string, includedTask taskfile.IncludedTaskfile) error {
|
err = t.Includes.Range(func(namespace string, includedTask taskfile.IncludedTaskfile) error {
|
||||||
if v >= 3.0 {
|
if v >= 3.0 {
|
||||||
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
|
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type Task struct {
|
|||||||
Method string
|
Method string
|
||||||
Prefix string
|
Prefix string
|
||||||
IgnoreError bool
|
IgnoreError bool
|
||||||
|
Run string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) Name() string {
|
func (t *Task) Name() string {
|
||||||
@@ -61,6 +62,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
Method string
|
Method string
|
||||||
Prefix string
|
Prefix string
|
||||||
IgnoreError bool `yaml:"ignore_error"`
|
IgnoreError bool `yaml:"ignore_error"`
|
||||||
|
Run string
|
||||||
}
|
}
|
||||||
if err := unmarshal(&task); err != nil {
|
if err := unmarshal(&task); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -81,5 +83,6 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
t.Method = task.Method
|
t.Method = task.Method
|
||||||
t.Prefix = task.Prefix
|
t.Prefix = task.Prefix
|
||||||
t.IgnoreError = task.IgnoreError
|
t.IgnoreError = task.IgnoreError
|
||||||
|
t.Run = task.Run
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type Taskfile struct {
|
|||||||
Tasks Tasks
|
Tasks Tasks
|
||||||
Silent bool
|
Silent bool
|
||||||
Dotenv []string
|
Dotenv []string
|
||||||
|
Run string
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalYAML implements yaml.Unmarshaler interface
|
// UnmarshalYAML implements yaml.Unmarshaler interface
|
||||||
@@ -32,6 +33,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
Tasks Tasks
|
Tasks Tasks
|
||||||
Silent bool
|
Silent bool
|
||||||
Dotenv []string
|
Dotenv []string
|
||||||
|
Run string
|
||||||
}
|
}
|
||||||
if err := unmarshal(&taskfile); err != nil {
|
if err := unmarshal(&taskfile); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -46,6 +48,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
tf.Tasks = taskfile.Tasks
|
tf.Tasks = taskfile.Tasks
|
||||||
tf.Silent = taskfile.Silent
|
tf.Silent = taskfile.Silent
|
||||||
tf.Dotenv = taskfile.Dotenv
|
tf.Dotenv = taskfile.Dotenv
|
||||||
|
tf.Run = taskfile.Run
|
||||||
if tf.Expansions <= 0 {
|
if tf.Expansions <= 0 {
|
||||||
tf.Expansions = 2
|
tf.Expansions = 2
|
||||||
}
|
}
|
||||||
|
|||||||
1
testdata/dotenv/env_var_in_path/.env.testing
vendored
Normal file
1
testdata/dotenv/env_var_in_path/.env.testing
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VAR_IN_DOTENV=var_in_dot_env_2
|
||||||
8
testdata/dotenv/env_var_in_path/Taskfile.yml
vendored
Normal file
8
testdata/dotenv/env_var_in_path/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
|
dotenv: [".env.{{.ENV_VAR}}"]
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- echo "VAR='$VAR_IN_DOTENV'" > var.txt
|
||||||
1
testdata/dotenv/local_env_in_path/.env.testing
vendored
Normal file
1
testdata/dotenv/local_env_in_path/.env.testing
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VAR_IN_DOTENV=var_in_dot_env_1
|
||||||
11
testdata/dotenv/local_env_in_path/Taskfile.yml
vendored
Normal file
11
testdata/dotenv/local_env_in_path/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
|
env:
|
||||||
|
LOCAL_ENV: testing
|
||||||
|
|
||||||
|
dotenv: [".env.{{.LOCAL_ENV}}"]
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- echo "VAR='$VAR_IN_DOTENV'" > var.txt
|
||||||
1
testdata/dotenv/local_var_in_path/.env.testing
vendored
Normal file
1
testdata/dotenv/local_var_in_path/.env.testing
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VAR_IN_DOTENV=var_in_dot_env_3
|
||||||
13
testdata/dotenv/local_var_in_path/Taskfile.yml
vendored
Normal file
13
testdata/dotenv/local_var_in_path/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
|
vars:
|
||||||
|
PART_1: test
|
||||||
|
PART_2: ing
|
||||||
|
LOCAL_VAR: "{{.PART_1}}{{.PART_2}}"
|
||||||
|
|
||||||
|
dotenv: [".env.{{.LOCAL_VAR}}"]
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- echo "VAR='$VAR_IN_DOTENV'" > var.txt
|
||||||
1
testdata/run/.gitignore
vendored
Normal file
1
testdata/run/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.txt
|
||||||
24
testdata/run/Taskfile.yml
vendored
Normal file
24
testdata/run/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
version: '3.7'
|
||||||
|
run: when_changed
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
generate-hash:
|
||||||
|
- rm -f hash.txt
|
||||||
|
- task: input-content
|
||||||
|
vars: { CONTENT: '1' }
|
||||||
|
- task: input-content
|
||||||
|
vars: { CONTENT: '2' }
|
||||||
|
- task: input-content
|
||||||
|
vars: { CONTENT: '2' }
|
||||||
|
|
||||||
|
input-content:
|
||||||
|
deps:
|
||||||
|
- task: create-output
|
||||||
|
vars: { CONTENT: '1' }
|
||||||
|
cmds:
|
||||||
|
- echo {{.CONTENT}} >> hash.txt
|
||||||
|
|
||||||
|
create-output:
|
||||||
|
run: once
|
||||||
|
cmds:
|
||||||
|
- echo starting {{.CONTENT}} >> hash.txt
|
||||||
12
testdata/status/Taskfile.yml
vendored
12
testdata/status/Taskfile.yml
vendored
@@ -4,5 +4,15 @@ tasks:
|
|||||||
gen-foo:
|
gen-foo:
|
||||||
cmds:
|
cmds:
|
||||||
- touch foo.txt
|
- touch foo.txt
|
||||||
|
sources:
|
||||||
|
- ./foo.txt
|
||||||
status:
|
status:
|
||||||
- test -f foo.txt
|
- test 1 = 0
|
||||||
|
|
||||||
|
gen-bar:
|
||||||
|
cmds:
|
||||||
|
- touch bar.txt
|
||||||
|
sources:
|
||||||
|
- ./bar.txt
|
||||||
|
status:
|
||||||
|
- test 1 = 1
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
Method: r.Replace(origTask.Method),
|
Method: r.Replace(origTask.Method),
|
||||||
Prefix: r.Replace(origTask.Prefix),
|
Prefix: r.Replace(origTask.Prefix),
|
||||||
IgnoreError: origTask.IgnoreError,
|
IgnoreError: origTask.IgnoreError,
|
||||||
|
Run: r.Replace(origTask.Run),
|
||||||
}
|
}
|
||||||
new.Dir, err = execext.Expand(new.Dir)
|
new.Dir, err = execext.Expand(new.Dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
4
watch.go
4
watch.go
@@ -11,8 +11,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
|
"github.com/go-task/task/v3/internal/status"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
"github.com/go-task/task/v3/taskfile"
|
||||||
"github.com/mattn/go-zglob"
|
|
||||||
"github.com/radovskyb/watcher"
|
"github.com/radovskyb/watcher"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...taskfile.Ca
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range task.Sources {
|
for _, s := range task.Sources {
|
||||||
files, err := zglob.Glob(s)
|
files, err := status.Glob(task.Dir, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("task: %s: %w", s, err)
|
return fmt.Errorf("task: %s: %w", s, err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user