Compare commits

...

97 Commits

Author SHA1 Message Date
Andrey Nering
989045489c v3.7.3
🙄
2021-09-04 22:06:45 -03:00
Andrey Nering
888338c60e v3.7.2
Attempt to fix Homebrew releasing by Goreleaser
2021-09-04 22:03:22 -03:00
Andrey Nering
e6c6cc7811 v3.7.1 2021-09-04 21:52:46 -03:00
Andrey Nering
fa0e72bd69 Documentation typo
Fixes #555
2021-09-04 21:30:28 -03:00
Andrey Nering
18decac44d Homebrew: Automate with Goreleaser and start publishing M1 binaries 2021-09-04 21:26:17 -03:00
Andrey Nering
1012a0cf2b Upgrade to Go 1.17. Set 1.16 as the minimal version 2021-09-04 21:04:40 -03:00
Andrey Nering
7e4de945cf Merge pull request #564 from JulienBreux/feat/upgrade-goreleaser-action
Upgrade Goreleaser action to v2 - To ensure mac/m1 mac/arm release
2021-09-04 20:56:16 -03:00
Andrey Nering
a468272726 Merge pull request #567 from LukeCarrier/go-1.16
Build with Go 1.16
2021-09-04 20:52:24 -03:00
Luke Carrier
039d8f000d Build with Go 1.16
This enables darwin-arm64 builds to support Apple Silicon-based Macs.
2021-09-04 22:56:39 +01:00
Julien Breux
d35f960a8a Upgrade Goreleaser action to v2 2021-09-02 17:45:01 +02:00
Andrey Nering
634f8ed574 Merge pull request #562 from notnmeyer/fix-fish-shell-completions
Fix fish completions
2021-09-01 13:34:10 -03:00
Nate Meyer
d369451308 keep the sed, we do want to skip the first line 2021-09-01 08:54:48 -07:00
Nate Meyer
8f1202424d properly format each line 2021-09-01 08:33:08 -07:00
Andrey Nering
50e5813222 Merge pull request #544 from AlexanderBeyn/fix-bash-completion
Fix bash completions when the current argument begins with a dash
2021-08-07 13:54:39 -03:00
Andrey Nering
7bc268aeaa Merge pull request #545 from go-task/dependabot/go_modules/mvdan.cc/sh/v3-3.3.1
Bump mvdan.cc/sh/v3 from 3.3.0 to 3.3.1
2021-08-07 13:53:22 -03:00
dependabot[bot]
537b5b1e25 Bump mvdan.cc/sh/v3 from 3.3.0 to 3.3.1
Bumps [mvdan.cc/sh/v3](https://github.com/mvdan/sh) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/mvdan/sh/releases)
- [Changelog](https://github.com/mvdan/sh/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mvdan/sh/compare/v3.3.0...v3.3.1)

---
updated-dependencies:
- dependency-name: mvdan.cc/sh/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-07 11:03:11 +00:00
Alexander Beyn
aae38f8ce7 Don't let grep treat the search pattern as an option 2021-08-07 02:28:52 -07:00
Andrey Nering
ad05432bcf v3.7.0 2021-07-31 21:36:03 -03:00
Andrey Nering
8aa983257d Small adjustments and CHANGELOG for #359 2021-07-31 20:29:59 -03:00
Andrey Nering
046a97d1e5 go.mod: Upgrade minimal Go version to v1.15 2021-07-31 20:29:19 -03:00
Ross Hammermeister
d28649b13d Refactoring startExecution 2021-07-31 19:36:40 -03:00
Ross Hammermeister
3e16ca37bc Updating version checking 2021-07-31 19:36:40 -03:00
Ross Hammermeister
2da38a5bdc Cleaning up the run code 2021-07-31 19:36:40 -03:00
Ross Hammermeister
bbe1d8b52e Adding proper version checking for taskfile when using run 2021-07-31 19:36:40 -03:00
Ross Hammermeister
97c85e39c3 Only run task once for #53 2021-07-31 19:36:40 -03:00
Andrey Nering
a7b59e5b12 community.md: Update title level 2021-07-23 18:32:13 -03:00
Andrey Nering
9eb1252ce9 Merge pull request #531 from biozz/patch-2
Update community.md about Sublime Text 4 package
2021-07-23 18:30:11 -03:00
Ivan Elfimov
0e01e13670 Update community.md about Sublime Text 4 package
Added a simple abstract about Sublime Text 4 package with the links to the source code and package control.
2021-07-23 23:45:22 +03:00
Andrey Nering
239e61e718 v3.6.0 2021-07-10 23:13:03 -03:00
Andrey Nering
22549e9fd8 CHANGELOG: Fix typo 2021-07-10 22:04:15 -03:00
Andrey Nering
1f9fd24064 Small improvement and CHANGELOG for #477 2021-07-10 21:58:03 -03:00
Andrey Nering
a7594740e3 Merge pull request #477 from mrngsht/sources_evaluation
Evaluate sources also if status is up-to-date
2021-07-10 21:54:47 -03:00
Andrey Nering
945c72cf6c Small optimization and bug fix: don't compute variables if not needed for dotenv:
Closes #517
2021-07-10 19:46:53 -03:00
Andrey Nering
824b0c0132 Improve error message 2021-07-10 19:33:36 -03:00
Andrey Nering
51e9f2f579 Site: Enable search 2021-07-10 19:13:04 -03:00
Andrey Nering
e8ec33d9d0 Site: Return to light theme 2021-07-10 19:07:17 -03:00
Andrey Nering
3bbbaf12fd v3.5.0 2021-07-04 21:53:22 -03:00
Andrey Nering
30ffacd879 Update currency 2021-07-04 21:47:36 -03:00
Andrey Nering
75e9b7791c Remove Patreon
Also, do a few more updates.
2021-07-04 21:45:07 -03:00
Andrey Nering
4b665ab19a Add CHANGELOG entry for 08265ed1d7 2021-06-05 16:04:01 -03:00
Nicolas Fouché
08265ed1d7 Allow vars in dotenv paths, including environment variables
Closes #453
Closes #434
Ref #433

Co-authored-by: Andrey Nering <andrey@nering.com.br>
2021-06-05 16:00:19 -03:00
Andrey Nering
cded9af90f v3.4.3 2021-05-30 23:03:10 -03:00
Andrey Nering
4e1f2ad017 Enforce NO_COLOR=1 on tests to make sure it passes
Closes #459
Fixes #480
Ref #343
Ref fatih/color#137
2021-05-30 22:48:48 -03:00
Andrey Nering
7f92b7072d Merge pull request #496 from go-task/dependabot/go_modules/github.com/fatih/color-1.12.0
Bump github.com/fatih/color from 1.11.0 to 1.12.0
2021-05-30 22:19:21 -03:00
dependabot[bot]
4a589ba6a4 Bump github.com/fatih/color from 1.11.0 to 1.12.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.11.0 to 1.12.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.11.0...v1.12.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-29 11:04:31 +00:00
Andrey Nering
7f16325fcc Merge pull request #491 from go-task/dependabot/go_modules/mvdan.cc/sh/v3-3.3.0
Bump mvdan.cc/sh/v3 from 3.2.4 to 3.3.0
2021-05-22 09:53:20 -03:00
dependabot[bot]
b62e5bf34c Bump mvdan.cc/sh/v3 from 3.2.4 to 3.3.0
Bumps [mvdan.cc/sh/v3](https://github.com/mvdan/sh) from 3.2.4 to 3.3.0.
- [Release notes](https://github.com/mvdan/sh/releases)
- [Changelog](https://github.com/mvdan/sh/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mvdan/sh/compare/v3.2.4...v3.3.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-22 11:03:11 +00:00
Andrey Nering
bd6b348cc7 Update CHANGELOG 2021-05-20 21:30:13 -03:00
Andrey Nering
62f35fe8c8 Merge pull request #485 from lpdyck/bug/watch-repects-task-dir
Changes --watch to respect task directory
2021-05-20 21:26:43 -03:00
Andrey Nering
cb2cb4659c Merge pull request #490 from per1234/update-setup-task-action
Update docs for new arduino/setup-task action repository
2021-05-20 21:23:42 -03:00
per1234
a2c58415cf Update docs for new arduino/setup-task action repository
The GitHub Actions action for installing Task has graduated from its original home in the experimental `arduino/action`
repository with a move to a dedicated permanent repository at `arduino/setup-task`. This move was accompanied by
significant upgrades to the project infrastructure to facilitate its maintenance and quality assurance.

A 1.0.0 release has been made and a `v1` ref that will track all releases in the major version 1 series. I have updated
the example snippet to use this `v1 ref because this will cause the user's workflows to use stable release versions of
the action while also benefiting from ongoing development to the action at each patch or minor release up until such time
as a new major release is made. At this time the user will be given the opportunity to evaluate whether any changes to
the workflow are required by the breaking change to the action that triggered the major release before manually updating
the major ref in the workflows (e.g., `uses: arduino/setup-task@v2`).
2021-05-20 02:40:09 -07:00
Andrey Nering
2cb9987c99 Merge pull request #489 from go-task/dependabot/go_modules/github.com/fatih/color-1.11.0
Bump github.com/fatih/color from 1.10.0 to 1.11.0
2021-05-17 21:54:18 -03:00
dependabot[bot]
36584cfb7c Bump github.com/fatih/color from 1.10.0 to 1.11.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.10.0 to 1.11.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.10.0...v1.11.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-15 11:03:43 +00:00
Landon Dyck
b825ad6a12 use status.Glob for watch globbing 2021-05-08 15:02:08 -05:00
Andrey Nering
f8545d4c61 v3.4.2 2021-04-23 18:11:31 -03:00
Oleg Butuzov
9b42ef5d46 "file does not exist" improving for watchers
Adds additional (initial missing) context to the go generic
`os.ErrNotExist` error, in addition to other errors that (possibly)
can be returned by `zglob.Glob`

Closes #472
2021-04-23 18:03:49 -03:00
Andrey Nering
05ddfc0495 Merge pull request #476 from Pix4D/fix-os-kill
signals: do not try to catch uncatchable signals
2021-04-23 17:45:56 -03:00
Andrey Nering
53b2cebb66 Updated the version output to use Go module build information if avalable. Enabled GoReleaser module proxying for verifiable builds.
Co-authored-by: Jamie Edge <JamieEdge@users.noreply.github.com>
Co-authored-by: Carlos Alexandro Becker <caarlos0@gmail.com>
2021-04-23 17:35:15 -03:00
mrngsht
58c69e36a1 Evaluate sources also if status is up-to-date 2021-04-21 21:56:25 +09:00
Marco Molteni
837fb71a24 signals: do not try to catch uncatchable signals
os.Kill is SIGKILL (kill -9), cannot be intercepted.
(see https://github.com/golang/go/issues/13080)
2021-04-20 14:57:23 +02:00
Marco Molteni
2e13cf5f74 gitignore more editors 2021-04-20 14:54:24 +02:00
Andrey Nering
0ae1681d9c CHANGELOG: Fix typos 2021-04-17 17:57:08 -03:00
Andrey Nering
ebb66ba8fb v3.4.1 2021-04-17 17:48:36 -03:00
Andrey Nering
e79354a039 Revert "Updated the version output to use Go module build information if available. Enabled GoReleaser module proxying for verifiable builds."
This reverts commit 2a3f049336.
2021-04-17 17:47:24 -03:00
Andrey Nering
a57beb1de4 Website: Remove GitHub logo
The website that hosted the SVG file is offline for a while.
2021-04-17 17:34:00 -03:00
Andrey Nering
1648c44ee2 Website: Use dark theme 2021-04-17 17:34:00 -03:00
Andrey Nering
efe47a149e Website: Remove "Examples" page 2021-04-17 17:33:52 -03:00
Andrey Nering
2d66a2f0f3 Improve YAML parse error reporting
Fixes #467
2021-04-17 17:12:39 -03:00
Andrey Nering
43a1f1314e Website: Add a "Community" page 2021-04-17 11:59:24 -03:00
Andrey Nering
4f4b282d7c Merge pull request #471 from go-task/dependabot/go_modules/github.com/stretchr/testify-1.7.0
Bump github.com/stretchr/testify from 1.5.1 to 1.7.0
2021-04-17 10:53:03 -03:00
dependabot[bot]
d3d4da18e5 Bump github.com/stretchr/testify from 1.5.1 to 1.7.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.5.1 to 1.7.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.5.1...v1.7.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-17 13:51:37 +00:00
Andrey Nering
b8da583986 Merge pull request #470 from go-task/dependabot/go_modules/github.com/fatih/color-1.10.0
Bump github.com/fatih/color from 1.7.0 to 1.10.0
2021-04-17 10:51:01 -03:00
Andrey Nering
73f6b42715 Merge pull request #469 from go-task/dependabot/go_modules/github.com/radovskyb/watcher-1.0.7
Bump github.com/radovskyb/watcher from 1.0.5 to 1.0.7
2021-04-17 10:50:21 -03:00
dependabot[bot]
0e2a4efdaa Bump github.com/radovskyb/watcher from 1.0.5 to 1.0.7
Bumps [github.com/radovskyb/watcher](https://github.com/radovskyb/watcher) from 1.0.5 to 1.0.7.
- [Release notes](https://github.com/radovskyb/watcher/releases)
- [Commits](https://github.com/radovskyb/watcher/compare/1.0.5...v1.0.7)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-17 13:47:39 +00:00
dependabot[bot]
6798e16aaf Bump github.com/fatih/color from 1.7.0 to 1.10.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.7.0 to 1.10.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.7.0...v1.10.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-17 13:47:38 +00:00
Andrey Nering
c9cc64ecfc Merge pull request #468 from go-task/dependabot/go_modules/github.com/mattn/go-zglob-0.0.3
Bump github.com/mattn/go-zglob from 0.0.1 to 0.0.3
2021-04-17 10:46:42 -03:00
dependabot[bot]
761f9045ac Bump github.com/mattn/go-zglob from 0.0.1 to 0.0.3
Bumps [github.com/mattn/go-zglob](https://github.com/mattn/go-zglob) from 0.0.1 to 0.0.3.
- [Release notes](https://github.com/mattn/go-zglob/releases)
- [Commits](https://github.com/mattn/go-zglob/compare/v0.0.1...v0.0.3)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-17 13:43:00 +00:00
Andrey Nering
dfae979287 Add .github/dependabot.yml 2021-04-17 10:42:18 -03:00
George Pollard
fe917affd2 Include task name in log output 2021-04-04 16:16:22 -03:00
Andrey Nering
d44207dd7f Merge pull request #463 from JamieEdge/update-install-docs
Update Go modules installation documentation for Go 1.16
2021-04-04 15:48:19 -03:00
Andrey Nering
ec8b1403bd Use early return and add CHANGELOG for #462 2021-04-04 15:40:34 -03:00
Jamie Edge
6f3d108c1e Updated the install script documentation. 2021-04-04 17:31:41 +01:00
Jamie Edge
c34ee9c1f9 Updated the Go modules installation documentation for Go 1.16. 2021-04-04 17:30:42 +01:00
Jamie Edge
2a3f049336 Updated the version output to use Go module build information if available. Enabled GoReleaser module proxying for verifiable builds. 2021-04-04 12:57:58 +01:00
Andrey Nering
0c91011e88 Merge pull request #460 from patrick-mota/patch-1
Fix typo default installation
2021-03-28 11:10:38 -03:00
Andrey Nering
8bcd8719aa Docs: Add GO111MODULE=on to go get command 2021-03-28 10:45:36 -03:00
Ganon
29a8af509b Fix typo default installation 2021-03-26 13:19:45 +01:00
Andrey Nering
d3cd9f17f9 Documentation: Update link 2021-03-20 13:30:38 -03:00
Andrey Nering
b9aea8c5ec v3.3.0 2021-03-20 13:21:00 -03:00
Andrey Nering
897619a961 Upgrade github.com/spf13/pflag to v1.0.5 2021-03-20 12:00:39 -03:00
Andrey Nering
e6c4706b73 Add support for delegating CLI arguments with "--" and a special CLI_ARGS variable
Closes #327
2021-03-20 11:58:45 -03:00
Andrey Nering
8994c50d34 Upgrade mvdan.cc/sh to v3.2.4 2021-03-20 10:38:13 -03:00
Andrey Nering
55b62e47eb Upgrade mvdan.cc/sh to v3.2.2 2021-03-07 15:32:22 -03:00
Ross Hammermeister
c6ecf70377 Adding a --concurrency (-C) flag 2021-03-07 09:49:57 -03:00
Andrey Nering
f0cd7d27fb Taskfile: Set CGO_ENABLED=0 globally
We want that also for running tests, and not only for building it.
2021-03-07 09:30:33 -03:00
Andrey Nering
f923bb499b CHANGELOG: Fix wrong year in release date 2021-02-16 17:53:28 -03:00
Andrey Nering
aa3a29fed2 CHANGELOG: Add missing release dates 2021-02-16 17:52:32 -03:00
56 changed files with 974 additions and 325 deletions

3
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,2 @@
open_collective: task open_collective: task
patreon: andreynering custom: 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A&currency_code=USD&source=url'
custom: 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A&currency_code=BRL&source=url'

10
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
day: saturday
time: '08:00'
timezone: America/Sao_Paulo

View File

@@ -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

View File

@@ -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:

3
.gitignore vendored
View File

@@ -19,8 +19,9 @@ dist/
.DS_Store .DS_Store
# intellij idea/goland # editors
.idea/ .idea/
.vscode/
# exuberant ctags # exuberant ctags
tags tags

View File

@@ -1,6 +1,6 @@
build: build:
binary: task binary: task
main: cmd/task/task.go main: ./cmd/task
goos: goos:
- windows - windows
- darwin - darwin
@@ -17,6 +17,11 @@ build:
goarch: 386 goarch: 386
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
ldflags:
- -s -w # Don't set main.version.
gomod:
proxy: true
archives: archives:
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}" - name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
@@ -45,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
token: "{{.Env.GORELEASER_TOKEN}}"
test:
system "#{bin}/task", "--help"
url_template: https://github.com/go-task/task/releases/download/{{.Tag}}/{{.ArtifactName}}
commit_msg_template: Bump {{.Tag}}

View File

@@ -1,6 +1,70 @@
# Changelog # Changelog
## v3.2.2 ## 3.7.3 - 2021-09-04
- Add official support to Apple M1 ([#564](https://github.com/go-task/task/pull/564), [#567](https://github.com/go-task/task/pull/567)).
- 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
- On watch, report which file failed to read
([#472](https://github.com/go-task/task/pull/472)).
- Do not try to catch SIGKILL signal, which are not actually possible
([#476](https://github.com/go-task/task/pull/476)).
- Improve version reporting when building Task from source using Go Modules
([#462](https://github.com/go-task/task/pull/462), [#473](https://github.com/go-task/task/pull/473)).
## v3.4.1 - 2021-04-17
- Improve error reporting when parsing YAML: in some situations where you
would just see an generic error, you'll now see the actual error with
more detail: the YAML line the failed to parse, for example
([#467](https://github.com/go-task/task/issues/467)).
- A JSON Schema was published [here](https://json.schemastore.org/taskfile.json)
and is automatically being used by some editors like Visual Studio Code
([#135](https://github.com/go-task/task/issues/135)).
- Print task name before the command in the log output
([#398](https://github.com/go-task/task/pull/398)).
## v3.3.0 - 2021-03-20
- Add support for delegating CLI arguments to commands with `--` and a
special `CLI_ARGS` variable
([#327](https://github.com/go-task/task/issues/327)).
- Add a `--concurrency` (alias `-C`) flag, to limit the number of tasks that
run concurrently. This is useful for heavy workloads.
([#345](https://github.com/go-task/task/pull/345)).
## v3.2.2 - 2021-01-12
- Improve performance of `--list` and `--summary` by skipping running shell - Improve performance of `--list` and `--summary` by skipping running shell
variables for these flags variables for these flags
@@ -13,14 +77,14 @@
- The install script is now working for ARM platforms - The install script is now working for ARM platforms
([#428](https://github.com/go-task/task/pull/428)). ([#428](https://github.com/go-task/task/pull/428)).
## v3.2.1 ## v3.2.1 - 2021-01-09
- Fixed some bugs and regressions regarding dynamic variables and directories - Fixed some bugs and regressions regarding dynamic variables and directories
([#426](https://github.com/go-task/task/issues/426)). ([#426](https://github.com/go-task/task/issues/426)).
- The [slim-sprig](https://github.com/go-task/slim-sprig) package was updated - The [slim-sprig](https://github.com/go-task/slim-sprig) package was updated
with the upstream [sprig](https://github.com/Masterminds/sprig). with the upstream [sprig](https://github.com/Masterminds/sprig).
## v3.2.0 ## v3.2.0 - 2021-01-07
- Fix the `.task` directory being created in the task directory instead of the - Fix the `.task` directory being created in the task directory instead of the
Taskfile directory Taskfile directory
@@ -33,7 +97,7 @@
should be more stable now should be more stable now
([#423](https://github.com/go-task/task/pull/423), [#365](https://github.com/go-task/task/issues/365)). ([#423](https://github.com/go-task/task/pull/423), [#365](https://github.com/go-task/task/issues/365)).
## v3.1.0 ## v3.1.0 - 2021-01-03
- Fix a bug when the checksum up-to-date resolution is used by a task - Fix a bug when the checksum up-to-date resolution is used by a task
with a custom `label:` attribute with a custom `label:` attribute
@@ -49,7 +113,7 @@
sentence was in the last line sentence was in the last line
([#403](https://github.com/go-task/task/issues/403)). ([#403](https://github.com/go-task/task/issues/403)).
## v3.0.1 ## v3.0.1 - 2020-12-26
- Allow use as a library by moving the required packages out of the `internal` - Allow use as a library by moving the required packages out of the `internal`
directory directory
@@ -59,7 +123,7 @@
- Fix panic when you have empty tasks in your Taskfile - Fix panic when you have empty tasks in your Taskfile
([#338](https://github.com/go-task/task/issues/338), [#362](https://github.com/go-task/task/pull/362)). ([#338](https://github.com/go-task/task/issues/338), [#362](https://github.com/go-task/task/pull/362)).
## v3.0.0 ## v3.0.0 - 2020-08-16
- On `v3`, all CLI variables will be considered global variables - On `v3`, all CLI variables will be considered global variables
([#336](https://github.com/go-task/task/issues/336), [#341](https://github.com/go-task/task/pull/341)) ([#336](https://github.com/go-task/task/issues/336), [#341](https://github.com/go-task/task/pull/341))
@@ -102,7 +166,7 @@
commands are green, errors are red, etc commands are green, errors are red, etc
([#207](https://github.com/go-task/task/pull/207)). ([#207](https://github.com/go-task/task/pull/207)).
## v2.8.1 - 2019-05-20 ## v2.8.1 - 2020-05-20
- Fix error code for the `--help` flag - Fix error code for the `--help` flag
([#300](https://github.com/go-task/task/issues/300), [#330](https://github.com/go-task/task/pull/330)). ([#300](https://github.com/go-task/task/issues/300), [#330](https://github.com/go-task/task/pull/330)).

View File

@@ -12,6 +12,9 @@ vars:
GO_PACKAGES: GO_PACKAGES:
sh: go list ./... sh: go list ./...
env:
CGO_ENABLED: '0'
tasks: tasks:
default: default:
cmds: cmds:
@@ -21,8 +24,6 @@ tasks:
desc: Installs Task desc: Installs Task
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
env:
CGO_ENABLED: '0'
mod: mod:
desc: Downloads and tidy Go modules desc: Downloads and tidy Go modules

View File

@@ -9,7 +9,7 @@ import (
// ParseV3 parses command line argument: tasks and global variables // ParseV3 parses command line argument: tasks and global variables
func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) { func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) {
var calls []taskfile.Call var calls []taskfile.Call
var globals *taskfile.Vars var globals = &taskfile.Vars{}
for _, arg := range args { for _, arg := range args {
if !strings.Contains(arg, "=") { if !strings.Contains(arg, "=") {
@@ -17,9 +17,6 @@ func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) {
continue continue
} }
if globals == nil {
globals = &taskfile.Vars{}
}
name, value := splitVar(arg) name, value := splitVar(arg)
globals.Set(name, taskfile.Var{Static: value}) globals.Set(name, taskfile.Var{Static: value})
} }
@@ -34,7 +31,7 @@ func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) {
// ParseV2 parses command line argument: tasks and vars of each task // ParseV2 parses command line argument: tasks and vars of each task
func ParseV2(args ...string) ([]taskfile.Call, *taskfile.Vars) { func ParseV2(args ...string) ([]taskfile.Call, *taskfile.Vars) {
var calls []taskfile.Call var calls []taskfile.Call
var globals *taskfile.Vars var globals = &taskfile.Vars{}
for _, arg := range args { for _, arg := range args {
if !strings.Contains(arg, "=") { if !strings.Contains(arg, "=") {
@@ -43,9 +40,6 @@ func ParseV2(args ...string) ([]taskfile.Call, *taskfile.Vars) {
} }
if len(calls) < 1 { if len(calls) < 1 {
if globals == nil {
globals = &taskfile.Vars{}
}
name, value := splitVar(arg) name, value := splitVar(arg)
globals.Set(name, taskfile.Var{Static: value}) globals.Set(name, taskfile.Var{Static: value})
} else { } else {

View File

@@ -96,7 +96,9 @@ func TestArgsV3(t *testing.T) {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) { t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, globals := args.ParseV3(test.Args...) calls, globals := args.ParseV3(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls) assert.Equal(t, test.ExpectedCalls, calls)
assert.Equal(t, test.ExpectedGlobals, globals) if test.ExpectedGlobals.Len() > 0 || globals.Len() > 0 {
assert.Equal(t, test.ExpectedGlobals, globals)
}
}) })
} }
} }
@@ -198,7 +200,10 @@ func TestArgsV2(t *testing.T) {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) { t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, globals := args.ParseV2(test.Args...) calls, globals := args.ParseV2(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls) assert.Equal(t, test.ExpectedCalls, calls)
assert.Equal(t, test.ExpectedGlobals, globals) if test.ExpectedGlobals.Len() > 0 || globals.Len() > 0 {
assert.Equal(t, test.ExpectedGlobals, globals)
}
}) })
} }
} }

View File

@@ -7,6 +7,8 @@ import (
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"runtime/debug"
"strings"
"syscall" "syscall"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@@ -18,7 +20,7 @@ import (
) )
var ( var (
version = "master" version = ""
) )
const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--taskfile] [--dry] [--summary] [task...] const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--taskfile] [--dry] [--summary] [task...]
@@ -65,6 +67,7 @@ func main() {
dry bool dry bool
summary bool summary bool
parallel bool parallel bool
concurrency int
dir string dir string
entrypoint string entrypoint string
output string output string
@@ -86,11 +89,12 @@ 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.Parse() pflag.Parse()
if versionFlag { if versionFlag {
fmt.Printf("Task version: %s\n", version) fmt.Printf("Task version: %s\n", getVersion())
return return
} }
@@ -122,16 +126,17 @@ func main() {
} }
e := task.Executor{ e := task.Executor{
Force: force, Force: force,
Watch: watch, Watch: watch,
Verbose: verbose, Verbose: verbose,
Silent: silent, Silent: silent,
Dir: dir, Dir: dir,
Dry: dry, Dry: dry,
Entrypoint: entrypoint, Entrypoint: entrypoint,
Summary: summary, Summary: summary,
Parallel: parallel, Parallel: parallel,
Color: color, Color: color,
Concurrency: concurrency,
Stdin: os.Stdin, Stdin: os.Stdin,
Stdout: os.Stdout, Stdout: os.Stdout,
@@ -154,14 +159,18 @@ func main() {
} }
var ( var (
calls []taskfile.Call calls []taskfile.Call
globals *taskfile.Vars globals *taskfile.Vars
tasksAndVars, cliArgs = getArgs()
) )
if v >= 3.0 { if v >= 3.0 {
calls, globals = args.ParseV3(pflag.Args()...) calls, globals = args.ParseV3(tasksAndVars...)
} else { } else {
calls, globals = args.ParseV2(pflag.Args()...) calls, globals = args.ParseV2(tasksAndVars...)
} }
globals.Set("CLI_ARGS", taskfile.Var{Static: strings.Join(cliArgs, " ")})
e.Taskfile.Vars.Merge(globals) e.Taskfile.Vars.Merge(globals)
ctx := context.Background() ctx := context.Background()
@@ -182,9 +191,25 @@ func main() {
} }
} }
func getArgs() (tasksAndVars, cliArgs []string) {
var (
args = pflag.Args()
doubleDashPos = pflag.CommandLine.ArgsLenAtDash()
)
if doubleDashPos != -1 {
tasksAndVars = args[:doubleDashPos]
cliArgs = args[doubleDashPos:]
} else {
tasksAndVars = args
}
return
}
func getSignalContext() context.Context { func getSignalContext() context.Context {
ch := make(chan os.Signal, 1) ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, os.Kill, syscall.SIGTERM) signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
go func() { go func() {
sig := <-ch sig := <-ch
@@ -193,3 +218,21 @@ func getSignalContext() context.Context {
}() }()
return ctx return ctx
} }
func getVersion() string {
if version != "" {
return version
}
info, ok := debug.ReadBuildInfo()
if !ok || info.Main.Version == "" {
return "unknown"
}
version = info.Main.Version
if info.Main.Sum != "" {
version += fmt.Sprintf(" (%s)", info.Main.Sum)
}
return version
}

View File

@@ -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

View File

@@ -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

25
concurrency.go Normal file
View File

@@ -0,0 +1,25 @@
package task
func (e *Executor) acquireConcurrencyLimit() func() {
if e.concurrencySemaphore == nil {
return emptyFunc
}
e.concurrencySemaphore <- struct{}{}
return func() {
<-e.concurrencySemaphore
}
}
func (e *Executor) releaseConcurrencyLimit() func() {
if e.concurrencySemaphore == nil {
return emptyFunc
}
<-e.concurrencySemaphore
return func() {
e.concurrencySemaphore <- struct{}{}
}
}
func emptyFunc() {}

View File

@@ -2,7 +2,7 @@
- [Usage](usage.md) - [Usage](usage.md)
- [Styleguide](styleguide.md) - [Styleguide](styleguide.md)
- [Taskfile Versions](taskfile_versions.md) - [Taskfile Versions](taskfile_versions.md)
- [Examples](examples.md) - [Community](community.md)
- [Releasing Task](releasing_task.md) - [Releasing Task](releasing_task.md)
- [Donate](donate.md) - [Donate](donate.md)
- [![GitHub](https://icongram.jgog.in/simple/github.svg?color=808080&size=16)GitHub](https://github.com/go-task/task) - [GitHub](https://github.com/go-task/task)

50
docs/community.md Normal file
View File

@@ -0,0 +1,50 @@
# Community
Some of the work to improve the Task ecosystem is done by the community, be
it installation methods or integrations with code editor. I (the author) am
thankful for everyone that helps me to improve the overall experience.
## Editor Integrations
### JSON Schema
[@KROSF](https://github.com/KROSF) worked on a JSON Schema [into this Gist](https://gist.github.com/KROSF/c5435acf590acd632f71bb720f685895),
which later was made officially available by [@Crandel](https://github.com/Crandel)
at [https://json.schemastore.org/taskfile.json](https://json.schemastore.org/taskfile.json).
Further improvements are possible by opening pull requests changing
[this file](https://github.com/SchemaStore/schemastore/blob/master/src/schemas/json/taskfile.json).
Some code editors, like Visual Studio Code, make use of Schema Store
automatically.
### Visual Studio Code extension
Additionally, there's also some work done by
[@paulvarache](https://github.com/paulvarache) in making an Visual Studio Code
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).
### 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
Some installation methods are maintained by third party:
- [GitHub Actions](https://github.com/arduino/setup-task)
by [@arduino](https://github.com/arduino)
- [AUR](https://aur.archlinux.org/packages/taskfile-git)
by [@kovetskiy](https://github.com/kovetskiy)
- [Scoop](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json)
## More
Also, thanks for all the [code contributors](https://github.com/go-task/task/graphs/contributors),
[financial contributors](https://opencollective.com/task), all those who
[reported bugs](https://github.com/go-task/task/issues?q=is%3Aissue) and
[answered questions](https://github.com/go-task/task/discussions).
If you know something that is missing in this document, please submit a
pull request.

View File

@@ -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&currency_code=BRL&source=url) - [Any value - One-time donation](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=GSVDU63RKG45A&currency_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>.

View File

@@ -1,7 +0,0 @@
# Examples
The [go-task/examples][examples] intends to be a collection of Taskfiles for
various use cases.
(It still lacks many examples, though. Contributions are welcome).
[examples]: https://github.com/go-task/examples

View File

@@ -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="//cdn.jsdelivr.net/npm/docsify-themeable/dist/css/theme-simple.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 {
@@ -20,6 +20,10 @@
.app-name-link img { .app-name-link img {
width: 125px; width: 125px;
} }
:root {
--base-font-size: 14px;
--theme-color: #29beb0;
}
</style> </style>
</head> </head>
<body> <body>
@@ -39,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>

View File

@@ -68,12 +68,12 @@ The `task_checksums.txt` file contains the SHA-256 checksum for each file.
#### **Install Script** #### **Install Script**
We also have a [install script][installscript], which is very useful on We also have an [install script][installscript] which is very useful in
scenarios like CIs. Many thanks to [GoDownloader][godownloader] for allowing scenarios like CI. Many thanks to [GoDownloader][godownloader] for enabling the
easily generating this script. easy generation of this script.
```bash ```bash
# For Default Installion to ./bin with debug logging # For Default Installation to ./bin with debug logging
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
# For Installation To /usr/local/bin for userwide access with debug logging # For Installation To /usr/local/bin for userwide access with debug logging
@@ -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.
@@ -107,29 +107,27 @@ This installation method is community owned.
First, make sure you have [Go][go] properly installed and setup. First, make sure you have [Go][go] properly installed and setup.
You can easily install it globally by running: You can easily install the latest release globally by running:
```bash ```bash
go get -u github.com/go-task/task/v3/cmd/task go install github.com/go-task/task/v3/cmd/task@latest
``` ```
Or you can install into another directory: Or you can install into another directory:
```bash ```bash
git clone https://github.com/go-task/task env GOBIN=/bin go install github.com/go-task/task/v3/cmd/task@latest
cd task ```
# Compiling binary to $GOPATH/bin If using Go 1.15 or earlier, instead use:
go install -v ./cmd/task
# Compiling it to another location. ```bash
# Use -o ./task.exe on Windows. env GO111MODULE=on go get -u github.com/go-task/task/v3/cmd/task@latest
go build -v -o ./task ./cmd/task
``` ```
> For CI environments we recommend using the [Install Script](#get-the-binary) > For CI environments we recommend using the [Install Script](#get-the-binary)
> instead, which is faster and more stable, since it'll just download the latest > instead, which is faster and more stable, since it'll just download the latest
> released binary, instead of compiling the edge (master branch) version. > released binary.
<!-- tabs:end --> <!-- tabs:end -->

BIN
docs/pix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -36,4 +36,4 @@ If you think its Task version is outdated, open an issue to let us know.
[gotaskrb]: https://github.com/go-task/homebrew-tap/blob/master/Formula/go-task.rb [gotaskrb]: https://github.com/go-task/homebrew-tap/blob/master/Formula/go-task.rb
[snappackage]: https://github.com/go-task/snap [snappackage]: https://github.com/go-task/snap
[snapcraftyaml]: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml#L2 [snapcraftyaml]: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml#L2
[snapcraftdashboard]: https://dashboard.snapcraft.io/ [snapcraftdashboard]: https://snapcraft.io/task/releases

View File

@@ -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.
@@ -520,6 +568,27 @@ tasks:
This works for all types of variables. This works for all types of variables.
## Forwarding CLI arguments to commands
If `--` is given in the CLI, all following paramaters are added to a
special `.CLI_ARGS` variable. This is useful to forward arguments to another
command.
The below example will run `yarn install`.
```bash
$ task yarn -- install
```
```yaml
version: '3'
tasks:
yarn:
cmds:
- yarn {{.CLI_ARGS}}
```
## Go's template engine ## Go's template engine
Task parse commands as [Go's template engine][gotemplate] before executing Task parse commands as [Go's template engine][gotemplate] before executing
@@ -548,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).

18
go.mod
View File

@@ -1,17 +1,17 @@
module github.com/go-task/task/v3 module github.com/go-task/task/v3
require ( require (
github.com/fatih/color v1.7.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-colorable v0.1.2 // indirect github.com/mattn/go-zglob v0.0.3
github.com/mattn/go-zglob v0.0.1 github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/radovskyb/watcher v1.0.5 github.com/radovskyb/watcher v1.0.7
github.com/spf13/pflag v1.0.3 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.5.1 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.1 mvdan.cc/sh/v3 v3.3.1
) )
go 1.13 go 1.16

72
go.sum
View File

@@ -1,62 +1,62 @@
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.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
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/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28= 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/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.5 h1:wqt7gb+HjGacvFoLTKeT44C+XVPxu7bvHvKT1IvZ7rw= github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
github.com/radovskyb/watcher v1.0.5/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.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
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=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407 h1:5zh5atpUEdIc478E/ebrIaHLKcfVvG6dL/fGv7BcMoM= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/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=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
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.1 h1:uQBpiGM+rEuHse3Q+W7ajuJUeOtFVJUN/6GeX4/dUWE= mvdan.cc/sh/v3 v3.3.1 h1:aA0i7NZOc1oV5jfAH20FCz+QsmI/TX7FiAquC5Rdo5o=
mvdan.cc/sh/v3 v3.2.1/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
View 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)
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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
View 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
}

View File

@@ -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...)
} }
} }

View File

@@ -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
} }

View File

@@ -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)

View File

@@ -63,7 +63,7 @@ func (r *Templater) ReplaceSlice(strs []string) []string {
} }
func (r *Templater) ReplaceVars(vars *taskfile.Vars) *taskfile.Vars { func (r *Templater) ReplaceVars(vars *taskfile.Vars) *taskfile.Vars {
if r.err != nil || vars == nil || len(vars.Keys) == 0 { if r.err != nil || vars.Len() == 0 {
return nil return nil
} }

View File

@@ -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 {

184
task.go
View File

@@ -32,16 +32,17 @@ const (
type Executor struct { type Executor struct {
Taskfile *taskfile.Taskfile Taskfile *taskfile.Taskfile
Dir string Dir string
Entrypoint string Entrypoint string
Force bool Force bool
Watch bool Watch bool
Verbose bool Verbose bool
Silent bool Silent bool
Dry bool Dry bool
Summary bool Summary bool
Parallel bool Parallel bool
Color bool Color bool
Concurrency int
Stdin io.Reader Stdin io.Reader
Stdout io.Writer Stdout io.Writer
@@ -54,8 +55,11 @@ type Executor struct {
taskvars *taskfile.Vars taskvars *taskfile.Vars
taskCallCount map[string]*int32 concurrencySemaphore chan struct{}
mkdirMutexMap map[string]*sync.Mutex taskCallCount map[string]*int32
mkdirMutexMap map[string]*sync.Mutex
executionHashes map[string]context.Context
executionHashesMutex sync.Mutex
} }
// Run runs Task // Run runs Task
@@ -147,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
@@ -174,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`)
} }
@@ -241,12 +265,34 @@ 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 {
e.taskCallCount[k] = new(int32) e.taskCallCount[k] = new(int32)
e.mkdirMutexMap[k] = &sync.Mutex{} e.mkdirMutexMap[k] = &sync.Mutex{}
} }
if e.Concurrency > 0 {
e.concurrencySemaphore = make(chan struct{}, e.Concurrency)
}
return nil return nil
} }
@@ -260,48 +306,53 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
return &MaximumTaskCallExceededError{task: call.Task} return &MaximumTaskCallExceededError{task: call.Task}
} }
if err := e.runDeps(ctx, t); err != nil { release := e.acquireConcurrencyLimit()
return err defer release()
}
if !e.Force { return e.startExecution(ctx, t, func(ctx context.Context) error {
preCondMet, err := e.areTaskPreconditionsMet(ctx, t) if err := e.runDeps(ctx, t); err != nil {
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 {
@@ -324,6 +375,9 @@ func (e *Executor) mkdir(t *taskfile.Task) error {
func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error { func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error {
g, ctx := errgroup.WithContext(ctx) g, ctx := errgroup.WithContext(ctx)
reacquire := e.releaseConcurrencyLimit()
defer reacquire()
for _, d := range t.Deps { for _, d := range t.Deps {
d := d d := d
@@ -344,6 +398,9 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
switch { switch {
case cmd.Task != "": case cmd.Task != "":
reacquire := e.releaseConcurrencyLimit()
defer reacquire()
err := e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars}) err := e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars})
if err != nil { if err != nil {
return err return err
@@ -351,7 +408,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
return nil return nil
case cmd.Cmd != "": case cmd.Cmd != "":
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", cmd.Cmd) e.Logger.Errf(logger.Green, "task: [%s] %s", t.Name(), cmd.Cmd)
} }
if e.Dry { if e.Dry {
@@ -382,7 +439,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
Stderr: stdErr, Stderr: stdErr,
}) })
if execext.IsExitError(err) && cmd.IgnoreError { if execext.IsExitError(err) && cmd.IgnoreError {
e.Logger.VerboseErrf(logger.Yellow, "task: command error ignored: %v", err) e.Logger.VerboseErrf(logger.Yellow, "task: [%s] command error ignored: %v", t.Name(), err)
return nil return nil
} }
return err return err
@@ -413,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)
}

View File

@@ -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 {
@@ -171,6 +175,22 @@ func TestVarsInvalidTmpl(t *testing.T) {
assert.EqualError(t, e.Run(context.Background(), taskfile.Call{Task: target}), expectError, "e.Run(target)") assert.EqualError(t, e.Run(context.Background(), taskfile.Call{Task: target}), expectError, "e.Run(target)")
} }
func TestConcurrency(t *testing.T) {
const (
dir = "testdata/concurrency"
target = "default"
)
e := &task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Concurrency: 1,
}
assert.NoError(t, e.Setup(), "e.Setup()")
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: target}), "e.Run(target)")
}
func TestParams(t *testing.T) { func TestParams(t *testing.T) {
tt := fileContentTest{ tt := fileContentTest{
Dir: "testdata/params", Dir: "testdata/params",
@@ -232,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
@@ -249,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) {
@@ -610,7 +652,7 @@ func TestDry(t *testing.T) {
assert.NoError(t, e.Setup()) assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"})) assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
assert.Equal(t, "task: touch file.txt", strings.TrimSpace(buff.String())) assert.Equal(t, "task: [build] touch file.txt", strings.TrimSpace(buff.String()))
if _, err := os.Stat(file); err == nil { if _, err := os.Stat(file); err == nil {
t.Errorf("File should not exist %s", file) t.Errorf("File should not exist %s", file)
} }
@@ -884,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"
@@ -899,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)
}

View File

@@ -1,7 +1,6 @@
package taskfile package taskfile
import ( import (
"errors"
"strings" "strings"
) )
@@ -20,13 +19,6 @@ type Dep struct {
Vars *Vars Vars *Vars
} }
var (
// ErrCantUnmarshalCmd is returned for invalid command YAML
ErrCantUnmarshalCmd = errors.New("task: can't unmarshal cmd value")
// ErrCantUnmarshalDep is returned for invalid dependency YAML
ErrCantUnmarshalDep = errors.New("task: can't unmarshal dep value")
)
// UnmarshalYAML implements yaml.Unmarshaler interface // UnmarshalYAML implements yaml.Unmarshaler interface
func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error { func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
var cmd string var cmd string
@@ -53,12 +45,12 @@ func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
Task string Task string
Vars *Vars Vars *Vars
} }
if err := unmarshal(&taskCall); err == nil { if err := unmarshal(&taskCall); err != nil {
c.Task = taskCall.Task return err
c.Vars = taskCall.Vars
return nil
} }
return ErrCantUnmarshalCmd c.Task = taskCall.Task
c.Vars = taskCall.Vars
return nil
} }
// UnmarshalYAML implements yaml.Unmarshaler interface // UnmarshalYAML implements yaml.Unmarshaler interface
@@ -72,10 +64,10 @@ func (d *Dep) UnmarshalYAML(unmarshal func(interface{}) error) error {
Task string Task string
Vars *Vars Vars *Vars
} }
if err := unmarshal(&taskCall); err == nil { if err := unmarshal(&taskCall); err != nil {
d.Task = taskCall.Task return err
d.Vars = taskCall.Vars
return nil
} }
return ErrCantUnmarshalDep d.Task = taskCall.Task
d.Vars = taskCall.Vars
return nil
} }

View File

@@ -6,11 +6,6 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
var (
// ErrCantUnmarshalIncludedTaskfile is returned for invalid var YAML.
ErrCantUnmarshalIncludedTaskfile = errors.New("task: can't unmarshal included value")
)
// IncludedTaskfile represents information about included tasksfile // IncludedTaskfile represents information about included tasksfile
type IncludedTaskfile struct { type IncludedTaskfile struct {
Taskfile string Taskfile string
@@ -98,12 +93,11 @@ func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) err
Taskfile string Taskfile string
Dir string Dir string
} }
if err := unmarshal(&includedTaskfile); err == nil { if err := unmarshal(&includedTaskfile); err != nil {
it.Dir = includedTaskfile.Dir return err
it.Taskfile = includedTaskfile.Taskfile
it.AdvancedImport = true
return nil
} }
it.Dir = includedTaskfile.Dir
return ErrCantUnmarshalIncludedTaskfile it.Taskfile = includedTaskfile.Taskfile
it.AdvancedImport = true
return nil
} }

50
taskfile/read/dotenv.go Normal file
View 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
}

View File

@@ -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}

View File

@@ -1,9 +1,5 @@
package taskfile package taskfile
import (
"errors"
)
// Tasks represents a group of tasks // Tasks represents a group of tasks
type Tasks map[string]*Task type Tasks map[string]*Task
@@ -26,13 +22,9 @@ type Task struct {
Method string Method string
Prefix string Prefix string
IgnoreError bool IgnoreError bool
Run string
} }
var (
// ErrCantUnmarshalTask is returned for invalid task YAML
ErrCantUnmarshalTask = errors.New("task: can't unmarshal task value")
)
func (t *Task) Name() string { func (t *Task) Name() string {
if t.Label != "" { if t.Label != "" {
return t.Label return t.Label
@@ -70,27 +62,27 @@ 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 {
t.Cmds = task.Cmds return err
t.Deps = task.Deps
t.Label = task.Label
t.Desc = task.Desc
t.Summary = task.Summary
t.Sources = task.Sources
t.Generates = task.Generates
t.Status = task.Status
t.Preconditions = task.Preconditions
t.Dir = task.Dir
t.Vars = task.Vars
t.Env = task.Env
t.Silent = task.Silent
t.Method = task.Method
t.Prefix = task.Prefix
t.IgnoreError = task.IgnoreError
return nil
} }
t.Cmds = task.Cmds
return ErrCantUnmarshalTask t.Deps = task.Deps
t.Label = task.Label
t.Desc = task.Desc
t.Summary = task.Summary
t.Sources = task.Sources
t.Generates = task.Generates
t.Status = task.Status
t.Preconditions = task.Preconditions
t.Dir = task.Dir
t.Vars = task.Vars
t.Env = task.Env
t.Silent = task.Silent
t.Method = task.Method
t.Prefix = task.Prefix
t.IgnoreError = task.IgnoreError
t.Run = task.Run
return nil
} }

View File

@@ -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
} }

View File

@@ -7,11 +7,6 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
var (
// ErrCantUnmarshalVar is returned for invalid var YAML.
ErrCantUnmarshalVar = errors.New("task: can't unmarshal var value")
)
// Vars is a string[string] variables map. // Vars is a string[string] variables map.
type Vars struct { type Vars struct {
Keys []string Keys []string
@@ -75,7 +70,7 @@ func (vs *Vars) Range(yield func(key string, value Var) error) error {
// ToCacheMap converts Vars to a map containing only the static // ToCacheMap converts Vars to a map containing only the static
// variables // variables
func (vs *Vars) ToCacheMap() (m map[string]interface{}) { func (vs *Vars) ToCacheMap() (m map[string]interface{}) {
m = make(map[string]interface{}, len(vs.Keys)) m = make(map[string]interface{}, vs.Len())
vs.Range(func(k string, v Var) error { vs.Range(func(k string, v Var) error {
if v.Sh != "" { if v.Sh != "" {
// Dynamic variable is not yet resolved; trigger // Dynamic variable is not yet resolved; trigger
@@ -93,6 +88,14 @@ func (vs *Vars) ToCacheMap() (m map[string]interface{}) {
return return
} }
// Len returns the size of the map
func (vs *Vars) Len() int {
if vs == nil {
return 0
}
return len(vs.Keys)
}
// Var represents either a static or dynamic variable. // Var represents either a static or dynamic variable.
type Var struct { type Var struct {
Static string Static string
@@ -116,10 +119,9 @@ func (v *Var) UnmarshalYAML(unmarshal func(interface{}) error) error {
var sh struct { var sh struct {
Sh string Sh string
} }
if err := unmarshal(&sh); err == nil { if err := unmarshal(&sh); err != nil {
v.Sh = sh.Sh return err
return nil
} }
v.Sh = sh.Sh
return ErrCantUnmarshalVar return nil
} }

32
testdata/concurrency/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
version: '2'
tasks:
default:
deps:
- t1
t1:
deps:
- t3
- t4
cmds:
- task: t2
- echo done 1
t2:
deps:
- t5
- t6
cmds:
- echo done 2
t3:
cmds:
- echo done 3
t4:
cmds:
- echo done 4
t5:
cmds:
- echo done 5
t6:
cmds:
- echo done 6

View File

@@ -0,0 +1 @@
VAR_IN_DOTENV=var_in_dot_env_2

View File

@@ -0,0 +1,8 @@
version: "3"
dotenv: [".env.{{.ENV_VAR}}"]
tasks:
default:
cmds:
- echo "VAR='$VAR_IN_DOTENV'" > var.txt

View File

@@ -0,0 +1 @@
VAR_IN_DOTENV=var_in_dot_env_1

View 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

View File

@@ -0,0 +1 @@
VAR_IN_DOTENV=var_in_dot_env_3

View 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
View File

@@ -0,0 +1 @@
*.txt

24
testdata/run/Taskfile.yml vendored Normal file
View 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

View File

@@ -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

View File

@@ -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 {

View File

@@ -2,6 +2,7 @@ package task
import ( import (
"context" "context"
"fmt"
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
@@ -10,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"
) )
@@ -127,9 +128,9 @@ 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 err return fmt.Errorf("task: %s: %w", s, err)
} }
for _, f := range files { for _, f := range files {
absFile, err := filepath.Abs(f) absFile, err := filepath.Abs(f)