Compare commits

..

3 Commits

Author SHA1 Message Date
Andrey Nering
36317f8ddd Fix Travis CI 2018-04-30 21:53:01 -03:00
Andrey Nering
115b4a308f YAML: Don't use strict unmarshaling anymore
Fixes #112
2018-04-30 21:51:08 -03:00
Andrey Nering
62ba8dbabf Update the release process once again:
- Release both Homebrew and Snapcraft packages manually for now;
- Fix some artifacts file names to keep them consistent.
2018-04-30 21:50:27 -03:00
653 changed files with 191776 additions and 19010 deletions

View File

@@ -7,7 +7,8 @@ insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
indent_style = tab
indent_size = 8
[*.{md,yml,yaml,json,toml,htm,html,js,css,svg,sh,bash}]
[*.{md,yml,yaml,json,toml}]
indent_style = space
indent_size = 2

View File

@@ -1,6 +1,12 @@
* Bug reports and feature requests are welcome in [the issues][issues]
* For questions and discussion there's the [Slack room][slack] ([invititation here][slackinvite])
* Pull Requests are welcome. For more complex changes and features it's
recommended to open an issue with the feature request first
recommended to open an issue first
* About 3 or 4 pull requests accepted one gets write access to the repo.
Even then, possible backward incompatible changes should be discussed first
in an issue or pull request
* Documentation contributions are as important as code contributions
[issues]: https://github.com/go-task/task/issues
[slack]: https://gophers.slack.com/messages/task
[slackinvite]: https://invite.slack.golangbridge.org/

3
.github/FUNDING.yml vendored
View File

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

9
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,9 @@
<!--
For questions and general talk there's the Slack room: https://gophers.slack.com/messages/task
Invite to the Slack is available in this link: https://invite.slack.golangbridge.org/
If relevant, include the following information:
- Task version
- OS
- Example Taskfile showing the issue
-->

View File

@@ -1,13 +0,0 @@
---
name: Bug Report
about: Use the template to report bugs and issues
---
- Task version:
- Operating System:
### Example Taskfile showing the issue
```yaml
```

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Questions, Ideas and General Discussions
url: https://github.com/go-task/task/discussions
about: Ask questions and discuss general ideas with the community

View File

@@ -1,11 +0,0 @@
---
name: Feature Request
about: Use the template to make feature requests
---
Describe in detail what feature do you want to see in Task.
Give examples if possible.
Please, search if this wasn't proposed before, and if this is more like an idea
than a strong feature request, consider opening a
[discussion](https://github.com/go-task/task/discussions) instead.

View File

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

View File

@@ -1,24 +0,0 @@
name: Lint
on:
pull_request:
push:
tags:
- v*
branches:
- master
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.18
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.46.1

View File

@@ -1,26 +0,0 @@
name: goreleaser
on:
push:
tags:
- '*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18.x
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{secrets.GH_PAT}}

View File

@@ -1,38 +0,0 @@
name: Test
on:
pull_request:
push:
tags:
- v*
branches:
- master
jobs:
test:
name: Test
strategy:
matrix:
go-version: [1.17.x, 1.18.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{matrix.platform}}
steps:
- name: Set up Go ${{matrix.go-version}}
uses: actions/setup-go@v3
with:
go-version: ${{matrix.go-version}}
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Download Go modules
run: go mod download
env:
GOPROXY: https://proxy.golang.org
- name: Build
run: go build -o ./bin/task -v ./cmd/task
- name: Test
run: ./bin/task test --output=group --output-group-begin='::group::{{.TASK}}' --output-group-end='::endgroup::'

View File

@@ -1,35 +0,0 @@
name: Website Deploy
on:
push:
branches:
- master
jobs:
website-deploy:
name: Website Deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16.x
cache: yarn
cache-dependency-path: ./docs/yarn.lock
- name: Install dependencies
run: yarn install --frozen-lockfile
working-directory: ./docs
- name: Build website
run: yarn build
working-directory: ./docs
- name: Website Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/build
user_name: task-bot
user_email: 106601941+task-bot@users.noreply.github.com

View File

@@ -1,27 +0,0 @@
name: Website Test
on:
pull_request:
branches:
- master
jobs:
website-test:
name: Website Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16.x
cache: yarn
cache-dependency-path: ./docs/yarn.lock
- name: Install dependencies
run: yarn install --frozen-lockfile
working-directory: ./docs
- name: Test build website
run: yarn build
working-directory: ./docs

13
.gitignore vendored
View File

@@ -14,19 +14,6 @@
.glide/
./task
.task
dist/
.DS_Store
# editors
.idea/
.vscode/
# exuberant ctags
tags
/bin/*
!/bin/.keep
/testdata/vars/v1
/tmp

View File

@@ -1,6 +1,6 @@
build:
binary: task
main: ./cmd/task
main: cmd/task/task.go
goos:
- windows
- darwin
@@ -8,30 +8,16 @@ build:
goarch:
- 386
- amd64
- arm
- arm64
goarm:
- 6
ignore:
- goos: darwin
goarch: 386
env:
- CGO_ENABLED=0
ldflags:
- -s -w # Don't set main.version.
gomod:
proxy: true
archive:
name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
archives:
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
files:
- README.md
- LICENSE
- completion/**/*
format_overrides:
- goos: windows
format: zip
format_overrides:
- goos: windows
format: zip
release:
draft: true
@@ -42,42 +28,15 @@ snapshot:
checksum:
name_template: "task_checksums.txt"
nfpms:
- vendor: Task
homepage: https://taskfile.dev
maintainer: Andrey Nering <andrey@nering.com.br>
description: Simple task runner written in Go
license: MIT
conflicts:
- taskwarrior
formats:
- deb
- rpm
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
contents:
- src: completion/bash/task.bash
dst: /etc/bash_completion.d/task
- src: completion/fish/task.fish
dst: /usr/share/fish/completions/task.fish
- src: completion/zsh/_task
dst: /usr/local/share/zsh/site-functions/_task
brews:
- name: go-task
description: Task runner / simpler Make alternative written in Go
license: MIT
homepage: https://taskfile.dev
folder: Formula
tap:
owner: go-task
name: homebrew-tap
test:
system "#{bin}/task", "--help"
install: |-
bin.install "task"
bash_completion.install "completion/bash/task.bash" => "task"
zsh_completion.install "completion/zsh/_task" => "_task"
fish_completion.install "completion/fish/task.fish"
commit_author:
name: task-bot
email: 106601941+task-bot@users.noreply.github.com
nfpm:
vendor: Task
homepage: https://github.com/go-task/task
maintainer: Andrey Nering <andrey.nering@gmail.com>
description: Simple task runner written in Go
license: MIT
conflicts:
- taskwarrior
formats:
- deb
- rpm
name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"

23
.travis.yml Normal file
View File

@@ -0,0 +1,23 @@
language: go
go:
- '1.8'
- '1.9'
- '1.10'
addons:
apt:
packages:
- rpm
script:
- go install github.com/go-task/task/cmd/task
- task ci
deploy:
- provider: script
skip_cleanup: true
script: curl -sL http://git.io/goreleaser | bash
on:
tags: true
condition: $TRAVIS_OS_NAME = linux

View File

@@ -1,534 +0,0 @@
# Changelog
## v3.14.0 - 2022-07-08
- Add ability to override the `.task` directory location with the
`TASK_TEMP_DIR` environment variable.
- Allow to override Task colors using environment variables:
`TASK_COLOR_RESET`, `TASK_COLOR_BLUE`, `TASK_COLOR_GREEN`,
`TASK_COLOR_CYAN`, `TASK_COLOR_YELLOW`, `TASK_COLOR_MAGENTA`
and `TASK_COLOR_RED`
([#568](https://github.com/go-task/task/pull/568), [#792](https://github.com/go-task/task/pull/792)).
- Fixed bug when using the `output: group` mode where STDOUT and STDERR were
being print in separated blocks instead of in the right order
([#779](https://github.com/go-task/task/issues/779)).
- Starting on this release, ARM architecture binaries are been released to Snap
as well
([#795](https://github.com/go-task/task/issues/795)).
- i386 binaries won't be available anymore on Snap because Ubuntu removed the support
for this architecture.
- Upgrade mvdan.cc/sh, which fixes a bug with associative arrays
([#785](https://github.com/go-task/task/issues/785), [mvdan/sh#884](https://github.com/mvdan/sh/issues/884), [mvdan/sh#893](https://github.com/mvdan/sh/pull/893)).
## v3.13.0 - 2022-06-13
- Added `-n` as an alias to `--dry`
([#776](https://github.com/go-task/task/issues/776), [#777](https://github.com/go-task/task/pull/777)).
- Fix behavior of interrupt (SIGINT, SIGTERM) signals. Task will now give time
for the processes running to do cleanup work
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728), [#769](https://github.com/go-task/task/pull/769)).
- Add new `--exit-code` (`-x`) flag that will pass-through the exit form the
command being ran
([#755](https://github.com/go-task/task/pull/755)).
## v3.12.1 - 2022-05-10
- Fixed bug where, on Windows, variables were ending with `\r` because we were
only removing the final `\n` but not `\r\n`
([#717](https://github.com/go-task/task/issues/717)).
## v3.12.0 - 2022-03-31
- The `--list` and `--list-all` flags can now be combined with the `--silent`
flag to print the task names only, without their description
([#691](https://github.com/go-task/task/pull/691)).
- Added support for multi-level inclusion of Taskfiles. This means that
included Taskfiles can also include other Taskfiles. Before this was limited
to one level
([#390](https://github.com/go-task/task/issues/390), [#623](https://github.com/go-task/task/discussions/623), [#656](https://github.com/go-task/task/pull/656)).
- Add ability to specify vars when including a Taskfile.
[Check out the documentation](https://taskfile.dev/#/usage?id=vars-of-included-taskfiles)
for more information.
([#677](https://github.com/go-task/task/pull/677)).
## v3.11.0 - 2022-02-19
- Task now supports printing begin and end messages when using the `group`
output mode, useful for grouping tasks in CI systems.
[Check out the documentation](http://taskfile.dev/#/usage?id=output-syntax) for more information
([#647](https://github.com/go-task/task/issues/647), [#651](https://github.com/go-task/task/pull/651)).
- Add `Taskfile.dist.yml` and `Taskfile.dist.yaml` to the supported file
name list. [Check out the documentation](https://taskfile.dev/#/usage?id=supported-file-names) for more information
([#498](https://github.com/go-task/task/issues/498), [#666](https://github.com/go-task/task/pull/666)).
## v3.10.0 - 2022-01-04
- A new `--list-all` (alias `-a`) flag is now available. It's similar to the
exiting `--list` (`-l`) but prints all tasks, even those without a
description
([#383](https://github.com/go-task/task/issues/383), [#401](https://github.com/go-task/task/pull/401)).
- It's now possible to schedule cleanup commands to run once a task finishes
with the `defer:` keyword
([Documentation](https://taskfile.dev/#/usage?id=doing-task-cleanup-with-defer), [#475](https://github.com/go-task/task/issues/475), [#626](https://github.com/go-task/task/pull/626)).
- Remove long deprecated and undocumented `$` variable prefix and `^` command
prefix
([#642](https://github.com/go-task/task/issues/642), [#644](https://github.com/go-task/task/issues/644), [#645](https://github.com/go-task/task/pull/645)).
- Add support for `.yaml` extension (as an alternative to `.yml`).
This was requested multiple times throughout the years. Enjoy!
([#183](https://github.com/go-task/task/issues/183), [#184](https://github.com/go-task/task/pull/184), [#369](https://github.com/go-task/task/issues/369), [#584](https://github.com/go-task/task/issues/584), [#621](https://github.com/go-task/task/pull/621)).
- Fixed error when computing a variable when the task directory do not exist
yet
([#481](https://github.com/go-task/task/issues/481), [#579](https://github.com/go-task/task/pull/579)).
## v3.9.2 - 2021-12-02
- Upgrade [mvdan/sh](https://github.com/mvdan/sh) which contains a fix a for
a important regression on Windows
([#619](https://github.com/go-task/task/issues/619), [mvdan/sh#768](https://github.com/mvdan/sh/issues/768), [mvdan/sh#769](https://github.com/mvdan/sh/pull/769)).
## v3.9.1 - 2021-11-28
- Add logging in verbose mode for when a task starts and finishes
([#533](https://github.com/go-task/task/issues/533), [#588](https://github.com/go-task/task/pull/588)).
- Fix an issue with preconditions and context errors
([#597](https://github.com/go-task/task/issues/597), [#598](https://github.com/go-task/task/pull/598)).
- Quote each `{{.CLI_ARGS}}` argument to prevent one with spaces to become many
([#613](https://github.com/go-task/task/pull/613)).
- Fix nil pointer when `cmd:` was left empty
([#612](https://github.com/go-task/task/issues/612), [#614](https://github.com/go-task/task/pull/614)).
- Upgrade [mvdan/sh](https://github.com/mvdan/sh) which contains two
relevant fixes:
- Fix quote of empty strings in `shellQuote`
([#609](https://github.com/go-task/task/issues/609), [mvdan/sh#763](https://github.com/mvdan/sh/issues/763)).
- Fix issue of wrong environment variable being picked when there's another
very similar one
([#586](https://github.com/go-task/task/issues/586), [mvdan/sh#745](https://github.com/mvdan/sh/pull/745)).
- Install shell completions automatically when installing via Homebrew
([#264](https://github.com/go-task/task/issues/264), [#592](https://github.com/go-task/task/pull/592), [go-task/homebrew-tap#2](https://github.com/go-task/homebrew-tap/pull/2)).
## v3.9.0 - 2021-10-02
- A new `shellQuote` function was added to the template system
(`{{shellQuote "a string"}}`) to ensure a string is safe for use in shell
([mvdan/sh#727](https://github.com/mvdan/sh/pull/727), [mvdan/sh#737](https://github.com/mvdan/sh/pull/737), [Documentation](https://pkg.go.dev/mvdan.cc/sh/v3@v3.4.0/syntax#Quote))
- In this version [mvdan.cc/sh](https://github.com/mvdan/sh) was upgraded
with some small fixes and features
- The `read -p` flag is now supported
([#314](https://github.com/go-task/task/issues/314), [mvdan/sh#551](https://github.com/mvdan/sh/issues/551), [mvdan/sh#772](https://github.com/mvdan/sh/pull/722))
- The `pwd -P` and `pwd -L` flags are now supported
([#553](https://github.com/go-task/task/issues/553), [mvdan/sh#724](https://github.com/mvdan/sh/issues/724), [mvdan/sh#728](https://github.com/mvdan/sh/pull/728))
- The `$GID` environment variable is now correctly being set
([#561](https://github.com/go-task/task/issues/561), [mvdan/sh#723](https://github.com/mvdan/sh/pull/723))
## v3.8.0 - 2021-09-26
- Add `interactive: true` setting to improve support for interactive CLI apps
([#217](https://github.com/go-task/task/issues/217), [#563](https://github.com/go-task/task/pull/563)).
- Fix some `nil` errors
([#534](https://github.com/go-task/task/issues/534), [#573](https://github.com/go-task/task/pull/573)).
- Add ability to declare an included Taskfile as optional
([#519](https://github.com/go-task/task/issues/519), [#552](https://github.com/go-task/task/pull/552)).
- Add support for including Taskfiles in the home directory by using `~`
([#539](https://github.com/go-task/task/issues/539), [#557](https://github.com/go-task/task/pull/557)).
## v3.7.3 - 2021-09-04
- Add official support to Apple M1 ([#564](https://github.com/go-task/task/pull/564), [#567](https://github.com/go-task/task/pull/567)).
- 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
variables for these flags
([#332](https://github.com/go-task/task/issues/332)).
- Fixed a bug where an environment in a Taskfile was not always overridable
by the system environment
([#425](https://github.com/go-task/task/issues/425)).
- Fixed environment from .env files not being available as variables
([#379](https://github.com/go-task/task/issues/379)).
- The install script is now working for ARM platforms
([#428](https://github.com/go-task/task/pull/428)).
## v3.2.1 - 2021-01-09
- Fixed some bugs and regressions regarding dynamic variables and directories
([#426](https://github.com/go-task/task/issues/426)).
- The [slim-sprig](https://github.com/go-task/slim-sprig) package was updated
with the upstream [sprig](https://github.com/Masterminds/sprig).
## v3.2.0 - 2021-01-07
- Fix the `.task` directory being created in the task directory instead of the
Taskfile directory
([#247](https://github.com/go-task/task/issues/247)).
- Fix a bug where dynamic variables (those declared with `sh:`) were not
running in the task directory when the task has a custom dir or it was
in an included Taskfile
([#384](https://github.com/go-task/task/issues/384)).
- The watch feature (via the `--watch` flag) got a few different bug fixes and
should be more stable now
([#423](https://github.com/go-task/task/pull/423), [#365](https://github.com/go-task/task/issues/365)).
## v3.1.0 - 2021-01-03
- Fix a bug when the checksum up-to-date resolution is used by a task
with a custom `label:` attribute
([#412](https://github.com/go-task/task/issues/412)).
- Starting from this release, we're releasing official ARMv6 and ARM64 binaries
for Linux
([#375](https://github.com/go-task/task/issues/375), [#418](https://github.com/go-task/task/issues/418)).
- Task now respects the order of declaration of included Taskfiles when
evaluating variables declaring by them
([#393](https://github.com/go-task/task/issues/393)).
- `set -e` is now automatically set on every command. This was done to fix an
issue where multiline string commands wouldn't really fail unless the
sentence was in the last line
([#403](https://github.com/go-task/task/issues/403)).
## v3.0.1 - 2020-12-26
- Allow use as a library by moving the required packages out of the `internal`
directory
([#358](https://github.com/go-task/task/pull/358)).
- Do not error if a specified dotenv file does not exist
([#378](https://github.com/go-task/task/issues/378), [#385](https://github.com/go-task/task/pull/385)).
- 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)).
## v3.0.0 - 2020-08-16
- 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))
- Add support to `.env` like files
([#324](https://github.com/go-task/task/issues/324), [#356](https://github.com/go-task/task/pull/356)).
- Add `label:` to task so you can override the task name in the logs
([#321](https://github.com/go-task/task/issues/321]), [#337](https://github.com/go-task/task/pull/337)).
- Refactor how variables work on version 3
([#311](https://github.com/go-task/task/pull/311)).
- Disallow `expansions` on v3 since it has no effect.
- `Taskvars.yml` is not automatically included anymore.
- `Taskfile_{{OS}}.yml` is not automatically included anymore.
- Allow interpolation on `includes`, so you can manually include a Taskfile
based on operation system, for example.
- Expose `.TASK` variable in templates with the task name
([#252](https://github.com/go-task/task/issues/252)).
- Implement short task syntax
([#194](https://github.com/go-task/task/issues/194), [#240](https://github.com/go-task/task/pull/240)).
- Added option to make included Taskfile run commands on its own directory
([#260](https://github.com/go-task/task/issues/260), [#144](https://github.com/go-task/task/issues/144))
- Taskfiles in version 1 are not supported anymore
([#237](https://github.com/go-task/task/pull/237)).
- Added global `method:` option. With this option, you can set a default
method to all tasks in a Taskfile
([#246](https://github.com/go-task/task/issues/246)).
- Changed default method from `timestamp` to `checksum`
([#246](https://github.com/go-task/task/issues/246)).
- New magic variables are now available when using `status:`:
`.TIMESTAMP` which contains the greatest modification date
from the files listed in `sources:`, and `.CHECKSUM`, which
contains a checksum of all files listed in `status:`.
This is useful for manual checking when using external, or even remote,
artifacts when using `status:`
([#216](https://github.com/go-task/task/pull/216)).
- We're now using [slim-sprig](https://github.com/go-task/slim-sprig) instead of
[sprig](https://github.com/Masterminds/sprig), which allowed a file size
reduction of about 22%
([#219](https://github.com/go-task/task/pull/219)).
- We now use some colors on Task output to better distinguish message types -
commands are green, errors are red, etc
([#207](https://github.com/go-task/task/pull/207)).
## v2.8.1 - 2020-05-20
- 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)).
- Print version to stdout instead of stderr
([#299](https://github.com/go-task/task/issues/299), [#329](https://github.com/go-task/task/pull/329)).
- Supress `context` errors when using the `--watch` flag
([#313](https://github.com/go-task/task/issues/313), [#317](https://github.com/go-task/task/pull/317)).
- Support templating on description
([#276](https://github.com/go-task/task/issues/276), [#283](https://github.com/go-task/task/pull/283)).
## v2.8.0 - 2019-12-07
- Add `--parallel` flag (alias `-p`) to run tasks given by the command line in
parallel
([#266](https://github.com/go-task/task/pull/266)).
- Fixed bug where calling the `task` CLI only informing global vars would not
execute the `default` task.
- Add hability to silent all tasks by adding `silent: true` a the root of the
Taskfile.
## v2.7.1 - 2019-11-10
- Fix error being raised when `exit 0` was called
([#251](https://github.com/go-task/task/issues/251)).
## v2.7.0 - 2019-09-22
- Fixed panic bug when assigning a global variable
([#229](https://github.com/go-task/task/issues/229), [#243](https://github.com/go-task/task/issues/234)).
- A task with `method: checksum` will now re-run if generated files are deleted
([#228](https://github.com/go-task/task/pull/228), [#238](https://github.com/go-task/task/issues/238)).
## v2.6.0 - 2019-07-21
- Fixed some bugs regarding minor version checks on `version:`.
- Add `preconditions:` to task
([#205](https://github.com/go-task/task/pull/205)).
- Create directory informed on `dir:` if it doesn't exist
([#209](https://github.com/go-task/task/issues/209), [#211](https://github.com/go-task/task/pull/211)).
- We now have a `--taskfile` flag (alias `-t`), which can be used to run
another Taskfile (other than the default `Taskfile.yml`)
([#221](https://github.com/go-task/task/pull/221)).
- It's now possible to install Task using Homebrew on Linux
([go-task/homebrew-tap#1](https://github.com/go-task/homebrew-tap/pull/1)).
## v2.5.2 - 2019-05-11
- Reverted YAML upgrade due issues with CRLF on Windows
([#201](https://github.com/go-task/task/issues/201), [go-yaml/yaml#450](https://github.com/go-yaml/yaml/issues/450)).
- Allow setting global variables through the CLI
([#192](https://github.com/go-task/task/issues/192)).
## 2.5.1 - 2019-04-27
- Fixed some issues with interactive command line tools, where sometimes
the output were not being shown, and similar issues
([#114](https://github.com/go-task/task/issues/114), [#190](https://github.com/go-task/task/issues/190), [#200](https://github.com/go-task/task/pull/200)).
- Upgraded [go-yaml/yaml](https://github.com/go-yaml/yaml) from v2 to v3.
## v2.5.0 - 2019-03-16
- We moved from the taskfile.org domain to the new fancy taskfile.dev domain.
While stuff is being redirected, we strongly recommend to everyone that use
[this install script](https://taskfile.dev/#/installation?id=install-script)
to use the new taskfile.dev domain on scripts from now on.
- Fixed to the ZSH completion
([#182](https://github.com/go-task/task/pull/182)).
- Add [`--summary` flag along with `summary:` task attribute](https://taskfile.org/#/usage?id=display-summary-of-task)
([#180](https://github.com/go-task/task/pull/180)).
## v2.4.0 - 2019-02-21
- Allow calling a task of the root Taskfile from an included Taskfile
by prefixing it with `:`
([#161](https://github.com/go-task/task/issues/161), [#172](https://github.com/go-task/task/issues/172)),
- Add flag to override the `output` option
([#173](https://github.com/go-task/task/pull/173));
- Fix bug where Task was persisting the new checksum on the disk when the Dry
Mode is enabled
([#166](https://github.com/go-task/task/issues/166));
- Fix file timestamp issue when the file name has spaces
([#176](https://github.com/go-task/task/issues/176));
- Mitigating path expanding issues on Windows
([#170](https://github.com/go-task/task/pull/170)).
## v2.3.0 - 2019-01-02
- On Windows, Task can now be installed using [Scoop](https://scoop.sh/)
([#152](https://github.com/go-task/task/pull/152));
- Fixed issue with file/directory globing
([#153](https://github.com/go-task/task/issues/153));
- Added ability to globally set environment variables
(
[#138](https://github.com/go-task/task/pull/138),
[#159](https://github.com/go-task/task/pull/159)
).
## v2.2.1 - 2018-12-09
- This repository now uses Go Modules (#143). We'll still keep the `vendor` directory in sync for some time, though;
- Fixing a bug when the Taskfile has no tasks but includes another Taskfile (#150);
- Fix a bug when calling another task or a dependency in an included Taskfile (#151).
## v2.2.0 - 2018-10-25
- Added support for [including other Taskfiles](https://taskfile.org/#/usage?id=including-other-taskfiles) (#98)
- This should be considered experimental. For now, only including local files is supported, but support for including remote Taskfiles is being discussed. If you have any feedback, please comment on #98.
- Task now have a dedicated documentation site: https://taskfile.org
- Thanks to [Docsify](https://docsify.js.org/) for making this pretty easy. To check the source code, just take a look at the [docs](https://github.com/go-task/task/tree/master/docs) directory of this repository. Contributions to the documentation is really appreciated.
## v2.1.1 - 2018-09-17
- Fix suggestion to use `task --init` not being shown anymore (when a `Taskfile.yml` is not found)
- Fix error when using checksum method and no file exists for a source glob (#131)
- Fix signal handling when the `--watch` flag is given (#132)
## v2.1.0 - 2018-08-19
- Add a `ignore_error` option to task and command (#123)
- Add a dry run mode (`--dry` flag) (#126)
## v2.0.3 - 2018-06-24
- Expand environment variables on "dir", "sources" and "generates" (#116)
- Fix YAML merging syntax (#112)
- Add ZSH completion (#111)
- Implement new `output` option. Please check out the [documentation](https://github.com/go-task/task#output-syntax)
## v2.0.2 - 2018-05-01
- Fix merging of YAML anchors (#112)
## v2.0.1 - 2018-03-11
- Fixes panic on `task --list`
## v2.0.0 - 2018-03-08
Version 2.0.0 is here, with a new Taskfile format.
Please, make sure to read the [Taskfile versions](https://github.com/go-task/task/blob/master/TASKFILE_VERSIONS.md) document, since it describes in depth what changed for this version.
* New Taskfile version 2 (https://github.com/go-task/task/issues/77)
* Possibility to have global variables in the `Taskfile.yml` instead of `Taskvars.yml` (https://github.com/go-task/task/issues/66)
* Small improvements and fixes
## v1.4.4 - 2017-11-19
- Handle SIGINT and SIGTERM (#75);
- List: print message with there's no task with description;
- Expand home dir ("~" symbol) on paths (#74);
- Add Snap as an installation method;
- Move examples to its own repo;
- Watch: also walk on tasks called on on "cmds", and not only on "deps";
- Print logs to stderr instead of stdout (#68);
- Remove deprecated `set` keyword;
- Add checksum based status check, alternative to timestamp based.
## v1.4.3 - 2017-09-07
- Allow assigning variables to tasks at run time via CLI (#33)
- Added suport for multiline variables from sh (#64)
- Fixes env: remove square braces and evaluate shell (#62)
- Watch: change watch library and few fixes and improvements
- When use watching, cancel and restart long running process on file change (#59 and #60)
## v1.4.2 - 2017-07-30
- Flag to set directory of execution
- Always echo command if is verbose mode
- Add silent mode to disable echoing of commands
- Fixes and improvements of variables (#56)
## v1.4.1 - 2017-07-15
- Allow use of YAML for dynamic variables instead of $ prefix
- `VAR: {sh: echo Hello}` instead of `VAR: $echo Hello`
- Add `--list` (or `-l`) flag to print existing tasks
- OS specific Taskvars file (e.g. `Taskvars_windows.yml`, `Taskvars_linux.yml`, etc)
- Consider task up-to-date on equal timestamps (#49)
- Allow absolute path in generates section (#48)
- Bugfix: allow templating when calling deps (#42)
- Fix panic for invalid task in cyclic dep detection
- Better error output for dynamic variables in Taskvars.yml (#41)
- Allow template evaluation in parameters
## v1.4.0 - 2017-07-06
- Cache dynamic variables
- Add verbose mode (`-v` flag)
- Support to task parameters (overriding vars) (#31) (#32)
- Print command, also when "set:" is specified (#35)
- Improve task command help text (#35)
## v1.3.1 - 2017-06-14
- Fix glob not working on commands (#28)
- Add ExeExt template function
- Add `--init` flag to create a new Taskfile
- Add status option to prevent task from running (#27)
- Allow interpolation on `generates` and `sources` attributes (#26)
## v1.3.0 - 2017-04-24
- Migrate from os/exec.Cmd to a native Go sh/bash interpreter
- This is a potentially breaking change if you use Windows.
- Now, `cmd` is not used anymore on Windows. Always use Bash-like syntax for your commands, even on Windows.
- Add "ToSlash" and "FromSlash" to template functions
- Use functions defined on github.com/Masterminds/sprig
- Do not redirect stdin while running variables commands
- Using `context` and `errgroup` packages (this will make other tasks to be cancelled, if one returned an error)
## v1.2.0 - 2017-04-02
- More tests and Travis integration
- Watch a task (experimental)
- Possibility to call another task
- Fix "=" not being reconized in variables/environment variables
- Tasks can now have a description, and help will print them (#10)
- Task dependencies now run concurrently
- Support for a default task (#16)
## v1.1.0 - 2017-03-08
- Support for YAML, TOML and JSON (#1)
- Support running command in another directory (#4)
- `--force` or `-f` flag to force execution of task even when it's up-to-date
- Detection of cyclic dependencies (#5)
- Support for variables (#6, #9, #14)
- Operation System specific commands and variables (#13)
## v1.0.0 - 2017-02-28
- Add LICENSE file

136
Gopkg.lock generated Normal file
View File

@@ -0,0 +1,136 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/Masterminds/semver"
packages = ["."]
revision = "15d8430ab86497c5c0da827b748823945e1cf1e1"
version = "v1.4.0"
[[projects]]
branch = "master"
name = "github.com/Masterminds/sprig"
packages = ["."]
revision = "9d9aa1f74c86fd9d36ecfe3f2a44a3093c3d4c15"
[[projects]]
name = "github.com/aokoli/goutils"
packages = ["."]
revision = "3391d3790d23d03408670993e957e8f408993c34"
version = "v1.0.1"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/huandu/xstrings"
packages = ["."]
revision = "2bf18b218c51864a87384c06996e40ff9dcff8e1"
[[projects]]
branch = "master"
name = "github.com/imdario/mergo"
packages = ["."]
revision = "0d4b488675fdec1dde48751b05ab530cf0b630e1"
[[projects]]
branch = "master"
name = "github.com/mattn/go-zglob"
packages = [
".",
"fastwalk"
]
revision = "4959821b481786922ac53e7ef25c61ae19fb7c36"
[[projects]]
branch = "master"
name = "github.com/mitchellh/go-homedir"
packages = ["."]
revision = "b8bc1bf767474819792c23f32d8286a45736f1c6"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
name = "github.com/radovskyb/watcher"
packages = ["."]
revision = "6145e1439b9de93806925353403f91d2abbad8a5"
version = "v1.0.2"
[[projects]]
name = "github.com/satori/go.uuid"
packages = ["."]
revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3"
version = "v1.2.0"
[[projects]]
branch = "master"
name = "github.com/spf13/pflag"
packages = ["."]
revision = "ee5fd03fd6acfd43e44aea0b4135958546ed8e73"
[[projects]]
name = "github.com/stretchr/testify"
packages = ["assert"]
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = [
"pbkdf2",
"scrypt",
"ssh/terminal"
]
revision = "91a49db82a88618983a78a06c1cbd4e00ab749ab"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["context"]
revision = "22ae77b79946ea320088417e4d50825671d82d57"
[[projects]]
branch = "master"
name = "golang.org/x/sync"
packages = ["errgroup"]
revision = "fd80eb99c8f653c847d294a001bdf2a3a6f768f5"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = [
"unix",
"windows"
]
revision = "dd2ff4accc098aceecb86b36eaa7829b2a17b1c9"
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "7f97868eec74b32b0982dd158a51a446d1da7eb5"
version = "v2.1.1"
[[projects]]
branch = "master"
name = "mvdan.cc/sh"
packages = [
"interp",
"syntax"
]
revision = "fb0bad77f8fa7a57e6f249f53074ec52b21558d1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "976972e7291789a9d4904805cc7f49d733476868bf80f120309078b02a095a65"
solver-name = "gps-cdcl"
solver-version = 1

44
Gopkg.toml Normal file
View File

@@ -0,0 +1,44 @@
[prune]
unused-packages = true
non-go = true
go-tests = true
[[constraint]]
branch = "master"
name = "github.com/Masterminds/sprig"
[[constraint]]
branch = "master"
name = "github.com/imdario/mergo"
[[constraint]]
branch = "master"
name = "github.com/mattn/go-zglob"
[[constraint]]
branch = "master"
name = "mvdan.cc/sh"
[[constraint]]
branch = "master"
name = "github.com/spf13/pflag"
[[constraint]]
branch = "master"
name = "golang.org/x/sync"
[[constraint]]
branch = "v2"
name = "gopkg.in/yaml.v2"
[[constraint]]
branch = "master"
name = "github.com/radovskyb/watcher"
[[constraint]]
branch = "master"
name = "github.com/mitchellh/go-homedir"
[[constraint]]
branch = "master"
name = "github.com/Masterminds/semver"

674
README.md
View File

@@ -1,15 +1,665 @@
<div align="center">
<a href="https://taskfile.dev">
<img src="docs/static/img/logo.svg" width="200px" height="200px" />
</a>
[![Join Slack room](https://img.shields.io/badge/%23task-chat%20room-blue.svg)](https://gophers.slack.com/messages/task)
[![Build Status](https://travis-ci.org/go-task/task.svg?branch=master)](https://travis-ci.org/go-task/task)
<h1>Task</h1>
# Task - A task runner / simpler Make alternative written in Go
<p>
Task is a task runner / build tool that aims to be simpler and easier to use than, for example, <a href="https://www.gnu.org/software/make/">GNU Make<a>.
</p>
> We recently released version 2.0.0 of Task. The Taskfile changed a bit.
Please, check the [Taskfile versions](TASKFILE_VERSIONS.md) document to see
what changed and how to upgrade.
<p>
See <a href="https://taskfile.dev">taskfile.dev</a> for the documentation.
</p>
</div>
Task is a simple tool that allows you to easily run development and build
tasks. Task is written in Golang, but can be used to develop any language.
It aims to be simpler and easier to use then [GNU Make][make].
- [Installation](#installation)
- [Go](#go)
- [Homebrew](#homebrew)
- [Snap](#snap)
- [Binary](#binary)
- [Usage](#usage)
- [Environment](#environment)
- [OS specific task](#os-specific-task)
- [Task directory](#task-directory)
- [Task dependencies](#task-dependencies)
- [Calling another task](#calling-another-task)
- [Prevent unnecessary work](#prevent-unnecessary-work)
- [Variables](#variables)
- [Dynamic variables](#dynamic-variables)
- [Go's template engine](#gos-template-engine)
- [Help](#help)
- [Silent mode](#silent-mode)
- [Watch tasks](#watch-tasks-experimental)
- [Examples](#examples)
- [Alternative task runners](#alternative-task-runners)
## Installation
### Go
If you have a [Golang][golang] environment setup, you can simply run:
```bash
go get -u -v github.com/go-task/task/cmd/task
```
### Homebrew
If you're on macOS and have [Homebrew][homebrew] installed, getting Task is
as simple as running:
```bash
brew update
brew tap go-task/tap
brew install go-task
```
### Snap
Task is available for [Snapcraft][snapcraft], but keep in mind that your
Linux distribution should allow classic confinement for Snaps to Task work
right:
```bash
sudo snap install task
```
### Binary
Or you can download the binary from the [releases][releases] page and add to
your `PATH`. DEB and RPM packages are also available.
The `task_checksums.txt` file contains the sha256 checksum for each file.
## Usage
Create a file called `Taskfile.yml` in the root of the project.
The `cmds` attribute should contains the commands of a task.
The example below allows compile a Go app and uses [Minify][minify] to concat
and minify multiple CSS files into a single one.
```yml
version: '2'
tasks:
build:
cmds:
- go build -v -i main.go
assets:
cmds:
- minify -o public/style.css src/css
```
Running the tasks is as simple as running:
```bash
task assets build
```
Task uses [github.com/mvdan/sh](https://github.com/mvdan/sh), a native Go sh
interpreter. So you can write sh/bash commands and it will work even on
Windows, where `sh` or `bash` is usually not available. Just remember any
executable called must be available by the OS or in PATH.
If you ommit a task name, "default" will be assumed.
### Environment
You can specify environment variables that are added when running a command:
```yml
version: '2'
tasks:
build:
cmds:
- echo $hallo
env:
hallo: welt
```
### OS specific task
If you add a `Taskfile_{{GOOS}}.yml` you can override or amend your taskfile
based on the operating system.
Example:
Taskfile.yml:
```yml
version: '2'
tasks:
build:
cmds:
- echo "default"
```
Taskfile_linux.yml:
```yml
tasks:
build:
cmds:
- echo "linux"
```
Will print out `linux` and not default.
It's also possible to have OS specific `Taskvars.yml` file, like
`Taskvars_windows.yml`, `Taskfile_linux.yml` or `Taskvars_darwin.yml`. See the
[variables section](#variables) below.
### Task directory
By default, tasks will be executed in the directory where the Taskfile is
located. But you can easily make the task run in another folder informing
`dir`:
```yml
version: '2'
tasks:
serve:
dir: public/www
cmds:
# run http server
- caddy
```
### Task dependencies
You may have tasks that depends on others. Just pointing them on `deps` will
make them run automatically before running the parent task:
```yml
version: '2'
tasks:
build:
deps: [assets]
cmds:
- go build -v -i main.go
assets:
cmds:
- minify -o public/style.css src/css
```
In the above example, `assets` will always run right before `build` if you run
`task build`.
A task can have only dependencies and no commands to group tasks together:
```yml
version: '2'
tasks:
assets:
deps: [js, css]
js:
cmds:
- minify -o public/script.js src/js
css:
cmds:
- minify -o public/style.css src/css
```
If there are more than one dependency, they always run in parallel for better
performance.
If you want to pass information to dependencies, you can do that the same
manner as you would to [call another task](#calling-another-task):
```yml
version: '2'
tasks:
default:
deps:
- task: echo_sth
vars: {TEXT: "before 1"}
- task: echo_sth
vars: {TEXT: "before 2"}
cmds:
- echo "after"
echo_sth:
cmds:
- echo {{.TEXT}}
```
### Calling another task
When a task has many dependencies, they are executed concurrently. This will
often result in a faster build pipeline. But in some situations you may need
to call other tasks serially. In this case, just use the following syntax:
```yml
version: '2'
tasks:
main-task:
cmds:
- task: task-to-be-called
- task: another-task
- echo "Both done"
task-to-be-called:
cmds:
- echo "Task to be called"
another-task:
cmds:
- echo "Another task"
```
Overriding variables in the called task is as simple as informing `vars`
attribute:
```yml
version: '2'
tasks:
main-task:
cmds:
- task: write-file
vars: {FILE: "hello.txt", CONTENT: "Hello!"}
- task: write-file
vars: {FILE: "world.txt", CONTENT: "World!"}
write-file:
cmds:
- echo "{{.CONTENT}}" > {{.FILE}}
```
The above syntax is also supported in `deps`.
### Prevent unnecessary work
If a task generates something, you can inform Task the source and generated
files, so Task will prevent to run them if not necessary.
```yml
version: '2'
tasks:
build:
deps: [js, css]
cmds:
- go build -v -i main.go
js:
cmds:
- minify -o public/script.js src/js
sources:
- src/js/**/*.js
generates:
- public/script.js
css:
cmds:
- minify -o public/style.css src/css
sources:
- src/css/**/*.css
generates:
- public/style.css
```
`sources` and `generates` can be files or file patterns. When both are given,
Task will compare the modification date/time of the files to determine if it's
necessary to run the task. If not, it will just print a message like
`Task "js" is up to date`.
If you prefer this check to be made by the content of the files, instead of
its timestamp, just set the `method` property to `checksum`.
You will probably want to ignore the `.task` folder in your `.gitignore` file
(It's there that Task stores the last checksum).
This feature is still experimental and can change until it's stable.
```yml
version: '2'
tasks:
build:
cmds:
- go build .
sources:
- ./*.go
generates:
- app{{exeExt}}
method: checksum
```
> TIP: method `none` skips any validation and always run the task.
Alternatively, you can inform a sequence of tests as `status`. If no error
is returned (exit status 0), the task is considered up-to-date:
```yml
version: '2'
tasks:
generate-files:
cmds:
- mkdir directory
- touch directory/file1.txt
- touch directory/file2.txt
# test existence of files
status:
- test -d directory
- test -f directory/file1.txt
- test -f directory/file2.txt
```
You can use `--force` or `-f` if you want to force a task to run even when
up-to-date.
Also, `task --status [tasks]...` will exit with non-zero exit code if any of
the tasks is not up-to-date.
### Variables
When doing interpolation of variables, Task will look for the below.
They are listed below in order of importance (e.g. most important first):
- Variables declared locally in the task
- Variables given while calling a task from another.
(See [Calling another task](#calling-another-task) above)
- Variables declared in the `vars:` option in the `Taskfile`
- Variables available in the `Taskvars.yml` file
- Environment variables
Example of sending parameters with environment variables:
```bash
$ TASK_VARIABLE=a-value task do-something
```
Since some shells don't support above syntax to set environment variables
(Windows) tasks also accepts a similar style when not in the beginning of
the command. Variables given in this form are only visible to the task called
right before.
```bash
$ task write-file FILE=file.txt "CONTENT=Hello, World!" print "MESSAGE=All done!"
```
Example of locally declared vars:
```yml
version: '2'
tasks:
print-var:
cmds:
echo "{{.VAR}}"
vars:
VAR: Hello!
```
Example of global vars in a `Taskfile.yml`:
```yml
version: '2'
vars:
GREETING: Hello from Taskfile!
tasks:
greet:
cmds:
- echo "{{.GREETING}}"
```
Example of `Taskvars.yml` file:
```yml
PROJECT_NAME: My Project
DEV_MODE: production
GIT_COMMIT: {sh: git log -n 1 --format=%h}
```
#### Variables expansion
Variables are expanded 2 times by default. You can change that by setting the
`expansions:` option. Change that will be necessary if you compose many
variables together:
```yml
version: '2'
expansions: 3
vars:
FOO: foo
BAR: bar
BAZ: baz
FOOBAR: "{{.FOO}}{{.BAR}}"
FOOBARBAZ: "{{.FOOBAR}}{{.BAZ}}"
tasks:
default:
cmds:
- echo "{{.FOOBARBAZ}}"
```
#### Dynamic variables
The below syntax (`sh:` prop in a variable) is considered a dynamic variable.
The value will be treated as a command and the output assigned. If there is one
or more trailing newlines, the last newline will be trimmed.
```yml
version: '2'
tasks:
build:
cmds:
- go build -ldflags="-X main.Version={{.GIT_COMMIT}}" main.go
vars:
GIT_COMMIT:
sh: git log -n 1 --format=%h
```
This works for all types of variables.
### Go's template engine
Task parse commands as [Go's template engine][gotemplate] before executing
them. Variables are accessible through dot syntax (`.VARNAME`).
All functions by the Go's [sprig lib](http://masterminds.github.io/sprig/)
are available. The following example gets the current date in a given format:
```yml
version: '2'
tasks:
print-date:
cmds:
- echo {{now | date "2006-01-02"}}
```
Task also adds the following functions:
- `OS`: Returns operating system. Possible values are "windows", "linux",
"darwin" (macOS) and "freebsd".
- `ARCH`: return the architecture Task was compiled to: "386", "amd64", "arm"
or "s390x".
- `splitLines`: Splits Unix (\n) and Windows (\r\n) styled newlines.
- `catLines`: Replaces Unix (\n) and Windows (\r\n) styled newlines with a space.
- `toSlash`: Does nothing on Unix, but on Windows converts a string from `\`
path format to `/`.
- `fromSlash`: Oposite of `toSlash`. Does nothing on Unix, but on Windows
converts a string from `\` path format to `/`.
- `exeExt`: Returns the right executable extension for the current OS
(`".exe"` for Windows, `""` for others).
Example:
```yml
version: '2'
tasks:
print-os:
cmds:
- echo '{{OS}} {{ARCH}}'
- echo '{{if eq OS "windows"}}windows-command{{else}}unix-command{{end}}'
# This will be path/to/file on Unix but path\to\file on Windows
- echo '{{fromSlash "path/to/file"}}'
enumerated-file:
vars:
CONTENT: |
foo
bar
cmds:
- |
cat << EOF > output.txt
{{range $i, $line := .CONTENT | splitLines -}}
{{printf "%3d" $i}}: {{$line}}
{{end}}EOF
```
### Help
Running `task --list` (or `task -l`) lists all tasks with a description.
The following taskfile:
```yml
version: '2'
tasks:
build:
desc: Build the go binary.
cmds:
- go build -v -i main.go
test:
desc: Run all the go tests.
cmds:
- go test -race ./...
js:
cmds:
- minify -o public/script.js src/js
css:
cmds:
- minify -o public/style.css src/css
```
would print the following output:
```bash
* build: Build the go binary.
* test: Run all the go tests.
```
## Silent mode
Silent mode disables echoing of commands before Task runs it.
For the following Taskfile:
```yml
version: '2'
tasks:
echo:
cmds:
- echo "Print something"
```
Normally this will be print:
```sh
echo "Print something"
Print something
```
With silent mode on, the below will be print instead:
```sh
Print something
```
There's three ways to enable silent mode:
* At command level:
```yml
version: '2'
tasks:
echo:
cmds:
- cmd: echo "Print something"
silent: true
```
* At task level:
```yml
version: '2'
tasks:
echo:
cmds:
- echo "Print something"
silent: true
```
* Or globally with `--silent` or `-s` flag
If you want to suppress stdout instead, just redirect a command to `/dev/null`:
```yml
version: '2'
tasks:
echo:
cmds:
- echo "This will print nothing" > /dev/null
```
## Watch tasks (experimental)
If you give a `--watch` or `-w` argument, task will watch for files changes
and run the task again. This requires the `sources` attribute to be given,
so task know which files to watch.
## 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).
## Alternative task runners
- YAML based:
- [goeuro/myke][myke]
- [dreadl0ck/zeus][zeus]
- [rliebz/tusk][tusk]
- Go based:
- [markbates/grift][grift]
- [magefile/mage][mage]
- Make based:
- [tj/mmake][mmake]
[make]: https://www.gnu.org/software/make/
[releases]: https://github.com/go-task/task/releases
[golang]: https://golang.org/
[gotemplate]: https://golang.org/pkg/text/template/
[myke]: https://github.com/goeuro/myke
[zeus]: https://github.com/dreadl0ck/zeus
[tusk]: https://github.com/rliebz/tusk
[grift]: https://github.com/markbates/grift
[mage]: https://github.com/magefile/mage
[mmake]: https://github.com/tj/mmake
[sh]: https://github.com/mvdan/sh
[minify]: https://github.com/tdewolff/minify/tree/master/cmd/minify
[examples]: https://github.com/go-task/examples
[snapcraft]: https://snapcraft.io/
[homebrew]: https://brew.sh/

32
RELEASING_TASK.md Normal file
View File

@@ -0,0 +1,32 @@
# Releasing Task
The release process of Task is done is done with the help of
[GoReleaser][goreleaser]. You can test the release process locally by calling
the `test-release` task of the Taskfile.
The Travis CI should release automatically when a new
Git tag is pushed to master, either for the artifact uploading (raw executables
and DEB and RPM packages)
# Homebrew
To release a new version on the [Homebrew tap][homebrewtap] edit the
[Formula/go-task.rb][gotaskrb] file, updating with the new version, download
URL and sha256.
# Snapcraft
The exception is the publishing of a new version of the
[snap package][snappackage]. This current require two steps after publishing
the binaries:
* Updating the current version on [snapcraft.yaml][snapcraftyaml];
* Moving either the `i386` and `amd64` new artifacts to the stable channel on
the [Snapscraft dashboard][snapcraftdashboard]
[goreleaser]: https://goreleaser.com/#continuous_integration
[homebrewtap]: https://github.com/go-task/homebrew-tap
[gotaskrb]: https://github.com/go-task/homebrew-tap/blob/master/Formula/go-task.rb
[snappackage]: https://github.com/go-task/snap
[snapcraftyaml]: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml#L2
[snapcraftdashboard]: https://dashboard.snapcraft.io/

91
TASKFILE_VERSIONS.md Normal file
View File

@@ -0,0 +1,91 @@
# Taskfile version
The Taskfile syntax and features changed with time. This document explains what
changed on each version and how to upgrade your Taskfile.
# What the Taskfile version mean
The Taskfile version follows the Task version. E.g. the change to Taskfile
version `2` means that Task `v2.0.0` should be release to support it.
The `version:` key on Taskfile accepts a semver string, so either `2`, `2.0` or
`2.0.0` is accepted. You you choose to use `2.0` Task will not enable future
`2.1` features, but if you choose to use `2`, than any `2.x.x` features will be
available, but not `3.0.0+`.
## Version 1
In the first version of the `Taskfile`, the `version:` key was not available,
because the tasks was in the root of the YAML document. Like this:
```yml
echo:
cmds:
- echo "Hello, World!"
```
The variable priority order was also different:
1. Call variables
2. Environment
3. Task variables
4. `Taskvars.yml` variables
## Version 2.0
At version 2, we introduced the `version:` key, to allow us to envolve Task
with new features without breaking existing Taskfiles. The new syntax is as
follows:
```yml
version: '2'
tasks:
echo:
cmds:
- echo "Hello, World!"
```
Version 2 allows you to write global variables directly in the Taskfile,
if you don't want to create a `Taskvars.yml`:
```yml
version: '2'
vars:
GREETING: Hello, World!
tasks:
greet:
cmds:
- echo "{{.GREETING}}"
```
The variable priority order changed to the following:
1. Task variables
2. Call variables
3. Taskfile variables
4. Taskvars file variables
5. Environment variables
A new global option was added to configure the number of variables expansions
(which default to 2):
```yml
version: '2'
expansions: 3
vars:
FOO: foo
BAR: bar
BAZ: baz
FOOBAR: "{{.FOO}}{{.BAR}}"
FOOBARBAZ: "{{.FOOBAR}}{{.BAZ}}"
tasks:
default:
cmds:
- echo "{{.FOOBARBAZ}}"
```

View File

@@ -1,86 +1,68 @@
version: '3'
includes:
docs:
taskfile: ./docs
dir: ./docs
vars:
GIT_COMMIT:
sh: git log -n 1 --format=%h
GO_PACKAGES:
sh: go list ./...
env:
CGO_ENABLED: '0'
version: '2'
tasks:
default:
cmds:
- task: lint
- task: test
# compiles current source code and make "task" executable available on
# $GOPATH/bin/task{.exe}
install:
desc: Installs Task
sources:
- './**/*.go'
cmds:
- go install -v -ldflags="-w -s -X main.version={{.GIT_COMMIT}}" ./cmd/task
mod:
desc: Downloads and tidy Go modules
dl-deps:
desc: Downloads cli dependencies
cmds:
- go mod download
- go mod tidy
- task: go-get
vars: {REPO: github.com/golang/lint/golint}
- task: go-get
vars: {REPO: github.com/asticode/go-astitodo/astitodo}
- task: go-get
vars: {REPO: github.com/golang/dep/cmd/dep}
- task: go-get
vars: {REPO: github.com/goreleaser/goreleaser}
- task: go-get
vars: {REPO: github.com/goreleaser/godownloader}
update-deps:
desc: Updates dependencies
cmds:
- dep ensure
- dep ensure -update
clean:
desc: Cleans temp files and folders
cmds:
- rm -rf dist/
- rm -rf tmp/
lint:
desc: Runs golangci-lint
sources:
- './**/*.go'
desc: Runs golint
cmds:
- golangci-lint run
sleepit:build:
desc: Builds the sleepit test helper
sources:
- ./cmd/sleepit/**/*.go
generates:
- ./bin/sleepit
cmds:
- go build -o ./bin/sleepit{{exeExt}} ./cmd/sleepit
sleepit:run:
desc: Builds the sleepit test helper
deps: [sleepit:build]
cmds:
- ./bin/sleepit {{.CLI_ARGS}}
- golint {{.GO_PACKAGES}}
silent: true
test:
desc: Runs test suite
deps: [install]
cmds:
- go test {{catLines .GO_PACKAGES}}
test:signals:
desc: Runs test suite with signals tests included
deps: [install, sleepit:build]
cmds:
- go test {{catLines .GO_PACKAGES}} -tags signals
- go test {{.GO_PACKAGES}}
test-release:
desc: Tests release process without publishing
cmds:
- goreleaser --snapshot --rm-dist
packages:
todo:
desc: Prints TODO comments present in the code
cmds:
- echo '{{.GO_PACKAGES}}'
- astitodo {{.GO_PACKAGES}}
silent: true
ci:
cmds:
- task: go-get
vars: {REPO: github.com/golang/lint/golint}
- task: lint
- task: test
go-get:
cmds:
- go get -u {{.REPO}}

15
Taskvars.yml Normal file
View File

@@ -0,0 +1,15 @@
GIT_COMMIT:
sh: git log -n 1 --format=%h
GO_PACKAGES:
.
./cmd/task
./internal/args
./internal/compiler
./internal/compiler/v1
./internal/compiler/v2
./internal/execext
./internal/logger
./internal/status
./internal/taskfile
./internal/templater

View File

@@ -1,64 +0,0 @@
package args
import (
"strings"
"github.com/go-task/task/v3/taskfile"
)
// ParseV3 parses command line argument: tasks and global variables
func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) {
var calls []taskfile.Call
var globals = &taskfile.Vars{}
for _, arg := range args {
if !strings.Contains(arg, "=") {
calls = append(calls, taskfile.Call{Task: arg})
continue
}
name, value := splitVar(arg)
globals.Set(name, taskfile.Var{Static: value})
}
if len(calls) == 0 {
calls = append(calls, taskfile.Call{Task: "default"})
}
return calls, globals
}
// ParseV2 parses command line argument: tasks and vars of each task
func ParseV2(args ...string) ([]taskfile.Call, *taskfile.Vars) {
var calls []taskfile.Call
var globals = &taskfile.Vars{}
for _, arg := range args {
if !strings.Contains(arg, "=") {
calls = append(calls, taskfile.Call{Task: arg})
continue
}
if len(calls) < 1 {
name, value := splitVar(arg)
globals.Set(name, taskfile.Var{Static: value})
} else {
if calls[len(calls)-1].Vars == nil {
calls[len(calls)-1].Vars = &taskfile.Vars{}
}
name, value := splitVar(arg)
calls[len(calls)-1].Vars.Set(name, taskfile.Var{Static: value})
}
}
if len(calls) == 0 {
calls = append(calls, taskfile.Call{Task: "default"})
}
return calls, globals
}
func splitVar(s string) (string, string) {
pair := strings.SplitN(s, "=", 2)
return pair[0], pair[1]
}

View File

@@ -1,209 +0,0 @@
package args_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3/args"
"github.com/go-task/task/v3/taskfile"
)
func TestArgsV3(t *testing.T) {
tests := []struct {
Args []string
ExpectedCalls []taskfile.Call
ExpectedGlobals *taskfile.Vars
}{
{
Args: []string{"task-a", "task-b", "task-c"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
},
},
{
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO", "BAR", "BAZ"},
Mapping: map[string]taskfile.Var{
"FOO": taskfile.Var{Static: "bar"},
"BAR": taskfile.Var{Static: "baz"},
"BAZ": taskfile.Var{Static: "foo"},
},
},
},
{
Args: []string{"task-a", "CONTENT=with some spaces"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"CONTENT"},
Mapping: map[string]taskfile.Var{
"CONTENT": taskfile.Var{Static: "with some spaces"},
},
},
},
{
Args: []string{"FOO=bar", "task-a", "task-b"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO"},
Mapping: map[string]taskfile.Var{
"FOO": {Static: "bar"},
},
},
},
{
Args: nil,
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{"FOO=bar", "BAR=baz"},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO", "BAR"},
Mapping: map[string]taskfile.Var{
"FOO": {Static: "bar"},
"BAR": {Static: "baz"},
},
},
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, globals := args.ParseV3(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls)
if test.ExpectedGlobals.Len() > 0 || globals.Len() > 0 {
assert.Equal(t, test.ExpectedGlobals, globals)
}
})
}
}
func TestArgsV2(t *testing.T) {
tests := []struct {
Args []string
ExpectedCalls []taskfile.Call
ExpectedGlobals *taskfile.Vars
}{
{
Args: []string{"task-a", "task-b", "task-c"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
},
},
{
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
ExpectedCalls: []taskfile.Call{
{
Task: "task-a",
Vars: &taskfile.Vars{
Keys: []string{"FOO"},
Mapping: map[string]taskfile.Var{
"FOO": taskfile.Var{Static: "bar"},
},
},
},
{Task: "task-b"},
{
Task: "task-c",
Vars: &taskfile.Vars{
Keys: []string{"BAR", "BAZ"},
Mapping: map[string]taskfile.Var{
"BAR": taskfile.Var{Static: "baz"},
"BAZ": taskfile.Var{Static: "foo"},
},
},
},
},
},
{
Args: []string{"task-a", "CONTENT=with some spaces"},
ExpectedCalls: []taskfile.Call{
{
Task: "task-a",
Vars: &taskfile.Vars{
Keys: []string{"CONTENT"},
Mapping: map[string]taskfile.Var{
"CONTENT": taskfile.Var{Static: "with some spaces"},
},
},
},
},
},
{
Args: []string{"FOO=bar", "task-a", "task-b"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO"},
Mapping: map[string]taskfile.Var{
"FOO": {Static: "bar"},
},
},
},
{
Args: nil,
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
},
{
Args: []string{"FOO=bar", "BAR=baz"},
ExpectedCalls: []taskfile.Call{
{Task: "default"},
},
ExpectedGlobals: &taskfile.Vars{
Keys: []string{"FOO", "BAR"},
Mapping: map[string]taskfile.Var{
"FOO": {Static: "bar"},
"BAR": {Static: "baz"},
},
},
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, globals := args.ParseV2(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls)
if test.ExpectedGlobals.Len() > 0 || globals.Len() > 0 {
assert.Equal(t, test.ExpectedGlobals, globals)
}
})
}
}

View File

View File

@@ -1,173 +0,0 @@
// This code is released under the MIT License
// Copyright (c) 2020 Marco Molteni and the timeit contributors.
package main
import (
"flag"
"fmt"
"os"
"os/signal"
"time"
)
const usage = `sleepit: sleep for the specified duration, optionally handling signals
When the line "sleepit: ready" is printed, it means that it is safe to send signals to it
Usage: sleepit <command> [<args>]
Commands
default Use default action: on reception of SIGINT terminate abruptly
handle Handle signals: on reception of SIGINT perform cleanup before exiting
version Show the sleepit version`
var (
// Filled by the linker.
fullVersion = "unknown" // example: v0.0.9-8-g941583d027-dirty
)
func main() {
os.Exit(run(os.Args[1:]))
}
func run(args []string) int {
if len(args) < 1 {
fmt.Fprintln(os.Stderr, usage)
return 2
}
defaultCmd := flag.NewFlagSet("default", flag.ExitOnError)
defaultSleep := defaultCmd.Duration("sleep", 5*time.Second, "Sleep duration")
handleCmd := flag.NewFlagSet("handle", flag.ExitOnError)
handleSleep := handleCmd.Duration("sleep", 5*time.Second, "Sleep duration")
handleCleanup := handleCmd.Duration("cleanup", 5*time.Second, "Cleanup duration")
handleTermAfter := handleCmd.Int("term-after", 0,
"Terminate immediately after `N` signals.\n"+
"Default is to terminate only when the cleanup phase has completed.")
versionCmd := flag.NewFlagSet("version", flag.ExitOnError)
switch args[0] {
case "default":
_ = defaultCmd.Parse(args[1:])
if len(defaultCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "default: unexpected arguments: %v\n", defaultCmd.Args())
return 2
}
return supervisor(*defaultSleep, 0, 0, nil)
case "handle":
_ = handleCmd.Parse(args[1:])
if *handleTermAfter == 1 {
fmt.Fprintf(os.Stderr, "handle: term-after cannot be 1\n")
return 2
}
if len(handleCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "handle: unexpected arguments: %v\n", handleCmd.Args())
return 2
}
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt) // Ctrl-C -> SIGINT
return supervisor(*handleSleep, *handleCleanup, *handleTermAfter, sigCh)
case "version":
_ = versionCmd.Parse(args[1:])
if len(versionCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "version: unexpected arguments: %v\n", versionCmd.Args())
return 2
}
fmt.Printf("sleepit version %s\n", fullVersion)
return 0
default:
fmt.Fprintln(os.Stderr, usage)
return 2
}
}
func supervisor(
sleep time.Duration,
cleanup time.Duration,
termAfter int,
sigCh <-chan os.Signal,
) int {
fmt.Printf("sleepit: ready\n")
fmt.Printf("sleepit: PID=%d sleep=%v cleanup=%v\n",
os.Getpid(), sleep, cleanup)
cancelWork := make(chan struct{})
workerDone := worker(cancelWork, sleep, "work")
cancelCleaner := make(chan struct{})
var cleanerDone <-chan struct{}
sigCount := 0
for {
select {
case sig := <-sigCh:
sigCount++
fmt.Printf("sleepit: got signal=%s count=%d\n", sig, sigCount)
if sigCount == 1 {
// since `cancelWork` is unbuffered, sending will be synchronous:
// we are ensured that the worker has terminated before starting cleanup.
// This is important in some real-life situations.
cancelWork <- struct{}{}
cleanerDone = worker(cancelCleaner, cleanup, "cleanup")
}
if sigCount == termAfter {
cancelCleaner <- struct{}{}
return 4
}
case <-workerDone:
return 0
case <-cleanerDone:
return 3
}
}
}
// Start a worker goroutine and return immediately a `workerDone` channel.
// The goroutine will prepend its prints with the prefix `name`.
// The goroutine will simulate some work and will terminate when one of the following
// conditions happens:
// 1. When `howlong` is elapsed. This case will be signaled on the `workerDone` channel.
// 2. When something happens on channel `canceled`. Note that this simulates real-life,
// so cancellation is not instantaneous: if the caller wants a synchronous cancel,
// it should send a message; if instead it wants an asynchronous cancel, it should
// close the channel.
func worker(
canceled <-chan struct{},
howlong time.Duration,
name string,
) <-chan struct{} {
workerDone := make(chan struct{})
deadline := time.Now().Add(howlong)
go func() {
fmt.Printf("sleepit: %s started\n", name)
for {
select {
case <-canceled:
fmt.Printf("sleepit: %s canceled\n", name)
return
default:
if doSomeWork(deadline) {
fmt.Printf("sleepit: %s done\n", name) // <== NOTE THIS LINE
workerDone <- struct{}{}
return
}
}
}
}()
return workerDone
}
// Do some work and then return, so that the caller can decide wether to continue or not.
// Return true when all work is done.
func doSomeWork(deadline time.Time) bool {
if time.Now().After(deadline) {
return true
}
timeout := 100 * time.Millisecond
time.Sleep(timeout)
return false
}

View File

@@ -2,27 +2,22 @@ package main
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"runtime/debug"
"strings"
"os/signal"
"syscall"
"github.com/go-task/task"
"github.com/go-task/task/internal/args"
"github.com/spf13/pflag"
"mvdan.cc/sh/v3/syntax"
"github.com/go-task/task/v3"
"github.com/go-task/task/v3/args"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/taskfile"
)
var (
version = ""
version = "master"
)
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] [task...]
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 specified.
@@ -31,14 +26,12 @@ Example: 'task hello' with the following 'Taskfile.yml' file will generate an
'output.txt' file with the content "hello".
'''
version: '3'
tasks:
hello:
cmds:
- echo "I am going to write a file named 'output.txt' now."
- echo "hello" > output.txt
generates:
- output.txt
hello:
cmds:
- echo "I am going to write a file named 'output.txt' now."
- echo "hello" > output.txt
generates:
- output.txt
'''
Options:
@@ -55,56 +48,29 @@ func main() {
var (
versionFlag bool
helpFlag bool
init bool
list bool
listAll bool
status bool
force bool
watch bool
verbose bool
silent bool
dry bool
summary bool
exitCode bool
parallel bool
concurrency int
dir string
entrypoint string
output taskfile.Output
color bool
)
pflag.BoolVar(&versionFlag, "version", false, "show Task version")
pflag.BoolVarP(&helpFlag, "help", "h", false, "shows Task usage")
pflag.BoolVarP(&init, "init", "i", false, "creates a new Taskfile.yaml in the current folder")
pflag.BoolVarP(&init, "init", "i", false, "creates a new Taskfile.yml in the current folder")
pflag.BoolVarP(&list, "list", "l", false, "lists tasks with description of current Taskfile")
pflag.BoolVarP(&listAll, "list-all", "a", false, "lists tasks with or without a description")
pflag.BoolVar(&status, "status", false, "exits with non-zero exit code if any of the given tasks is not up-to-date")
pflag.BoolVarP(&force, "force", "f", false, "forces execution even when the task is up-to-date")
pflag.BoolVarP(&watch, "watch", "w", false, "enables watch of the given task")
pflag.BoolVarP(&verbose, "verbose", "v", false, "enables verbose mode")
pflag.BoolVarP(&silent, "silent", "s", false, "disables echoing")
pflag.BoolVarP(&parallel, "parallel", "p", false, "executes tasks provided on command line in parallel")
pflag.BoolVarP(&dry, "dry", "n", false, "compiles and prints tasks in the order that they would be run, without executing them")
pflag.BoolVar(&summary, "summary", false, "show summary about a task")
pflag.BoolVarP(&exitCode, "exit-code", "x", false, "pass-through the exit code of the task command")
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(&output.Name, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
pflag.StringVar(&output.Group.Begin, "output-group-begin", "", "message template to print before a task's grouped output")
pflag.StringVar(&output.Group.End, "output-group-end", "", "message template to print after a task's grouped 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()
if versionFlag {
fmt.Printf("Task version: %s\n", getVersion())
return
}
if helpFlag {
pflag.Usage()
log.Printf("Task version: %s\n", version)
return
}
@@ -119,149 +85,59 @@ func main() {
return
}
if dir != "" && entrypoint != "" {
log.Fatal("task: You can't set both --dir and --taskfile")
return
}
if entrypoint != "" {
dir = filepath.Dir(entrypoint)
entrypoint = filepath.Base(entrypoint)
}
if output.Name != "group" {
if output.Group.Begin != "" {
log.Fatal("task: You can't set --output-group-begin without --output=group")
return
}
if output.Group.End != "" {
log.Fatal("task: You can't set --output-group-end without --output=group")
return
}
}
e := task.Executor{
Force: force,
Watch: watch,
Verbose: verbose,
Silent: silent,
Dir: dir,
Dry: dry,
Entrypoint: entrypoint,
Summary: summary,
Parallel: parallel,
Color: color,
Concurrency: concurrency,
Force: force,
Watch: watch,
Verbose: verbose,
Silent: silent,
Dir: dir,
Context: getSignalContext(),
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
OutputStyle: output,
}
if (list || listAll) && silent {
e.ListTaskNames(listAll)
return
}
if err := e.Setup(); err != nil {
log.Fatal(err)
}
v, err := e.Taskfile.ParsedVersion()
if err != nil {
log.Fatal(err)
return
}
if list {
e.ListTasksWithDesc()
e.PrintTasksHelp()
return
}
if listAll {
e.ListAllTasks()
return
arguments := pflag.Args()
if len(arguments) == 0 {
log.Println("task: No argument given, trying default task")
arguments = []string{"default"}
}
var (
calls []taskfile.Call
globals *taskfile.Vars
)
tasksAndVars, cliArgs, err := getArgs()
calls, err := args.Parse(arguments...)
if err != nil {
log.Fatal(err)
}
if v >= 3.0 {
calls, globals = args.ParseV3(tasksAndVars...)
} else {
calls, globals = args.ParseV2(tasksAndVars...)
}
globals.Set("CLI_ARGS", taskfile.Var{Static: cliArgs})
e.Taskfile.Vars.Merge(globals)
if !watch {
e.InterceptInterruptSignals()
}
ctx := context.Background()
if status {
if err := e.Status(ctx, calls...); err != nil {
if err = e.Status(calls...); err != nil {
log.Fatal(err)
}
return
}
if err := e.Run(ctx, calls...); err != nil {
e.Logger.Errf(logger.Red, "%v", err)
if exitCode {
if err, ok := err.(*task.TaskRunError); ok {
os.Exit(err.ExitCode())
}
}
os.Exit(1)
if err := e.Run(calls...); err != nil {
log.Fatal(err)
}
}
func getArgs() ([]string, string, error) {
var (
args = pflag.Args()
doubleDashPos = pflag.CommandLine.ArgsLenAtDash()
)
if doubleDashPos == -1 {
return args, "", nil
}
var quotedCliArgs []string
for _, arg := range args[doubleDashPos:] {
quotedCliArg, err := syntax.Quote(arg, syntax.LangBash)
if err != nil {
return nil, "", err
}
quotedCliArgs = append(quotedCliArgs, quotedCliArg)
}
return args[:doubleDashPos], strings.Join(quotedCliArgs, " "), nil
}
func 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
func getSignalContext() context.Context {
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, os.Kill, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background())
go func() {
sig := <-ch
log.Printf("task: signal received: %s", sig)
cancel()
}()
return ctx
}

View File

@@ -1,26 +0,0 @@
#!/bin/bash
GO_TASK_PROGNAME=task
_go_task_completion()
{
local cur
_get_comp_words_by_ref -n : cur
case "$cur" in
--*)
local options
options="$(_parse_help task)"
mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur")
;;
*)
local tasks
tasks="$($GO_TASK_PROGNAME --list-all 2> /dev/null | awk 'NR>1 { sub(/:$/,"",$2); print $2 }')"
mapfile -t COMPREPLY < <(compgen -W "$tasks" -- "$cur")
;;
esac
__ltrim_colon_completions "$cur"
}
complete -F _go_task_completion $GO_TASK_PROGNAME

View File

@@ -1,29 +0,0 @@
set GO_TASK_PROGNAME task
function __task_get_tasks --description "Prints all available tasks with their description"
set -l output ($GO_TASK_PROGNAME --list-all | sed '1d; s/\* \(.*\):\s*\(.*\)/\1\t\2/' | string split0)
if test $output
echo $output
end
end
complete -c $GO_TASK_PROGNAME -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
specified.' -xa "(__task_get_tasks)"
complete -c $GO_TASK_PROGNAME -s c -l color -d 'colored output (default true)'
complete -c $GO_TASK_PROGNAME -s d -l dir -d 'sets directory of execution'
complete -c $GO_TASK_PROGNAME -l dry -d 'compiles and prints tasks in the order that they would be run, without executing them'
complete -c $GO_TASK_PROGNAME -s f -l force -d 'forces execution even when the task is up-to-date'
complete -c $GO_TASK_PROGNAME -s h -l help -d 'shows Task usage'
complete -c $GO_TASK_PROGNAME -s i -l init -d 'creates a new Taskfile.yml in the current folder'
complete -c $GO_TASK_PROGNAME -s l -l list -d 'lists tasks with description of current Taskfile'
complete -c $GO_TASK_PROGNAME -s o -l output -d 'sets output style: [interleaved|group|prefixed]' -xa "interleaved group prefixed"
complete -c $GO_TASK_PROGNAME -s p -l parallel -d 'executes tasks provided on command line in parallel'
complete -c $GO_TASK_PROGNAME -s s -l silent -d 'disables echoing'
complete -c $GO_TASK_PROGNAME -l status -d 'exits with non-zero exit code if any of the given tasks is not up-to-date'
complete -c $GO_TASK_PROGNAME -l summary -d 'show summary about a task'
complete -c $GO_TASK_PROGNAME -s t -l taskfile -d 'choose which Taskfile to run. Defaults to "Taskfile.yml"'
complete -c $GO_TASK_PROGNAME -s v -l verbose -d 'enables verbose mode'
complete -c $GO_TASK_PROGNAME -l version -d 'show Task version'
complete -c $GO_TASK_PROGNAME -s w -l watch -d 'enables watch of the given task'

View File

@@ -1,10 +0,0 @@
$scriptBlock = {
param($commandName, $wordToComplete, $cursorPosition)
$curReg = "task{.exe}? (.*?)$"
$startsWith = $wordToComplete | Select-String $curReg -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value }
$reg = "\* ($startsWith.+?):"
$listOutput = $(task -l)
$listOutput | Select-String $reg -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value + " " }
}
Register-ArgumentCompleter -Native -CommandName task -ScriptBlock $scriptBlock

View File

@@ -1,60 +0,0 @@
#compdef task
local context state state_descr line
typeset -A opt_args
# Listing commands from Taskfile.yml
function __task_list() {
local -a scripts cmd
local -i enabled=0
local taskfile item task desc
cmd=(task)
taskfile="${(v)opt_args[(i)-t|--taskfile]}"
if [[ -n "$taskfile" && -f "$taskfile" ]]; then
enabled=1
cmd+=(--taskfile "$taskfile")
else
for taskfile in Taskfile{,.dist}.{yaml,yml}; do
if [[ -f "$taskfile" ]]; then
enabled=1
break
fi
done
fi
(( enabled )) || return 0
scripts=()
for item in "${(@)${(f)$("${cmd[@]}" --list)}[2,-1]#\* }"; do
task="${item%%:[[:space:]]*}"
desc="${item##[^[:space:]]##[[:space:]]##}"
scripts+=( "${task//:/\\:}:$desc" )
done
_describe 'Task to run' scripts
}
_arguments \
'(-C --concurrency)'{-C,--concurrency}'[limit number of concurrent tasks]: ' \
'(-p --parallel)'{-p,--parallel}'[run command-line tasks in parallel]' \
'(-f --force)'{-f,--force}'[run even if task is up-to-date]' \
'(-c --color)'{-c,--color}'[colored output]' \
'(-d --dir)'{-d,--dir}'[dir to run in]:execution dir:_dirs' \
'(--dry)--dry[dry-run mode, compile and print tasks only]' \
'(-o --output)'{-o,--output}'[set output style]:style:(interleaved group prefixed)' \
'(--output-group-begin)--output-group-begin[message template before grouped output]:template text: ' \
'(--output-group-end)--output-group-end[message template after grouped output]:template text: ' \
'(-s --silent)'{-s,--silent}'[disable echoing]' \
'(--status)--status[exit non-zero if supplied tasks not up-to-date]' \
'(--summary)--summary[show summary\: field from tasks instead of running them]' \
'(-t --taskfile)'{-t,--taskfile}'[specify a different taskfile]:taskfile:_files' \
'(-v --verbose)'{-v,--verbose}'[verbose mode]' \
'(-w --watch)'{-w,--watch}'[watch-mode for given tasks, re-run when inputs change]' \
+ '(operation)' \
{-l,--list}'[list describable tasks]' \
{-a,--list-all}'[list all tasks]' \
{-i,--init}'[create new Taskfile.yaml]' \
'(-*)'{-h,--help}'[show help]' \
'(-*)--version[show version and exit]' \
'*: :__task_list'

View File

@@ -1,25 +0,0 @@
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() {}

20
docs/.gitignore vendored
View File

@@ -1,20 +0,0 @@
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

View File

@@ -1,29 +0,0 @@
version: '3'
tasks:
setup:
desc: Setup Docusaurus locally
cmds:
- yarn install
start:
desc: Start website
vars:
PORT: '{{default "3001" .PORT}}'
cmds:
- npx docusaurus start --no-open --port={{.PORT}}
build:
desc: Build website
cmds:
- npx docusaurus build
clean:
desc: Clean temp directories
cmds:
- rm -rf ./build
deploy:
desc: Build and deploy Docusaurus. Requires GIT_USER and GIT_PASS envs to be previous set
cmds:
- npx docusaurus deploy

View File

@@ -1,3 +0,0 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};

View File

@@ -1,5 +0,0 @@
andreynering:
name: Andrey Nering
title: Maintainer of Task
url: https://github.com/andreynering
image_url: https://github.com/andreynering.png

View File

@@ -1,219 +0,0 @@
---
slug: /api/
sidebar_position: 4
---
# API Reference
## CLI
Task command line tool has the following syntax:
```bash
task [--flags] [tasks...] [-- CLI_ARGS...]
```
:::tip
If `--` is given, all remaning arguments will be assigned to a special `CLI_ARGS`
variable
:::
| Short | Flag | Type | Default | Description |
| - | - | - | - | - |
| `-c` | `--color` | `bool` | `true` | Colored output. Enabled by default. Set flag to `false` or use `NO_COLOR=1` to disable. |
| `-C` | `--concurrency` | `int` | `0` | Limit number tasks to run concurrently. Zero means unlimited. |
| `-d` | `--dir` | `string` | Working directory | Sets directory of execution. |
| `-n` | `--dry` | `bool` | `false` | Compiles and prints tasks in the order that they would be run, without executing them. |
| `-x` | `--exit-code` | `bool` | `false` | Pass-through the exit code of the task command. |
| `-f` | `--force` | `bool` | `false` | Forces execution even when the task is up-to-date. |
| `-h` | `--help` | `bool` | `false` | Shows Task usage. |
| `-i` | `--init` | `bool` | `false` | Creates a new Taskfile.yaml in the current folder. |
| `-l` | `--list` | `bool` | `false` | Lists tasks with description of current Taskfile. |
| `-a` | `--list-all` | `bool` | `false` | Lists tasks with or without a description. |
| `-o` | `--output` | `string` | Default set in the Taskfile or `intervealed` | Sets output style: [`interleaved`/`group`/`prefixed`]. |
| | `--output-group-begin` | `string` | | Message template to print before a task's grouped output. |
| | `--output-group-end ` | `string` | | Message template to print after a task's grouped output. |
| `-p` | `--parallel` | `bool` | `false` | Executes tasks provided on command line in parallel. |
| `-s` | `--silent` | `bool` | `false` | Disables echoing. |
| | `--status` | `bool` | `false` | Exits with non-zero exit code if any of the given tasks is not up-to-date. |
| | `--summary` | `bool` | `false` | Show summary about a task. |
| `-t` | `--taskfile` | `string` | `Taskfile.yml` or `Taskfile.yaml` | |
| `-v` | `--verbose` | `bool` | `false` | Enables verbose mode. |
| | `--version` | `bool` | `false` | Show Task version. |
| `-w` | `--watch` | `bool` | `false` | Enables watch of the given task. |
## ENV
Some environment variables can be overriden to adjust Task behavior.
| ENV | Default | Description |
| - | - | - |
| `TASK_TEMP_DIR` | `.task` | Location of the temp dir. Can relative to the project like `tmp/task` or absolute like `/tmp/.task` or `~/.task`. |
| `TASK_COLOR_RESET` | `0` | Color used for white. |
| `TASK_COLOR_BLUE` | `34` | Color used for blue. |
| `TASK_COLOR_GREEN` | `32` | Color used for green. |
| `TASK_COLOR_CYAN` | `36` | Color used for cyan. |
| `TASK_COLOR_YELLOW` | `33` | Color used for yellow. |
| `TASK_COLOR_MAGENTA` | `35` | Color used for magenta. |
| `TASK_COLOR_RED` | `31` | Color used for red. |
## Schema
### Taskfile
| Attribute | Type | Default | Description |
| - | - | - | - |
| `version` | `string` | | Version of the Taskfile. The current version is `3`. |
| `includes` | [`map[string]Include`](#include) | | Additional Taskfiles to be included. |
| `output` | `string` | `interleaved` | Output mode. Available options: `interleaved`, `group` and `prefixed`. |
| `method` | `string` | `checksum` | Default method in this Taskfile. Can be overriden in a task by task basis. Available options: `checksum`, `timestamp` and `none`. |
| `silent` | `bool` | `false` | Default "silent" options for this Taskfile. If `false`, can be overidden with `true` in a task by task basis. |
| `run` | `string` | `always` | Default "run" option for this Taskfile. Available options: `always`, `once` and `when_changed`. |
| `vars` | [`map[string]Variable`](#variable) | | Global variables. |
| `env` | [`map[string]Variable`](#variable) | | Global environment. |
| `dotenv` | `[]string` | | A list of `.env` file paths to be parsed. |
| `tasks` | [`map[string]Task`](#task) | | The task definitions. |
### Include
| Attribute | Type | Default | Description |
| - | - | - | - |
| `taskfile` | `string` | | The path for the Taskfile or directory to be included. If a directory, Task will look for files named `Taskfile.yml` or `Taskfile.yaml` inside that directory. |
| `dir` | `string` | The parent Taskfile directory | The working directory of the included tasks when run. |
| `optional` | `bool` | `false` | If `true`, no errors will be thrown if the specified file does not exist. |
:::info
Informing only a string like below is equivalent to setting that value to the `taskfile` attribute.
```yaml
includes:
foo: ./path
```
:::
### Task
| Attribute | Type | Default | Description |
| - | - | - | - |
| `desc` | `string` | | A short description of the task. This is listed when calling `task --list`. |
| `summary` | `string` | | A longer description of the task. This is listed when calling `task --summary [task]`. |
| `sources` | `[]string` | | List of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs. |
| `dir` | `string` | | The current directory which this task should run. |
| `method` | `string` | `checksum` | Method used by this task. Default to the one declared globally or `checksum`. Available options: `checksum`, `timestamp` and `none` |
| `silent` | `bool` | `false` | Skips some output for this task. Note that STDOUT and STDERR of the commands will still be redirected. |
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
| `prefix` | `string` | | Allows to override the prefix print before the STDOUT. Only relevant when using the `prefixed` output mode. |
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the commands. |
| `generates` | `[]string` | | List of files meant to be generated by this task. Relevant for `timestamp` method. Can be file paths or star globs. |
| `status` | `[]string` | | List of commands to check if this task should run. The task is skipped otherwise. This overrides `method`, `sources` and `generates`. |
| `preconditions` | [`[]Precondition`](#precondition) | | List of commands to check if this task should run. The task errors otherwise. |
| `vars` | [`map[string]Variable`](#variable) | | Task variables. |
| `env` | [`map[string]Variable`](#variable) | | Task environment. |
| `deps` | [`[]Dependency`](#dependency) | | List of dependencies of this task. |
| `cmds` | [`[]Command`](#command) | | List of commands to be executed. |
:::info
These alternative syntaxes are available. They will set the given values to
`cmds` and everything else will be set to their default values:
```yaml
tasks:
foo: echo "foo"
foobar:
- echo "foo"
- echo "bar"
```
:::
### Dependency
| Attribute | Type | Default | Description |
| - | - | - | - |
| `task` | `string` | | The task to be execute as a dependency. |
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to this task. |
:::tip
If you don't want to set additional variables, it's enough to declare the
dependency as a list of strings (they will be assigned to `task`):
```yaml
tasks:
foo:
deps: [foo, bar]
```
:::
### Command
| Attribute | Type | Default | Description |
| - | - | - | - |
| `cmd` | `string` | | The shell command to be executed. |
| `defer` | `string` | | Alternative to `cmd`, but schedules the command to be executed at the end of this task instead of immediately. This cannot be used together with `cmd`. |
| `silent` | `bool` | `false` | Skips some output for this command. Note that STDOUT and STDERR of the commands will still be redirected. |
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
| `task` | `string` | | Set this to trigger execution of another task instead of running a command. This cannot be set together with `cmd`. |
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the referenced task. Only relevant when setting `task` instead of `cmd`. |
:::info
If given as a a string, the value will be assigned to `cmd`:
```yaml
tasks:
foo:
cmds:
- echo "foo"
- echo "bar"
```
:::
### Variable
| Attribute | Type | Default | Description |
| - | - | - | - |
| *itself* | `string` | | A static value that will be set to the variable. |
| `sh` | `string` | | A shell command. The output (`STDOUT`) will be assigned to the variable. |
:::info
Static and dynamic variables have different syntaxes, like below:
```yaml
vars:
STATIC: static
DYNAMIC:
sh: echo "dynamic"
```
:::
### Precondition
| Attribute | Type | Default | Description |
| - | - | - | - |
| `sh` | `string` | | Command to be executed. If a non-zero exit code is returned, the task errors without executing its commands. |
| `msg` | `string` | | Optional message to print if the precondition isn't met. |
:::tip
If you don't want to set a different message, you can declare a precondition
like this and the value will be assigned to `sh`:
```yaml
tasks:
foo:
precondition: test -f Taskfile.yml
```
:::

View File

@@ -1,539 +0,0 @@
---
slug: /changelog/
sidebar_position: 6
---
# Changelog
## v3.14.0 - 2022-07-08
- Add ability to override the `.task` directory location with the
`TASK_TEMP_DIR` environment variable.
- Allow to override Task colors using environment variables:
`TASK_COLOR_RESET`, `TASK_COLOR_BLUE`, `TASK_COLOR_GREEN`,
`TASK_COLOR_CYAN`, `TASK_COLOR_YELLOW`, `TASK_COLOR_MAGENTA`
and `TASK_COLOR_RED`
([#568](https://github.com/go-task/task/pull/568), [#792](https://github.com/go-task/task/pull/792)).
- Fixed bug when using the `output: group` mode where STDOUT and STDERR were
being print in separated blocks instead of in the right order
([#779](https://github.com/go-task/task/issues/779)).
- Starting on this release, ARM architecture binaries are been released to Snap
as well
([#795](https://github.com/go-task/task/issues/795)).
- i386 binaries won't be available anymore on Snap because Ubuntu removed the support
for this architecture.
- Upgrade mvdan.cc/sh, which fixes a bug with associative arrays
([#785](https://github.com/go-task/task/issues/785), [mvdan/sh#884](https://github.com/mvdan/sh/issues/884), [mvdan/sh#893](https://github.com/mvdan/sh/pull/893)).
## v3.13.0 - 2022-06-13
- Added `-n` as an alias to `--dry`
([#776](https://github.com/go-task/task/issues/776), [#777](https://github.com/go-task/task/pull/777)).
- Fix behavior of interrupt (SIGINT, SIGTERM) signals. Task will now give time
for the processes running to do cleanup work
([#458](https://github.com/go-task/task/issues/458), [#479](https://github.com/go-task/task/pull/479), [#728](https://github.com/go-task/task/issues/728), [#769](https://github.com/go-task/task/pull/769)).
- Add new `--exit-code` (`-x`) flag that will pass-through the exit form the
command being ran
([#755](https://github.com/go-task/task/pull/755)).
## v3.12.1 - 2022-05-10
- Fixed bug where, on Windows, variables were ending with `\r` because we were
only removing the final `\n` but not `\r\n`
([#717](https://github.com/go-task/task/issues/717)).
## v3.12.0 - 2022-03-31
- The `--list` and `--list-all` flags can now be combined with the `--silent`
flag to print the task names only, without their description
([#691](https://github.com/go-task/task/pull/691)).
- Added support for multi-level inclusion of Taskfiles. This means that
included Taskfiles can also include other Taskfiles. Before this was limited
to one level
([#390](https://github.com/go-task/task/issues/390), [#623](https://github.com/go-task/task/discussions/623), [#656](https://github.com/go-task/task/pull/656)).
- Add ability to specify vars when including a Taskfile.
[Check out the documentation](https://taskfile.dev/#/usage?id=vars-of-included-taskfiles)
for more information.
([#677](https://github.com/go-task/task/pull/677)).
## v3.11.0 - 2022-02-19
- Task now supports printing begin and end messages when using the `group`
output mode, useful for grouping tasks in CI systems.
[Check out the documentation](http://taskfile.dev/#/usage?id=output-syntax) for more information
([#647](https://github.com/go-task/task/issues/647), [#651](https://github.com/go-task/task/pull/651)).
- Add `Taskfile.dist.yml` and `Taskfile.dist.yaml` to the supported file
name list. [Check out the documentation](https://taskfile.dev/#/usage?id=supported-file-names) for more information
([#498](https://github.com/go-task/task/issues/498), [#666](https://github.com/go-task/task/pull/666)).
## v3.10.0 - 2022-01-04
- A new `--list-all` (alias `-a`) flag is now available. It's similar to the
exiting `--list` (`-l`) but prints all tasks, even those without a
description
([#383](https://github.com/go-task/task/issues/383), [#401](https://github.com/go-task/task/pull/401)).
- It's now possible to schedule cleanup commands to run once a task finishes
with the `defer:` keyword
([Documentation](https://taskfile.dev/#/usage?id=doing-task-cleanup-with-defer), [#475](https://github.com/go-task/task/issues/475), [#626](https://github.com/go-task/task/pull/626)).
- Remove long deprecated and undocumented `$` variable prefix and `^` command
prefix
([#642](https://github.com/go-task/task/issues/642), [#644](https://github.com/go-task/task/issues/644), [#645](https://github.com/go-task/task/pull/645)).
- Add support for `.yaml` extension (as an alternative to `.yml`).
This was requested multiple times throughout the years. Enjoy!
([#183](https://github.com/go-task/task/issues/183), [#184](https://github.com/go-task/task/pull/184), [#369](https://github.com/go-task/task/issues/369), [#584](https://github.com/go-task/task/issues/584), [#621](https://github.com/go-task/task/pull/621)).
- Fixed error when computing a variable when the task directory do not exist
yet
([#481](https://github.com/go-task/task/issues/481), [#579](https://github.com/go-task/task/pull/579)).
## v3.9.2 - 2021-12-02
- Upgrade [mvdan/sh](https://github.com/mvdan/sh) which contains a fix a for
a important regression on Windows
([#619](https://github.com/go-task/task/issues/619), [mvdan/sh#768](https://github.com/mvdan/sh/issues/768), [mvdan/sh#769](https://github.com/mvdan/sh/pull/769)).
## v3.9.1 - 2021-11-28
- Add logging in verbose mode for when a task starts and finishes
([#533](https://github.com/go-task/task/issues/533), [#588](https://github.com/go-task/task/pull/588)).
- Fix an issue with preconditions and context errors
([#597](https://github.com/go-task/task/issues/597), [#598](https://github.com/go-task/task/pull/598)).
- Quote each `{{.CLI_ARGS}}` argument to prevent one with spaces to become many
([#613](https://github.com/go-task/task/pull/613)).
- Fix nil pointer when `cmd:` was left empty
([#612](https://github.com/go-task/task/issues/612), [#614](https://github.com/go-task/task/pull/614)).
- Upgrade [mvdan/sh](https://github.com/mvdan/sh) which contains two
relevant fixes:
- Fix quote of empty strings in `shellQuote`
([#609](https://github.com/go-task/task/issues/609), [mvdan/sh#763](https://github.com/mvdan/sh/issues/763)).
- Fix issue of wrong environment variable being picked when there's another
very similar one
([#586](https://github.com/go-task/task/issues/586), [mvdan/sh#745](https://github.com/mvdan/sh/pull/745)).
- Install shell completions automatically when installing via Homebrew
([#264](https://github.com/go-task/task/issues/264), [#592](https://github.com/go-task/task/pull/592), [go-task/homebrew-tap#2](https://github.com/go-task/homebrew-tap/pull/2)).
## v3.9.0 - 2021-10-02
- A new `shellQuote` function was added to the template system
(`{{shellQuote "a string"}}`) to ensure a string is safe for use in shell
([mvdan/sh#727](https://github.com/mvdan/sh/pull/727), [mvdan/sh#737](https://github.com/mvdan/sh/pull/737), [Documentation](https://pkg.go.dev/mvdan.cc/sh/v3@v3.4.0/syntax#Quote))
- In this version [mvdan.cc/sh](https://github.com/mvdan/sh) was upgraded
with some small fixes and features
- The `read -p` flag is now supported
([#314](https://github.com/go-task/task/issues/314), [mvdan/sh#551](https://github.com/mvdan/sh/issues/551), [mvdan/sh#772](https://github.com/mvdan/sh/pull/722))
- The `pwd -P` and `pwd -L` flags are now supported
([#553](https://github.com/go-task/task/issues/553), [mvdan/sh#724](https://github.com/mvdan/sh/issues/724), [mvdan/sh#728](https://github.com/mvdan/sh/pull/728))
- The `$GID` environment variable is now correctly being set
([#561](https://github.com/go-task/task/issues/561), [mvdan/sh#723](https://github.com/mvdan/sh/pull/723))
## v3.8.0 - 2021-09-26
- Add `interactive: true` setting to improve support for interactive CLI apps
([#217](https://github.com/go-task/task/issues/217), [#563](https://github.com/go-task/task/pull/563)).
- Fix some `nil` errors
([#534](https://github.com/go-task/task/issues/534), [#573](https://github.com/go-task/task/pull/573)).
- Add ability to declare an included Taskfile as optional
([#519](https://github.com/go-task/task/issues/519), [#552](https://github.com/go-task/task/pull/552)).
- Add support for including Taskfiles in the home directory by using `~`
([#539](https://github.com/go-task/task/issues/539), [#557](https://github.com/go-task/task/pull/557)).
## v3.7.3 - 2021-09-04
- Add official support to Apple M1 ([#564](https://github.com/go-task/task/pull/564), [#567](https://github.com/go-task/task/pull/567)).
- 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
variables for these flags
([#332](https://github.com/go-task/task/issues/332)).
- Fixed a bug where an environment in a Taskfile was not always overridable
by the system environment
([#425](https://github.com/go-task/task/issues/425)).
- Fixed environment from .env files not being available as variables
([#379](https://github.com/go-task/task/issues/379)).
- The install script is now working for ARM platforms
([#428](https://github.com/go-task/task/pull/428)).
## v3.2.1 - 2021-01-09
- Fixed some bugs and regressions regarding dynamic variables and directories
([#426](https://github.com/go-task/task/issues/426)).
- The [slim-sprig](https://github.com/go-task/slim-sprig) package was updated
with the upstream [sprig](https://github.com/Masterminds/sprig).
## v3.2.0 - 2021-01-07
- Fix the `.task` directory being created in the task directory instead of the
Taskfile directory
([#247](https://github.com/go-task/task/issues/247)).
- Fix a bug where dynamic variables (those declared with `sh:`) were not
running in the task directory when the task has a custom dir or it was
in an included Taskfile
([#384](https://github.com/go-task/task/issues/384)).
- The watch feature (via the `--watch` flag) got a few different bug fixes and
should be more stable now
([#423](https://github.com/go-task/task/pull/423), [#365](https://github.com/go-task/task/issues/365)).
## v3.1.0 - 2021-01-03
- Fix a bug when the checksum up-to-date resolution is used by a task
with a custom `label:` attribute
([#412](https://github.com/go-task/task/issues/412)).
- Starting from this release, we're releasing official ARMv6 and ARM64 binaries
for Linux
([#375](https://github.com/go-task/task/issues/375), [#418](https://github.com/go-task/task/issues/418)).
- Task now respects the order of declaration of included Taskfiles when
evaluating variables declaring by them
([#393](https://github.com/go-task/task/issues/393)).
- `set -e` is now automatically set on every command. This was done to fix an
issue where multiline string commands wouldn't really fail unless the
sentence was in the last line
([#403](https://github.com/go-task/task/issues/403)).
## v3.0.1 - 2020-12-26
- Allow use as a library by moving the required packages out of the `internal`
directory
([#358](https://github.com/go-task/task/pull/358)).
- Do not error if a specified dotenv file does not exist
([#378](https://github.com/go-task/task/issues/378), [#385](https://github.com/go-task/task/pull/385)).
- 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)).
## v3.0.0 - 2020-08-16
- 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))
- Add support to `.env` like files
([#324](https://github.com/go-task/task/issues/324), [#356](https://github.com/go-task/task/pull/356)).
- Add `label:` to task so you can override the task name in the logs
([#321](https://github.com/go-task/task/issues/321]), [#337](https://github.com/go-task/task/pull/337)).
- Refactor how variables work on version 3
([#311](https://github.com/go-task/task/pull/311)).
- Disallow `expansions` on v3 since it has no effect.
- `Taskvars.yml` is not automatically included anymore.
- `Taskfile_{{OS}}.yml` is not automatically included anymore.
- Allow interpolation on `includes`, so you can manually include a Taskfile
based on operation system, for example.
- Expose `.TASK` variable in templates with the task name
([#252](https://github.com/go-task/task/issues/252)).
- Implement short task syntax
([#194](https://github.com/go-task/task/issues/194), [#240](https://github.com/go-task/task/pull/240)).
- Added option to make included Taskfile run commands on its own directory
([#260](https://github.com/go-task/task/issues/260), [#144](https://github.com/go-task/task/issues/144))
- Taskfiles in version 1 are not supported anymore
([#237](https://github.com/go-task/task/pull/237)).
- Added global `method:` option. With this option, you can set a default
method to all tasks in a Taskfile
([#246](https://github.com/go-task/task/issues/246)).
- Changed default method from `timestamp` to `checksum`
([#246](https://github.com/go-task/task/issues/246)).
- New magic variables are now available when using `status:`:
`.TIMESTAMP` which contains the greatest modification date
from the files listed in `sources:`, and `.CHECKSUM`, which
contains a checksum of all files listed in `status:`.
This is useful for manual checking when using external, or even remote,
artifacts when using `status:`
([#216](https://github.com/go-task/task/pull/216)).
- We're now using [slim-sprig](https://github.com/go-task/slim-sprig) instead of
[sprig](https://github.com/Masterminds/sprig), which allowed a file size
reduction of about 22%
([#219](https://github.com/go-task/task/pull/219)).
- We now use some colors on Task output to better distinguish message types -
commands are green, errors are red, etc
([#207](https://github.com/go-task/task/pull/207)).
## v2.8.1 - 2020-05-20
- 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)).
- Print version to stdout instead of stderr
([#299](https://github.com/go-task/task/issues/299), [#329](https://github.com/go-task/task/pull/329)).
- Supress `context` errors when using the `--watch` flag
([#313](https://github.com/go-task/task/issues/313), [#317](https://github.com/go-task/task/pull/317)).
- Support templating on description
([#276](https://github.com/go-task/task/issues/276), [#283](https://github.com/go-task/task/pull/283)).
## v2.8.0 - 2019-12-07
- Add `--parallel` flag (alias `-p`) to run tasks given by the command line in
parallel
([#266](https://github.com/go-task/task/pull/266)).
- Fixed bug where calling the `task` CLI only informing global vars would not
execute the `default` task.
- Add hability to silent all tasks by adding `silent: true` a the root of the
Taskfile.
## v2.7.1 - 2019-11-10
- Fix error being raised when `exit 0` was called
([#251](https://github.com/go-task/task/issues/251)).
## v2.7.0 - 2019-09-22
- Fixed panic bug when assigning a global variable
([#229](https://github.com/go-task/task/issues/229), [#243](https://github.com/go-task/task/issues/234)).
- A task with `method: checksum` will now re-run if generated files are deleted
([#228](https://github.com/go-task/task/pull/228), [#238](https://github.com/go-task/task/issues/238)).
## v2.6.0 - 2019-07-21
- Fixed some bugs regarding minor version checks on `version:`.
- Add `preconditions:` to task
([#205](https://github.com/go-task/task/pull/205)).
- Create directory informed on `dir:` if it doesn't exist
([#209](https://github.com/go-task/task/issues/209), [#211](https://github.com/go-task/task/pull/211)).
- We now have a `--taskfile` flag (alias `-t`), which can be used to run
another Taskfile (other than the default `Taskfile.yml`)
([#221](https://github.com/go-task/task/pull/221)).
- It's now possible to install Task using Homebrew on Linux
([go-task/homebrew-tap#1](https://github.com/go-task/homebrew-tap/pull/1)).
## v2.5.2 - 2019-05-11
- Reverted YAML upgrade due issues with CRLF on Windows
([#201](https://github.com/go-task/task/issues/201), [go-yaml/yaml#450](https://github.com/go-yaml/yaml/issues/450)).
- Allow setting global variables through the CLI
([#192](https://github.com/go-task/task/issues/192)).
## 2.5.1 - 2019-04-27
- Fixed some issues with interactive command line tools, where sometimes
the output were not being shown, and similar issues
([#114](https://github.com/go-task/task/issues/114), [#190](https://github.com/go-task/task/issues/190), [#200](https://github.com/go-task/task/pull/200)).
- Upgraded [go-yaml/yaml](https://github.com/go-yaml/yaml) from v2 to v3.
## v2.5.0 - 2019-03-16
- We moved from the taskfile.org domain to the new fancy taskfile.dev domain.
While stuff is being redirected, we strongly recommend to everyone that use
[this install script](https://taskfile.dev/#/installation?id=install-script)
to use the new taskfile.dev domain on scripts from now on.
- Fixed to the ZSH completion
([#182](https://github.com/go-task/task/pull/182)).
- Add [`--summary` flag along with `summary:` task attribute](https://taskfile.org/#/usage?id=display-summary-of-task)
([#180](https://github.com/go-task/task/pull/180)).
## v2.4.0 - 2019-02-21
- Allow calling a task of the root Taskfile from an included Taskfile
by prefixing it with `:`
([#161](https://github.com/go-task/task/issues/161), [#172](https://github.com/go-task/task/issues/172)),
- Add flag to override the `output` option
([#173](https://github.com/go-task/task/pull/173));
- Fix bug where Task was persisting the new checksum on the disk when the Dry
Mode is enabled
([#166](https://github.com/go-task/task/issues/166));
- Fix file timestamp issue when the file name has spaces
([#176](https://github.com/go-task/task/issues/176));
- Mitigating path expanding issues on Windows
([#170](https://github.com/go-task/task/pull/170)).
## v2.3.0 - 2019-01-02
- On Windows, Task can now be installed using [Scoop](https://scoop.sh/)
([#152](https://github.com/go-task/task/pull/152));
- Fixed issue with file/directory globing
([#153](https://github.com/go-task/task/issues/153));
- Added ability to globally set environment variables
(
[#138](https://github.com/go-task/task/pull/138),
[#159](https://github.com/go-task/task/pull/159)
).
## v2.2.1 - 2018-12-09
- This repository now uses Go Modules (#143). We'll still keep the `vendor` directory in sync for some time, though;
- Fixing a bug when the Taskfile has no tasks but includes another Taskfile (#150);
- Fix a bug when calling another task or a dependency in an included Taskfile (#151).
## v2.2.0 - 2018-10-25
- Added support for [including other Taskfiles](https://taskfile.org/#/usage?id=including-other-taskfiles) (#98)
- This should be considered experimental. For now, only including local files is supported, but support for including remote Taskfiles is being discussed. If you have any feedback, please comment on #98.
- Task now have a dedicated documentation site: https://taskfile.org
- Thanks to [Docsify](https://docsify.js.org/) for making this pretty easy. To check the source code, just take a look at the [docs](https://github.com/go-task/task/tree/master/docs) directory of this repository. Contributions to the documentation is really appreciated.
## v2.1.1 - 2018-09-17
- Fix suggestion to use `task --init` not being shown anymore (when a `Taskfile.yml` is not found)
- Fix error when using checksum method and no file exists for a source glob (#131)
- Fix signal handling when the `--watch` flag is given (#132)
## v2.1.0 - 2018-08-19
- Add a `ignore_error` option to task and command (#123)
- Add a dry run mode (`--dry` flag) (#126)
## v2.0.3 - 2018-06-24
- Expand environment variables on "dir", "sources" and "generates" (#116)
- Fix YAML merging syntax (#112)
- Add ZSH completion (#111)
- Implement new `output` option. Please check out the [documentation](https://github.com/go-task/task#output-syntax)
## v2.0.2 - 2018-05-01
- Fix merging of YAML anchors (#112)
## v2.0.1 - 2018-03-11
- Fixes panic on `task --list`
## v2.0.0 - 2018-03-08
Version 2.0.0 is here, with a new Taskfile format.
Please, make sure to read the [Taskfile versions](https://github.com/go-task/task/blob/master/TASKFILE_VERSIONS.md) document, since it describes in depth what changed for this version.
* New Taskfile version 2 (https://github.com/go-task/task/issues/77)
* Possibility to have global variables in the `Taskfile.yml` instead of `Taskvars.yml` (https://github.com/go-task/task/issues/66)
* Small improvements and fixes
## v1.4.4 - 2017-11-19
- Handle SIGINT and SIGTERM (#75);
- List: print message with there's no task with description;
- Expand home dir ("~" symbol) on paths (#74);
- Add Snap as an installation method;
- Move examples to its own repo;
- Watch: also walk on tasks called on on "cmds", and not only on "deps";
- Print logs to stderr instead of stdout (#68);
- Remove deprecated `set` keyword;
- Add checksum based status check, alternative to timestamp based.
## v1.4.3 - 2017-09-07
- Allow assigning variables to tasks at run time via CLI (#33)
- Added suport for multiline variables from sh (#64)
- Fixes env: remove square braces and evaluate shell (#62)
- Watch: change watch library and few fixes and improvements
- When use watching, cancel and restart long running process on file change (#59 and #60)
## v1.4.2 - 2017-07-30
- Flag to set directory of execution
- Always echo command if is verbose mode
- Add silent mode to disable echoing of commands
- Fixes and improvements of variables (#56)
## v1.4.1 - 2017-07-15
- Allow use of YAML for dynamic variables instead of $ prefix
- `VAR: {sh: echo Hello}` instead of `VAR: $echo Hello`
- Add `--list` (or `-l`) flag to print existing tasks
- OS specific Taskvars file (e.g. `Taskvars_windows.yml`, `Taskvars_linux.yml`, etc)
- Consider task up-to-date on equal timestamps (#49)
- Allow absolute path in generates section (#48)
- Bugfix: allow templating when calling deps (#42)
- Fix panic for invalid task in cyclic dep detection
- Better error output for dynamic variables in Taskvars.yml (#41)
- Allow template evaluation in parameters
## v1.4.0 - 2017-07-06
- Cache dynamic variables
- Add verbose mode (`-v` flag)
- Support to task parameters (overriding vars) (#31) (#32)
- Print command, also when "set:" is specified (#35)
- Improve task command help text (#35)
## v1.3.1 - 2017-06-14
- Fix glob not working on commands (#28)
- Add ExeExt template function
- Add `--init` flag to create a new Taskfile
- Add status option to prevent task from running (#27)
- Allow interpolation on `generates` and `sources` attributes (#26)
## v1.3.0 - 2017-04-24
- Migrate from os/exec.Cmd to a native Go sh/bash interpreter
- This is a potentially breaking change if you use Windows.
- Now, `cmd` is not used anymore on Windows. Always use Bash-like syntax for your commands, even on Windows.
- Add "ToSlash" and "FromSlash" to template functions
- Use functions defined on github.com/Masterminds/sprig
- Do not redirect stdin while running variables commands
- Using `context` and `errgroup` packages (this will make other tasks to be cancelled, if one returned an error)
## v1.2.0 - 2017-04-02
- More tests and Travis integration
- Watch a task (experimental)
- Possibility to call another task
- Fix "=" not being reconized in variables/environment variables
- Tasks can now have a description, and help will print them (#10)
- Task dependencies now run concurrently
- Support for a default task (#16)
## v1.1.0 - 2017-03-08
- Support for YAML, TOML and JSON (#1)
- Support running command in another directory (#4)
- `--force` or `-f` flag to force execution of task even when it's up-to-date
- Detection of cyclic dependencies (#5)
- Support for variables (#6, #9, #14)
- Operation System specific commands and variables (#13)
## v1.0.0 - 2017-02-28
- Add LICENSE file

View File

@@ -1,63 +0,0 @@
---
slug: /community/
sidebar_position: 6
---
# 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).
### IntelliJ plugin
There's a JetBrains IntelliJ plugin done by
[@lechuckroh](https://github.com/lechuckroh), which has its code [here](https://github.com/lechuckroh/task-intellij-plugin)
and is published [here](https://plugins.jetbrains.com/plugin/17058-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)
- [Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/)
- [NixOS](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/tools/go-task/default.nix)
## 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

@@ -1,36 +0,0 @@
---
slug: /donate/
sidebar_position: 9
---
# Donate
If you find this project useful, you can consider donating by using one of the
channels listed below.
This is just a way of saying "thank you", it won't give you any benefits like
higher priority on issues or something similar.
## Open Collective
Task is on [Open Collective](https://opencollective.com/task) and you have
these options to donate:
- [$2 per month](https://opencollective.com/task/contribute/backer-4034/checkout)
- [$5 per month](https://opencollective.com/task/contribute/supporter-8404/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)
## GitHub Sponsors
- [@andreynering](https://github.com/sponsors/andreynering)
## PayPal
- [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
[using this QR Code](/img/pix.png).

View File

@@ -1,177 +0,0 @@
---
slug: /installation/
sidebar_position: 2
---
# Installation
Task offers many installation methods. Check out the available methods below.
## Package Managers
### Homebrew
If you're on macOS or Linux and have [Homebrew][homebrew] installed, getting
Task is as simple as running:
```bash
brew install go-task/tap/go-task
```
### Snap
Task is available in [Snapcraft][snapcraft], but keep in mind that your
Linux distribution should allow classic confinement for Snaps to Task work
right:
```bash
sudo snap install task --classic
```
### Chocolatey
If you're on Windows and have [Chocolatey][choco] installed, getting
Task is as simple as running:
```bash
choco install go-task
```
This installation method is community owned.
### Scoop
If you're on Windows and have [Scoop][scoop] installed, getting
Task is as simple as running:
```cmd
scoop install task
```
This installation method is community owned. After a new release of Task, it
may take some time until it's available on Scoop.
### AUR
If you're on Arch Linux you can install Task from
[AUR](https://aur.archlinux.org/packages/taskfile-git) using your favorite
package manager such as `yay`, `pacaur` or `yaourt`:
```cmd
yay -S taskfile-git
```
This installation method is community owned, but since it's `-git` version of
the package, it's always latest available version based on the Git repository.
### Fedora
If you're on Fedora Linux you can install Task from the official
[Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/) repository using `dnf`:
```cmd
sudo dnf install go-task
```
This installation method is community owned. After a new release of Task, it
may take some time until it's available in [Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/).
### Nix
If you're on NixOS or have Nix installed
you can install Task from [nixpkgs](https://github.com/NixOS/nixpkgs):
```cmd
nix-env -iA nixpkgs.go-task
```
This installation method is community owned. After a new release of Task, it
may take some time until it's available in [nixpkgs](https://github.com/NixOS/nixpkgs).
## Get The Binary
### Binary
You can download the binary from the [releases page on GitHub][releases] and
add to your `$PATH`.
DEB and RPM packages are also available.
The `task_checksums.txt` file contains the SHA-256 checksum for each file.
### Install Script
We also have an [install script][installscript] which is very useful in
scenarios like CI. Many thanks to [GoDownloader][godownloader] for enabling the
easy generation of this script.
```bash
# For Default Installation to ./bin with debug logging
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
# For Installation To /usr/local/bin for userwide access with debug logging
# May require sudo sh
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
```
:::info
This method will download the binary on the local `./bin` directory by default.
:::
### GitHub Actions
If you want to install Task in GitHub Actions you can try using
[this action](https://github.com/arduino/setup-task)
by the Arduino team:
```yaml
- name: Install Task
uses: arduino/setup-task@v1
```
This installation method is community owned.
## Build From Source
### Go Modules
First, make sure you have [Go][go] properly installed and setup.
You can easily install the latest release globally by running:
```bash
go install github.com/go-task/task/v3/cmd/task@latest
```
Or you can install into another directory:
```bash
env GOBIN=/bin go install github.com/go-task/task/v3/cmd/task@latest
```
If using Go 1.15 or earlier, instead use:
```bash
env GO111MODULE=on go get -u github.com/go-task/task/v3/cmd/task@latest
```
:::tip
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
released binary.
:::
[go]: https://golang.org/
[snapcraft]: https://snapcraft.io/task
[homebrew]: https://brew.sh/
[installscript]: https://github.com/go-task/task/blob/master/install-task.sh
[releases]: https://github.com/go-task/task/releases
[godownloader]: https://github.com/goreleaser/godownloader
[choco]: https://chocolatey.org/
[scoop]: https://scoop.sh/

View File

@@ -1,57 +0,0 @@
---
slug: /
sidebar_position: 1
title: Home
---
# Task
<div align="center">
<img id="logo" src="img/logo.svg" height="250px" width="250px" />
</div>
Task is a task runner / build tool that aims to be simpler and easier to use
than, for example, [GNU Make][make].
Since it's written in [Go][go], Task is just a single binary and has no other
dependencies, which means you don't need to mess with any complicated install
setups just to use a build tool.
Once [installed](installation.md), you just need to describe your build tasks
using a simple [YAML][yaml] schema in a file called `Taskfile.yml`:
```yaml title="Taskfile.yml"
version: '3'
tasks:
hello:
cmds:
- echo 'Hello World from Task!'
silent: true
```
And call it by running `task hello` from your terminal.
The above example is just the start, you can take a look at the [usage](/usage)
guide to check the full schema documentation and Task features.
## Features
- [Easy installation](installation.md): just download a single binary, add to
`$PATH` and you're done! Or you can also install using [Homebrew][homebrew],
[Snapcraft][snapcraft], or [Scoop][scoop] if you want.
- Available on CIs: by adding [this simple command](installation.md#install-script)
to install on your CI script and you're done to use Task as part of your CI pipeline;
- Truly cross-platform: while most build tools only work well on Linux or macOS,
Task also supports Windows thanks to [this shell interpreter for Go][sh].
- Great for code generation: you can easily [prevent a task from running](/usage#prevent-unnecessary-work)
if a given set of files haven't changed since last run (based either on its
timestamp or content).
[make]: https://www.gnu.org/software/make/
[go]: https://go.dev/
[yaml]: http://yaml.org/
[homebrew]: https://brew.sh/
[snapcraft]: https://snapcraft.io/
[scoop]: https://scoop.sh/
[sh]: https://github.com/mvdan/sh

View File

@@ -1,51 +0,0 @@
---
slug: /releasing/
sidebar_position: 7
---
# Releasing
The release process of Task is done with the help of
[GoReleaser][goreleaser]. You can test the release process locally by calling
the `test-release` task of the Taskfile.
[GitHub Actions](https://github.com/go-task/task/actions) should release
artifacts automatically when a new Git tag is pushed to master
(raw executables and DEB and RPM packages).
# Homebrew
To release a new version on the [Homebrew tap][homebrewtap] edit the
[Formula/go-task.rb][gotaskrb] file, updating with the new version, download
URL and sha256.
# Snapcraft
The exception is the publishing of a new version of the
[snap package][snappackage]. This current require two steps after publishing
the binaries:
* Updating the current version on [snapcraft.yaml][snapcraftyaml].
* Moving both `amd64`, `armhf` and `arm64` new artifacts to the stable channel on
the [Snapcraft dashboard][snapcraftdashboard].
# Scoop
Scoop is a community owned installation method. Scoop owners usually take care
of updating versions there by editing
[this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json).
If you think its Task version is outdated, open an issue to let us know.
# Nix
Nix is a community owned installation method. Nix package maintainers usually take care
of updating versions there by editing
[this file](https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/development/tools/go-task/default.nix).
If you think its Task version is outdated, open an issue to let us know.
[goreleaser]: https://goreleaser.com/
[homebrewtap]: https://github.com/go-task/homebrew-tap
[gotaskrb]: https://github.com/go-task/homebrew-tap/blob/master/Formula/go-task.rb
[snappackage]: https://github.com/go-task/snap
[snapcraftyaml]: https://github.com/go-task/snap/blob/master/snap/snapcraft.yaml#L2
[snapcraftdashboard]: https://snapcraft.io/task/releases

View File

@@ -1,216 +0,0 @@
---
slug: /styleguide/
sidebar_position: 5
---
# Styleguide
This is the official Task styleguide for `Taskfile.yml` files. This guide
contains some basic instructions to keep your Taskfile clean and familiar to
other users.
This contains general guidelines, but they don't necessarily need to be strictly
followed. Feel free to disagree and proceed differently at some point if you
need or want to. Also, feel free to open issues or pull requests with
improvements to this guide.
## Use `Taskfile.yml` and not `taskfile.yml`
```yaml
# bad
taskfile.yml
# good
Taskfile.yml
```
This is important especially for Linux users. Windows and macOS have case
insensitive filesystems, so `taskfile.yml` will end up working, even that not
officially supported. On Linux, only `Taskfile.yml` will work, though.
## Use the correct order of keywords
- `version:`
- `includes:`
- Configuration ones, like `output:`, `silent:`, `method:` and `run:`
- `vars:`
- `env:`, `dotenv:`
- `tasks:`
## Use 2 spaces for indentation
This is the most common convention for YAML files, and Task follows it.
```yaml
# bad
tasks:
foo:
cmds:
- echo 'foo'
# good
tasks:
foo:
cmds:
- echo 'foo'
```
## Separate with spaces the mains sections
```yaml
# bad
version: '3'
includes:
docker: ./docker/Taskfile.yml
output: prefixed
vars:
FOO: bar
env:
BAR: baz
tasks:
# ...
# good
version: '3'
includes:
docker: ./docker/Taskfile.yml
output: prefixed
vars:
FOO: bar
env:
BAR: baz
tasks:
# ...
```
## Add spaces between tasks
```yaml
# bad
version: '3'
tasks:
foo:
cmds:
- echo 'foo'
bar:
cmds:
- echo 'bar'
baz:
cmds:
- echo 'baz'
# good
version: '3'
tasks:
foo:
cmds:
- echo 'foo'
bar:
cmds:
- echo 'bar'
baz:
cmds:
- echo 'baz'
```
## Use upper-case variable names
```yaml
# bad
version: '3'
vars:
binary_name: myapp
tasks:
build:
cmds:
- go build -o {{.binary_name}} .
# good
version: '3'
vars:
BINARY_NAME: myapp
tasks:
build:
cmds:
- go build -o {{.BINARY_NAME}} .
```
## Don't wrap vars in spaces when templating
```yaml
# bad
version: '3'
tasks:
greet:
cmds:
- echo '{{ .MESSAGE }}'
# good
version: '3'
tasks:
greet:
cmds:
- echo '{{.MESSAGE}}'
```
This convention is also used by most people for any Go templating.
## Separate task name words with a dash
```yaml
# bad
version: '3'
tasks:
do_something_fancy:
cmds:
- echo 'Do something'
# good
version: '3'
tasks:
do-something-fancy:
cmds:
- echo 'Do something'
```
## Use colon for task namespacing
```yaml
# good
version: '3'
tasks:
docker:build:
cmds:
- docker ...
docker:run:
cmds:
- docker-compose ...
```
This is also done automatically when using included Taskfiles.

View File

@@ -1,230 +0,0 @@
---
slug: /taskfile-versions/
sidebar_position: 8
---
# Taskfile Versions
The Taskfile syntax and features changed with time. This document explains what
changed on each version and how to upgrade your Taskfile.
## What the Taskfile version mean
The Taskfile version follows the Task version. E.g. the change to Taskfile
version `2` means that Task `v2.0.0` should be release to support it.
The `version:` key on Taskfile accepts a semver string, so either `2`, `2.0` or
`2.0.0` is accepted. If you choose to use `2.0` Task will not enable future
`2.1` features, but if you choose to use `2`, then any `2.x.x` features will be
available, but not `3.0.0+`.
## Version 1
> NOTE: Taskfiles in version 1 are not supported on Task >= v3.0.0 anymore.
In the first version of the `Taskfile`, the `version:` key was not available,
because the tasks was in the root of the YAML document. Like this:
```yaml
echo:
cmds:
- echo "Hello, World!"
```
The variable priority order was also different:
1. Call variables
2. Environment
3. Task variables
4. `Taskvars.yml` variables
## Version 2.0
At version 2, we introduced the `version:` key, to allow us to evolve Task
with new features without breaking existing Taskfiles. The new syntax is as
follows:
```yaml
version: '2'
tasks:
echo:
cmds:
- echo "Hello, World!"
```
Version 2 allows you to write global variables directly in the Taskfile,
if you don't want to create a `Taskvars.yml`:
```yaml
version: '2'
vars:
GREETING: Hello, World!
tasks:
greet:
cmds:
- echo "{{.GREETING}}"
```
The variable priority order changed to the following:
1. Task variables
2. Call variables
3. Taskfile variables
4. Taskvars file variables
5. Environment variables
A new global option was added to configure the number of variables expansions
(which default to 2):
```yaml
version: '2'
expansions: 3
vars:
FOO: foo
BAR: bar
BAZ: baz
FOOBAR: "{{.FOO}}{{.BAR}}"
FOOBARBAZ: "{{.FOOBAR}}{{.BAZ}}"
tasks:
default:
cmds:
- echo "{{.FOOBARBAZ}}"
```
## Version 2.1
Version 2.1 includes a global `output` option, to allow having more control
over how commands output are printed to the console
(see [documentation][output] for more info):
```yaml
version: '2'
output: prefixed
tasks:
server:
cmds:
- go run main.go
prefix: server
```
From this version it's also possible to ignore errors of a command or task
(check documentation [here][ignore_errors]):
```yaml
version: '2'
tasks:
example-1:
cmds:
- cmd: exit 1
ignore_error: true
- echo "This will be print"
example-2:
cmds:
- exit 1
- echo "This will be print"
ignore_error: true
```
## Version 2.2
Version 2.2 comes with a global `includes` options to include other
Taskfiles:
```yaml
version: '2'
includes:
docs: ./documentation # will look for ./documentation/Taskfile.yml
docker: ./DockerTasks.yml
```
## Version 2.6
Version 2.6 comes with `preconditions` stanza in tasks.
```yaml
version: '2'
tasks:
upload_environment:
preconditions:
- test -f .env
cmds:
- aws s3 cp .env s3://myenvironment
```
Please check the [documentation][includes]
[output]: usage.md#output-syntax
[ignore_errors]: usage.md#ignore-errors
[includes]: usage.md#including-other-taskfiles
## Version 3
These are some major changes done on `v3`:
- Task's output will now be colored
- Added support for `.env` like files
- Added `label:` setting to task so one can override how the task name
appear in the logs
- A global `method:` was added to allow setting the default method,
and Task's default changed to `checksum`
- Two magic variables were added when using `status:`: `CHECKSUM` and
`TIMESTAMP` which contains, respectively, the md5 checksum and greatest
modification timestamp of the files listed on `sources:`
- Also, the `TASK` variable is always available with the current task name
- CLI variables are always treated as global variables
- Added `dir:` option to `includes` to allow choosing on which directory an
included Taskfile will run:
```yaml
includes:
docs:
taskfile: ./docs
dir: ./docs
```
- Implemented short task syntax. All below syntaxes are equivalent:
```yaml
version: '3'
tasks:
print:
cmds:
- echo "Hello, World!"
```
```yaml
version: '3'
tasks:
print:
- echo "Hello, World!"
```
```yaml
version: '3'
tasks:
print: echo "Hello, World!"
```
- There was a major refactor on how variables are handled. They're now easier
to understand. The `expansions:` setting was removed as it became unncessary.
This is the order in which Task will process variables, each level can see
the variables set by the previous one and override those.
- Environment variables
- Global + CLI variables
- Call variables
- Task variables

File diff suppressed because it is too large Load Diff

View File

@@ -1,179 +0,0 @@
// @ts-check
// Note: type annotations allow type checking and IDEs autocompletion
const lightCodeTheme = require('prism-react-renderer/themes/github');
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
const GITHUB_URL = 'https://github.com/go-task/task';
const TWITTER_URL = 'https://twitter.com/taskfiledev';
const DISCORD_URL = 'https://discord.gg/6TY36E39UK';
/** @type {import('@docusaurus/types').Config} */
const config = {
title: 'Task',
tagline: 'A task runner / simpler Make alternative written in Go ',
url: 'https://taskfile.dev',
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'throw',
favicon: 'img/favicon.ico',
organizationName: 'go-task',
projectName: 'task',
deploymentBranch: 'gh-pages',
i18n: {
defaultLocale: 'en',
locales: ['en']
},
presets: [
[
'classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
routeBasePath: '/',
sidebarPath: require.resolve('./sidebars.js')
},
blog: false,
theme: {
customCss: [
require.resolve('./src/css/custom.css'),
require.resolve('./src/css/carbon.css')
]
},
gtag: {
trackingID: 'G-4RT25NXQ7N',
anonymizeIP: true
},
sitemap: {
changefreq: 'weekly',
priority: 0.5,
ignorePatterns: ['/tags/**']
}
})
]
],
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
metadata: [
{
name: 'og:image',
content: 'https://taskfile.dev/img/og-image.png'
}
],
navbar: {
title: 'Task',
logo: {
alt: 'Task Logo',
src: 'img/logo.svg'
},
items: [
{
type: 'doc',
docId: 'installation',
position: 'left',
label: 'Installation'
},
{
type: 'doc',
docId: 'usage',
position: 'left',
label: 'Usage'
},
{
type: 'doc',
docId: 'api_reference',
position: 'left',
label: 'API'
},
{
type: 'doc',
docId: 'donate',
position: 'left',
label: 'Donate'
},
{
href: GITHUB_URL,
label: 'GitHub',
position: 'right'
},
{
href: TWITTER_URL,
label: 'Twitter',
position: 'right'
},
{
href: DISCORD_URL,
label: 'Discord',
position: 'right'
}
]
},
footer: {
style: 'dark',
links: [
{
title: 'Pages',
items: [
{
label: 'Installation',
to: '/installation/'
},
{
label: 'Usage',
to: '/usage/'
},
{
label: 'Donate',
to: '/donate/'
}
]
},
{
title: 'Community',
items: [
{
label: 'GitHub',
href: GITHUB_URL
},
{
label: 'Twitter',
href: TWITTER_URL
},
{
label: 'Discord',
href: DISCORD_URL
},
{
label: 'OpenCollective',
href: 'https://opencollective.com/task'
}
]
}
]
},
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme
},
// NOTE(@andreynering): Don't worry, these keys are meant to be public =)
algolia: {
appId: '7IZIJ13AI7',
apiKey: '34b64ae4fc8d9da43d9a13d9710aaddc',
indexName: 'taskfile'
}
}),
scripts: [
{
src: '/js/carbon.js',
async: true
}
]
};
module.exports = config;

View File

@@ -1,41 +0,0 @@
{
"name": "taskfile-dev",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "2.0.0-beta.20",
"@docusaurus/preset-classic": "2.0.0-beta.20",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.1.1",
"prism-react-renderer": "^1.3.1",
"raw-loader": "^4.0.2",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.0.0-beta.20"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

View File

@@ -1,4 +0,0 @@
module.exports = {
trailingComma: 'none',
singleQuote: true
};

View File

@@ -1,14 +0,0 @@
// @ts-check
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
tutorialSidebar: [
{ type: 'autogenerated', dirName: '.' },
{
type: 'html',
value: '<div id="sidebar-ads"></div>'
}
]
};
module.exports = sidebars;

View File

@@ -1,65 +0,0 @@
#carbonads * {
margin: initial;
padding: initial;
}
#carbonads {
display: flex;
max-width: 330px;
background-color: hsl(0, 0%, 98%);
box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, 0.1);
z-index: 100;
}
#carbonads a {
color: inherit;
text-decoration: none;
}
#carbonads a:hover {
color: inherit;
}
#carbonads span {
position: relative;
display: block;
overflow: hidden;
}
#carbonads .carbon-wrap {
display: flex;
}
#carbonads .carbon-img {
display: block;
margin: 0;
line-height: 1;
}
#carbonads .carbon-img img {
display: block;
}
#carbonads .carbon-text {
font-size: 13px;
padding: 10px;
margin-bottom: 16px;
line-height: 1.5;
text-align: left;
}
#carbonads .carbon-poweredby {
display: block;
padding: 6px 8px;
background: #f1f1f2;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
font-size: 8px;
line-height: 1;
border-top-left-radius: 3px;
position: absolute;
bottom: 0;
right: 0;
}
[data-theme='dark'] #carbonads {
background-color: hsl(0, 0%, 35%);
box-shadow: 0 1px 4px 1px hsl(0, 0%, 55%);
}
[data-theme='dark'] #carbonads .carbon-poweredby {
background-color: hsl(0, 0%, 55%);
}

View File

@@ -1,31 +0,0 @@
:root {
--ifm-color-primary: #43ABA2 ;
--ifm-color-primary-dark: #3AB2A6;
--ifm-color-primary-darker: #32B8AB;
--ifm-color-primary-darkest: #29BEB0;
--ifm-color-primary-light: #4CA59D;
--ifm-color-primary-lighter: #559F98;
--ifm-color-primary-lightest: #5D9993;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
}
[data-theme='dark'] {
--ifm-color-primary: #43ABA2 ;
--ifm-color-primary-dark: #3AB2A6;
--ifm-color-primary-darker: #32B8AB;
--ifm-color-primary-darkest: #29BEB0;
--ifm-color-primary-light: #4CA59D;
--ifm-color-primary-lighter: #559F98;
--ifm-color-primary-lightest: #5D9993;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}
.code-block--max-width {
width: 100%;
}
#carbonads {
margin-top: 30px;
margin-right: 10px;
}

View File

View File

1
docs/static/CNAME vendored
View File

@@ -1 +0,0 @@
taskfile.dev

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 375 375" width="500" height="500">
<path fill="#29beb0" d="M 187.570312 190.933594 L 187.570312 375 L 30.070312 279.535156 L 30.070312 95.464844 Z"/>
<path fill="#69d2c8" d="M 187.570312 190.933594 L 187.570312 375 L 345.070312 279.535156 L 345.070312 95.464844 Z"/>
<path fill="#94dfd8" d="M 187.570312 190.933594 L 30.070312 95.464844 L 187.570312 0 L 345.070312 95.464844 Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 446 B

View File

@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 375 375" width="500" height="500">
<path d="M 29.021972 281.445466 L 183.370289 375 L 183.370289 194.622441 L 29.021972 101.064089 Z M 345.978037 281.445466 L 345.978037 101.064079 L 191.629731 194.622431 L 191.629731 375 Z M 342.140723 93.731759 L 187.5 0 L 32.859297 93.731759 L 187.5 187.467349 Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

383
docs/static/install.sh vendored
View File

@@ -1,383 +0,0 @@
#!/bin/sh
set -e
# Code generated by godownloader on 2021-01-12T13:40:40Z. DO NOT EDIT.
#
usage() {
this=$1
cat <<EOF
$this: download go binaries for go-task/task
Usage: $this [-b] bindir [-d] [tag]
-b sets bindir or installation directory, Defaults to ./bin
-d turns on debug logging
[tag] is a tag from
https://github.com/go-task/task/releases
If tag is missing, then the latest will be used.
Generated by godownloader
https://github.com/goreleaser/godownloader
EOF
exit 2
}
parse_args() {
#BINDIR is ./bin unless set be ENV
# over-ridden by flag below
BINDIR=${BINDIR:-./bin}
while getopts "b:dh?x" arg; do
case "$arg" in
b) BINDIR="$OPTARG" ;;
d) log_set_priority 10 ;;
h | \?) usage "$0" ;;
x) set -x ;;
esac
done
shift $((OPTIND - 1))
TAG=$1
}
# this function wraps all the destructive operations
# if a curl|bash cuts off the end of the script due to
# network, either nothing will happen or will syntax error
# out preventing half-done work
execute() {
tmpdir=$(mktemp -d)
log_debug "downloading files into ${tmpdir}"
http_download "${tmpdir}/${TARBALL}" "${TARBALL_URL}"
http_download "${tmpdir}/${CHECKSUM}" "${CHECKSUM_URL}"
hash_sha256_verify "${tmpdir}/${TARBALL}" "${tmpdir}/${CHECKSUM}"
srcdir="${tmpdir}"
(cd "${tmpdir}" && untar "${TARBALL}")
test ! -d "${BINDIR}" && install -d "${BINDIR}"
for binexe in $BINARIES; do
if [ "$OS" = "windows" ]; then
binexe="${binexe}.exe"
fi
install "${srcdir}/${binexe}" "${BINDIR}/"
log_info "installed ${BINDIR}/${binexe}"
done
rm -rf "${tmpdir}"
}
get_binaries() {
case "$PLATFORM" in
darwin/amd64) BINARIES="task" ;;
darwin/arm64) BINARIES="task" ;;
darwin/armv6) BINARIES="task" ;;
linux/386) BINARIES="task" ;;
linux/amd64) BINARIES="task" ;;
linux/arm64) BINARIES="task" ;;
linux/armv6) BINARIES="task" ;;
windows/386) BINARIES="task" ;;
windows/amd64) BINARIES="task" ;;
windows/arm64) BINARIES="task" ;;
windows/armv6) BINARIES="task" ;;
*)
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
exit 1
;;
esac
}
tag_to_version() {
if [ -z "${TAG}" ]; then
log_info "checking GitHub for latest tag"
else
log_info "checking GitHub for tag '${TAG}'"
fi
REALTAG=$(github_release "$OWNER/$REPO" "${TAG}") && true
if test -z "$REALTAG"; then
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${PREFIX}/releases for details"
exit 1
fi
# if version starts with 'v', remove it
TAG="$REALTAG"
VERSION=${TAG#v}
}
adjust_format() {
# change format (tar.gz or zip) based on OS
case ${OS} in
windows) FORMAT=zip ;;
esac
true
}
adjust_os() {
# adjust archive name based on OS
true
}
adjust_arch() {
# adjust archive name based on ARCH
true
}
cat /dev/null <<EOF
------------------------------------------------------------------------
https://github.com/client9/shlib - portable posix shell functions
Public domain - http://unlicense.org
https://github.com/client9/shlib/blob/master/LICENSE.md
but credit (and pull requests) appreciated.
------------------------------------------------------------------------
EOF
is_command() {
command -v "$1" >/dev/null
}
echoerr() {
echo "$@" 1>&2
}
log_prefix() {
echo "$0"
}
_logp=6
log_set_priority() {
_logp="$1"
}
log_priority() {
if test -z "$1"; then
echo "$_logp"
return
fi
[ "$1" -le "$_logp" ]
}
log_tag() {
case $1 in
0) echo "emerg" ;;
1) echo "alert" ;;
2) echo "crit" ;;
3) echo "err" ;;
4) echo "warning" ;;
5) echo "notice" ;;
6) echo "info" ;;
7) echo "debug" ;;
*) echo "$1" ;;
esac
}
log_debug() {
log_priority 7 || return 0
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
}
log_info() {
log_priority 6 || return 0
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
}
log_err() {
log_priority 3 || return 0
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
}
log_crit() {
log_priority 2 || return 0
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
}
uname_os() {
os=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$os" in
cygwin_nt*) os="windows" ;;
mingw*) os="windows" ;;
msys_nt*) os="windows" ;;
esac
echo "$os"
}
uname_arch() {
arch=$(uname -m)
case $arch in
x86_64) arch="amd64" ;;
x86) arch="386" ;;
i686) arch="386" ;;
i386) arch="386" ;;
aarch64) arch="arm64" ;;
armv5*) arch="armv5" ;;
armv6*) arch="armv6" ;;
armv7*) arch="armv7" ;;
esac
echo ${arch}
}
uname_os_check() {
os=$(uname_os)
case "$os" in
darwin) return 0 ;;
dragonfly) return 0 ;;
freebsd) return 0 ;;
linux) return 0 ;;
android) return 0 ;;
nacl) return 0 ;;
netbsd) return 0 ;;
openbsd) return 0 ;;
plan9) return 0 ;;
solaris) return 0 ;;
windows) return 0 ;;
esac
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
return 1
}
uname_arch_check() {
arch=$(uname_arch)
case "$arch" in
386) return 0 ;;
amd64) return 0 ;;
arm64) return 0 ;;
armv5) return 0 ;;
armv6) return 0 ;;
armv7) return 0 ;;
ppc64) return 0 ;;
ppc64le) return 0 ;;
mips) return 0 ;;
mipsle) return 0 ;;
mips64) return 0 ;;
mips64le) return 0 ;;
s390x) return 0 ;;
amd64p32) return 0 ;;
esac
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
return 1
}
untar() {
tarball=$1
case "${tarball}" in
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;;
*.tar) tar --no-same-owner -xf "${tarball}" ;;
*.zip) unzip "${tarball}" ;;
*)
log_err "untar unknown archive format for ${tarball}"
return 1
;;
esac
}
http_download_curl() {
local_file=$1
source_url=$2
header=$3
if [ -z "$header" ]; then
code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url")
else
code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url")
fi
if [ "$code" != "200" ]; then
log_debug "http_download_curl received HTTP status $code"
return 1
fi
return 0
}
http_download_wget() {
local_file=$1
source_url=$2
header=$3
if [ -z "$header" ]; then
wget -q -O "$local_file" "$source_url"
else
wget -q --header "$header" -O "$local_file" "$source_url"
fi
}
http_download() {
log_debug "http_download $2"
if is_command curl; then
http_download_curl "$@"
return
elif is_command wget; then
http_download_wget "$@"
return
fi
log_crit "http_download unable to find wget or curl"
return 1
}
http_copy() {
tmp=$(mktemp)
http_download "${tmp}" "$1" "$2" || return 1
body=$(cat "$tmp")
rm -f "${tmp}"
echo "$body"
}
github_release() {
owner_repo=$1
version=$2
test -z "$version" && version="latest"
giturl="https://github.com/${owner_repo}/releases/${version}"
json=$(http_copy "$giturl" "Accept:application/json")
test -z "$json" && return 1
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
test -z "$version" && return 1
echo "$version"
}
hash_sha256() {
TARGET=${1:-/dev/stdin}
if is_command gsha256sum; then
hash=$(gsha256sum "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command sha256sum; then
hash=$(sha256sum "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command shasum; then
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command openssl; then
hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f a
else
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
return 1
fi
}
hash_sha256_verify() {
TARGET=$1
checksums=$2
if [ -z "$checksums" ]; then
log_err "hash_sha256_verify checksum file not specified in arg2"
return 1
fi
BASENAME=${TARGET##*/}
want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)
if [ -z "$want" ]; then
log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'"
return 1
fi
got=$(hash_sha256 "$TARGET")
if [ "$want" != "$got" ]; then
log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got"
return 1
fi
}
cat /dev/null <<EOF
------------------------------------------------------------------------
End of functions from https://github.com/client9/shlib
------------------------------------------------------------------------
EOF
PROJECT_NAME="task"
OWNER=go-task
REPO="task"
BINARY=task
FORMAT=tar.gz
OS=$(uname_os)
ARCH=$(uname_arch)
PREFIX="$OWNER/$REPO"
# use in logging routines
log_prefix() {
echo "$PREFIX"
}
PLATFORM="${OS}/${ARCH}"
GITHUB_DOWNLOAD=https://github.com/${OWNER}/${REPO}/releases/download
uname_os_check "$OS"
uname_arch_check "$ARCH"
parse_args "$@"
get_binaries
tag_to_version
adjust_format
adjust_os
adjust_arch
log_info "found version: ${VERSION} for ${TAG}/${OS}/${ARCH}"
NAME=${BINARY}_${OS}_${ARCH}
TARBALL=${NAME}.${FORMAT}
TARBALL_URL=${GITHUB_DOWNLOAD}/${TAG}/${TARBALL}
CHECKSUM=task_checksums.txt
CHECKSUM_URL=${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM}
execute

View File

@@ -1,24 +0,0 @@
(function () {
function attachAd() {
const el = document.createElement('script');
el.setAttribute('type', 'text/javascript');
el.setAttribute('id', '_carbonads_js');
el.setAttribute(
'src',
'//cdn.carbonads.com/carbon.js?serve=CESI65QJ&placement=taskfiledev'
);
el.setAttribute('async', 'async');
const wrapper = document.getElementById('sidebar-ads');
wrapper.innerHTML = '';
wrapper.appendChild(el);
}
setTimeout(function () {
attachAd();
window.addEventListener('popstate', function () {
attachAd();
});
}, 1000);
})();

File diff suppressed because it is too large Load Diff

View File

@@ -3,8 +3,6 @@ package task
import (
"errors"
"fmt"
"mvdan.cc/sh/v3/interp"
)
var (
@@ -12,6 +10,14 @@ var (
ErrTaskfileAlreadyExists = errors.New("task: A Taskfile already exists")
)
type taskFileNotFound struct {
taskFile string
}
func (err taskFileNotFound) Error() string {
return fmt.Sprintf(`task: No task file found (is it named "%s"?). Use "task --init" to create a new one`, err.taskFile)
}
type taskNotFoundError struct {
taskName string
}
@@ -20,21 +26,29 @@ func (err *taskNotFoundError) Error() string {
return fmt.Sprintf(`task: Task "%s" not found`, err.taskName)
}
type TaskRunError struct {
type taskRunError struct {
taskName string
err error
}
func (err *TaskRunError) Error() string {
func (err *taskRunError) Error() string {
return fmt.Sprintf(`task: Failed to run task "%s": %v`, err.taskName, err.err)
}
func (err *TaskRunError) ExitCode() int {
if c, ok := interp.IsExitStatus(err.err); ok {
return int(c)
}
type cyclicDepError struct {
taskName string
}
return 1
func (err *cyclicDepError) Error() string {
return fmt.Sprintf(`task: Cyclic dependency of task "%s" detected`, err.taskName)
}
type cantWatchNoSourcesError struct {
taskName string
}
func (err *cantWatchNoSourcesError) Error() string {
return fmt.Sprintf(`task: Can't watch task "%s" because it has no specified sources`, err.taskName)
}
// MaximumTaskCallExceededError is returned when a task is called too

27
go.mod
View File

@@ -1,27 +0,0 @@
module github.com/go-task/task/v3
require (
github.com/fatih/color v1.13.0
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
github.com/joho/godotenv v1.4.0
github.com/mattn/go-zglob v0.0.3
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/radovskyb/watcher v1.0.7
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
go 1.17

76
go.sum
View File

@@ -1,76 +0,0 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/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.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/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To=
github.com/mattn/go-zglob v0.0.3/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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899 h1:nwm4t5PtLlFd/H342GP50CtYf7vyMCOZkPx3g9shO0c=
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=

28
hash.go
View File

@@ -1,28 +0,0 @@
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)
}

81
help.go
View File

@@ -2,103 +2,36 @@ package task
import (
"fmt"
"io"
"log"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/taskfile"
"github.com/go-task/task/internal/taskfile"
)
// ListTasksWithDesc reports tasks that have a description spec.
func (e *Executor) ListTasksWithDesc() {
e.printTasks(false)
}
// ListAllTasks reports all tasks, with or without a description spec.
func (e *Executor) ListAllTasks() {
e.printTasks(true)
}
func (e *Executor) printTasks(listAll bool) {
var tasks []*taskfile.Task
if listAll {
tasks = e.allTaskNames()
} else {
tasks = e.tasksWithDesc()
}
// PrintTasksHelp prints help os tasks that have a description
func (e *Executor) PrintTasksHelp() {
tasks := e.tasksWithDesc()
if len(tasks) == 0 {
if listAll {
e.Logger.Outf(logger.Yellow, "task: No tasks available")
} else {
e.Logger.Outf(logger.Yellow, "task: No tasks with description available. Try --list-all to list all tasks")
}
e.Logger.Outf("task: No tasks with description available")
return
}
e.Logger.Outf(logger.Default, "task: Available tasks for this project:")
e.Logger.Outf("task: Available tasks for this project:")
// Format in tab-separated columns with a tab stop of 8.
w := tabwriter.NewWriter(e.Stdout, 0, 8, 0, '\t', 0)
for _, task := range tasks {
fmt.Fprintf(w, "* %s: \t%s\n", task.Name(), task.Desc)
fmt.Fprintf(w, "* %s: \t%s\n", task.Task, task.Desc)
}
w.Flush()
}
func (e *Executor) allTaskNames() (tasks []*taskfile.Task) {
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
for _, task := range e.Taskfile.Tasks {
tasks = append(tasks, task)
}
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
return
}
func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
for _, task := range e.Taskfile.Tasks {
if task.Desc != "" {
compiledTask, err := e.FastCompiledTask(taskfile.Call{Task: task.Task})
if err == nil {
task = compiledTask
}
tasks = append(tasks, task)
}
}
sort.Slice(tasks, func(i, j int) bool { return tasks[i].Task < tasks[j].Task })
return
}
// PrintTaskNames prints only the task names in a Taskfile.
// Only tasks with a non-empty description are printed if allTasks is false.
// Otherwise, all task names are printed.
func (e *Executor) ListTaskNames(allTasks bool) {
// if called from cmd/task.go, e.Taskfile has not yet been parsed
if e.Taskfile == nil {
if err := e.readTaskfile(); err != nil {
log.Fatal(err)
return
}
}
// use stdout if no output defined
var w io.Writer = os.Stdout
if e.Stdout != nil {
w = e.Stdout
}
// create a string slice from all map values (*taskfile.Task)
s := make([]string, 0, len(e.Taskfile.Tasks))
for _, t := range e.Taskfile.Tasks {
if allTasks || t.Desc != "" {
s = append(s, strings.TrimRight(t.Task, ":"))
}
}
// sort and print all task names
sort.Strings(s)
for _, t := range s {
fmt.Fprintln(w, t)
}
}

11
init.go
View File

@@ -3,13 +3,14 @@ package task
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
)
const defaultTaskfile = `# https://taskfile.dev
const defaultTaskfile = `# github.com/go-task/task
version: '3'
version: '2'
vars:
GREETING: Hello, World!
@@ -23,15 +24,15 @@ tasks:
// InitTaskfile Taskfile creates a new Taskfile
func InitTaskfile(w io.Writer, dir string) error {
f := filepath.Join(dir, "Taskfile.yaml")
f := filepath.Join(dir, "Taskfile.yml")
if _, err := os.Stat(f); err == nil {
return ErrTaskfileAlreadyExists
}
if err := os.WriteFile(f, []byte(defaultTaskfile), 0644); err != nil {
if err := ioutil.WriteFile(f, []byte(defaultTaskfile), 0644); err != nil {
return err
}
fmt.Fprintf(w, "Taskfile.yaml created in the current directory\n")
fmt.Fprintf(w, "Taskfile.yml created in the current directory\n")
return nil
}

View File

@@ -1,383 +0,0 @@
#!/bin/sh
set -e
# Code generated by godownloader on 2021-01-12T13:40:40Z. DO NOT EDIT.
#
usage() {
this=$1
cat <<EOF
$this: download go binaries for go-task/task
Usage: $this [-b] bindir [-d] [tag]
-b sets bindir or installation directory, Defaults to ./bin
-d turns on debug logging
[tag] is a tag from
https://github.com/go-task/task/releases
If tag is missing, then the latest will be used.
Generated by godownloader
https://github.com/goreleaser/godownloader
EOF
exit 2
}
parse_args() {
#BINDIR is ./bin unless set be ENV
# over-ridden by flag below
BINDIR=${BINDIR:-./bin}
while getopts "b:dh?x" arg; do
case "$arg" in
b) BINDIR="$OPTARG" ;;
d) log_set_priority 10 ;;
h | \?) usage "$0" ;;
x) set -x ;;
esac
done
shift $((OPTIND - 1))
TAG=$1
}
# this function wraps all the destructive operations
# if a curl|bash cuts off the end of the script due to
# network, either nothing will happen or will syntax error
# out preventing half-done work
execute() {
tmpdir=$(mktemp -d)
log_debug "downloading files into ${tmpdir}"
http_download "${tmpdir}/${TARBALL}" "${TARBALL_URL}"
http_download "${tmpdir}/${CHECKSUM}" "${CHECKSUM_URL}"
hash_sha256_verify "${tmpdir}/${TARBALL}" "${tmpdir}/${CHECKSUM}"
srcdir="${tmpdir}"
(cd "${tmpdir}" && untar "${TARBALL}")
test ! -d "${BINDIR}" && install -d "${BINDIR}"
for binexe in $BINARIES; do
if [ "$OS" = "windows" ]; then
binexe="${binexe}.exe"
fi
install "${srcdir}/${binexe}" "${BINDIR}/"
log_info "installed ${BINDIR}/${binexe}"
done
rm -rf "${tmpdir}"
}
get_binaries() {
case "$PLATFORM" in
darwin/amd64) BINARIES="task" ;;
darwin/arm64) BINARIES="task" ;;
darwin/armv6) BINARIES="task" ;;
linux/386) BINARIES="task" ;;
linux/amd64) BINARIES="task" ;;
linux/arm64) BINARIES="task" ;;
linux/armv6) BINARIES="task" ;;
windows/386) BINARIES="task" ;;
windows/amd64) BINARIES="task" ;;
windows/arm64) BINARIES="task" ;;
windows/armv6) BINARIES="task" ;;
*)
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
exit 1
;;
esac
}
tag_to_version() {
if [ -z "${TAG}" ]; then
log_info "checking GitHub for latest tag"
else
log_info "checking GitHub for tag '${TAG}'"
fi
REALTAG=$(github_release "$OWNER/$REPO" "${TAG}") && true
if test -z "$REALTAG"; then
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${PREFIX}/releases for details"
exit 1
fi
# if version starts with 'v', remove it
TAG="$REALTAG"
VERSION=${TAG#v}
}
adjust_format() {
# change format (tar.gz or zip) based on OS
case ${OS} in
windows) FORMAT=zip ;;
esac
true
}
adjust_os() {
# adjust archive name based on OS
true
}
adjust_arch() {
# adjust archive name based on ARCH
true
}
cat /dev/null <<EOF
------------------------------------------------------------------------
https://github.com/client9/shlib - portable posix shell functions
Public domain - http://unlicense.org
https://github.com/client9/shlib/blob/master/LICENSE.md
but credit (and pull requests) appreciated.
------------------------------------------------------------------------
EOF
is_command() {
command -v "$1" >/dev/null
}
echoerr() {
echo "$@" 1>&2
}
log_prefix() {
echo "$0"
}
_logp=6
log_set_priority() {
_logp="$1"
}
log_priority() {
if test -z "$1"; then
echo "$_logp"
return
fi
[ "$1" -le "$_logp" ]
}
log_tag() {
case $1 in
0) echo "emerg" ;;
1) echo "alert" ;;
2) echo "crit" ;;
3) echo "err" ;;
4) echo "warning" ;;
5) echo "notice" ;;
6) echo "info" ;;
7) echo "debug" ;;
*) echo "$1" ;;
esac
}
log_debug() {
log_priority 7 || return 0
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
}
log_info() {
log_priority 6 || return 0
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
}
log_err() {
log_priority 3 || return 0
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
}
log_crit() {
log_priority 2 || return 0
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
}
uname_os() {
os=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$os" in
cygwin_nt*) os="windows" ;;
mingw*) os="windows" ;;
msys_nt*) os="windows" ;;
esac
echo "$os"
}
uname_arch() {
arch=$(uname -m)
case $arch in
x86_64) arch="amd64" ;;
x86) arch="386" ;;
i686) arch="386" ;;
i386) arch="386" ;;
aarch64) arch="arm64" ;;
armv5*) arch="armv5" ;;
armv6*) arch="armv6" ;;
armv7*) arch="armv7" ;;
esac
echo ${arch}
}
uname_os_check() {
os=$(uname_os)
case "$os" in
darwin) return 0 ;;
dragonfly) return 0 ;;
freebsd) return 0 ;;
linux) return 0 ;;
android) return 0 ;;
nacl) return 0 ;;
netbsd) return 0 ;;
openbsd) return 0 ;;
plan9) return 0 ;;
solaris) return 0 ;;
windows) return 0 ;;
esac
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
return 1
}
uname_arch_check() {
arch=$(uname_arch)
case "$arch" in
386) return 0 ;;
amd64) return 0 ;;
arm64) return 0 ;;
armv5) return 0 ;;
armv6) return 0 ;;
armv7) return 0 ;;
ppc64) return 0 ;;
ppc64le) return 0 ;;
mips) return 0 ;;
mipsle) return 0 ;;
mips64) return 0 ;;
mips64le) return 0 ;;
s390x) return 0 ;;
amd64p32) return 0 ;;
esac
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
return 1
}
untar() {
tarball=$1
case "${tarball}" in
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;;
*.tar) tar --no-same-owner -xf "${tarball}" ;;
*.zip) unzip "${tarball}" ;;
*)
log_err "untar unknown archive format for ${tarball}"
return 1
;;
esac
}
http_download_curl() {
local_file=$1
source_url=$2
header=$3
if [ -z "$header" ]; then
code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url")
else
code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url")
fi
if [ "$code" != "200" ]; then
log_debug "http_download_curl received HTTP status $code"
return 1
fi
return 0
}
http_download_wget() {
local_file=$1
source_url=$2
header=$3
if [ -z "$header" ]; then
wget -q -O "$local_file" "$source_url"
else
wget -q --header "$header" -O "$local_file" "$source_url"
fi
}
http_download() {
log_debug "http_download $2"
if is_command curl; then
http_download_curl "$@"
return
elif is_command wget; then
http_download_wget "$@"
return
fi
log_crit "http_download unable to find wget or curl"
return 1
}
http_copy() {
tmp=$(mktemp)
http_download "${tmp}" "$1" "$2" || return 1
body=$(cat "$tmp")
rm -f "${tmp}"
echo "$body"
}
github_release() {
owner_repo=$1
version=$2
test -z "$version" && version="latest"
giturl="https://github.com/${owner_repo}/releases/${version}"
json=$(http_copy "$giturl" "Accept:application/json")
test -z "$json" && return 1
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
test -z "$version" && return 1
echo "$version"
}
hash_sha256() {
TARGET=${1:-/dev/stdin}
if is_command gsha256sum; then
hash=$(gsha256sum "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command sha256sum; then
hash=$(sha256sum "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command shasum; then
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command openssl; then
hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f a
else
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
return 1
fi
}
hash_sha256_verify() {
TARGET=$1
checksums=$2
if [ -z "$checksums" ]; then
log_err "hash_sha256_verify checksum file not specified in arg2"
return 1
fi
BASENAME=${TARGET##*/}
want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)
if [ -z "$want" ]; then
log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'"
return 1
fi
got=$(hash_sha256 "$TARGET")
if [ "$want" != "$got" ]; then
log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got"
return 1
fi
}
cat /dev/null <<EOF
------------------------------------------------------------------------
End of functions from https://github.com/client9/shlib
------------------------------------------------------------------------
EOF
PROJECT_NAME="task"
OWNER=go-task
REPO="task"
BINARY=task
FORMAT=tar.gz
OS=$(uname_os)
ARCH=$(uname_arch)
PREFIX="$OWNER/$REPO"
# use in logging routines
log_prefix() {
echo "$PREFIX"
}
PLATFORM="${OS}/${ARCH}"
GITHUB_DOWNLOAD=https://github.com/${OWNER}/${REPO}/releases/download
uname_os_check "$OS"
uname_arch_check "$ARCH"
parse_args "$@"
get_binaries
tag_to_version
adjust_format
adjust_os
adjust_arch
log_info "found version: ${VERSION} for ${TAG}/${OS}/${ARCH}"
NAME=${BINARY}_${OS}_${ARCH}
TARBALL=${NAME}.${FORMAT}
TARBALL_URL=${GITHUB_DOWNLOAD}/${TAG}/${TARBALL}
CHECKSUM=task_checksums.txt
CHECKSUM_URL=${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM}
execute

36
internal/args/args.go Normal file
View File

@@ -0,0 +1,36 @@
package args
import (
"errors"
"strings"
"github.com/go-task/task/internal/taskfile"
)
var (
// ErrVariableWithoutTask is returned when variables are given before any task
ErrVariableWithoutTask = errors.New("task: variable given before any task")
)
// Parse parses command line argument: tasks and vars of each task
func Parse(args ...string) ([]taskfile.Call, error) {
var calls []taskfile.Call
for _, arg := range args {
if !strings.Contains(arg, "=") {
calls = append(calls, taskfile.Call{Task: arg})
continue
}
if len(calls) < 1 {
return nil, ErrVariableWithoutTask
}
if calls[len(calls)-1].Vars == nil {
calls[len(calls)-1].Vars = make(taskfile.Vars)
}
pair := strings.SplitN(arg, "=", 2)
calls[len(calls)-1].Vars[pair[0]] = taskfile.Var{Static: pair[1]}
}
return calls, nil
}

View File

@@ -0,0 +1,70 @@
package args_test
import (
"fmt"
"testing"
"github.com/go-task/task/internal/args"
"github.com/go-task/task/internal/taskfile"
"github.com/stretchr/testify/assert"
)
func TestArgs(t *testing.T) {
tests := []struct {
Args []string
Expected []taskfile.Call
Err error
}{
{
Args: []string{"task-a", "task-b", "task-c"},
Expected: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
},
},
{
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
Expected: []taskfile.Call{
{
Task: "task-a",
Vars: taskfile.Vars{
"FOO": taskfile.Var{Static: "bar"},
},
},
{Task: "task-b"},
{
Task: "task-c",
Vars: taskfile.Vars{
"BAR": taskfile.Var{Static: "baz"},
"BAZ": taskfile.Var{Static: "foo"},
},
},
},
},
{
Args: []string{"task-a", "CONTENT=with some spaces"},
Expected: []taskfile.Call{
{
Task: "task-a",
Vars: taskfile.Vars{
"CONTENT": taskfile.Var{Static: "with some spaces"},
},
},
},
},
{
Args: []string{"FOO=bar", "task-a"},
Err: args.ErrVariableWithoutTask,
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, err := args.Parse(test.Args...)
assert.Equal(t, test.Err, err)
assert.Equal(t, test.Expected, calls)
})
}
}

View File

@@ -1,15 +1,12 @@
package compiler
import (
"github.com/go-task/task/v3/taskfile"
"github.com/go-task/task/internal/taskfile"
)
// Compiler handles compilation of a task before its execution.
// E.g. variable merger, template processing, etc.
type Compiler interface {
GetTaskfileVariables() (*taskfile.Vars, error)
GetVariables(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)
ResetCache()
GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error)
HandleDynamicVar(v taskfile.Var) (string, error)
}

View File

@@ -4,17 +4,21 @@ import (
"os"
"strings"
"github.com/go-task/task/v3/taskfile"
"github.com/go-task/task/internal/taskfile"
)
// GetEnviron the all return all environment variables encapsulated on a
// taskfile.Vars
func GetEnviron() *taskfile.Vars {
m := &taskfile.Vars{}
for _, e := range os.Environ() {
func GetEnviron() taskfile.Vars {
var (
env = os.Environ()
m = make(taskfile.Vars, len(env))
)
for _, e := range env {
keyVal := strings.SplitN(e, "=", 2)
key, val := keyVal[0], keyVal[1]
m.Set(key, taskfile.Var{Static: val})
m[key] = taskfile.Var{Static: val}
}
return m
}

View File

@@ -0,0 +1,136 @@
package v1
import (
"bytes"
"fmt"
"strings"
"sync"
"github.com/go-task/task/internal/compiler"
"github.com/go-task/task/internal/execext"
"github.com/go-task/task/internal/logger"
"github.com/go-task/task/internal/taskfile"
"github.com/go-task/task/internal/templater"
)
var _ compiler.Compiler = &CompilerV1{}
type CompilerV1 struct {
Dir string
Vars taskfile.Vars
Logger *logger.Logger
dynamicCache map[string]string
muDynamicCache sync.Mutex
}
// GetVariables returns fully resolved variables following the priority order:
// 1. Call variables (should already have been resolved)
// 2. Environment (should not need to be resolved)
// 3. Task variables, resolved with access to:
// - call, taskvars and environment variables
// 4. Taskvars variables, resolved with access to:
// - environment variables
func (c *CompilerV1) GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error) {
merge := func(dest taskfile.Vars, srcs ...taskfile.Vars) {
for _, src := range srcs {
for k, v := range src {
dest[k] = v
}
}
}
varsKeys := func(srcs ...taskfile.Vars) []string {
m := make(map[string]struct{})
for _, src := range srcs {
for k := range src {
m[k] = struct{}{}
}
}
lst := make([]string, 0, len(m))
for k := range m {
lst = append(lst, k)
}
return lst
}
replaceVars := func(dest taskfile.Vars, keys []string) error {
r := templater.Templater{Vars: dest}
for _, k := range keys {
v := dest[k]
dest[k] = taskfile.Var{
Static: r.Replace(v.Static),
Sh: r.Replace(v.Sh),
}
}
return r.Err()
}
resolveShell := func(dest taskfile.Vars, keys []string) error {
for _, k := range keys {
v := dest[k]
static, err := c.HandleDynamicVar(v)
if err != nil {
return err
}
dest[k] = taskfile.Var{Static: static}
}
return nil
}
update := func(dest taskfile.Vars, srcs ...taskfile.Vars) error {
merge(dest, srcs...)
// updatedKeys ensures template evaluation is run only once.
updatedKeys := varsKeys(srcs...)
if err := replaceVars(dest, updatedKeys); err != nil {
return err
}
return resolveShell(dest, updatedKeys)
}
// Resolve taskvars variables to "result" with environment override variables.
override := compiler.GetEnviron()
result := make(taskfile.Vars, len(c.Vars)+len(t.Vars)+len(override))
if err := update(result, c.Vars, override); err != nil {
return nil, err
}
// Resolve task variables to "result" with environment and call override variables.
merge(override, call.Vars)
if err := update(result, t.Vars, override); err != nil {
return nil, err
}
return result, nil
}
func (c *CompilerV1) HandleDynamicVar(v taskfile.Var) (string, error) {
if v.Static != "" || v.Sh == "" {
return v.Static, nil
}
c.muDynamicCache.Lock()
defer c.muDynamicCache.Unlock()
if c.dynamicCache == nil {
c.dynamicCache = make(map[string]string, 30)
}
if result, ok := c.dynamicCache[v.Sh]; ok {
return result, nil
}
var stdout bytes.Buffer
opts := &execext.RunCommandOptions{
Command: v.Sh,
Dir: c.Dir,
Stdout: &stdout,
Stderr: c.Logger.Stderr,
}
if err := execext.RunCommand(opts); err != nil {
return "", fmt.Errorf(`task: Command "%s" in taskvars file failed: %s`, opts.Command, err)
}
// Trim a single trailing newline from the result to make most command
// output easier to use in shell commands.
result := strings.TrimSuffix(stdout.String(), "\n")
c.dynamicCache[v.Sh] = result
c.Logger.VerboseErrf(`task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
return result, nil
}

View File

@@ -2,16 +2,15 @@ package v2
import (
"bytes"
"context"
"fmt"
"strings"
"sync"
"github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
"github.com/go-task/task/internal/compiler"
"github.com/go-task/task/internal/execext"
"github.com/go-task/task/internal/logger"
"github.com/go-task/task/internal/taskfile"
"github.com/go-task/task/internal/templater"
)
var _ compiler.Compiler = &CompilerV2{}
@@ -19,8 +18,8 @@ var _ compiler.Compiler = &CompilerV2{}
type CompilerV2 struct {
Dir string
Taskvars *taskfile.Vars
TaskfileVars *taskfile.Vars
Taskvars taskfile.Vars
TaskfileVars taskfile.Vars
Expansions int
@@ -30,29 +29,15 @@ type CompilerV2 struct {
muDynamicCache sync.Mutex
}
func (c *CompilerV2) GetTaskfileVariables() (*taskfile.Vars, error) {
return &taskfile.Vars{}, nil
}
// FastGetVariables is a no-op on v2
func (c *CompilerV2) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
return c.GetVariables(t, call)
}
// GetVariables returns fully resolved variables following the priority order:
// 1. Task variables
// 2. Call variables
// 3. Taskfile variables
// 4. Taskvars file variables
// 5. Environment variables
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
vr := varResolver{
c: c,
vars: compiler.GetEnviron(),
}
vr.vars.Set("TASK", taskfile.Var{Static: t.Task})
for _, vars := range []*taskfile.Vars{c.Taskvars, c.TaskfileVars, call.Vars, t.Vars} {
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error) {
vr := varResolver{c: c, vars: compiler.GetEnviron()}
for _, vars := range []taskfile.Vars{c.Taskvars, c.TaskfileVars, call.Vars, t.Vars} {
for i := 0; i < c.Expansions; i++ {
vr.merge(vars)
}
@@ -62,32 +47,31 @@ func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfi
type varResolver struct {
c *CompilerV2
vars *taskfile.Vars
vars taskfile.Vars
err error
}
func (vr *varResolver) merge(vars *taskfile.Vars) {
func (vr *varResolver) merge(vars taskfile.Vars) {
if vr.err != nil {
return
}
tr := templater.Templater{Vars: vr.vars}
_ = vars.Range(func(k string, v taskfile.Var) error {
for k, v := range vars {
v = taskfile.Var{
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),
}
static, err := vr.c.HandleDynamicVar(v, "")
static, err := vr.c.HandleDynamicVar(v)
if err != nil {
vr.err = err
return err
return
}
vr.vars.Set(k, taskfile.Var{Static: static})
return nil
})
vr.vars[k] = taskfile.Var{Static: static}
}
vr.err = tr.Err()
}
func (c *CompilerV2) HandleDynamicVar(v taskfile.Var, _ string) (string, error) {
func (c *CompilerV2) HandleDynamicVar(v taskfile.Var) (string, error) {
if v.Static != "" || v.Sh == "" {
return v.Static, nil
}
@@ -105,11 +89,12 @@ func (c *CompilerV2) HandleDynamicVar(v taskfile.Var, _ string) (string, error)
var stdout bytes.Buffer
opts := &execext.RunCommandOptions{
Command: v.Sh,
Dir: c.Dir,
Stdout: &stdout,
Stderr: c.Logger.Stderr,
}
if err := execext.RunCommand(context.Background(), opts); err != nil {
return "", fmt.Errorf(`task: Command "%s" failed: %s`, opts.Command, err)
if err := execext.RunCommand(opts); err != nil {
return "", fmt.Errorf(`task: Command "%s" in taskvars file failed: %s`, opts.Command, err)
}
// Trim a single trailing newline from the result to make most command
@@ -117,15 +102,7 @@ func (c *CompilerV2) HandleDynamicVar(v taskfile.Var, _ string) (string, error)
result := strings.TrimSuffix(stdout.String(), "\n")
c.dynamicCache[v.Sh] = result
c.Logger.VerboseErrf(logger.Magenta, `task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
c.Logger.VerboseErrf(`task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
return result, nil
}
// ResetCache clear the dymanic variables cache
func (c *CompilerV2) ResetCache() {
c.muDynamicCache.Lock()
defer c.muDynamicCache.Unlock()
c.dynamicCache = nil
}

View File

@@ -1,169 +0,0 @@
package v3
import (
"bytes"
"context"
"fmt"
"path/filepath"
"strings"
"sync"
"github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
)
var _ compiler.Compiler = &CompilerV3{}
type CompilerV3 struct {
Dir string
TaskfileEnv *taskfile.Vars
TaskfileVars *taskfile.Vars
Logger *logger.Logger
dynamicCache map[string]string
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) {
return c.getVariables(t, &call, true)
}
func (c *CompilerV3) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
return c.getVariables(t, &call, false)
}
func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
result := compiler.GetEnviron()
if t != nil {
result.Set("TASK", taskfile.Var{Static: t.Task})
}
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
return func(k string, v taskfile.Var) error {
tr := templater.Templater{Vars: result, RemoveNoValue: true}
if !evaluateShVars {
result.Set(k, taskfile.Var{Static: tr.Replace(v.Static)})
return nil
}
v = taskfile.Var{
Static: tr.Replace(v.Static),
Sh: tr.Replace(v.Sh),
Dir: v.Dir,
}
if err := tr.Err(); err != nil {
return err
}
static, err := c.HandleDynamicVar(v, dir)
if err != nil {
return err
}
result.Set(k, taskfile.Var{Static: static})
return nil
}
}
rangeFunc := getRangeFunc(c.Dir)
var taskRangeFunc func(k string, v taskfile.Var) error
if t != nil {
// NOTE(@andreynering): We're manually joining these paths here because
// this is the raw task, not the compiled one.
tr := templater.Templater{Vars: result, RemoveNoValue: true}
dir := tr.Replace(t.Dir)
if err := tr.Err(); err != nil {
return nil, err
}
if !filepath.IsAbs(dir) {
dir = filepath.Join(c.Dir, dir)
}
taskRangeFunc = getRangeFunc(dir)
}
if err := c.TaskfileEnv.Range(rangeFunc); err != nil {
return nil, err
}
if err := c.TaskfileVars.Range(rangeFunc); err != nil {
return nil, err
}
if t != nil {
if err := t.IncludedTaskfileVars.Range(taskRangeFunc); err != nil {
return nil, err
}
if err := t.IncludeVars.Range(rangeFunc); err != nil {
return nil, err
}
}
if t == nil || call == nil {
return result, nil
}
if err := call.Vars.Range(rangeFunc); err != nil {
return nil, err
}
if err := t.Vars.Range(taskRangeFunc); err != nil {
return nil, err
}
return result, nil
}
func (c *CompilerV3) HandleDynamicVar(v taskfile.Var, dir string) (string, error) {
if v.Static != "" || v.Sh == "" {
return v.Static, nil
}
c.muDynamicCache.Lock()
defer c.muDynamicCache.Unlock()
if c.dynamicCache == nil {
c.dynamicCache = make(map[string]string, 30)
}
if result, ok := c.dynamicCache[v.Sh]; ok {
return result, nil
}
// NOTE(@andreynering): If a var have a specific dir, use this instead
if v.Dir != "" {
dir = v.Dir
}
var stdout bytes.Buffer
opts := &execext.RunCommandOptions{
Command: v.Sh,
Dir: dir,
Stdout: &stdout,
Stderr: c.Logger.Stderr,
}
if err := execext.RunCommand(context.Background(), opts); err != nil {
return "", fmt.Errorf(`task: Command "%s" failed: %s`, opts.Command, err)
}
// Trim a single trailing newline from the result to make most command
// output easier to use in shell commands.
result := strings.TrimSuffix(stdout.String(), "\r\n")
result = strings.TrimSuffix(result, "\n")
c.dynamicCache[v.Sh] = result
c.Logger.VerboseErrf(logger.Magenta, `task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
return result, nil
}
// ResetCache clear the dymanic variables cache
func (c *CompilerV3) ResetCache() {
c.muDynamicCache.Lock()
defer c.muDynamicCache.Unlock()
c.dynamicCache = nil
}

View File

@@ -1,13 +0,0 @@
package execext
import (
"io"
)
var _ io.ReadWriteCloser = devNull{}
type devNull struct{}
func (devNull) Read(p []byte) (int, error) { return 0, io.EOF }
func (devNull) Write(p []byte) (int, error) { return len(p), nil }
func (devNull) Close() error { return nil }

View File

@@ -4,19 +4,15 @@ import (
"context"
"errors"
"io"
"os"
"path/filepath"
"strings"
"time"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/shell"
"mvdan.cc/sh/v3/syntax"
"mvdan.cc/sh/interp"
"mvdan.cc/sh/syntax"
)
// RunCommandOptions is the options for the RunCommand func
type RunCommandOptions struct {
Context context.Context
Command string
Dir string
Env []string
@@ -31,7 +27,7 @@ var (
)
// RunCommand runs a shell command
func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
func RunCommand(opts *RunCommandOptions) error {
if opts == nil {
return ErrNilOptions
}
@@ -41,73 +37,20 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
return err
}
environ := opts.Env
if len(environ) == 0 {
environ = os.Environ()
}
r, err := interp.New(
interp.Params("-e"),
interp.Env(expand.ListEnviron(environ...)),
interp.ExecHandler(interp.DefaultExecHandler(15*time.Second)),
interp.OpenHandler(openHandler),
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
dirOption(opts.Dir),
)
if err != nil {
return err
}
return r.Run(ctx, p)
}
// IsExitError returns true the given error is an exis status error
func IsExitError(err error) bool {
if _, ok := interp.IsExitStatus(err); ok {
return true
}
return false
}
// Expand is a helper to mvdan.cc/shell.Fields that returns the first field
// if available.
func Expand(s string) (string, error) {
s = filepath.ToSlash(s)
s = strings.Replace(s, " ", `\ `, -1)
fields, err := shell.Fields(s, nil)
if err != nil {
return "", err
}
if len(fields) > 0 {
return fields[0], nil
}
return "", nil
}
func openHandler(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
if path == "/dev/null" {
return devNull{}, nil
}
return interp.DefaultOpenHandler()(ctx, path, flag, perm)
}
func dirOption(path string) interp.RunnerOption {
return func(r *interp.Runner) error {
err := interp.Dir(path)(r)
if err == nil {
return nil
}
// If the specified directory doesn't exist, it will be created later.
// Therefore, even if `interp.Dir` method returns an error, the
// directory path should be set only when the directory cannot be found.
if absPath, _ := filepath.Abs(path); absPath != "" {
if _, err := os.Stat(absPath); os.IsNotExist(err) {
r.Dir = absPath
return nil
}
}
r := interp.Runner{
Context: opts.Context,
Dir: opts.Dir,
Env: opts.Env,
Exec: interp.DefaultExec,
Open: interp.OpenDevImpls(interp.DefaultOpen),
Stdin: opts.Stdin,
Stdout: opts.Stdout,
Stderr: opts.Stderr,
}
if err = r.Reset(); err != nil {
return err
}
return r.Run(p)
}

View File

@@ -1,24 +0,0 @@
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

@@ -1,90 +1,38 @@
package logger
import (
"fmt"
"io"
"os"
"strconv"
"github.com/fatih/color"
)
type Color func() PrintFunc
type PrintFunc func(io.Writer, string, ...interface{})
func Default() PrintFunc {
return color.New(envColor("TASK_COLOR_RESET", color.Reset)).FprintfFunc()
}
func Blue() PrintFunc {
return color.New(envColor("TASK_COLOR_BLUE", color.FgBlue)).FprintfFunc()
}
func Green() PrintFunc {
return color.New(envColor("TASK_COLOR_GREEN", color.FgGreen)).FprintfFunc()
}
func Cyan() PrintFunc {
return color.New(envColor("TASK_COLOR_CYAN", color.FgCyan)).FprintfFunc()
}
func Yellow() PrintFunc {
return color.New(envColor("TASK_COLOR_YELLOW", color.FgYellow)).FprintfFunc()
}
func Magenta() PrintFunc {
return color.New(envColor("TASK_COLOR_MAGENTA", color.FgMagenta)).FprintfFunc()
}
func Red() PrintFunc {
return color.New(envColor("TASK_COLOR_RED", color.FgRed)).FprintfFunc()
}
func envColor(env string, defaultColor color.Attribute) color.Attribute {
override, err := strconv.Atoi(os.Getenv(env))
if err == nil {
return color.Attribute(override)
}
return defaultColor
}
// Logger is just a wrapper that prints stuff to STDOUT or STDERR,
// with optional color.
type Logger struct {
Stdout io.Writer
Stderr io.Writer
Verbose bool
Color bool
}
// Outf prints stuff to STDOUT.
func (l *Logger) Outf(color Color, s string, args ...interface{}) {
func (l *Logger) Outf(s string, args ...interface{}) {
if len(args) == 0 {
s, args = "%s", []interface{}{s}
}
if !l.Color {
color = Default
}
print := color()
print(l.Stdout, s+"\n", args...)
fmt.Fprintf(l.Stdout, s+"\n", args...)
}
// VerboseOutf prints stuff to STDOUT if verbose mode is enabled.
func (l *Logger) VerboseOutf(color Color, s string, args ...interface{}) {
func (l *Logger) VerboseOutf(s string, args ...interface{}) {
if l.Verbose {
l.Outf(color, s, args...)
l.Outf(s, args...)
}
}
// Errf prints stuff to STDERR.
func (l *Logger) Errf(color Color, s string, args ...interface{}) {
func (l *Logger) Errf(s string, args ...interface{}) {
if len(args) == 0 {
s, args = "%s", []interface{}{s}
}
if !l.Color {
color = Default
}
print := color()
print(l.Stderr, s+"\n", args...)
fmt.Fprintf(l.Stderr, s+"\n", args...)
}
// VerboseErrf prints stuff to STDERR if verbose mode is enabled.
func (l *Logger) VerboseErrf(color Color, s string, args ...interface{}) {
func (l *Logger) VerboseErrf(s string, args ...interface{}) {
if l.Verbose {
l.Errf(color, s, args...)
l.Errf(s, args...)
}
}

View File

@@ -1,44 +0,0 @@
package output
import (
"bytes"
"io"
)
type Group struct {
Begin, End string
}
func (g Group) WrapWriter(stdOut, _ io.Writer, _ string, tmpl Templater) (io.Writer, io.Writer, CloseFunc) {
gw := &groupWriter{writer: stdOut}
if g.Begin != "" {
gw.begin = tmpl.Replace(g.Begin) + "\n"
}
if g.End != "" {
gw.end = tmpl.Replace(g.End) + "\n"
}
return gw, gw, func() error { return gw.close() }
}
type groupWriter struct {
writer io.Writer
buff bytes.Buffer
begin, end string
}
func (gw *groupWriter) Write(p []byte) (int, error) {
return gw.buff.Write(p)
}
func (gw *groupWriter) close() error {
if gw.buff.Len() == 0 {
// don't print begin/end messages if there's no buffered entries
return nil
}
if _, err := io.WriteString(gw.writer, gw.begin); err != nil {
return err
}
gw.buff.WriteString(gw.end)
_, err := io.Copy(gw.writer, &gw.buff)
return err
}

View File

@@ -1,11 +0,0 @@
package output
import (
"io"
)
type Interleaved struct{}
func (Interleaved) WrapWriter(stdOut, stdErr io.Writer, _ string, _ Templater) (io.Writer, io.Writer, CloseFunc) {
return stdOut, stdErr, func() error { return nil }
}

View File

@@ -1,51 +0,0 @@
package output
import (
"fmt"
"io"
"github.com/go-task/task/v3/taskfile"
)
// Templater executes a template engine.
// It is provided by the templater.Templater package.
type Templater interface {
// Replace replaces the provided template string with a rendered string.
Replace(tmpl string) string
}
type Output interface {
WrapWriter(stdOut, stdErr io.Writer, prefix string, tmpl Templater) (io.Writer, io.Writer, CloseFunc)
}
type CloseFunc func() error
// Build the Output for the requested taskfile.Output.
func BuildFor(o *taskfile.Output) (Output, error) {
switch o.Name {
case "interleaved", "":
if err := checkOutputGroupUnset(o); err != nil {
return nil, err
}
return Interleaved{}, nil
case "group":
return Group{
Begin: o.Group.Begin,
End: o.Group.End,
}, nil
case "prefixed":
if err := checkOutputGroupUnset(o); err != nil {
return nil, err
}
return Prefixed{}, nil
default:
return nil, fmt.Errorf(`task: output style %q not recognized`, o.Name)
}
}
func checkOutputGroupUnset(o *taskfile.Output) error {
if o.Group.IsSet() {
return fmt.Errorf("task: output style %q does not support the group begin/end parameter", o.Name)
}
return nil
}

View File

@@ -1,104 +0,0 @@
package output_test
import (
"bytes"
"fmt"
"io"
"testing"
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3/internal/output"
)
func TestInterleaved(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Interleaved{}
var w, _, _ = o.WrapWriter(&b, io.Discard, "", nil)
fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, "foo\nbar\n", b.String())
fmt.Fprintln(w, "baz")
assert.Equal(t, "foo\nbar\nbaz\n", b.String())
}
func TestGroup(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Group{}
var stdOut, stdErr, cleanup = o.WrapWriter(&b, io.Discard, "", nil)
fmt.Fprintln(stdOut, "out\nout")
assert.Equal(t, "", b.String())
fmt.Fprintln(stdErr, "err\nerr")
assert.Equal(t, "", b.String())
fmt.Fprintln(stdOut, "out")
assert.Equal(t, "", b.String())
fmt.Fprintln(stdErr, "err")
assert.Equal(t, "", b.String())
assert.NoError(t, cleanup())
assert.Equal(t, "out\nout\nerr\nerr\nout\nerr\n", b.String())
}
func TestGroupWithBeginEnd(t *testing.T) {
tmpl := templater.Templater{
Vars: &taskfile.Vars{
Keys: []string{"VAR1"},
Mapping: map[string]taskfile.Var{
"VAR1": {Static: "example-value"},
},
},
}
var o output.Output = output.Group{
Begin: "::group::{{ .VAR1 }}",
End: "::endgroup::",
}
t.Run("simple", func(t *testing.T) {
var b bytes.Buffer
var w, _, cleanup = o.WrapWriter(&b, io.Discard, "", &tmpl)
fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, "", b.String())
fmt.Fprintln(w, "baz")
assert.Equal(t, "", b.String())
assert.NoError(t, cleanup())
assert.Equal(t, "::group::example-value\nfoo\nbar\nbaz\n::endgroup::\n", b.String())
})
t.Run("no output", func(t *testing.T) {
var b bytes.Buffer
var _, _, cleanup = o.WrapWriter(&b, io.Discard, "", &tmpl)
assert.NoError(t, cleanup())
assert.Equal(t, "", b.String())
})
}
func TestPrefixed(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Prefixed{}
var w, _, cleanup = o.WrapWriter(&b, io.Discard, "prefix", nil)
t.Run("simple use cases", func(t *testing.T) {
b.Reset()
fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, "[prefix] foo\n[prefix] bar\n", b.String())
fmt.Fprintln(w, "baz")
assert.Equal(t, "[prefix] foo\n[prefix] bar\n[prefix] baz\n", b.String())
assert.NoError(t, cleanup())
})
t.Run("multiple writes for a single line", func(t *testing.T) {
b.Reset()
for _, char := range []string{"T", "e", "s", "t", "!"} {
fmt.Fprint(w, char)
assert.Equal(t, "", b.String())
}
assert.NoError(t, cleanup())
assert.Equal(t, "[prefix] Test!\n", b.String())
})
}

View File

@@ -1,66 +0,0 @@
package output
import (
"bytes"
"fmt"
"io"
"strings"
)
type Prefixed struct{}
func (Prefixed) WrapWriter(stdOut, _ io.Writer, prefix string, _ Templater) (io.Writer, io.Writer, CloseFunc) {
pw := &prefixWriter{writer: stdOut, prefix: prefix}
return pw, pw, func() error { return pw.close() }
}
type prefixWriter struct {
writer io.Writer
prefix string
buff bytes.Buffer
}
func (pw *prefixWriter) Write(p []byte) (int, error) {
n, err := pw.buff.Write(p)
if err != nil {
return n, err
}
return n, pw.writeOutputLines(false)
}
func (pw *prefixWriter) close() error {
return pw.writeOutputLines(true)
}
func (pw *prefixWriter) writeOutputLines(force bool) error {
for {
switch line, err := pw.buff.ReadString('\n'); err {
case nil:
if err = pw.writeLine(line); err != nil {
return err
}
case io.EOF:
// if this line was not a complete line, re-add to the buffer
if !force && !strings.HasSuffix(line, "\n") {
_, err = pw.buff.WriteString(line)
return err
}
return pw.writeLine(line)
default:
return err
}
}
}
func (pw *prefixWriter) writeLine(line string) error {
if line == "" {
return nil
}
if !strings.HasSuffix(line, "\n") {
line += "\n"
}
_, err := fmt.Fprintf(pw.writer, "[%s] %s", pw.prefix, line)
return err
}

View File

@@ -1,176 +0,0 @@
// This code is released under the MIT License
// Copyright (c) 2020 Marco Molteni and the timeit contributors.
package main
import (
"flag"
"fmt"
"os"
"os/signal"
"time"
)
const usage = `sleepit: sleep for the specified duration, optionally handling signals
When the line "sleepit: ready" is printed, it means that it is safe to send signals to it
Usage: sleepit <command> [<args>]
Commands
default Use default action: on reception of SIGINT terminate abruptly
handle Handle signals: on reception of SIGINT perform cleanup before exiting
version Show the sleepit version`
var (
// Filled by the linker.
fullVersion = "unknown" // example: v0.0.9-8-g941583d027-dirty
)
func main() {
os.Exit(run(os.Args[1:]))
}
func run(args []string) int {
if len(args) < 1 {
fmt.Fprintln(os.Stderr, usage)
return 2
}
defaultCmd := flag.NewFlagSet("default", flag.ExitOnError)
defaultSleep := defaultCmd.Duration("sleep", 5*time.Second, "Sleep duration")
handleCmd := flag.NewFlagSet("handle", flag.ExitOnError)
handleSleep := handleCmd.Duration("sleep", 5*time.Second, "Sleep duration")
handleCleanup := handleCmd.Duration("cleanup", 5*time.Second, "Cleanup duration")
handleTermAfter := handleCmd.Int("term-after", 0,
"Terminate immediately after `N` signals.\n"+
"Default is to terminate only when the cleanup phase has completed.")
versionCmd := flag.NewFlagSet("version", flag.ExitOnError)
switch args[0] {
case "default":
_ = defaultCmd.Parse(args[1:])
if len(defaultCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "default: unexpected arguments: %v\n", defaultCmd.Args())
return 2
}
return supervisor(*defaultSleep, 0, 0, nil)
case "handle":
_ = handleCmd.Parse(args[1:])
if *handleTermAfter == 1 {
fmt.Fprintf(os.Stderr, "handle: term-after cannot be 1\n")
return 2
}
if len(handleCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "handle: unexpected arguments: %v\n", handleCmd.Args())
return 2
}
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt) // Ctrl-C -> SIGINT
return supervisor(*handleSleep, *handleCleanup, *handleTermAfter, sigCh)
case "version":
_ = versionCmd.Parse(args[1:])
if len(versionCmd.Args()) > 0 {
fmt.Fprintf(os.Stderr, "version: unexpected arguments: %v\n", versionCmd.Args())
return 2
}
fmt.Printf("sleepit version %s\n", fullVersion)
return 0
default:
fmt.Fprintln(os.Stderr, usage)
return 2
}
}
func supervisor(
sleep time.Duration,
cleanup time.Duration,
termAfter int,
sigCh <-chan os.Signal,
) int {
fmt.Printf("sleepit: ready\n")
fmt.Printf("sleepit: PID=%d sleep=%v cleanup=%v\n",
os.Getpid(), sleep, cleanup)
cancelWork := make(chan struct{})
workerDone := worker(cancelWork, sleep, "work")
cancelCleaner := make(chan struct{})
var cleanerDone <-chan struct{}
sigCount := 0
for {
select {
case sig := <-sigCh:
sigCount++
fmt.Printf("sleepit: got signal=%s count=%d\n", sig, sigCount)
if sigCount == 1 {
// since `cancelWork` is unbuffered, sending will be synchronous:
// we are ensured that the worker has terminated before starting cleanup.
// This is important in some real-life situations.
cancelWork <- struct{}{}
cleanerDone = worker(cancelCleaner, cleanup, "cleanup")
}
if sigCount == termAfter {
cancelCleaner <- struct{}{}
return 4
}
case <-workerDone:
return 0
case <-cleanerDone:
return 3
}
}
}
// Start a worker goroutine and return immediately a `workerDone` channel.
// The goroutine will prepend its prints with the prefix `name`.
// The goroutine will simulate some work and will terminate when one of the following
// conditions happens:
// 1. When `howlong` is elapsed. This case will be signaled on the `workerDone` channel.
// 2. When something happens on channel `canceled`. Note that this simulates real-life,
// so cancellation is not instantaneous: if the caller wants a synchronous cancel,
// it should send a message; if instead it wants an asynchronous cancel, it should
// close the channel.
func worker(
canceled <-chan struct{},
howlong time.Duration,
name string,
) <-chan struct{} {
workerDone := make(chan struct{})
deadline := time.Now().Add(howlong)
go func() {
fmt.Printf("sleepit: %s started\n", name)
for {
select {
case <-canceled:
fmt.Printf("sleepit: %s canceled\n", name)
return
default:
if doSomeWork(deadline) {
fmt.Printf("sleepit: %s done\n", name) // <== NOTE THIS LINE
workerDone <- struct{}{}
return
}
}
}
}()
return workerDone
}
// Do some work and then return, so that the caller can decide wether to continue or not.
// Return true when all work is done.
func doSomeWork(deadline time.Time) bool {
if time.Now().After(deadline) {
return true
}
timeout := 100 * time.Millisecond
time.Sleep(timeout)
return false
}

View File

@@ -4,6 +4,7 @@ import (
"crypto/md5"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
@@ -13,26 +14,19 @@ import (
// Checksum validades if a task is up to date by calculating its source
// files checksum
type Checksum struct {
TempDir string
TaskDir string
Task string
Sources []string
Generates []string
Dry bool
Dir string
Task string
Sources []string
}
// IsUpToDate implements the Checker interface
func (c *Checksum) IsUpToDate() (bool, error) {
if len(c.Sources) == 0 {
return false, nil
}
checksumFile := c.checksumFilePath()
data, _ := os.ReadFile(checksumFile)
data, _ := ioutil.ReadFile(checksumFile)
oldMd5 := strings.TrimSpace(string(data))
sources, err := globs(c.TaskDir, c.Sources)
sources, err := glob(c.Dir, c.Sources)
if err != nil {
return false, err
}
@@ -42,29 +36,10 @@ func (c *Checksum) IsUpToDate() (bool, error) {
return false, nil
}
if !c.Dry {
_ = os.MkdirAll(filepath.Join(c.TempDir, "checksum"), 0755)
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
return false, err
}
_ = os.MkdirAll(filepath.Join(c.Dir, ".task", "checksum"), 0755)
if err = ioutil.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
return false, err
}
if len(c.Generates) > 0 {
// For each specified 'generates' field, check whether the files actually exist
for _, g := range c.Generates {
generates, err := Glob(c.TaskDir, g)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, err
}
if len(generates) == 0 {
return false, nil
}
}
}
return oldMd5 == newMd5, nil
}
@@ -72,14 +47,21 @@ func (c *Checksum) checksum(files ...string) (string, error) {
h := md5.New()
for _, f := range files {
// also sum the filename, so checksum changes for renaming a file
if _, err := io.Copy(h, strings.NewReader(filepath.Base(f))); err != nil {
return "", err
}
f, err := os.Open(f)
if err != nil {
return "", err
}
info, err := f.Stat()
if err != nil {
return "", err
}
if info.IsDir() {
continue
}
// also sum the filename, so checksum changes for renaming a file
if _, err = io.Copy(h, strings.NewReader(info.Name())); err != nil {
return "", err
}
if _, err = io.Copy(h, f); err != nil {
return "", err
}
@@ -88,26 +70,13 @@ func (c *Checksum) checksum(files ...string) (string, error) {
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
// Value implements the Checker Interface
func (c *Checksum) Value() (interface{}, error) {
return c.checksum()
}
// OnError implements the Checker interface
func (c *Checksum) OnError() error {
if len(c.Sources) == 0 {
return nil
}
return os.Remove(c.checksumFilePath())
}
// Kind implements the Checker Interface
func (*Checksum) Kind() string {
return "checksum"
}
func (c *Checksum) checksumFilePath() string {
return filepath.Join(c.TempDir, "checksum", c.normalizeFilename(c.Task))
return filepath.Join(c.Dir, ".task", "checksum", c.normalizeFilename(c.Task))
}
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")

View File

@@ -1,50 +1,28 @@
package status
import (
"os"
"path/filepath"
"sort"
"github.com/mattn/go-zglob"
"github.com/go-task/task/v3/internal/execext"
"github.com/mitchellh/go-homedir"
)
func globs(dir string, globs []string) ([]string, error) {
files := make([]string, 0)
func glob(dir string, globs []string) (files []string, err error) {
for _, g := range globs {
f, err := Glob(dir, g)
if !filepath.IsAbs(g) {
g = filepath.Join(dir, g)
}
g, err = homedir.Expand(g)
if err != nil {
continue
return nil, err
}
f, err := zglob.Glob(g)
if err != nil {
return nil, err
}
files = append(files, f...)
}
sort.Strings(files)
return files, nil
}
func Glob(dir string, g string) ([]string, error) {
files := make([]string, 0)
if !filepath.IsAbs(g) {
g = filepath.Join(dir, g)
}
g, err := execext.Expand(g)
if err != nil {
return nil, err
}
fs, err := zglob.Glob(g)
if err != nil {
return nil, err
}
for _, f := range fs {
info, err := os.Stat(f)
if err != nil {
return nil, err
}
if info.IsDir() {
continue
}
files = append(files, f)
}
return files, nil
return
}

View File

@@ -8,15 +8,6 @@ func (None) IsUpToDate() (bool, error) {
return false, nil
}
// Value implements the Checker interface
func (None) Value() (interface{}, error) {
return "", nil
}
func (None) Kind() string {
return "none"
}
// OnError implements the Checker interface
func (None) OnError() error {
return nil

View File

@@ -9,7 +9,5 @@ var (
// Checker is an interface that checks if the status is up-to-date
type Checker interface {
IsUpToDate() (bool, error)
Value() (interface{}, error)
OnError() error
Kind() string
}

View File

@@ -19,11 +19,11 @@ func (t *Timestamp) IsUpToDate() (bool, error) {
return false, nil
}
sources, err := globs(t.Dir, t.Sources)
sources, err := glob(t.Dir, t.Sources)
if err != nil {
return false, nil
}
generates, err := globs(t.Dir, t.Generates)
generates, err := glob(t.Dir, t.Generates)
if err != nil {
return false, nil
}
@@ -41,29 +41,6 @@ func (t *Timestamp) IsUpToDate() (bool, error) {
return !generatesMinTime.Before(sourcesMaxTime), nil
}
func (t *Timestamp) Kind() string {
return "timestamp"
}
// Value implements the Checker Interface
func (t *Timestamp) Value() (interface{}, error) {
sources, err := globs(t.Dir, t.Sources)
if err != nil {
return time.Now(), err
}
sourcesMaxTime, err := getMaxTime(sources...)
if err != nil {
return time.Now(), err
}
if sourcesMaxTime.IsZero() {
return time.Unix(0, 0), nil
}
return sourcesMaxTime, nil
}
func getMinTime(files ...string) (time.Time, error) {
var t time.Time
for _, f := range files {

View File

@@ -1,103 +0,0 @@
package summary
import (
"strings"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/taskfile"
)
func PrintTasks(l *logger.Logger, t *taskfile.Taskfile, c []taskfile.Call) {
for i, call := range c {
PrintSpaceBetweenSummaries(l, i)
PrintTask(l, t.Tasks[call.Task])
}
}
func PrintSpaceBetweenSummaries(l *logger.Logger, i int) {
spaceRequired := i > 0
if !spaceRequired {
return
}
l.Outf(logger.Default, "")
l.Outf(logger.Default, "")
}
func PrintTask(l *logger.Logger, t *taskfile.Task) {
printTaskName(l, t)
printTaskDescribingText(t, l)
printTaskDependencies(l, t)
printTaskCommands(l, t)
}
func printTaskDescribingText(t *taskfile.Task, l *logger.Logger) {
if hasSummary(t) {
printTaskSummary(l, t)
} else if hasDescription(t) {
printTaskDescription(l, t)
} else {
printNoDescriptionOrSummary(l)
}
}
func hasSummary(t *taskfile.Task) bool {
return t.Summary != ""
}
func printTaskSummary(l *logger.Logger, t *taskfile.Task) {
lines := strings.Split(t.Summary, "\n")
for i, line := range lines {
notLastLine := i+1 < len(lines)
if notLastLine || line != "" {
l.Outf(logger.Default, line)
}
}
}
func printTaskName(l *logger.Logger, t *taskfile.Task) {
l.Outf(logger.Default, "task: %s", t.Name())
l.Outf(logger.Default, "")
}
func hasDescription(t *taskfile.Task) bool {
return t.Desc != ""
}
func printTaskDescription(l *logger.Logger, t *taskfile.Task) {
l.Outf(logger.Default, t.Desc)
}
func printNoDescriptionOrSummary(l *logger.Logger) {
l.Outf(logger.Default, "(task does not have description or summary)")
}
func printTaskDependencies(l *logger.Logger, t *taskfile.Task) {
if len(t.Deps) == 0 {
return
}
l.Outf(logger.Default, "")
l.Outf(logger.Default, "dependencies:")
for _, d := range t.Deps {
l.Outf(logger.Default, " - %s", d.Task)
}
}
func printTaskCommands(l *logger.Logger, t *taskfile.Task) {
if len(t.Cmds) == 0 {
return
}
l.Outf(logger.Default, "")
l.Outf(logger.Default, "commands:")
for _, c := range t.Cmds {
isCommand := c.Cmd != ""
if isCommand {
l.Outf(logger.Default, " - %s", c.Cmd)
} else {
l.Outf(logger.Default, " - Task: %s", c.Task)
}
}
}

View File

@@ -1,173 +0,0 @@
package summary_test
import (
"bytes"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/summary"
"github.com/go-task/task/v3/taskfile"
)
func TestPrintsDependenciesIfPresent(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Deps: []*taskfile.Dep{
{Task: "dep1"},
{Task: "dep2"},
{Task: "dep3"},
},
}
summary.PrintTask(&l, task)
assert.Contains(t, buffer.String(), "\ndependencies:\n - dep1\n - dep2\n - dep3\n")
}
func createDummyLogger() (*bytes.Buffer, logger.Logger) {
buffer := &bytes.Buffer{}
l := logger.Logger{
Stderr: buffer,
Stdout: buffer,
Verbose: false,
}
return buffer, l
}
func TestDoesNotPrintDependenciesIfMissing(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Deps: []*taskfile.Dep{},
}
summary.PrintTask(&l, task)
assert.NotContains(t, buffer.String(), "dependencies:")
}
func TestPrintTaskName(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Task: "my-task-name",
}
summary.PrintTask(&l, task)
assert.Contains(t, buffer.String(), "task: my-task-name\n")
}
func TestPrintTaskCommandsIfPresent(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Cmds: []*taskfile.Cmd{
{Cmd: "command-1"},
{Cmd: "command-2"},
{Task: "task-1"},
},
}
summary.PrintTask(&l, task)
assert.Contains(t, buffer.String(), "\ncommands:\n")
assert.Contains(t, buffer.String(), "\n - command-1\n")
assert.Contains(t, buffer.String(), "\n - command-2\n")
assert.Contains(t, buffer.String(), "\n - Task: task-1\n")
}
func TestDoesNotPrintCommandIfMissing(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Cmds: []*taskfile.Cmd{},
}
summary.PrintTask(&l, task)
assert.NotContains(t, buffer.String(), "commands")
}
func TestLayout(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Task: "sample-task",
Summary: "line1\nline2\nline3\n",
Deps: []*taskfile.Dep{
{Task: "dependency"},
},
Cmds: []*taskfile.Cmd{
{Cmd: "command"},
},
}
summary.PrintTask(&l, task)
assert.Equal(t, expectedOutput(), buffer.String())
}
func expectedOutput() string {
expected := `task: sample-task
line1
line2
line3
dependencies:
- dependency
commands:
- command
`
return expected
}
func TestPrintDescriptionAsFallback(t *testing.T) {
buffer, l := createDummyLogger()
taskWithoutSummary := &taskfile.Task{
Desc: "description",
}
taskWithSummary := &taskfile.Task{
Desc: "description",
Summary: "summary",
}
taskWithoutSummaryOrDescription := &taskfile.Task{}
summary.PrintTask(&l, taskWithoutSummary)
assert.Contains(t, buffer.String(), "description")
buffer.Reset()
summary.PrintTask(&l, taskWithSummary)
assert.NotContains(t, buffer.String(), "description")
buffer.Reset()
summary.PrintTask(&l, taskWithoutSummaryOrDescription)
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n")
}
func TestPrintAllWithSpaces(t *testing.T) {
buffer, l := createDummyLogger()
t1 := &taskfile.Task{Task: "t1"}
t2 := &taskfile.Task{Task: "t2"}
t3 := &taskfile.Task{Task: "t3"}
tasks := make(taskfile.Tasks, 3)
tasks["t1"] = t1
tasks["t2"] = t2
tasks["t3"] = t3
summary.PrintTasks(&l,
&taskfile.Taskfile{Tasks: tasks},
[]taskfile.Call{{Task: "t1"}, {Task: "t2"}, {Task: "t3"}})
assert.True(t, strings.HasPrefix(buffer.String(), "task: t1"))
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n\n\ntask: t2")
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n\n\ntask: t3")
}

View File

@@ -3,5 +3,5 @@ package taskfile
// Call is the parameters to a task call
type Call struct {
Task string
Vars *Vars
Vars Vars
}

Some files were not shown because too many files have changed in this diff Show More