mirror of
https://github.com/go-task/task.git
synced 2026-05-18 21:26:37 +02:00
Compare commits
57 Commits
bump-versi
...
v3.45.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbde227167 | ||
|
|
fc06e92a87 | ||
|
|
a0cab3f5ec | ||
|
|
bb4c254211 | ||
|
|
57bf348829 | ||
|
|
092b9b6391 | ||
|
|
cd8c831204 | ||
|
|
0d03f4f266 | ||
|
|
b8bf298c84 | ||
|
|
9a91c4cb21 | ||
|
|
2921450bf7 | ||
|
|
dffa355cad | ||
|
|
48039be12c | ||
|
|
43cb64e6cc | ||
|
|
25a7b5936f | ||
|
|
4ae3071845 | ||
|
|
242523c797 | ||
|
|
0fdb5e8665 | ||
|
|
534dfa089c | ||
|
|
51a3bcaacd | ||
|
|
6289fcf34c | ||
|
|
2959737d7d | ||
|
|
a3047d3cd8 | ||
|
|
725600f220 | ||
|
|
fd83414074 | ||
|
|
6c645a33f7 | ||
|
|
9d969e5971 | ||
|
|
8b382a3bae | ||
|
|
a34892ad94 | ||
|
|
e55bb29554 | ||
|
|
1168ef32df | ||
|
|
245d7f747f | ||
|
|
b216ae885c | ||
|
|
61cb15ad01 | ||
|
|
04579c0c44 | ||
|
|
39462cbfde | ||
|
|
72dfec68b0 | ||
|
|
f89c12ddf0 | ||
|
|
c903d07332 | ||
|
|
138b9a5a4f | ||
|
|
1e2121a99f | ||
|
|
9495fb2b1c | ||
|
|
1fda55910e | ||
|
|
e6c808c02b | ||
|
|
0fc26a43a9 | ||
|
|
c0b4c19443 | ||
|
|
1a8df44e9e | ||
|
|
82ad1de8d0 | ||
|
|
d59c795502 | ||
|
|
504cb94e8b | ||
|
|
e7606635fe | ||
|
|
9a05ceaa80 | ||
|
|
083654d8c9 | ||
|
|
79c93fb42b | ||
|
|
64fc538a16 | ||
|
|
4da081e5c3 | ||
|
|
4bdfe5ce3b |
@@ -8,6 +8,6 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = tab
|
||||
|
||||
[*.{md,mdx,yml,yaml,json,toml,htm,html,js,ts,css,svg,sh,bash,fish}]
|
||||
[*.{md,mdx,yml,yaml,json,toml,htm,html,js,ts,vue,css,svg,sh,bash,fish}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
41
.github/workflows/lint.yml
vendored
41
.github/workflows/lint.yml
vendored
@@ -13,14 +13,14 @@ jobs:
|
||||
name: Lint
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x, 1.24.x]
|
||||
go-version: [1.24.x, 1.25.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{matrix.go-version}}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
@@ -32,43 +32,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
python-version: 3.13
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: install check-jsonschema
|
||||
run: python -m pip install 'check-jsonschema==0.27.3'
|
||||
|
||||
- name: check-jsonschema (metaschema)
|
||||
run: check-jsonschema --check-metaschema website/static/schema.json
|
||||
check_doc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Get changed files in the docs folder
|
||||
id: changed-files-specific
|
||||
uses: tj-actions/changed-files@v46
|
||||
with:
|
||||
files: website/versioned_docs/**
|
||||
|
||||
- uses: actions/github-script@v7
|
||||
if: steps.changed-files-specific.outputs.any_changed == 'true'
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('website/versioned_docs has changed. Instead you need to update the docs in the website/docs folder.')
|
||||
check_schema:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Get changed files in the docs folder
|
||||
id: changed-files-specific
|
||||
uses: tj-actions/changed-files@v46
|
||||
with:
|
||||
files: |
|
||||
website/static/schema.json
|
||||
website/static/schema-taskrc.json
|
||||
- uses: actions/github-script@v7
|
||||
if: steps.changed-files-specific.outputs.any_changed == 'true'
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('schema.json or schema-taskrc.json has changed. Instead you need to update next-schema.json or next-schema-taskrc.json.')
|
||||
run: check-jsonschema --check-metaschema website/src/public/schema.json
|
||||
|
||||
9
.github/workflows/release-nightly.yml
vendored
9
.github/workflows/release-nightly.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Realease nightly
|
||||
name: Release nightly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -9,21 +9,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.25.x
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: latest
|
||||
args: release --clean --nightly
|
||||
args: release --clean --nightly -f .goreleaser-nightly.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
||||
CLOUDSMITH_TOKEN: ${{secrets.CLOUDSMITH_TOKEN}}
|
||||
|
||||
27
.github/workflows/release.yml
vendored
27
.github/workflows/release.yml
vendored
@@ -10,21 +10,42 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.25.x
|
||||
|
||||
- name: npm-login
|
||||
run: |
|
||||
npm config set '//registry.npmjs.org/:_authToken'=${{ secrets.NPM_TOKEN }}
|
||||
- name: Install Task
|
||||
uses: go-task/setup-task@v1
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.x'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: latest
|
||||
args: release --clean
|
||||
args: release --clean --draft
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
||||
CLOUDSMITH_TOKEN: ${{secrets.CLOUDSMITH_TOKEN}}
|
||||
|
||||
- name: Deploy Website
|
||||
shell: bash
|
||||
run: |
|
||||
task website:deploy:prod
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Test
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x, 1.24.x]
|
||||
go-version: [1.24.x, 1.25.x]
|
||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{matrix.platform}}
|
||||
steps:
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Download Go modules
|
||||
run: go mod download
|
||||
|
||||
15
.goreleaser-nightly.yml
Normal file
15
.goreleaser-nightly.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||
version: 2
|
||||
pro: true
|
||||
|
||||
release:
|
||||
name_template: 'v{{.Version}}'
|
||||
|
||||
nightly:
|
||||
publish_release: true
|
||||
keep_single_release: true
|
||||
version_template: "{{incminor .Version}}-nightly"
|
||||
|
||||
includes:
|
||||
- from_file:
|
||||
path: ./.goreleaser.yml
|
||||
@@ -30,7 +30,8 @@ builds:
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w # Don't set main.version.
|
||||
- "-s -w"
|
||||
- "{{if .IsNightly}}-X github.com/go-task/task/v3/internal/version.version={{.Version}}{{end}}"
|
||||
|
||||
gomod:
|
||||
proxy: true
|
||||
@@ -45,19 +46,10 @@ archives:
|
||||
- goos: windows
|
||||
formats: [zip]
|
||||
|
||||
release:
|
||||
draft: true
|
||||
|
||||
|
||||
git:
|
||||
ignore_tags:
|
||||
- "{{if not .IsNightly}}nightly{{end}}"
|
||||
|
||||
nightly:
|
||||
publish_release: true
|
||||
keep_single_release: true
|
||||
version_template: "{{incminor .Version}}-nightly"
|
||||
|
||||
snapshot:
|
||||
version_template: '{{.Version}}'
|
||||
|
||||
@@ -76,6 +68,7 @@ nfpms:
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
- apk
|
||||
file_name_template: '{{.ProjectName}}_{{.Os}}_{{.Arch}}'
|
||||
contents:
|
||||
- src: completion/bash/task.bash
|
||||
@@ -143,3 +136,37 @@ winget:
|
||||
owner: microsoft
|
||||
name: winget-pkgs
|
||||
branch: master
|
||||
|
||||
|
||||
npms:
|
||||
- name: "@go-task/cli"
|
||||
repository: "git+https://github.com/go-task/task.git"
|
||||
bugs: https://github.com/go-task/task/issues
|
||||
description: A task runner / simpler Make alternative written in Go
|
||||
homepage: https://taskfile.dev
|
||||
license: MIT
|
||||
author: "The Task authors"
|
||||
access: public
|
||||
keywords:
|
||||
- "task"
|
||||
- "taskfile"
|
||||
- "build-tool"
|
||||
- "task-runner"
|
||||
|
||||
|
||||
cloudsmiths:
|
||||
- organization: "task"
|
||||
repository: "{{if not .IsNightly}}task{{end}}"
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
- apk
|
||||
distributions:
|
||||
deb:
|
||||
- "any-distro/any-version"
|
||||
rpm:
|
||||
- "any-distro/any-version"
|
||||
alpine:
|
||||
- "alpine/any-version"
|
||||
component: main
|
||||
republish: true
|
||||
|
||||
53
CHANGELOG.md
53
CHANGELOG.md
@@ -1,5 +1,58 @@
|
||||
# Changelog
|
||||
|
||||
## v3.45.1 - 2025-09-15
|
||||
|
||||
- Task now includes built-in core utilities to greatly improve compatibility on
|
||||
Windows. This means that your commands that uses `cp`, `mv`, `mkdir` or any
|
||||
other common core utility will now work by default on Windows, without extra
|
||||
setup. This is something we wanted to address for many many years, and it's
|
||||
finally being shipped!
|
||||
[Read our blog post this the topic](https://taskfile.dev/blog/windows-core-utils).
|
||||
(#197, #2360 by @andreynering).
|
||||
- :sparkles: Built and deployed a [brand new website](https://taskfile.dev)
|
||||
using [VitePress](https://vitepress.dev) (#2359, #2369, #2371, #2375, #2378 by
|
||||
@vmaerten, @andreynering, @pd93).
|
||||
- Began releasing
|
||||
[nightly builds](https://github.com/go-task/task/releases/tag/nightly). This
|
||||
will allow people to test our changes before they are fully released and
|
||||
without having to install Go to build them (#2358 by @vmaerten).
|
||||
- Added support for global config files in `$XDG_CONFIG_HOME/task/taskrc.yml` or
|
||||
`$HOME/.taskrc.yml`. Check out our new
|
||||
[configuration guide](https://taskfile.dev/docs/reference/config) for more
|
||||
details (#2247, #2380, #2390, #2391 by @vmaerten, @pd93).
|
||||
- Added experiments to the taskrc schema to clarify the expected keys and values
|
||||
(#2235 by @vmaerten).
|
||||
- Added support for new properties in `.taskrc.yml`: insecure, verbose,
|
||||
concurrency, remote offline, remote timeout, and remote expiry. :warning:
|
||||
Note: setting offline via environment variable is no longer supported. (#2389
|
||||
by @vmaerten)
|
||||
- Added a `--nested` flag when outputting tasks using `--list --json`. This will
|
||||
output tasks in a nested structure when tasks are namespaced (#2415 by @pd93).
|
||||
- Enhanced support for tasks with wildcards: they are now logged correctly, and
|
||||
wildcard parameters are fully considered during fingerprinting (#1808, #1795
|
||||
by @vmaerten).
|
||||
- Fixed panic when a variable was declared as an empty hash (`{}`) (#2416, #2417
|
||||
by @trulede).
|
||||
|
||||
#### Package API
|
||||
|
||||
- Bumped the minimum version of Go to 1.24 (#2358 by @vmaerten).
|
||||
|
||||
#### Other news
|
||||
|
||||
We recently released our
|
||||
[official GitHub Action](https://github.com/go-task/setup-task). This is based
|
||||
on the fantastic work by the Arduino team who created and maintained the
|
||||
community version. Now that this is officially adopted, fixes/updates should be
|
||||
more timely. We have already merged a couple of longstanding PRs in our
|
||||
[first release](https://github.com/go-task/setup-task/releases/tag/v1.0.0) (by
|
||||
@pd93, @shrink, @trim21 and all the previous contributors to
|
||||
[arduino/setup-task](https://github.com/arduino/setup-task/)).
|
||||
|
||||
## v3.45.0 - 2025-09-15
|
||||
|
||||
Failed due to an issue with our release process.
|
||||
|
||||
## v3.44.1 - 2025-07-23
|
||||
|
||||
- Internal tasks will no longer be shown as suggestions since they cannot be
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div align="center">
|
||||
<a href="https://taskfile.dev">
|
||||
<img src="website/static/img/logo.svg" width="200px" height="200px" />
|
||||
<img src="website/src/public/img/logo.svg" width="200px" height="200px" />
|
||||
</a>
|
||||
|
||||
<h1>Task</h1>
|
||||
@@ -19,7 +19,7 @@
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a target="_blank" href="https://devowl.io">
|
||||
<img src="/website/static/img/devowl.io.svg" height="100px" title="devowl.io" />
|
||||
<img src="https://devowl.io/wp-content/uploads/meta/favicon.webp" height="100px" title="devowl.io" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
25
Taskfile.yml
25
Taskfile.yml
@@ -8,6 +8,7 @@ includes:
|
||||
|
||||
vars:
|
||||
BIN: "{{.ROOT_DIR}}/bin"
|
||||
GOTESTSUM_FORMAT: '{{if .CI}}github-actions{{else}}pkgname{{end}}'
|
||||
|
||||
env:
|
||||
CGO_ENABLED: '0'
|
||||
@@ -131,29 +132,37 @@ tasks:
|
||||
test:
|
||||
desc: Runs test suite
|
||||
aliases: [t]
|
||||
deps: [gotestsum:install]
|
||||
sources:
|
||||
- "**/*.go"
|
||||
- "testdata/**/*"
|
||||
cmds:
|
||||
- go test ./...
|
||||
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' ./...
|
||||
|
||||
test:watch:
|
||||
desc: Runs test suite with watch tests included
|
||||
deps: [sleepit:build]
|
||||
deps: [sleepit:build, gotestsum:install]
|
||||
cmds:
|
||||
- go test ./... -tags 'watch'
|
||||
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' ./... -tags 'watch'
|
||||
|
||||
test:all:
|
||||
desc: Runs test suite with signals and watch tests included
|
||||
deps: [sleepit:build]
|
||||
deps: [sleepit:build, gotestsum:install]
|
||||
cmds:
|
||||
- go test -tags 'signals watch' ./...
|
||||
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' -tags 'signals watch' ./...
|
||||
|
||||
goreleaser:test:
|
||||
desc: Tests release process without publishing
|
||||
cmds:
|
||||
- goreleaser --snapshot --clean
|
||||
|
||||
gotestsum:install:
|
||||
desc: Installs gotestsum
|
||||
status:
|
||||
- command -v gotestsum
|
||||
cmds:
|
||||
- go install gotest.tools/gotestsum@latest
|
||||
|
||||
goreleaser:install:
|
||||
desc: Installs goreleaser
|
||||
cmds:
|
||||
@@ -203,7 +212,6 @@ tasks:
|
||||
Please wait for the CI to finish and then do the following:
|
||||
|
||||
- Copy the changelog for v{{.VERSION}} to the GitHub release
|
||||
- Publish the package to NPM with `task npm:publish`
|
||||
- Update and push the snapcraft manifest in https://github.com/go-task/snap/blob/main/snap/snapcraft.yaml
|
||||
preconditions:
|
||||
- sh: test $(git rev-parse --abbrev-ref HEAD) = "main"
|
||||
@@ -222,8 +230,3 @@ tasks:
|
||||
- "git push origin tag v{{.VERSION}}"
|
||||
- cmd: printf "%s" '{{.COMPLETE_MESSAGE}}'
|
||||
silent: true
|
||||
|
||||
npm:publish:
|
||||
desc: Publish release to npm
|
||||
cmds:
|
||||
- npm publish --access=public
|
||||
|
||||
@@ -3,33 +3,23 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/otiai10/copy"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
changelogSource = "CHANGELOG.md"
|
||||
changelogTarget = "website/docs/changelog.mdx"
|
||||
docsSource = "website/docs"
|
||||
docsTarget = "website/versioned_docs/version-latest"
|
||||
schemaSource = "website/static/next-schema.json"
|
||||
schemaTarget = "website/static/schema.json"
|
||||
schemaTaskrcSource = "website/static/next-schema-taskrc.json"
|
||||
schemaTaskrcTarget = "website/static/schema-taskrc.json"
|
||||
changelogSource = "CHANGELOG.md"
|
||||
changelogTarget = "website/src/docs/changelog.md"
|
||||
versionFile = "internal/version/version.txt"
|
||||
)
|
||||
|
||||
var (
|
||||
changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
|
||||
versionRegex = regexp.MustCompile(`(?m)^ "version": "\d+\.\d+\.\d+",$`)
|
||||
)
|
||||
var changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
|
||||
|
||||
// Flags
|
||||
var (
|
||||
@@ -53,7 +43,7 @@ func release() error {
|
||||
return errors.New("error: expected version number")
|
||||
}
|
||||
|
||||
version, err := getVersion()
|
||||
version, err := getVersion(versionFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -71,36 +61,18 @@ func release() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := setVersionFile("internal/version/version.txt", version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := setJSONVersion("package.json", version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := setJSONVersion("package-lock.json", version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := docs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := schema(); err != nil {
|
||||
if err := setVersionFile(versionFile, version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getVersion() (*semver.Version, error) {
|
||||
cmd := exec.Command("git", "describe", "--tags", "--abbrev=0")
|
||||
b, err := cmd.Output()
|
||||
func getVersion(filename string) (*semver.Version, error) {
|
||||
b, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return semver.NewVersion(strings.TrimSpace(string(b)))
|
||||
}
|
||||
|
||||
@@ -159,37 +131,3 @@ func changelog(version *semver.Version) error {
|
||||
func setVersionFile(fileName string, version *semver.Version) error {
|
||||
return os.WriteFile(fileName, []byte(version.String()+"\n"), 0o644)
|
||||
}
|
||||
|
||||
func setJSONVersion(fileName string, version *semver.Version) error {
|
||||
// Read the JSON file
|
||||
b, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Replace the version
|
||||
new := versionRegex.ReplaceAllString(string(b), fmt.Sprintf(` "version": "%s",`, version.String()))
|
||||
|
||||
// Write the JSON file
|
||||
return os.WriteFile(fileName, []byte(new), 0o644)
|
||||
}
|
||||
|
||||
func docs() error {
|
||||
if err := os.RemoveAll(docsTarget); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copy.Copy(docsSource, docsTarget); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func schema() error {
|
||||
if err := copy.Copy(schemaSource, schemaTarget); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copy.Copy(schemaTaskrcSource, schemaTaskrcTarget); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@ func run() error {
|
||||
flags.ListAll,
|
||||
flags.ListJson,
|
||||
flags.NoStatus,
|
||||
flags.Nested,
|
||||
)
|
||||
if listOptions.ShouldListTasks() {
|
||||
if flags.Silent {
|
||||
|
||||
@@ -162,7 +162,7 @@ func (v MissingVar) String() string {
|
||||
}
|
||||
|
||||
func (err *TaskMissingRequiredVarsError) Error() string {
|
||||
var vars []string
|
||||
vars := make([]string, 0, len(err.MissingVars))
|
||||
for _, v := range err.MissingVars {
|
||||
vars = append(vars, v.String())
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package task_test
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -189,7 +188,7 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
||||
}
|
||||
|
||||
// Run the task and check for errors
|
||||
ctx := context.Background()
|
||||
ctx := t.Context()
|
||||
if err := e.Run(ctx, call); tt.wantRunError {
|
||||
require.Error(t, err)
|
||||
tt.writeFixtureErrRun(t, g, err)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/joho/godotenv"
|
||||
|
||||
"github.com/go-task/task/v3/taskrc"
|
||||
"github.com/go-task/task/v3/taskrc/ast"
|
||||
)
|
||||
|
||||
const envPrefix = "TASK_X_"
|
||||
@@ -31,16 +32,15 @@ var (
|
||||
var xList []Experiment
|
||||
|
||||
func Parse(dir string) {
|
||||
config, _ := taskrc.GetConfig(dir)
|
||||
|
||||
ParseWithConfig(dir, config)
|
||||
}
|
||||
|
||||
func ParseWithConfig(dir string, config *ast.TaskRC) {
|
||||
// Read any .env files
|
||||
readDotEnv(dir)
|
||||
|
||||
// Create a node for the Task config reader
|
||||
node, _ := taskrc.NewNode("", dir)
|
||||
|
||||
// Read the Task config file
|
||||
reader := taskrc.NewReader()
|
||||
config, _ := reader.Read(node)
|
||||
|
||||
// Initialize the experiments
|
||||
GentleForce = New("GENTLE_FORCE", config, 1)
|
||||
RemoteTaskfiles = New("REMOTE_TASKFILES", config, 1)
|
||||
|
||||
21
go.mod
21
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/go-task/task/v3
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/Ladicle/tabwriter v1.0.0
|
||||
@@ -19,16 +19,16 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/otiai10/copy v1.14.1
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
||||
github.com/sajari/fuzzy v1.0.0
|
||||
github.com/sebdah/goldie/v2 v2.7.1
|
||||
github.com/spf13/pflag v1.0.7
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/zeebo/xxh3 v1.0.2
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/term v0.33.0
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/term v0.35.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/sh/moreinterp v0.0.0-20250807215248-5a1a658912aa
|
||||
mvdan.cc/sh/v3 v3.12.0
|
||||
)
|
||||
|
||||
@@ -39,23 +39,28 @@ require (
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/u-root/u-root v0.14.1-0.20250807200646-5e7721023dc7 // indirect
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
50
go.sum
50
go.sum
@@ -11,12 +11,10 @@ github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNx
|
||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.19.0 h1:Im+SLRgT8maArxv81mULDWN8oKxkzboH07CHesxElq4=
|
||||
github.com/alecthomas/chroma/v2 v2.19.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
||||
github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
@@ -36,6 +34,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
|
||||
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
|
||||
@@ -76,8 +76,12 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
@@ -94,10 +98,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -121,14 +123,22 @@ github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnB
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
||||
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/u-root/u-root v0.14.1-0.20250807200646-5e7721023dc7 h1:ax+jBy7xFhh+Ka0IGLmH5mft+YDuqvzEjSgWuAP0nsM=
|
||||
github.com/u-root/u-root v0.14.1-0.20250807200646-5e7721023dc7/go.mod h1:/0Qr7qJeDwWxoKku2xKQ4Szc+SwBE3g9VE8jNiamsmc=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
@@ -138,13 +148,15 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -154,11 +166,15 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
@@ -173,5 +189,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
mvdan.cc/sh/moreinterp v0.0.0-20250807215248-5a1a658912aa h1:sRmA9AmA5+9CbK6a7N52q9W9jAeoBy1EJ7cncm+SLxw=
|
||||
mvdan.cc/sh/moreinterp v0.0.0-20250807215248-5a1a658912aa/go.mod h1:Of9PCedbLDYT8b3EyiYG64rNnx5nOp27OLCVdDrjJyo=
|
||||
mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
|
||||
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
|
||||
|
||||
65
help.go
65
help.go
@@ -24,15 +24,17 @@ type ListOptions struct {
|
||||
ListAllTasks bool
|
||||
FormatTaskListAsJSON bool
|
||||
NoStatus bool
|
||||
Nested bool
|
||||
}
|
||||
|
||||
// NewListOptions creates a new ListOptions instance
|
||||
func NewListOptions(list, listAll, listAsJson, noStatus bool) ListOptions {
|
||||
func NewListOptions(list, listAll, listAsJson, noStatus, nested bool) ListOptions {
|
||||
return ListOptions{
|
||||
ListOnlyTasksWithDescriptions: list,
|
||||
ListAllTasks: listAll,
|
||||
FormatTaskListAsJSON: listAsJson,
|
||||
NoStatus: noStatus,
|
||||
Nested: nested,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +65,7 @@ func (e *Executor) ListTasks(o ListOptions) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
if o.FormatTaskListAsJSON {
|
||||
output, err := e.ToEditorOutput(tasks, o.NoStatus)
|
||||
output, err := e.ToEditorOutput(tasks, o.NoStatus, o.Nested)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -135,33 +137,17 @@ func (e *Executor) ListTaskNames(allTasks bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool) (*editors.Taskfile, error) {
|
||||
o := &editors.Taskfile{
|
||||
Tasks: make([]editors.Task, len(tasks)),
|
||||
Location: e.Taskfile.Location,
|
||||
}
|
||||
func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool, nested bool) (*editors.Namespace, error) {
|
||||
var g errgroup.Group
|
||||
editorTasks := make([]editors.Task, len(tasks))
|
||||
|
||||
// Look over each task in parallel and turn it into an editor task
|
||||
for i := range tasks {
|
||||
aliases := []string{}
|
||||
if len(tasks[i].Aliases) > 0 {
|
||||
aliases = tasks[i].Aliases
|
||||
}
|
||||
g.Go(func() error {
|
||||
o.Tasks[i] = editors.Task{
|
||||
Name: tasks[i].Name(),
|
||||
Task: tasks[i].Task,
|
||||
Desc: tasks[i].Desc,
|
||||
Summary: tasks[i].Summary,
|
||||
Aliases: aliases,
|
||||
UpToDate: false,
|
||||
Location: &editors.Location{
|
||||
Line: tasks[i].Location.Line,
|
||||
Column: tasks[i].Location.Column,
|
||||
Taskfile: tasks[i].Location.Taskfile,
|
||||
},
|
||||
}
|
||||
editorTask := editors.NewTask(tasks[i])
|
||||
|
||||
if noStatus {
|
||||
editorTasks[i] = editorTask
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -180,10 +166,35 @@ func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool) (*editors.Ta
|
||||
return err
|
||||
}
|
||||
|
||||
o.Tasks[i].UpToDate = upToDate
|
||||
|
||||
editorTask.UpToDate = &upToDate
|
||||
editorTasks[i] = editorTask
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return o, g.Wait()
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the root namespace
|
||||
var tasksLen int
|
||||
if !nested {
|
||||
tasksLen = len(editorTasks)
|
||||
}
|
||||
rootNamespace := &editors.Namespace{
|
||||
Tasks: make([]editors.Task, tasksLen),
|
||||
Location: e.Taskfile.Location,
|
||||
}
|
||||
|
||||
// Recursively add namespaces to the root namespace or if nesting is
|
||||
// disabled add them all to the root namespace
|
||||
for i, task := range editorTasks {
|
||||
taskNamespacePath := strings.Split(task.Task, ast.NamespaceSeparator)
|
||||
if nested {
|
||||
rootNamespace.AddNamespace(taskNamespacePath, task)
|
||||
} else {
|
||||
rootNamespace.Tasks[i] = task
|
||||
}
|
||||
}
|
||||
|
||||
return rootNamespace, g.Wait()
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package editors
|
||||
|
||||
import (
|
||||
"github.com/go-task/task/v3/taskfile/ast"
|
||||
)
|
||||
|
||||
type (
|
||||
// Taskfile wraps task list output for use in editor integrations (e.g. VSCode, etc)
|
||||
Taskfile struct {
|
||||
Tasks []Task `json:"tasks"`
|
||||
Location string `json:"location"`
|
||||
// Namespace wraps task list output for use in editor integrations (e.g. VSCode, etc)
|
||||
Namespace struct {
|
||||
Tasks []Task `json:"tasks"`
|
||||
Namespaces map[string]*Namespace `json:"namespaces,omitempty"`
|
||||
Location string `json:"location,omitempty"`
|
||||
}
|
||||
// Task describes a single task
|
||||
Task struct {
|
||||
@@ -13,7 +18,7 @@ type (
|
||||
Desc string `json:"desc"`
|
||||
Summary string `json:"summary"`
|
||||
Aliases []string `json:"aliases"`
|
||||
UpToDate bool `json:"up_to_date"`
|
||||
UpToDate *bool `json:"up_to_date,omitempty"`
|
||||
Location *Location `json:"location"`
|
||||
}
|
||||
// Location describes a task's location in a taskfile
|
||||
@@ -23,3 +28,59 @@ type (
|
||||
Taskfile string `json:"taskfile"`
|
||||
}
|
||||
)
|
||||
|
||||
func NewTask(task *ast.Task) Task {
|
||||
aliases := []string{}
|
||||
if len(task.Aliases) > 0 {
|
||||
aliases = task.Aliases
|
||||
}
|
||||
return Task{
|
||||
Name: task.Name(),
|
||||
Task: task.Task,
|
||||
Desc: task.Desc,
|
||||
Summary: task.Summary,
|
||||
Aliases: aliases,
|
||||
Location: &Location{
|
||||
Line: task.Location.Line,
|
||||
Column: task.Location.Column,
|
||||
Taskfile: task.Location.Taskfile,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (parent *Namespace) AddNamespace(namespacePath []string, task Task) {
|
||||
if len(namespacePath) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// If there are no child namespaces, then we have found a task and we can
|
||||
// simply add it to the current namespace
|
||||
if len(namespacePath) == 1 {
|
||||
parent.Tasks = append(parent.Tasks, task)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the key of the current namespace in the path
|
||||
namespaceKey := namespacePath[0]
|
||||
|
||||
// Add the namespace to the parent namespaces map using the namespace key
|
||||
if parent.Namespaces == nil {
|
||||
parent.Namespaces = make(map[string]*Namespace, 0)
|
||||
}
|
||||
|
||||
// Search for the current namespace in the parent namespaces map
|
||||
// If it doesn't exist, create it
|
||||
namespace, ok := parent.Namespaces[namespaceKey]
|
||||
if !ok {
|
||||
namespace = &Namespace{}
|
||||
parent.Namespaces[namespaceKey] = namespace
|
||||
}
|
||||
|
||||
// Remove the current namespace key from the namespace path.
|
||||
childNamespacePath := namespacePath[1:]
|
||||
|
||||
// If there are no child namespaces in the task name, then we have found the
|
||||
// namespace of the task and we can add it to the current namespace.
|
||||
// Otherwise, we need to go deeper
|
||||
namespace.AddNamespace(childNamespacePath, task)
|
||||
}
|
||||
|
||||
20
internal/execext/coreutils.go
Normal file
20
internal/execext/coreutils.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package execext
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-task/task/v3/internal/env"
|
||||
)
|
||||
|
||||
var useGoCoreUtils bool
|
||||
|
||||
func init() {
|
||||
// If TASK_CORE_UTILS is set to either true or false, respect that.
|
||||
// By default, enable on Windows only.
|
||||
if v, err := strconv.ParseBool(env.GetTaskEnv("CORE_UTILS")); err == nil {
|
||||
useGoCoreUtils = v
|
||||
} else {
|
||||
useGoCoreUtils = runtime.GOOS == "windows"
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"mvdan.cc/sh/moreinterp/coreutils"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
@@ -59,7 +59,7 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
||||
r, err := interp.New(
|
||||
interp.Params(params...),
|
||||
interp.Env(expand.ListEnviron(environ...)),
|
||||
interp.ExecHandlers(execHandler),
|
||||
interp.ExecHandlers(execHandlers()...),
|
||||
interp.OpenHandler(openHandler),
|
||||
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
|
||||
dirOption(opts.Dir),
|
||||
@@ -143,8 +143,11 @@ func ExpandFields(s string) ([]string, error) {
|
||||
return expand.Fields(cfg, words...)
|
||||
}
|
||||
|
||||
func execHandler(next interp.ExecHandlerFunc) interp.ExecHandlerFunc {
|
||||
return interp.DefaultExecHandler(15 * time.Second)
|
||||
func execHandlers() (handlers []func(next interp.ExecHandlerFunc) interp.ExecHandlerFunc) {
|
||||
if useGoCoreUtils {
|
||||
handlers = append(handlers, coreutils.ExecHandler)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func openHandler(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package fingerprint
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -164,7 +163,7 @@ func TestIsTaskUpToDate(t *testing.T) {
|
||||
}
|
||||
|
||||
result, err := IsTaskUpToDate(
|
||||
context.Background(),
|
||||
t.Context(),
|
||||
tt.task,
|
||||
WithStatusChecker(mockStatusChecker),
|
||||
WithSourcesChecker(mockSourcesChecker),
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
@@ -13,9 +12,10 @@ import (
|
||||
"github.com/go-task/task/v3"
|
||||
"github.com/go-task/task/v3/errors"
|
||||
"github.com/go-task/task/v3/experiments"
|
||||
"github.com/go-task/task/v3/internal/env"
|
||||
"github.com/go-task/task/v3/internal/sort"
|
||||
"github.com/go-task/task/v3/taskfile/ast"
|
||||
"github.com/go-task/task/v3/taskrc"
|
||||
taskrcast "github.com/go-task/task/v3/taskrc/ast"
|
||||
)
|
||||
|
||||
const usage = `Usage: task [flags...] [task...]
|
||||
@@ -51,6 +51,7 @@ var (
|
||||
TaskSort string
|
||||
Status bool
|
||||
NoStatus bool
|
||||
Nested bool
|
||||
Insecure bool
|
||||
Force bool
|
||||
ForceAll bool
|
||||
@@ -95,7 +96,9 @@ func init() {
|
||||
|
||||
// Parse the experiments
|
||||
dir = cmp.Or(dir, filepath.Dir(entrypoint))
|
||||
experiments.Parse(dir)
|
||||
|
||||
config, _ := taskrc.GetConfig(dir)
|
||||
experiments.ParseWithConfig(dir, config)
|
||||
|
||||
// Parse the rest of the flags
|
||||
log.SetFlags(0)
|
||||
@@ -104,10 +107,7 @@ func init() {
|
||||
log.Print(usage)
|
||||
pflag.PrintDefaults()
|
||||
}
|
||||
offline, err := strconv.ParseBool(cmp.Or(env.GetTaskEnv("OFFLINE"), "false"))
|
||||
if err != nil {
|
||||
offline = false
|
||||
}
|
||||
|
||||
pflag.BoolVar(&Version, "version", false, "Show Task version.")
|
||||
pflag.BoolVarP(&Help, "help", "h", false, "Shows Task usage.")
|
||||
pflag.BoolVarP(&Init, "init", "i", false, "Creates a new Taskfile.yml in the current folder.")
|
||||
@@ -118,9 +118,10 @@ func init() {
|
||||
pflag.StringVar(&TaskSort, "sort", "", "Changes the order of the tasks when listed. [default|alphanumeric|none].")
|
||||
pflag.BoolVar(&Status, "status", false, "Exits with non-zero exit code if any of the given tasks is not up-to-date.")
|
||||
pflag.BoolVar(&NoStatus, "no-status", false, "Ignore status when listing tasks as JSON")
|
||||
pflag.BoolVar(&Insecure, "insecure", false, "Forces Task to download Taskfiles over insecure connections.")
|
||||
pflag.BoolVar(&Nested, "nested", false, "Nest namespaces when listing tasks as JSON")
|
||||
pflag.BoolVar(&Insecure, "insecure", getConfig(config, func() *bool { return config.Remote.Insecure }, false), "Forces Task to download Taskfiles over insecure connections.")
|
||||
pflag.BoolVarP(&Watch, "watch", "w", false, "Enables watch of the given task.")
|
||||
pflag.BoolVarP(&Verbose, "verbose", "v", false, "Enables verbose mode.")
|
||||
pflag.BoolVarP(&Verbose, "verbose", "v", getConfig(config, func() *bool { return config.Verbose }, false), "Enables verbose mode.")
|
||||
pflag.BoolVarP(&Silent, "silent", "s", false, "Disables echoing.")
|
||||
pflag.BoolVarP(&AssumeYes, "yes", "y", false, "Assume \"yes\" as answer to all prompts.")
|
||||
pflag.BoolVarP(&Parallel, "parallel", "p", false, "Executes tasks provided on command line in parallel.")
|
||||
@@ -134,7 +135,7 @@ func init() {
|
||||
pflag.StringVar(&Output.Group.End, "output-group-end", "", "Message template to print after a task's grouped output.")
|
||||
pflag.BoolVar(&Output.Group.ErrorOnly, "output-group-error-only", false, "Swallow output from successful tasks.")
|
||||
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 of tasks to run concurrently.")
|
||||
pflag.IntVarP(&Concurrency, "concurrency", "C", getConfig(config, func() *int { return config.Concurrency }, 0), "Limit number of tasks to run concurrently.")
|
||||
pflag.DurationVarP(&Interval, "interval", "I", 0, "Interval to watch for changes.")
|
||||
pflag.BoolVarP(&Global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.")
|
||||
pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.")
|
||||
@@ -150,12 +151,11 @@ func init() {
|
||||
// Remote Taskfiles experiment will adds the "download" and "offline" flags
|
||||
if experiments.RemoteTaskfiles.Enabled() {
|
||||
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
||||
pflag.BoolVar(&Offline, "offline", offline, "Forces Task to only use local or cached Taskfiles.")
|
||||
pflag.DurationVar(&Timeout, "timeout", time.Second*10, "Timeout for downloading remote Taskfiles.")
|
||||
pflag.BoolVar(&Offline, "offline", getConfig(config, func() *bool { return config.Remote.Offline }, false), "Forces Task to only use local or cached Taskfiles.")
|
||||
pflag.DurationVar(&Timeout, "timeout", getConfig(config, func() *time.Duration { return config.Remote.Timeout }, time.Second*10), "Timeout for downloading remote Taskfiles.")
|
||||
pflag.BoolVar(&ClearCache, "clear-cache", false, "Clear the remote cache.")
|
||||
pflag.DurationVar(&CacheExpiryDuration, "expiry", 0, "Expiry duration for cached remote Taskfiles.")
|
||||
pflag.DurationVar(&CacheExpiryDuration, "expiry", getConfig(config, func() *time.Duration { return config.Remote.Timeout }, 0), "Expiry duration for cached remote Taskfiles.")
|
||||
}
|
||||
|
||||
pflag.Parse()
|
||||
}
|
||||
|
||||
@@ -196,6 +196,10 @@ func Validate() error {
|
||||
return errors.New("task: --no-status only applies to --json with --list or --list-all")
|
||||
}
|
||||
|
||||
if Nested && !ListJson {
|
||||
return errors.New("task: --nested only applies to --json with --list or --list-all")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -251,3 +255,16 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
||||
task.WithVersionCheck(true),
|
||||
)
|
||||
}
|
||||
|
||||
// getConfig extracts a config value directly from a pointer field with a fallback default
|
||||
func getConfig[T any](config *taskrcast.TaskRC, fieldFunc func() *T, fallback T) T {
|
||||
if config == nil {
|
||||
return fallback
|
||||
}
|
||||
|
||||
field := fieldFunc()
|
||||
if field != nil {
|
||||
return *field
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
@@ -37,51 +37,87 @@ func DefaultDir(entrypoint, dir string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Search will look for files with the given possible filenames using the given
|
||||
// entrypoint and directory. If the entrypoint is set, it will check if the
|
||||
// ResolveDir returns an absolute path to the directory that the task should be
|
||||
// run in. If the entrypoint and dir are BOTH set, then the Taskfile will not
|
||||
// sit inside the directory specified by dir and we should ensure that the dir
|
||||
// is absolute. Otherwise, the dir will always be the parent directory of the
|
||||
// resolved entrypoint, so we should return that parent directory.
|
||||
func ResolveDir(entrypoint, resolvedEntrypoint, dir string) (string, error) {
|
||||
if entrypoint != "" && dir != "" {
|
||||
return filepath.Abs(dir)
|
||||
}
|
||||
return filepath.Dir(resolvedEntrypoint), nil
|
||||
}
|
||||
|
||||
// Search looks for files with the given possible filenames using the given
|
||||
// entrypoint and directory. If the entrypoint is set, it checks if the
|
||||
// entrypoint matches a file or if it matches a directory containing one of the
|
||||
// possible filenames. Otherwise, it will walk up the file tree starting at the
|
||||
// given directory and perform a search in each directory for the possible
|
||||
// possible filenames. Otherwise, it walks up the file tree starting at the
|
||||
// given directory and performs a search in each directory for the possible
|
||||
// filenames until it finds a match or reaches the root directory. If the
|
||||
// entrypoint and directory are both empty, it will default the directory to the
|
||||
// current working directory and perform a recursive search starting there. If a
|
||||
// match is found, the absolute path to the file will be returned with its
|
||||
// directory. If no match is found, an error will be returned.
|
||||
func Search(entrypoint, dir string, possibleFilenames []string) (string, string, error) {
|
||||
// entrypoint and directory are both empty, it defaults the directory to the
|
||||
// current working directory and performs a recursive search starting there. If
|
||||
// a match is found, the absolute path to the file is returned with its
|
||||
// directory. If no match is found, an error is returned.
|
||||
func Search(entrypoint, dir string, possibleFilenames []string) (string, error) {
|
||||
var err error
|
||||
if entrypoint != "" {
|
||||
entrypoint, err = SearchPath(entrypoint, possibleFilenames)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
if dir == "" {
|
||||
dir = filepath.Dir(entrypoint)
|
||||
} else {
|
||||
dir, err = filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
}
|
||||
return entrypoint, dir, nil
|
||||
return entrypoint, nil
|
||||
}
|
||||
if dir == "" {
|
||||
dir, err = os.Getwd()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
entrypoint, err = SearchPathRecursively(dir, possibleFilenames)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
dir = filepath.Dir(entrypoint)
|
||||
return entrypoint, dir, nil
|
||||
return entrypoint, nil
|
||||
}
|
||||
|
||||
// Search will check if a file at the given path exists or not. If it does, it
|
||||
// will return the path to it. If it does not, it will search for any files at
|
||||
// the given path with any of the given possible names. If any of these match a
|
||||
// file, the first matching path will be returned. If no files are found, an
|
||||
// SearchAll looks for files with the given possible filenames using the given
|
||||
// entrypoint and directory. If the entrypoint is set, it checks if the
|
||||
// entrypoint matches a file or if it matches a directory containing one of the
|
||||
// possible filenames and add it to a list of matches. It then walks up the file
|
||||
// tree starting at the given directory and performs a search in each directory
|
||||
// for the possible filenames until it finds a match or reaches the root
|
||||
// directory. If the entrypoint and directory are both empty, it defaults the
|
||||
// directory to the current working directory and performs a recursive search
|
||||
// starting there. If matches are found, the absolute path to each file is added
|
||||
// to the list and returned.
|
||||
func SearchAll(entrypoint, dir string, possibleFilenames []string) ([]string, error) {
|
||||
var err error
|
||||
var entrypoints []string
|
||||
if entrypoint != "" {
|
||||
entrypoint, err = SearchPath(entrypoint, possibleFilenames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entrypoints = append(entrypoints, entrypoint)
|
||||
}
|
||||
if dir == "" {
|
||||
dir, err = os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
paths, err := SearchNPathRecursively(dir, possibleFilenames, -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(entrypoints, paths...), nil
|
||||
}
|
||||
|
||||
// SearchPath will check if a file at the given path exists or not. If it does,
|
||||
// it will return the path to it. If it does not, it will search for any files
|
||||
// at the given path with any of the given possible names. If any of these match
|
||||
// a file, the first matching path will be returned. If no files are found, an
|
||||
// error will be returned.
|
||||
func SearchPath(path string, possibleFilenames []string) (string, error) {
|
||||
// Get file info about the path
|
||||
@@ -111,36 +147,56 @@ func SearchPath(path string, possibleFilenames []string) (string, error) {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
|
||||
// SearchRecursively will check if a file at the given path exists by calling
|
||||
// the exists function. If a file is not found, it will walk up the directory
|
||||
// tree calling the Search function until it finds a file or reaches the root
|
||||
// directory. On supported operating systems, it will also check if the user ID
|
||||
// of the directory changes and abort if it does.
|
||||
// SearchPathRecursively walks up the directory tree starting at the given
|
||||
// path, calling the Search function in each directory until it finds a matching
|
||||
// file or reaches the root directory. On supported operating systems, it will
|
||||
// also check if the user ID of the directory changes and abort if it does.
|
||||
func SearchPathRecursively(path string, possibleFilenames []string) (string, error) {
|
||||
owner, err := sysinfo.Owner(path)
|
||||
paths, err := SearchNPathRecursively(path, possibleFilenames, 1)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for {
|
||||
if len(paths) == 0 {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
return paths[0], nil
|
||||
}
|
||||
|
||||
// SearchNPathRecursively walks up the directory tree starting at the given
|
||||
// path, calling the Search function in each directory and adding each matching
|
||||
// file that it finds to a list until it reaches the root directory or the
|
||||
// length of the list exceeds n. On supported operating systems, it will also
|
||||
// check if the user ID of the directory changes and abort if it does.
|
||||
func SearchNPathRecursively(path string, possibleFilenames []string, n int) ([]string, error) {
|
||||
var paths []string
|
||||
|
||||
owner, err := sysinfo.Owner(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for n == -1 || len(paths) < n {
|
||||
fpath, err := SearchPath(path, possibleFilenames)
|
||||
if err == nil {
|
||||
return fpath, nil
|
||||
paths = append(paths, fpath)
|
||||
}
|
||||
|
||||
// Get the parent path/user id
|
||||
parentPath := filepath.Dir(path)
|
||||
parentOwner, err := sysinfo.Owner(parentPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Error if we reached the root directory and still haven't found a file
|
||||
// OR if the user id of the directory changes
|
||||
if path == parentPath || (parentOwner != owner) {
|
||||
return "", os.ErrNotExist
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
owner = parentOwner
|
||||
path = parentPath
|
||||
}
|
||||
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
@@ -71,35 +71,30 @@ func TestSearch(t *testing.T) {
|
||||
dir string
|
||||
possibleFilenames []string
|
||||
expectedEntrypoint string
|
||||
expectedDir string
|
||||
}{
|
||||
{
|
||||
name: "find foo.txt using relative entrypoint",
|
||||
entrypoint: "./testdata/foo.txt",
|
||||
possibleFilenames: []string{"foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using absolute entrypoint",
|
||||
entrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
possibleFilenames: []string{"foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using relative dir",
|
||||
dir: "./testdata",
|
||||
possibleFilenames: []string{"foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using absolute dir",
|
||||
dir: filepath.Join(wd, "testdata"),
|
||||
possibleFilenames: []string{"foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using relative dir and relative entrypoint",
|
||||
@@ -107,7 +102,6 @@ func TestSearch(t *testing.T) {
|
||||
dir: "./testdata/some/other/dir",
|
||||
possibleFilenames: []string{"foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata", "some", "other", "dir"),
|
||||
},
|
||||
{
|
||||
name: "find fs.go using no entrypoint or dir",
|
||||
@@ -115,7 +109,6 @@ func TestSearch(t *testing.T) {
|
||||
dir: "",
|
||||
possibleFilenames: []string{"fs.go"},
|
||||
expectedEntrypoint: filepath.Join(wd, "fs.go"),
|
||||
expectedDir: wd,
|
||||
},
|
||||
{
|
||||
name: "find ../../Taskfile.yml using no entrypoint or dir by walking",
|
||||
@@ -123,30 +116,109 @@ func TestSearch(t *testing.T) {
|
||||
dir: "",
|
||||
possibleFilenames: []string{"Taskfile.yml"},
|
||||
expectedEntrypoint: filepath.Join(wd, "..", "..", "Taskfile.yml"),
|
||||
expectedDir: filepath.Join(wd, "..", ".."),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt first if listed first in possible filenames",
|
||||
entrypoint: "./testdata",
|
||||
possibleFilenames: []string{"foo.txt", "bar.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find bar.txt first if listed first in possible filenames",
|
||||
entrypoint: "./testdata",
|
||||
possibleFilenames: []string{"bar.txt", "foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "bar.txt"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
entrypoint, err := Search(tt.entrypoint, tt.dir, tt.possibleFilenames)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expectedEntrypoint, entrypoint)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
entrypoint string
|
||||
resolvedEntrypoint string
|
||||
dir string
|
||||
expectedDir string
|
||||
}{
|
||||
{
|
||||
name: "find foo.txt using relative entrypoint",
|
||||
entrypoint: "./testdata/foo.txt",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using absolute entrypoint",
|
||||
entrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using relative dir",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
dir: "./testdata",
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using absolute dir",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
dir: filepath.Join(wd, "testdata"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using relative dir and relative entrypoint",
|
||||
entrypoint: "./testdata/foo.txt",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
dir: "./testdata/some/other/dir",
|
||||
expectedDir: filepath.Join(wd, "testdata", "some", "other", "dir"),
|
||||
},
|
||||
{
|
||||
name: "find fs.go using no entrypoint or dir",
|
||||
entrypoint: "",
|
||||
resolvedEntrypoint: filepath.Join(wd, "fs.go"),
|
||||
dir: "",
|
||||
expectedDir: wd,
|
||||
},
|
||||
{
|
||||
name: "find ../../Taskfile.yml using no entrypoint or dir by walking",
|
||||
entrypoint: "",
|
||||
resolvedEntrypoint: filepath.Join(wd, "..", "..", "Taskfile.yml"),
|
||||
dir: "",
|
||||
expectedDir: filepath.Join(wd, "..", ".."),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt first if listed first in possible filenames",
|
||||
entrypoint: "./testdata",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find bar.txt first if listed first in possible filenames",
|
||||
entrypoint: "./testdata",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "bar.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
entrypoint, dir, err := Search(tt.entrypoint, tt.dir, tt.possibleFilenames)
|
||||
dir, err := ResolveDir(tt.entrypoint, tt.resolvedEntrypoint, tt.dir)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expectedEntrypoint, entrypoint)
|
||||
require.Equal(t, tt.expectedDir, dir)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
_ "embed"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -48,10 +46,6 @@ func getCommit(info *debug.BuildInfo) string {
|
||||
// However, it can also be overridden at build time using:
|
||||
// -ldflags="-X 'github.com/go-task/task/v3/internal/version.version=vX.X.X'".
|
||||
func GetVersion() string {
|
||||
// If its a development version, we bump the minor version.
|
||||
if commit != "" || dirty {
|
||||
return semver.MustParse(version).IncMinor().String()
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
@@ -67,7 +61,7 @@ func GetVersionWithBuildInfo() string {
|
||||
buildMetadata = append(buildMetadata, "dirty")
|
||||
}
|
||||
if len(buildMetadata) > 0 {
|
||||
return GetVersion() + "+" + strings.Join(buildMetadata, ".")
|
||||
return version + "+" + strings.Join(buildMetadata, ".")
|
||||
}
|
||||
return GetVersion()
|
||||
return version
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.44.1
|
||||
3.45.1
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestVersionTxt(t *testing.T) {
|
||||
// Check that the version.txt is a valid semver version.
|
||||
require.NotEmpty(t, GetVersion(), "version.txt is not semver compliant")
|
||||
}
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
version string
|
||||
commit string
|
||||
dirty bool
|
||||
want string
|
||||
}{
|
||||
{"1.2.3", "", false, "1.2.3"},
|
||||
{"1.2.3", "", true, "1.3.0"},
|
||||
{"1.2.3", "abcdefg", false, "1.3.0"},
|
||||
{"1.2.3", "abcdefg", true, "1.3.0"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
version = tt.version
|
||||
commit = tt.commit
|
||||
dirty = tt.dirty
|
||||
t.Run(tt.want, func(t *testing.T) {
|
||||
require.Equal(t, tt.want, GetVersion())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVersionWithBuildInfo(t *testing.T) {
|
||||
tests := []struct {
|
||||
version string
|
||||
commit string
|
||||
dirty bool
|
||||
want string
|
||||
}{
|
||||
{"1.2.3", "", false, "1.2.3"},
|
||||
{"1.2.3", "", true, "1.3.0+dirty"},
|
||||
{"1.2.3", "abcdefg", false, "1.3.0+abcdefg"},
|
||||
{"1.2.3", "abcdefg", true, "1.3.0+abcdefg.dirty"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
version = tt.version
|
||||
commit = tt.commit
|
||||
dirty = tt.dirty
|
||||
t.Run(tt.want, func(t *testing.T) {
|
||||
require.Equal(t, tt.want, GetVersionWithBuildInfo())
|
||||
})
|
||||
}
|
||||
}
|
||||
32
package-lock.json
generated
32
package-lock.json
generated
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"name": "@go-task/cli",
|
||||
"version": "3.44.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@go-task/cli",
|
||||
"version": "3.26.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@go-task/go-npm": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@go-task/go-npm": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@go-task/go-npm/-/go-npm-0.2.0.tgz",
|
||||
"integrity": "sha512-vQbdtBvesHm8EUFHX8QKg4rbBodmu9VsAXH1ozpbiN5jdTMOYHTCMM31EurAYmY+rNNtxJQ4JGy6t383RPlqbw==",
|
||||
"bin": {
|
||||
"go-npm": "bin/index.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@go-task/go-npm": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@go-task/go-npm/-/go-npm-0.2.0.tgz",
|
||||
"integrity": "sha512-vQbdtBvesHm8EUFHX8QKg4rbBodmu9VsAXH1ozpbiN5jdTMOYHTCMM31EurAYmY+rNNtxJQ4JGy6t383RPlqbw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
34
package.json
34
package.json
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "@go-task/cli",
|
||||
"version": "3.44.1",
|
||||
"description": "A task runner / simpler Make alternative written in Go",
|
||||
"scripts": {
|
||||
"postinstall": "go-npm install",
|
||||
"preuninstall": "go-npm uninstall"
|
||||
},
|
||||
"goBinary": {
|
||||
"name": "task",
|
||||
"path": "./bin",
|
||||
"url": "https://github.com/go-task/task/releases/download/v{{version}}/task_{{platform}}_{{arch}}{{archive_ext}}"
|
||||
},
|
||||
"files": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/go-task/task.git"
|
||||
},
|
||||
"keywords": [
|
||||
"task",
|
||||
"taskfile",
|
||||
"build-tool",
|
||||
"task-runner"
|
||||
],
|
||||
"author": "The Task authors",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/go-task/task/issues"
|
||||
},
|
||||
"homepage": "https://taskfile.dev",
|
||||
"dependencies": {
|
||||
"@go-task/go-npm": "^0.2.0"
|
||||
}
|
||||
}
|
||||
2
task.go
2
task.go
@@ -172,7 +172,6 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
||||
if t.Method != "" {
|
||||
method = t.Method
|
||||
}
|
||||
|
||||
upToDate, err := fingerprint.IsTaskUpToDate(ctx, t,
|
||||
fingerprint.WithMethod(method),
|
||||
fingerprint.WithTempDir(e.TempDir.Fingerprint),
|
||||
@@ -467,7 +466,6 @@ func (e *Executor) GetTask(call *Call) (*ast.Task, error) {
|
||||
DidYouMean: didYouMean,
|
||||
}
|
||||
}
|
||||
|
||||
return matchingTask, nil
|
||||
}
|
||||
|
||||
|
||||
168
task_test.go
168
task_test.go
@@ -2,7 +2,6 @@ package task_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
@@ -356,7 +355,7 @@ func (fct fileContentTest) Run(t *testing.T) {
|
||||
)
|
||||
|
||||
require.NoError(t, e.Setup(), "e.Setup()")
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: fct.Target}), "e.Run(target)")
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: fct.Target}), "e.Run(target)")
|
||||
for name, expectContent := range fct.Files {
|
||||
t.Run(fct.name(name), func(t *testing.T) {
|
||||
path := filepathext.SmartJoin(e.Dir, name)
|
||||
@@ -407,7 +406,7 @@ func TestGenerates(t *testing.T) {
|
||||
fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask)
|
||||
|
||||
// Run task for the first time.
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: theTask}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: theTask}))
|
||||
|
||||
if _, err := os.Stat(srcFile); err != nil {
|
||||
t.Errorf("File should exist: %v", err)
|
||||
@@ -422,7 +421,7 @@ func TestGenerates(t *testing.T) {
|
||||
buff.Reset()
|
||||
|
||||
// Re-run task to ensure it's now found to be up-to-date.
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: theTask}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: theTask}))
|
||||
if buff.String() != upToDate {
|
||||
t.Errorf("Wrong output message: %s", buff.String())
|
||||
}
|
||||
@@ -438,6 +437,7 @@ func TestStatusChecksum(t *testing.T) { // nolint:paralleltest // cannot run in
|
||||
task string
|
||||
}{
|
||||
{[]string{"generated.txt", ".task/checksum/build"}, "build"},
|
||||
{[]string{"generated-wildcard.txt", ".task/checksum/build-wildcard"}, "build-wildcard"},
|
||||
{[]string{"generated.txt", ".task/checksum/build-with-status"}, "build-with-status"},
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ func TestStatusChecksum(t *testing.T) { // nolint:paralleltest // cannot run in
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: test.task}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: test.task}))
|
||||
for _, f := range test.files {
|
||||
_, err := os.Stat(filepathext.SmartJoin(dir, f))
|
||||
require.NoError(t, err)
|
||||
@@ -476,7 +476,7 @@ func TestStatusChecksum(t *testing.T) { // nolint:paralleltest // cannot run in
|
||||
time := s.ModTime()
|
||||
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: test.task}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: test.task}))
|
||||
assert.Equal(t, `task: Task "`+test.task+`" is up to date`+"\n", buff.String())
|
||||
|
||||
s, err = os.Stat(filepathext.SmartJoin(tempDir.Fingerprint, "checksum/"+test.task))
|
||||
@@ -507,12 +507,12 @@ func TestStatusVariables(t *testing.T) {
|
||||
task.WithVerbose(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build-checksum"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-checksum"}))
|
||||
|
||||
assert.Contains(t, buff.String(), "3e464c4b03f4b65d740e1e130d4d108a")
|
||||
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build-ts"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-ts"}))
|
||||
|
||||
inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt"))
|
||||
require.NoError(t, err)
|
||||
@@ -543,12 +543,12 @@ func TestCmdsVariables(t *testing.T) {
|
||||
task.WithVerbose(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build-checksum"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-checksum"}))
|
||||
|
||||
assert.Contains(t, buff.String(), "3e464c4b03f4b65d740e1e130d4d108a")
|
||||
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build-ts"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-ts"}))
|
||||
inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt"))
|
||||
require.NoError(t, err)
|
||||
ts := fmt.Sprintf("%d", inf.ModTime().Unix())
|
||||
@@ -569,7 +569,7 @@ func TestCyclicDep(t *testing.T) {
|
||||
task.WithStderr(io.Discard),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
assert.IsType(t, &errors.TaskCalledTooManyTimesError{}, e.Run(context.Background(), &task.Call{Task: "task-1"}))
|
||||
assert.IsType(t, &errors.TaskCalledTooManyTimesError{}, e.Run(t.Context(), &task.Call{Task: "task-1"}))
|
||||
}
|
||||
|
||||
func TestTaskVersion(t *testing.T) {
|
||||
@@ -619,10 +619,10 @@ func TestTaskIgnoreErrors(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "task-should-pass"}))
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "task-should-fail"}))
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "cmd-should-pass"}))
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "cmd-should-fail"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task-should-pass"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "task-should-fail"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "cmd-should-pass"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "cmd-should-fail"}))
|
||||
}
|
||||
|
||||
func TestExpand(t *testing.T) {
|
||||
@@ -642,7 +642,7 @@ func TestExpand(t *testing.T) {
|
||||
task.WithStderr(&buff),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "pwd"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "pwd"}))
|
||||
assert.Equal(t, home, strings.TrimSpace(buff.String()))
|
||||
}
|
||||
|
||||
@@ -663,7 +663,7 @@ func TestDry(t *testing.T) {
|
||||
task.WithDry(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"}))
|
||||
|
||||
assert.Equal(t, "task: [build] touch file.txt", strings.TrimSpace(buff.String()))
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
@@ -692,13 +692,13 @@ func TestDryChecksum(t *testing.T) {
|
||||
task.WithDry(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
|
||||
_, err := os.Stat(checksumFile)
|
||||
require.Error(t, err, "checksum file should not exist")
|
||||
|
||||
e.Dry = false
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
_, err = os.Stat(checksumFile)
|
||||
require.NoError(t, err, "checksum file should exist")
|
||||
}
|
||||
@@ -840,7 +840,7 @@ func TestIncludesRemote(t *testing.T) {
|
||||
path := filepath.Join(dir, outputFile)
|
||||
require.NoError(t, os.RemoveAll(path))
|
||||
|
||||
require.NoError(t, e.executor.Run(context.Background(), taskCall))
|
||||
require.NoError(t, e.executor.Run(t.Context(), taskCall))
|
||||
|
||||
actualContent, err := os.ReadFile(path)
|
||||
require.NoError(t, err)
|
||||
@@ -1120,11 +1120,11 @@ func TestIncludesRelativePath(t *testing.T) {
|
||||
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "common:pwd"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "common:pwd"}))
|
||||
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
|
||||
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "included:common:pwd"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "included:common:pwd"}))
|
||||
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
|
||||
}
|
||||
|
||||
@@ -1156,7 +1156,7 @@ func TestIncludesInternal(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
@@ -1203,7 +1203,7 @@ func TestIncludesFlatten(t *testing.T) {
|
||||
assert.EqualError(t, err, test.expectedOutput)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
_ = e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
_ = e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
assert.Equal(t, test.expectedOutput, buff.String())
|
||||
}
|
||||
})
|
||||
@@ -1235,7 +1235,7 @@ func TestIncludesInterpolation(t *testing.T) { // nolint:paralleltest // cannot
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
@@ -1258,20 +1258,20 @@ func TestIncludesWithExclude(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "included:bar"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "included:bar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "bar\n", buff.String())
|
||||
buff.Reset()
|
||||
|
||||
err = e.Run(context.Background(), &task.Call{Task: "included:foo"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "included:foo"})
|
||||
require.Error(t, err)
|
||||
buff.Reset()
|
||||
|
||||
err = e.Run(context.Background(), &task.Call{Task: "bar"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "bar"})
|
||||
require.Error(t, err)
|
||||
buff.Reset()
|
||||
|
||||
err = e.Run(context.Background(), &task.Call{Task: "foo"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "foo"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "foo\n", buff.String())
|
||||
}
|
||||
@@ -1301,7 +1301,7 @@ func TestIncludedTaskfileVarMerging(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, buff.String(), test.expectedOutput)
|
||||
})
|
||||
@@ -1336,7 +1336,7 @@ func TestInternalTask(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
@@ -1421,7 +1421,7 @@ func TestSummary(t *testing.T) {
|
||||
task.WithSilent(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "task-with-summary"}, &task.Call{Task: "other-task-with-summary"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task-with-summary"}, &task.Call{Task: "other-task-with-summary"}))
|
||||
|
||||
data, err := os.ReadFile(filepathext.SmartJoin(dir, "task-with-summary.txt"))
|
||||
require.NoError(t, err)
|
||||
@@ -1447,7 +1447,7 @@ func TestWhenNoDirAttributeItRunsInSameDirAsTaskfile(t *testing.T) {
|
||||
)
|
||||
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "whereami"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "whereami"}))
|
||||
|
||||
// got should be the "dir" part of "testdata/dir"
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
@@ -1467,7 +1467,7 @@ func TestWhenDirAttributeAndDirExistsItRunsInThatDir(t *testing.T) {
|
||||
)
|
||||
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "whereami"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "whereami"}))
|
||||
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
assert.Equal(t, expected, got, "Mismatch in the working directory")
|
||||
@@ -1493,7 +1493,7 @@ func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) {
|
||||
t.Errorf("Directory should not exist: %v", err)
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: target}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: target}))
|
||||
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
assert.Equal(t, expected, got, "Mismatch in the working directory")
|
||||
@@ -1522,7 +1522,7 @@ func TestDynamicVariablesRunOnTheNewCreatedDir(t *testing.T) {
|
||||
t.Errorf("Directory should not exist: %v", err)
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: target}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: target}))
|
||||
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
assert.Equal(t, expected, got, "Mismatch in the working directory")
|
||||
@@ -1593,7 +1593,7 @@ func TestShortTaskNotation(t *testing.T) {
|
||||
task.WithSilent(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
assert.Equal(t, "string-slice-1\nstring-slice-2\nstring\n", buff.String())
|
||||
}
|
||||
|
||||
@@ -1791,7 +1791,7 @@ func TestExitImmediately(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
assert.Contains(t, buff.String(), `"this_should_fail": executable file not found in $PATH`)
|
||||
}
|
||||
|
||||
@@ -1811,6 +1811,22 @@ func TestRunOnlyRunsJobsHashOnce(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunOnlyRunsJobsHashOnceWithWildcard(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tt := fileContentTest{
|
||||
Dir: "testdata/run",
|
||||
Target: "deploy",
|
||||
Files: map[string]string{
|
||||
"wildcard.txt": "Deploy infra\nDeploy js\nDeploy go\n",
|
||||
},
|
||||
}
|
||||
t.Run("", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
tt.Run(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunOnceSharedDeps(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1824,7 +1840,7 @@ func TestRunOnceSharedDeps(t *testing.T) {
|
||||
task.WithForceAll(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"}))
|
||||
|
||||
rx := regexp.MustCompile(`task: \[service-[a,b]:library:build\] echo "build library"`)
|
||||
matches := rx.FindAllStringSubmatch(buff.String(), -1)
|
||||
@@ -1856,10 +1872,10 @@ task-1 ran successfully
|
||||
task: [task-1] echo 'task-1 ran successfully'
|
||||
task-1 ran successfully
|
||||
`)
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "task-2"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "task-2"}))
|
||||
assert.Contains(t, buff.String(), expectedOutputOrder)
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "parent"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "parent"}))
|
||||
assert.Contains(t, buff.String(), "child task deferred value-from-parent")
|
||||
}
|
||||
|
||||
@@ -1875,7 +1891,7 @@ func TestExitCodeZero(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "exit-zero"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "exit-zero"}))
|
||||
assert.Equal(t, "FOO=bar - DYNAMIC_FOO=bar - EXIT_CODE=", strings.TrimSpace(buff.String()))
|
||||
}
|
||||
|
||||
@@ -1891,7 +1907,7 @@ func TestExitCodeOne(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "exit-one"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "exit-one"}))
|
||||
assert.Equal(t, "FOO=bar - DYNAMIC_FOO=bar - EXIT_CODE=1", strings.TrimSpace(buff.String()))
|
||||
}
|
||||
|
||||
@@ -1920,7 +1936,7 @@ func TestIgnoreNilElements(t *testing.T) {
|
||||
task.WithSilent(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
assert.Equal(t, "string-slice-1\n", buff.String())
|
||||
})
|
||||
}
|
||||
@@ -1948,7 +1964,7 @@ task: [bye] echo 'Bye!'
|
||||
Bye!
|
||||
::endgroup::
|
||||
`)
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "bye"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "bye"}))
|
||||
t.Log(buff.String())
|
||||
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
|
||||
}
|
||||
@@ -1965,7 +1981,7 @@ func TestOutputGroupErrorOnlySwallowsOutputOnSuccess(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "passing"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "passing"}))
|
||||
t.Log(buff.String())
|
||||
assert.Empty(t, buff.String())
|
||||
}
|
||||
@@ -1982,7 +1998,7 @@ func TestOutputGroupErrorOnlyShowsOutputOnFailure(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "failing"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "failing"}))
|
||||
t.Log(buff.String())
|
||||
assert.Contains(t, "failing-output", strings.TrimSpace(buff.String()))
|
||||
assert.NotContains(t, "passing", strings.TrimSpace(buff.String()))
|
||||
@@ -2014,7 +2030,7 @@ VAR_1 is included-default-var1
|
||||
task: [included3:task1] echo "VAR_2 is included-default-var2"
|
||||
VAR_2 is included-default-var2
|
||||
`)
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "task1"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task1"}))
|
||||
t.Log(buff.String())
|
||||
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
|
||||
}
|
||||
@@ -2052,7 +2068,7 @@ Hello foo
|
||||
task: [bar:lib:greet] echo 'Hello bar'
|
||||
Hello bar
|
||||
`)
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
t.Log(buff.String())
|
||||
assert.Equal(t, expectedOutputOrder, strings.TrimSpace(buff.String()))
|
||||
}
|
||||
@@ -2090,7 +2106,7 @@ func TestErrorCode(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
require.Error(t, err)
|
||||
taskRunErr, ok := err.(*errors.TaskRunError)
|
||||
assert.True(t, ok, "cannot cast returned error to *task.TaskRunError")
|
||||
@@ -2142,7 +2158,7 @@ func TestEvaluateSymlinksInPaths(t *testing.T) { // nolint:paralleltest // canno
|
||||
for _, test := range tests { // nolint:paralleltest // cannot run in parallel
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require.NoError(t, e.Setup())
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expected, strings.TrimSpace(buff.String()))
|
||||
buff.Reset()
|
||||
@@ -2185,7 +2201,7 @@ func TestTaskfileWalk(t *testing.T) {
|
||||
task.WithStderr(&buff),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
assert.Equal(t, test.expected, buff.String())
|
||||
})
|
||||
}
|
||||
@@ -2203,7 +2219,7 @@ func TestUserWorkingDirectory(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", wd), buff.String())
|
||||
}
|
||||
|
||||
@@ -2225,7 +2241,7 @@ func TestUserWorkingDirectoryWithIncluded(t *testing.T) {
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "included:echo"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "included:echo"}))
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", wd), buff.String())
|
||||
}
|
||||
|
||||
@@ -2239,7 +2255,7 @@ func TestPlatforms(t *testing.T) {
|
||||
task.WithStderr(&buff),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build-" + runtime.GOOS}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-" + runtime.GOOS}))
|
||||
assert.Equal(t, fmt.Sprintf("task: [build-%s] echo 'Running task on %s'\nRunning task on %s\n", runtime.GOOS, runtime.GOOS, runtime.GOOS), buff.String())
|
||||
}
|
||||
|
||||
@@ -2254,7 +2270,7 @@ func TestPOSIXShellOptsGlobalLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "pipefail"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "pipefail"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||
}
|
||||
@@ -2270,7 +2286,7 @@ func TestPOSIXShellOptsTaskLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "pipefail"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "pipefail"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||
}
|
||||
@@ -2286,7 +2302,7 @@ func TestPOSIXShellOptsCommandLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "pipefail"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "pipefail"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||
}
|
||||
@@ -2302,7 +2318,7 @@ func TestBashShellOptsGlobalLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "globstar"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "globstar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||
}
|
||||
@@ -2318,7 +2334,7 @@ func TestBashShellOptsTaskLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "globstar"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "globstar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||
}
|
||||
@@ -2334,7 +2350,7 @@ func TestBashShellOptsCommandLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "globstar"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "globstar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||
}
|
||||
@@ -2354,7 +2370,7 @@ func TestSplitArgs(t *testing.T) {
|
||||
vars := ast.NewVars()
|
||||
vars.Set("CLI_ARGS", ast.Var{Value: "foo bar 'foo bar baz'"})
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "default", Vars: vars})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "default", Vars: vars})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "3\n", buff.String())
|
||||
}
|
||||
@@ -2395,14 +2411,14 @@ func TestSilence(t *testing.T) {
|
||||
|
||||
// Then test the two basic cases where the task is silent or not.
|
||||
// A silenced task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "silent"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "silent"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "siWhile running lent: Expected not see output, because the task is silent")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty (not silent) task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "chatty"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "chatty"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "chWhile running atty: Expected to see output, because the task is not silent")
|
||||
|
||||
@@ -2410,42 +2426,42 @@ func TestSilence(t *testing.T) {
|
||||
|
||||
// Then test invoking the two task from other tasks.
|
||||
// A silenced task that calls a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-silent-calls-chatty-non-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-silent-calls-chatty-non-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-silent-calls-chatty-non-silenced: Expected to see output. The task is silenced, but the called task is not. Silence does not propagate to called tasks.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A silent task that does a silent call to a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-silent-calls-chatty-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-silent-calls-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-silent-calls-chatty-silenced: Expected not to see output. The task calls chatty task, but the call is silenced.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that does a call to a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-chatty-calls-chatty-non-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-chatty-calls-chatty-non-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-chatty-calls-chatty-non-silenced: Expected to see output. Both caller and callee are chatty and not silenced.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that does a silenced call to a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-chatty-calls-chatty-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-chatty-calls-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-chatty-calls-chatty-silenced: Expected to see output. Call to a chatty task is silenced, but the parent task is not.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task with no cmd's of its own that does a silenced call to a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-no-cmds-calls-chatty-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-no-cmds-calls-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-no-cmds-calls-chatty-silenced: Expected not to see output. While the task itself is not silenced, it does not have any cmds and only does an invocation of a silenced task.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that does a silenced invocation of a task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-chatty-calls-silenced-cmd"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-chatty-calls-silenced-cmd"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-chatty-calls-silenced-cmd: Expected not to see output. While the task itself is not silenced, its call to the chatty task is silent.")
|
||||
|
||||
@@ -2453,21 +2469,21 @@ func TestSilence(t *testing.T) {
|
||||
|
||||
// Then test calls via dependencies.
|
||||
// A silent task that depends on a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-is-silent-depends-on-chatty-non-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-is-silent-depends-on-chatty-non-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-is-silent-depends-on-chatty-non-silenced: Expected to see output. The task is silent and depends on a chatty task. Dependencies does not inherit silence.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A silent task that depends on a silenced chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-is-silent-depends-on-chatty-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-is-silent-depends-on-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-is-silent-depends-on-chatty-silenced: Expected not to see output. The task is silent and has a silenced dependency on a chatty task.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that, depends on a silenced chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-is-chatty-depends-on-chatty-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-is-chatty-depends-on-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-is-chatty-depends-on-chatty-silenced: Expected not to see output. The task is chatty but does not have commands and has a silenced dependency on a chatty task.")
|
||||
|
||||
@@ -2519,7 +2535,7 @@ func TestForce(t *testing.T) {
|
||||
task.WithForceAll(tt.forceAll),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "task-with-dep"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task-with-dep"}))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2579,10 +2595,10 @@ func TestWildcard(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
if test.wantErr {
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: test.call}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: test.call}))
|
||||
return
|
||||
}
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: test.call}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: test.call}))
|
||||
assert.Equal(t, test.expectedOutput, buff.String())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -46,17 +46,22 @@ type Task struct {
|
||||
Namespace string
|
||||
IncludeVars *Vars
|
||||
IncludedTaskfileVars *Vars
|
||||
|
||||
FullName string
|
||||
}
|
||||
|
||||
func (t *Task) Name() string {
|
||||
if t.Label != "" {
|
||||
return t.Label
|
||||
}
|
||||
if t.FullName != "" {
|
||||
return t.FullName
|
||||
}
|
||||
return t.Task
|
||||
}
|
||||
|
||||
func (t *Task) LocalName() string {
|
||||
name := t.Task
|
||||
name := t.FullName
|
||||
name = strings.TrimPrefix(name, t.Namespace)
|
||||
name = strings.TrimPrefix(name, ":")
|
||||
return name
|
||||
@@ -220,6 +225,7 @@ func (t *Task) DeepCopy() *Task {
|
||||
Location: t.Location.DeepCopy(),
|
||||
Requires: t.Requires.DeepCopy(),
|
||||
Namespace: t.Namespace,
|
||||
FullName: t.FullName,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ type Var struct {
|
||||
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||
switch node.Kind {
|
||||
case yaml.MappingNode:
|
||||
key := node.Content[0].Value
|
||||
key := "<none>"
|
||||
if len(node.Content) > 0 {
|
||||
key = node.Content[0].Value
|
||||
}
|
||||
switch key {
|
||||
case "sh", "ref", "map":
|
||||
var m struct {
|
||||
|
||||
@@ -18,15 +18,21 @@ type FileNode struct {
|
||||
}
|
||||
|
||||
func NewFileNode(entrypoint, dir string, opts ...NodeOption) (*FileNode, error) {
|
||||
var err error
|
||||
base := NewBaseNode(dir, opts...)
|
||||
entrypoint, base.dir, err = fsext.Search(entrypoint, base.dir, defaultTaskfiles)
|
||||
// Find the entrypoint file
|
||||
resolvedEntrypoint, err := fsext.Search(entrypoint, dir, defaultTaskfiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Resolve the directory
|
||||
resolvedDir, err := fsext.ResolveDir(entrypoint, resolvedEntrypoint, dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &FileNode{
|
||||
baseNode: base,
|
||||
entrypoint: entrypoint,
|
||||
baseNode: NewBaseNode(resolvedDir, opts...),
|
||||
entrypoint: resolvedEntrypoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,48 @@
|
||||
package ast
|
||||
|
||||
import "github.com/Masterminds/semver/v3"
|
||||
import (
|
||||
"cmp"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
)
|
||||
|
||||
type TaskRC struct {
|
||||
Version *semver.Version `yaml:"version"`
|
||||
Verbose *bool `yaml:"verbose"`
|
||||
Concurrency *int `yaml:"concurrency"`
|
||||
Remote Remote `yaml:"remote"`
|
||||
Experiments map[string]int `yaml:"experiments"`
|
||||
}
|
||||
|
||||
type Remote struct {
|
||||
Insecure *bool `yaml:"insecure"`
|
||||
Offline *bool `yaml:"offline"`
|
||||
Timeout *time.Duration `yaml:"timeout"`
|
||||
CacheExpiry *time.Duration `yaml:"cache-expiry"`
|
||||
}
|
||||
|
||||
// Merge combines the current TaskRC with another TaskRC, prioritizing non-nil fields from the other TaskRC.
|
||||
func (t *TaskRC) Merge(other *TaskRC) {
|
||||
if other == nil {
|
||||
return
|
||||
}
|
||||
|
||||
t.Version = cmp.Or(other.Version, t.Version)
|
||||
|
||||
if t.Experiments == nil && other.Experiments != nil {
|
||||
t.Experiments = other.Experiments
|
||||
} else if t.Experiments != nil && other.Experiments != nil {
|
||||
maps.Copy(t.Experiments, other.Experiments)
|
||||
}
|
||||
|
||||
// Merge Remote fields
|
||||
t.Remote.Insecure = cmp.Or(other.Remote.Insecure, t.Remote.Insecure)
|
||||
t.Remote.Offline = cmp.Or(other.Remote.Offline, t.Remote.Offline)
|
||||
t.Remote.Timeout = cmp.Or(other.Remote.Timeout, t.Remote.Timeout)
|
||||
t.Remote.CacheExpiry = cmp.Or(other.Remote.CacheExpiry, t.Remote.CacheExpiry)
|
||||
|
||||
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
|
||||
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
package taskrc
|
||||
|
||||
import "github.com/go-task/task/v3/internal/fsext"
|
||||
import (
|
||||
"github.com/go-task/task/v3/internal/fsext"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
entrypoint string
|
||||
dir string
|
||||
}
|
||||
|
||||
func NewNode(
|
||||
entrypoint string,
|
||||
dir string,
|
||||
possibleFileNames []string,
|
||||
) (*Node, error) {
|
||||
dir = fsext.DefaultDir(entrypoint, dir)
|
||||
var err error
|
||||
entrypoint, dir, err = fsext.Search(entrypoint, dir, defaultTaskRCs)
|
||||
resolvedEntrypoint, err := fsext.SearchPath(dir, possibleFileNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Node{
|
||||
entrypoint: entrypoint,
|
||||
dir: dir,
|
||||
entrypoint: resolvedEntrypoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,89 @@
|
||||
package taskrc
|
||||
|
||||
var defaultTaskRCs = []string{
|
||||
".taskrc.yml",
|
||||
".taskrc.yaml",
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/go-task/task/v3/internal/fsext"
|
||||
"github.com/go-task/task/v3/taskrc/ast"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultXDGTaskRCs = []string{
|
||||
"taskrc.yml",
|
||||
"taskrc.yaml",
|
||||
}
|
||||
defaultTaskRCs = []string{
|
||||
".taskrc.yml",
|
||||
".taskrc.yaml",
|
||||
}
|
||||
)
|
||||
|
||||
// GetConfig loads and merges local and global Task configuration files
|
||||
func GetConfig(dir string) (*ast.TaskRC, error) {
|
||||
var config *ast.TaskRC
|
||||
reader := NewReader()
|
||||
|
||||
// Read the XDG config file
|
||||
if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
|
||||
xdgConfigNode, err := NewNode("", filepath.Join(xdgConfigHome, "task"), defaultXDGTaskRCs)
|
||||
if err == nil && xdgConfigNode != nil {
|
||||
xdgConfig, err := reader.Read(xdgConfigNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config = xdgConfig
|
||||
}
|
||||
}
|
||||
|
||||
// If the current path does not contain $HOME
|
||||
// If it does contain $HOME, then we will find this config later anyway
|
||||
home, err := os.UserHomeDir()
|
||||
if err == nil && !strings.Contains(home, dir) {
|
||||
homeNode, err := NewNode("", home, defaultTaskRCs)
|
||||
if err == nil && homeNode != nil {
|
||||
homeConfig, err := reader.Read(homeNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config == nil {
|
||||
config = homeConfig
|
||||
} else {
|
||||
config.Merge(homeConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find all the nodes from the given directory up to the users home directory
|
||||
entrypoints, err := fsext.SearchAll("", dir, defaultTaskRCs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Reverse the entrypoints since we want the child files to override parent ones
|
||||
slices.Reverse(entrypoints)
|
||||
|
||||
// Loop over the nodes, and merge them into the main config
|
||||
for _, entrypoint := range entrypoints {
|
||||
node, err := NewNode("", entrypoint, defaultTaskRCs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localConfig, err := reader.Read(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if localConfig == nil {
|
||||
continue
|
||||
}
|
||||
if config == nil {
|
||||
config = localConfig
|
||||
continue
|
||||
}
|
||||
config.Merge(localConfig)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
137
taskrc/taskrc_test.go
Normal file
137
taskrc/taskrc_test.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package taskrc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/go-task/task/v3/taskrc/ast"
|
||||
)
|
||||
|
||||
const (
|
||||
xdgConfigYAML = `
|
||||
experiments:
|
||||
FOO: 1
|
||||
BAR: 1
|
||||
BAZ: 1
|
||||
`
|
||||
|
||||
homeConfigYAML = `
|
||||
experiments:
|
||||
FOO: 2
|
||||
BAR: 2
|
||||
`
|
||||
|
||||
localConfigYAML = `
|
||||
experiments:
|
||||
FOO: 3
|
||||
`
|
||||
)
|
||||
|
||||
func setupDirs(t *testing.T) (string, string, string) {
|
||||
t.Helper()
|
||||
|
||||
xdgConfigDir := t.TempDir()
|
||||
xdgTaskConfigDir := filepath.Join(xdgConfigDir, "task")
|
||||
require.NoError(t, os.Mkdir(xdgTaskConfigDir, 0o755))
|
||||
|
||||
homeDir := t.TempDir()
|
||||
|
||||
localDir := filepath.Join(homeDir, "local")
|
||||
require.NoError(t, os.Mkdir(localDir, 0o755))
|
||||
|
||||
t.Setenv("XDG_CONFIG_HOME", xdgConfigDir)
|
||||
t.Setenv("HOME", homeDir)
|
||||
|
||||
return xdgTaskConfigDir, homeDir, localDir
|
||||
}
|
||||
|
||||
func writeFile(t *testing.T, dir, filename, content string) {
|
||||
t.Helper()
|
||||
err := os.WriteFile(filepath.Join(dir, filename), []byte(content), 0o644)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetConfig_NoConfigFiles(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||
_, _, localDir := setupDirs(t)
|
||||
|
||||
cfg, err := GetConfig(localDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, cfg)
|
||||
}
|
||||
|
||||
func TestGetConfig_OnlyXDG(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||
xdgDir, _, localDir := setupDirs(t)
|
||||
|
||||
writeFile(t, xdgDir, "taskrc.yml", xdgConfigYAML)
|
||||
|
||||
cfg, err := GetConfig(localDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &ast.TaskRC{
|
||||
Version: nil,
|
||||
Experiments: map[string]int{
|
||||
"FOO": 1,
|
||||
"BAR": 1,
|
||||
"BAZ": 1,
|
||||
},
|
||||
}, cfg)
|
||||
}
|
||||
|
||||
func TestGetConfig_OnlyHome(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||
_, homeDir, localDir := setupDirs(t)
|
||||
|
||||
writeFile(t, homeDir, ".taskrc.yml", homeConfigYAML)
|
||||
|
||||
cfg, err := GetConfig(localDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &ast.TaskRC{
|
||||
Version: nil,
|
||||
Experiments: map[string]int{
|
||||
"FOO": 2,
|
||||
"BAR": 2,
|
||||
},
|
||||
}, cfg)
|
||||
}
|
||||
|
||||
func TestGetConfig_OnlyLocal(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||
_, _, localDir := setupDirs(t)
|
||||
|
||||
writeFile(t, localDir, ".taskrc.yml", localConfigYAML)
|
||||
|
||||
cfg, err := GetConfig(localDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &ast.TaskRC{
|
||||
Version: nil,
|
||||
Experiments: map[string]int{
|
||||
"FOO": 3,
|
||||
},
|
||||
}, cfg)
|
||||
}
|
||||
|
||||
func TestGetConfig_All(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||
xdgConfigDir, homeDir, localDir := setupDirs(t)
|
||||
|
||||
// Write local config
|
||||
writeFile(t, localDir, ".taskrc.yml", localConfigYAML)
|
||||
|
||||
// Write home config
|
||||
writeFile(t, homeDir, ".taskrc.yml", homeConfigYAML)
|
||||
|
||||
// Write XDG config
|
||||
writeFile(t, xdgConfigDir, "taskrc.yml", xdgConfigYAML)
|
||||
|
||||
cfg, err := GetConfig(localDir)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, cfg)
|
||||
assert.Equal(t, &ast.TaskRC{
|
||||
Version: nil,
|
||||
Experiments: map[string]int{
|
||||
"FOO": 3,
|
||||
"BAR": 2,
|
||||
"BAZ": 1,
|
||||
},
|
||||
}, cfg)
|
||||
}
|
||||
8
testdata/checksum/Taskfile.yml
vendored
8
testdata/checksum/Taskfile.yml
vendored
@@ -12,6 +12,14 @@ tasks:
|
||||
generates:
|
||||
- ./generated.txt
|
||||
method: checksum
|
||||
build-*:
|
||||
cmds:
|
||||
- cp ./source.txt ./generated-{{index .MATCH 0}}.txt
|
||||
sources:
|
||||
- ./source.txt
|
||||
generates:
|
||||
- ./generated-{{index .MATCH 0}}.txt
|
||||
method: checksum
|
||||
|
||||
build-with-status:
|
||||
cmds:
|
||||
|
||||
1
testdata/checksum/generated-wildcard.txt
vendored
Normal file
1
testdata/checksum/generated-wildcard.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Hello, World!
|
||||
11
testdata/run/Taskfile.yml
vendored
11
testdata/run/Taskfile.yml
vendored
@@ -22,3 +22,14 @@ tasks:
|
||||
run: once
|
||||
cmds:
|
||||
- echo starting {{.CONTENT}} >> hash.txt
|
||||
|
||||
deploy:
|
||||
cmds:
|
||||
- rm -rf wildcard.txt
|
||||
- task: deploy:infra
|
||||
- task: deploy:js
|
||||
- task: deploy:go
|
||||
|
||||
deploy:*:
|
||||
run: once
|
||||
cmd: echo "Deploy {{index .MATCH 0}}" >> wildcard.txt
|
||||
|
||||
@@ -44,9 +44,14 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fullName := origTask.Task
|
||||
if matches, exists := vars.Get("MATCH"); exists {
|
||||
for _, match := range matches.Value.([]string) {
|
||||
fullName = strings.Replace(fullName, "*", match, 1)
|
||||
}
|
||||
}
|
||||
|
||||
cache := &templater.Cache{Vars: vars}
|
||||
|
||||
new := ast.Task{
|
||||
Task: origTask.Task,
|
||||
Label: templater.Replace(origTask.Label, cache),
|
||||
@@ -76,6 +81,7 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err
|
||||
Requires: origTask.Requires,
|
||||
Watch: origTask.Watch,
|
||||
Namespace: origTask.Namespace,
|
||||
FullName: fullName,
|
||||
}
|
||||
new.Dir, err = execext.ExpandLiteral(new.Dir)
|
||||
if err != nil {
|
||||
|
||||
18
website/.gitignore
vendored
18
website/.gitignore
vendored
@@ -1,21 +1,9 @@
|
||||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
i18n
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.vitepress/cache
|
||||
.vitepress/dist
|
||||
.task/
|
||||
|
||||
1
website/.prettierignore
Normal file
1
website/.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
pnpm-lock.yaml
|
||||
5
website/.vitepress/components.d.ts
vendored
Normal file
5
website/.vitepress/components.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
97
website/.vitepress/components/AuthorCard.vue
Normal file
97
website/.vitepress/components/AuthorCard.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="author-compact" v-if="author">
|
||||
<img :src="author.avatar" :alt="author.name" class="author-avatar" />
|
||||
<div class="author-info">
|
||||
<div class="author-name-line">
|
||||
<span class="author-name">{{ author.name }}</span>
|
||||
|
||||
<div class="author-socials">
|
||||
<a
|
||||
v-for="{ link, icon } in author.links"
|
||||
:key="link"
|
||||
:href="link"
|
||||
target="_blank"
|
||||
class="social-link"
|
||||
>
|
||||
<span :class="`vpi-social-${icon}`"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<span class="author-bio">{{ author.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { team } from '../team.ts';
|
||||
import { computed } from 'vue';
|
||||
const props = defineProps({
|
||||
author: String
|
||||
});
|
||||
|
||||
const author = computed(() => {
|
||||
return team.find((m) => m.slug === props.author) || null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.author-compact {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.author-avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.author-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.1rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.author-name-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.author-name {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.author-bio {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.author-socials {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.social-link {
|
||||
color: var(--vp-c-text-2);
|
||||
transition: color 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.social-link:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.author-compact {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
182
website/.vitepress/components/BlogPost.vue
Normal file
182
website/.vitepress/components/BlogPost.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<article class="blog-post">
|
||||
<div class="post-header">
|
||||
<h3 class="post-title">
|
||||
<a :href="url">{{ title }}</a>
|
||||
</h3>
|
||||
|
||||
<div class="post-meta">
|
||||
<time :datetime="date" class="post-date">
|
||||
{{ formatDate(date) }}
|
||||
</time>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="post-content">
|
||||
<div class="post-image" v-if="image">
|
||||
<img :src="image" :alt="title" />
|
||||
</div>
|
||||
|
||||
<div class="post-text">
|
||||
<AuthorCard :author="author" />
|
||||
|
||||
<p class="post-description">{{ description }}</p>
|
||||
|
||||
<div class="post-footer">
|
||||
<div class="post-tags" v-if="tags?.length">
|
||||
<strong>Tags:</strong>
|
||||
<code v-for="tag in tags" :key="tag" class="post-tag">{{
|
||||
tag
|
||||
}}</code>
|
||||
</div>
|
||||
|
||||
<a :href="url" class="read-more"> Read more → </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import AuthorCard from './AuthorCard.vue';
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
url: String,
|
||||
date: String,
|
||||
author: String,
|
||||
description: String,
|
||||
tags: Array,
|
||||
image: String
|
||||
});
|
||||
|
||||
function formatDate(date) {
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.blog-post {
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
padding-bottom: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.blog-post:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 1.8rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.post-title a {
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.post-title a:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.post-date {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.post-image {
|
||||
flex-shrink: 0;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.post-image img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
object-fit: cover;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.post-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.post-description {
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.6;
|
||||
margin: 1.5rem 0;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.post-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
margin-top: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.post-tags {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.post-tag {
|
||||
background: var(--vp-c-default-soft);
|
||||
color: var(--vp-c-text-2);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
margin-left: 0.5rem;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
}
|
||||
|
||||
.read-more {
|
||||
color: var(--vp-c-brand-1);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--vp-c-brand-1);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.read-more:hover {
|
||||
background: var(--vp-c-brand-1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.post-content {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.post-image {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.post-footer {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
20
website/.vitepress/components/HomePage.vue
Normal file
20
website/.vitepress/components/HomePage.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { VPHomeSponsors } from 'vitepress/theme';
|
||||
import { sponsors } from '../sponsors';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="content-container">
|
||||
<main class="main">
|
||||
<VPHomeSponsors
|
||||
v-if="sponsors"
|
||||
message="Task is free and open source, made possible by wonderful sponsors."
|
||||
:data="sponsors"
|
||||
/>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
245
website/.vitepress/components/VPTeamMembersItem.vue
Normal file
245
website/.vitepress/components/VPTeamMembersItem.vue
Normal file
@@ -0,0 +1,245 @@
|
||||
<script setup lang="ts">
|
||||
import type { DefaultTheme } from 'vitepress/theme';
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
import VPSocialLinks from 'vitepress/dist/client/theme-default/components/VPSocialLinks.vue';
|
||||
|
||||
interface Props {
|
||||
size?: 'small' | 'medium';
|
||||
member: TeamMember;
|
||||
}
|
||||
|
||||
interface TeamMember extends DefaultTheme.TeamMember {
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 'medium'
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article class="VPTeamMembersItem" :class="[size]">
|
||||
<div class="profile">
|
||||
<figure class="avatar">
|
||||
<img class="avatar-img" :src="member.avatar" :alt="member.name" />
|
||||
</figure>
|
||||
<div class="data">
|
||||
<h1 class="name">
|
||||
<img :src="member.icon" alt="profile-icon" />
|
||||
{{ member.name }}
|
||||
</h1>
|
||||
<p v-if="member.title || member.org" class="affiliation">
|
||||
<span v-if="member.title" class="title">
|
||||
{{ member.title }}
|
||||
</span>
|
||||
<span v-if="member.title && member.org" class="at"> @ </span>
|
||||
<VPLink
|
||||
v-if="member.org"
|
||||
class="org"
|
||||
:class="{ link: member.orgLink }"
|
||||
:href="member.orgLink"
|
||||
no-icon
|
||||
>
|
||||
{{ member.org }}
|
||||
</VPLink>
|
||||
</p>
|
||||
<p v-if="member.desc" class="desc" v-html="member.desc" />
|
||||
<div v-if="member.links" class="links">
|
||||
<VPSocialLinks :links="member.links" :me="false" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="member.sponsor" class="sp">
|
||||
<VPLink class="sp-link" :href="member.sponsor" no-icon>
|
||||
<span class="vpi-heart sp-icon" /> {{ member.actionText || 'Sponsor' }}
|
||||
</VPLink>
|
||||
</div>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.VPTeamMembersItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .profile {
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .data {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .avatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .name {
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .affiliation {
|
||||
padding-top: 4px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .desc {
|
||||
padding-top: 12px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .links {
|
||||
margin: 0 -16px -20px;
|
||||
padding: 10px 0 0;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .profile {
|
||||
padding: 48px 32px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .data {
|
||||
padding-top: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .avatar {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .name {
|
||||
letter-spacing: 0.15px;
|
||||
line-height: 28px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .affiliation {
|
||||
padding-top: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .desc {
|
||||
padding-top: 16px;
|
||||
max-width: 288px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .links {
|
||||
margin: 0 -16px -12px;
|
||||
padding: 16px 12px 0;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem .profile .name {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem .profile .name img {
|
||||
display: inline-block;
|
||||
height: 22px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.profile {
|
||||
flex-grow: 1;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.data {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
margin: 0 auto;
|
||||
border-radius: 50%;
|
||||
box-shadow: var(--vp-shadow-3);
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.affiliation {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.org.link {
|
||||
color: var(--vp-c-text-2);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.org.link:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.desc :deep(a) {
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-brand-1);
|
||||
text-decoration-style: dotted;
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.sp-link {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-sponsor);
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
transition:
|
||||
color 0.25s,
|
||||
background-color 0.25s;
|
||||
}
|
||||
|
||||
.sp .sp-link.link:hover,
|
||||
.sp .sp-link.link:focus {
|
||||
outline: none;
|
||||
color: var(--vp-c-white);
|
||||
background-color: var(--vp-c-sponsor);
|
||||
}
|
||||
|
||||
.sp-icon {
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
7
website/.vitepress/components/Version.vue
Normal file
7
website/.vitepress/components/Version.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { VPBadge } from 'vitepress/theme';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPBadge type="info"> <slot />+ </VPBadge>
|
||||
</template>
|
||||
348
website/.vitepress/config.ts
Normal file
348
website/.vitepress/config.ts
Normal file
@@ -0,0 +1,348 @@
|
||||
import { defineConfig } from 'vitepress';
|
||||
import githubLinksPlugin from './plugins/github-links';
|
||||
import { readFileSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs';
|
||||
import {
|
||||
groupIconMdPlugin,
|
||||
groupIconVitePlugin,
|
||||
localIconLoader
|
||||
} from 'vitepress-plugin-group-icons';
|
||||
import { team } from './team.ts';
|
||||
import { taskDescription, taskName } from './meta.ts';
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
|
||||
const version = readFileSync(
|
||||
resolve(__dirname, '../../internal/version/version.txt'),
|
||||
'utf8'
|
||||
).trim();
|
||||
|
||||
const urlVersion =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? {
|
||||
current: 'https://taskfile.dev/',
|
||||
next: 'http://localhost:3002/'
|
||||
}
|
||||
: {
|
||||
current: 'https://taskfile.dev/',
|
||||
next: 'https://next.taskfile.dev/'
|
||||
};
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: taskName,
|
||||
description: taskDescription,
|
||||
lang: 'en-US',
|
||||
head: [
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/x-icon',
|
||||
href: '/img/favicon.icon',
|
||||
sizes: '48x48'
|
||||
}
|
||||
],
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'icon',
|
||||
sizes: 'any',
|
||||
type: 'image/svg+xml',
|
||||
href: '/img/logo.svg'
|
||||
}
|
||||
],
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'canonical',
|
||||
href: 'https://taskfile.dev/'
|
||||
}
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{ name: 'author', content: `${team.map((c) => c.name).join(', ')}` }
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
name: 'keywords',
|
||||
content:
|
||||
'task runner, build tool, taskfile, yaml build tool, go task runner, make alternative, cross-platform build tool, makefile alternative, automation tool, ci cd pipeline, developer productivity, build automation, command line tool, go binary, yaml configuration'
|
||||
}
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{
|
||||
async: '',
|
||||
src: 'https://www.googletagmanager.com/gtag/js?id=G-4RT25NXQ7N'
|
||||
}
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{},
|
||||
`window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag("js", new Date());
|
||||
gtag("config", "G-4RT25NXQ7N");`
|
||||
],
|
||||
[
|
||||
"script",
|
||||
{
|
||||
defer: "",
|
||||
src: "https://umami.taskfile.dev/script.js",
|
||||
"data-website-id": "084030b0-0e3f-4891-8d2a-0c12c40f5933"
|
||||
}
|
||||
]
|
||||
],
|
||||
srcDir: 'src',
|
||||
cleanUrls: true,
|
||||
markdown: {
|
||||
config: (md) => {
|
||||
md.use(githubLinksPlugin, {
|
||||
baseUrl: 'https://github.com',
|
||||
repo: 'go-task/task'
|
||||
});
|
||||
md.use(tabsMarkdownPlugin);
|
||||
md.use(groupIconMdPlugin);
|
||||
}
|
||||
},
|
||||
vite: {
|
||||
plugins: [
|
||||
groupIconVitePlugin({
|
||||
customIcon: {
|
||||
'.taskrc.yml': localIconLoader(
|
||||
import.meta.url,
|
||||
'./theme/icons/task.svg'
|
||||
),
|
||||
'Taskfile.yml': localIconLoader(
|
||||
import.meta.url,
|
||||
'./theme/icons/task.svg'
|
||||
)
|
||||
}
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: /^.*\/VPTeamMembersItem\.vue$/,
|
||||
replacement: fileURLToPath(
|
||||
new URL('./components/VPTeamMembersItem.vue', import.meta.url)
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
themeConfig: {
|
||||
logo: '/img/logo.svg',
|
||||
carbonAds: {
|
||||
code: 'CESI65QJ',
|
||||
placement: 'taskfiledev'
|
||||
},
|
||||
search: {
|
||||
provider: 'algolia',
|
||||
options: {
|
||||
appId: '7IZIJ13AI7',
|
||||
apiKey: '34b64ae4fc8d9da43d9a13d9710aaddc',
|
||||
indexName: 'taskfile'
|
||||
}
|
||||
},
|
||||
nav: [
|
||||
{ text: 'Home', link: '/' },
|
||||
{
|
||||
text: 'Docs',
|
||||
link: '/docs/guide',
|
||||
activeMatch: '^/docs'
|
||||
},
|
||||
{ text: 'Blog', link: '/blog', activeMatch: '^/blog' },
|
||||
{ text: 'Donate', link: '/donate' },
|
||||
{ text: 'Team', link: '/team' },
|
||||
{
|
||||
text: process.env.NODE_ENV === 'development' ? 'Next' : `v${version}`,
|
||||
items: [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: `v${version}`,
|
||||
link: urlVersion.current
|
||||
},
|
||||
{
|
||||
text: 'Next',
|
||||
link: urlVersion.next
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
sidebar: {
|
||||
'/blog/': [
|
||||
{
|
||||
text: '2025',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'Built-in Core Utilities',
|
||||
link: '/blog/windows-core-utils'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '2024',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'Any Variables',
|
||||
link: '/blog/any-variables'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '2023',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'Introducing Experiments',
|
||||
link: '/blog/task-in-2023'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'/': [
|
||||
{
|
||||
text: 'Installation',
|
||||
link: '/docs/installation'
|
||||
},
|
||||
{
|
||||
text: 'Getting Started',
|
||||
link: '/docs/getting-started'
|
||||
},
|
||||
{
|
||||
text: 'Guide',
|
||||
link: '/docs/guide'
|
||||
},
|
||||
{
|
||||
text: 'Reference',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: 'Taskfile Schema',
|
||||
link: '/docs/reference/schema'
|
||||
},
|
||||
{
|
||||
text: 'Environment',
|
||||
link: '/docs/reference/environment'
|
||||
},
|
||||
{
|
||||
text: 'Configuration',
|
||||
link: '/docs/reference/config'
|
||||
},
|
||||
{
|
||||
text: 'CLI',
|
||||
link: '/docs/reference/cli'
|
||||
},
|
||||
{
|
||||
text: 'Templating',
|
||||
link: '/docs/reference/templating'
|
||||
},
|
||||
{
|
||||
text: 'Package API',
|
||||
link: '/docs/reference/package'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Experiments',
|
||||
collapsed: true,
|
||||
link: '/docs/experiments/',
|
||||
items: [
|
||||
{
|
||||
text: 'Env Precedence (#1038)',
|
||||
link: '/docs/experiments/env-precedence'
|
||||
},
|
||||
{
|
||||
text: 'Gentle Force (#1200)',
|
||||
link: '/docs/experiments/gentle-force'
|
||||
},
|
||||
{
|
||||
text: 'Remote Taskfiles (#1317)',
|
||||
link: '/docs/experiments/remote-taskfiles'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Deprecations',
|
||||
collapsed: true,
|
||||
link: '/docs/deprecations/',
|
||||
items: [
|
||||
{
|
||||
text: 'Completion Scripts',
|
||||
link: '/docs/deprecations/completion-scripts'
|
||||
},
|
||||
{
|
||||
text: 'Template Functions',
|
||||
link: '/docs/deprecations/template-functions'
|
||||
},
|
||||
{
|
||||
text: 'Version 2 Schema (#1197)',
|
||||
link: '/docs/deprecations/version-2-schema'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Taskfile Versions',
|
||||
link: '/docs/taskfile-versions'
|
||||
},
|
||||
{
|
||||
text: 'Integrations',
|
||||
link: '/docs/integrations'
|
||||
},
|
||||
{
|
||||
text: 'Community',
|
||||
link: '/docs/community'
|
||||
},
|
||||
{
|
||||
text: 'Style Guide',
|
||||
link: '/docs/styleguide'
|
||||
},
|
||||
{
|
||||
text: 'Contributing',
|
||||
link: '/docs/contributing'
|
||||
},
|
||||
{
|
||||
text: 'Releasing',
|
||||
link: '/docs/releasing'
|
||||
},
|
||||
{
|
||||
text: 'Changelog',
|
||||
link: '/docs/changelog'
|
||||
},
|
||||
{
|
||||
text: 'FAQ',
|
||||
link: '/docs/faq'
|
||||
}
|
||||
],
|
||||
// Hacky to disable sidebar for these pages
|
||||
'/donate': [],
|
||||
'/team': []
|
||||
},
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/go-task/task' },
|
||||
{ icon: 'discord', link: 'https://discord.gg/6TY36E39UK' },
|
||||
{ icon: 'x', link: 'https://twitter.com/taskfiledev' },
|
||||
{ icon: 'bluesky', link: 'https://bsky.app/profile/taskfile.dev' },
|
||||
{ icon: 'mastodon', link: 'https://fosstodon.org/@task' }
|
||||
],
|
||||
|
||||
footer: {
|
||||
message:
|
||||
'Built with <a target="_blank" href="https://www.netlify.com">Netlify</a>'
|
||||
}
|
||||
},
|
||||
sitemap: {
|
||||
hostname: 'https://taskfile.dev'
|
||||
}
|
||||
});
|
||||
5
website/.vitepress/meta.ts
Normal file
5
website/.vitepress/meta.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const taskName = 'Task';
|
||||
export const taskDescription =
|
||||
'A fast, cross-platform build tool inspired by Make, designed for modern workflows.';
|
||||
|
||||
export const ogUrl = 'https://taskfile.dev/';
|
||||
63
website/.vitepress/plugins/github-links.ts
Normal file
63
website/.vitepress/plugins/github-links.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import type MarkdownIt from 'markdown-it';
|
||||
|
||||
interface PluginOptions {
|
||||
repo: string;
|
||||
}
|
||||
|
||||
function githubLinksPlugin(
|
||||
md: MarkdownIt,
|
||||
options: PluginOptions = {} as PluginOptions
|
||||
): void {
|
||||
const baseUrl = 'https://github.com';
|
||||
const { repo } = options;
|
||||
|
||||
md.core.ruler.after('inline', 'github-links', (state): void => {
|
||||
const tokens = state.tokens;
|
||||
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
if (tokens[i].type === 'inline' && tokens[i].children) {
|
||||
const inlineTokens = tokens[i].children!;
|
||||
|
||||
for (let j = 0; j < inlineTokens.length; j++) {
|
||||
if (inlineTokens[j].type === 'text') {
|
||||
let text: string = inlineTokens[j].content!;
|
||||
|
||||
const protectedRefs: string[] = [];
|
||||
let protectIndex: number = 0;
|
||||
|
||||
text = text.replace(
|
||||
/[\w.-]+\/[\w.-]+#(\d+)/g,
|
||||
(match: string): string => {
|
||||
const placeholder: string = `__PROTECTED_${protectIndex}__`;
|
||||
protectedRefs[protectIndex] = match;
|
||||
protectIndex++;
|
||||
return placeholder;
|
||||
}
|
||||
);
|
||||
|
||||
text = text.replace(
|
||||
/#(\d+)/g,
|
||||
`<a href="${baseUrl}/${repo}/issues/$1" target="_blank" class="github-pr-link">#$1</a>`
|
||||
);
|
||||
|
||||
text = text.replace(
|
||||
/@([a-zA-Z0-9_-]+)(?![\w@.])/g,
|
||||
`<a href="${baseUrl}/$1" target="_blank" class="github-user-mention">@$1</a>`
|
||||
);
|
||||
|
||||
protectedRefs.forEach((ref: string, index: number): void => {
|
||||
text = text.replace(`__PROTECTED_${index}__`, ref);
|
||||
});
|
||||
|
||||
if (text !== inlineTokens[j].content) {
|
||||
inlineTokens[j].content = text;
|
||||
inlineTokens[j].type = 'html_inline';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default githubLinksPlugin;
|
||||
13
website/.vitepress/sponsors.ts
Normal file
13
website/.vitepress/sponsors.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export const sponsors = [
|
||||
{
|
||||
tier: 'Gold Sponsors',
|
||||
size: 'big',
|
||||
items: [
|
||||
{
|
||||
name: 'devowl',
|
||||
url: 'https://devowl.io/',
|
||||
img: '/img/devowl.io.svg'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
45
website/.vitepress/team.ts
Normal file
45
website/.vitepress/team.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
export const team = [
|
||||
{
|
||||
slug: 'andreynering',
|
||||
avatar: 'https://www.github.com/andreynering.png',
|
||||
name: 'Andrey Nering',
|
||||
icon: '/img/flag-brazil.svg',
|
||||
title: 'Creator & Maintainer',
|
||||
sponsor: 'https://github.com/sponsors/andreynering',
|
||||
links: [
|
||||
{ icon: 'github', link: 'https://github.com/andreynering' },
|
||||
{ icon: 'discord', link: 'https://discord.com/users/310141681926275082' },
|
||||
{ icon: 'x', link: 'https://x.com/andreynering' },
|
||||
{
|
||||
icon: 'bluesky',
|
||||
link: 'https://bsky.app/profile/andreynering.bsky.social'
|
||||
},
|
||||
{ icon: 'mastodon', link: 'https://mastodon.social/@andreynering' }
|
||||
]
|
||||
},
|
||||
{
|
||||
slug: 'pd93',
|
||||
avatar: 'https://www.github.com/pd93.png',
|
||||
name: 'Pete Davison',
|
||||
icon: '/img/flag-wales.svg',
|
||||
title: 'Maintainer',
|
||||
sponsor: 'https://github.com/sponsors/pd93',
|
||||
links: [
|
||||
{ icon: 'github', link: 'https://github.com/pd93' },
|
||||
{ icon: 'bluesky', link: 'https://bsky.app/profile/pd93.uk' }
|
||||
]
|
||||
},
|
||||
{
|
||||
slug: 'vmaerten',
|
||||
avatar: 'https://www.github.com/vmaerten.png',
|
||||
name: 'Valentin Maerten',
|
||||
icon: '/img/flag-france.svg',
|
||||
title: 'Maintainer',
|
||||
sponsor: 'https://github.com/sponsors/vmaerten',
|
||||
links: [
|
||||
{ icon: 'github', link: 'https://github.com/vmaerten' },
|
||||
{ icon: 'x', link: 'https://x.com/v_maerten' },
|
||||
{ icon: 'bluesky', link: 'https://bsky.app/profile/vmaerten.bsky.social' }
|
||||
]
|
||||
}
|
||||
];
|
||||
147
website/.vitepress/theme/custom.css
Normal file
147
website/.vitepress/theme/custom.css
Normal file
@@ -0,0 +1,147 @@
|
||||
:root {
|
||||
--ifm-color-primary: #43aba2;
|
||||
--vp-home-hero-name-color: var(--ifm-color-primary);
|
||||
--vp-c-brand-1: var(--ifm-color-primary);
|
||||
--vp-c-brand-2: var(--ifm-color-primary);
|
||||
--vp-c-brand-3: var(--ifm-color-primary);
|
||||
|
||||
--vp-icon-info: #3b82f6;
|
||||
--vp-icon-tip: #10b981;
|
||||
--vp-icon-warning: #f59e0b;
|
||||
--vp-icon-danger: #ef4444;
|
||||
--vp-icon-details: #6b7280;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--vp-icon-info: #93c5fd;
|
||||
--vp-icon-tip: #34d399;
|
||||
--vp-icon-warning: #fbbf24;
|
||||
--vp-icon-danger: #f87171;
|
||||
--vp-icon-details: #9ca3af;
|
||||
}
|
||||
|
||||
img[src*='shields.io'] {
|
||||
display: inline;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
img[src*='custom-icon-badges.demolab.com'] {
|
||||
display: inline;
|
||||
height: 1em;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.github-user-mention {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.vp-doc .blog-post:first-of-type {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.blog-post {
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.blog-post:nth-of-type(1) {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
.blog-post:nth-of-type(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
.blog-post:nth-of-type(3) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.custom-block .custom-block-title::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
flex-shrink: 0;
|
||||
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-size: contain;
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: contain;
|
||||
}
|
||||
|
||||
.custom-block.info .custom-block-title::before {
|
||||
background-color: var(--vp-icon-info);
|
||||
-webkit-mask-image: url('./icons/info.svg');
|
||||
mask-image: url('./icons/info.svg');
|
||||
}
|
||||
|
||||
.custom-block.tip .custom-block-title::before {
|
||||
background-color: var(--vp-icon-tip);
|
||||
-webkit-mask-image: url('./icons/tip.svg');
|
||||
mask-image: url('./icons/tip.svg');
|
||||
}
|
||||
|
||||
.custom-block.warning .custom-block-title::before {
|
||||
background-color: var(--vp-icon-warning);
|
||||
-webkit-mask-image: url('./icons/warning.svg');
|
||||
mask-image: url('./icons/warning.svg');
|
||||
}
|
||||
|
||||
.custom-block.danger .custom-block-title::before {
|
||||
background-color: var(--vp-icon-danger);
|
||||
-webkit-mask-image: url('./icons/danger.svg');
|
||||
mask-image: url('./icons/danger.svg');
|
||||
}
|
||||
|
||||
.custom-block.details[open] summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.custom-block .custom-block-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@supports not (mask-image: none) {
|
||||
.custom-block .custom-block-title::before,
|
||||
.custom-block.details summary::before {
|
||||
font-size: 18px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
background: none !important;
|
||||
-webkit-mask: none !important;
|
||||
mask: none !important;
|
||||
}
|
||||
|
||||
.custom-block.info .custom-block-title::before {
|
||||
content: 'ℹ️';
|
||||
}
|
||||
|
||||
.custom-block.tip .custom-block-title::before {
|
||||
content: '💡';
|
||||
}
|
||||
|
||||
.custom-block.warning .custom-block-title::before {
|
||||
content: '⚠️';
|
||||
}
|
||||
|
||||
.custom-block.danger .custom-block-title::before {
|
||||
content: '🔥';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.VPTeamPage > .VPTeamPageTitle {
|
||||
padding-top: 0
|
||||
}
|
||||
3
website/.vitepress/theme/icons/danger.svg
Normal file
3
website/.vitepress/theme/icons/danger.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M7.998 14.5c2.832 0 5-1.98 5-4.5 0-1.463-.68-2.19-1.879-3.383l-.036-.037c-1.013-1.008-2.3-2.29-2.834-4.434-.322.256-.63.579-.864.953-.432.696-.621 1.58-.046 2.73.473.947.67 2.284-.278 3.232-.61.61-1.545.84-2.403.633a2.788 2.788 0 0 1-1.436-.874A3.21 3.21 0 0 0 3 10c0 2.53 2.164 4.5 4.998 4.5zM9.533.753C9.496.34 9.16.009 8.77.146 7.035.75 4.34 3.187 5.997 6.5c.344.689.285 1.218.003 1.5-.419.419-1.54.487-2.04-.832-.173-.454-.659-.762-1.035-.454C2.036 7.44 1.5 8.702 1.5 10c0 3.512 2.998 6 6.498 6s6.5-2.5 6.5-6c0-2.137-1.128-3.26-2.312-4.438-1.19-1.184-2.436-2.425-2.653-4.81z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 681 B |
3
website/.vitepress/theme/icons/info.svg
Normal file
3
website/.vitepress/theme/icons/info.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 350 B |
|
Before Width: | Height: | Size: 435 B After Width: | Height: | Size: 435 B |
3
website/.vitepress/theme/icons/tip.svg
Normal file
3
website/.vitepress/theme/icons/tip.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 796 B |
3
website/.vitepress/theme/icons/warning.svg
Normal file
3
website/.vitepress/theme/icons/warning.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 405 B |
24
website/.vitepress/theme/index.ts
Normal file
24
website/.vitepress/theme/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import DefaultTheme from 'vitepress/theme';
|
||||
import type { Theme } from 'vitepress';
|
||||
import './custom.css';
|
||||
import HomePage from '../components/HomePage.vue';
|
||||
import AuthorCard from '../components/AuthorCard.vue';
|
||||
import BlogPost from '../components/BlogPost.vue';
|
||||
import Version from '../components/Version.vue';
|
||||
import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client';
|
||||
import { h } from 'vue';
|
||||
import 'virtual:group-icons.css';
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
Layout() {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
'home-features-after': () => h(HomePage)
|
||||
});
|
||||
},
|
||||
enhanceApp({ app }) {
|
||||
app.component('AuthorCard', AuthorCard);
|
||||
app.component('BlogPost', BlogPost);
|
||||
app.component('Version', Version);
|
||||
enhanceAppWithTabs(app);
|
||||
}
|
||||
} satisfies Theme;
|
||||
@@ -1,29 +1,35 @@
|
||||
version: "3"
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
yarn:install:
|
||||
desc: Setup Docusaurus locally
|
||||
install:
|
||||
desc: Setup VitePress locally
|
||||
cmds:
|
||||
- yarn install
|
||||
- pnpm install
|
||||
sources:
|
||||
- package.json
|
||||
- yarn.lock
|
||||
- pnpm-lock.yaml
|
||||
|
||||
default:
|
||||
desc: Start website
|
||||
deps: [yarn:install]
|
||||
deps: [install]
|
||||
aliases: [s, start]
|
||||
vars:
|
||||
HOST: '{{default "0.0.0.0" .HOST}}'
|
||||
PORT: '{{default "3001" .PORT}}'
|
||||
cmds:
|
||||
- npx docusaurus start --no-open --host={{.HOST}} --port={{.PORT}}
|
||||
- pnpm dev --host={{.HOST}} --port={{.PORT}}
|
||||
|
||||
lint:
|
||||
desc: Lint website
|
||||
deps: [install]
|
||||
cmds:
|
||||
- pnpm lint
|
||||
|
||||
build:
|
||||
desc: Build website
|
||||
deps: [yarn:install]
|
||||
deps: [install]
|
||||
cmds:
|
||||
- npx docusaurus build
|
||||
- pnpm build
|
||||
|
||||
preview:
|
||||
desc: Preview Website
|
||||
@@ -33,20 +39,19 @@ tasks:
|
||||
HOST: '{{default "localhost" .HOST}}'
|
||||
PORT: '{{default "3001" .PORT}}'
|
||||
cmds:
|
||||
- npx docusaurus serve --no-open --host={{.HOST}} --port={{.PORT}}
|
||||
- pnpm preview --host={{.HOST}} --port={{.PORT}}
|
||||
|
||||
clean:
|
||||
desc: Clean temp directories
|
||||
cmds:
|
||||
- rm -rf ./build
|
||||
- rm -rf ./vitepress/dist
|
||||
|
||||
deploy:
|
||||
desc: Build and deploy Docusaurus
|
||||
summary: Requires GIT_USER and GIT_PASS envs to be previous set
|
||||
deploy:next:
|
||||
desc: Build and deploy next.taskfile.dev
|
||||
cmds:
|
||||
- npx docusaurus deploy
|
||||
- pnpm netlify deploy --prod --site=4e13dfcf-fc0d-4bec-ad60-b918a8dc3942
|
||||
|
||||
upgrade:
|
||||
desc: Upgrade Docusaurus
|
||||
deploy:prod:
|
||||
desc: Build and deploy taskfile.dev
|
||||
cmds:
|
||||
- yarn upgrade @docusaurus/core@latest @docusaurus/preset-classic@latest @docusaurus/module-type-aliases@latest @docusaurus/tsconfig@latest @docusaurus/types@latest
|
||||
- pnpm netlify deploy --prod --site=e625bc6a-1cd3-465d-ad30-7bbddaeb4f31
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export default {
|
||||
presets: ['@docusaurus/core/lib/babel/preset'],
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
andreynering:
|
||||
name: Andrey Nering
|
||||
title: Maintainer of Task
|
||||
url: https://github.com/andreynering
|
||||
image_url: https://github.com/andreynering.png
|
||||
pd93:
|
||||
name: Pete Davison
|
||||
title: Maintainer of Task
|
||||
url: https://github.com/pd93
|
||||
image_url: https://github.com/pd93.png
|
||||
@@ -1,7 +0,0 @@
|
||||
export const GITHUB_URL = 'https://github.com/go-task/task';
|
||||
export const TWITTER_URL = 'https://twitter.com/taskfiledev';
|
||||
export const BLUESKY_URL = 'https://bsky.app/profile/taskfile.dev';
|
||||
export const MASTODON_URL = 'https://fosstodon.org/@task';
|
||||
export const DISCORD_URL = 'https://discord.gg/6TY36E39UK';
|
||||
export const STACK_OVERFLOW = 'https://stackoverflow.com/questions/tagged/taskfile';
|
||||
export const ANSWER_OVERFLOW = 'https://www.answeroverflow.com/c/974121106208354339';
|
||||
@@ -1,177 +0,0 @@
|
||||
---
|
||||
slug: /contributing/
|
||||
sidebar_position: 12
|
||||
---
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions to Task are very welcome, but we ask that you read this document
|
||||
before submitting a PR.
|
||||
|
||||
:::note
|
||||
|
||||
This document applies to the core [Task][task] repository _and_ [Task for Visual
|
||||
Studio Code][vscode-task].
|
||||
|
||||
:::
|
||||
|
||||
## Before you start
|
||||
|
||||
- **Check existing work** - Is there an existing PR? Are there issues discussing
|
||||
the feature/change you want to make? Please make sure you consider/address
|
||||
these discussions in your work.
|
||||
- **Backwards compatibility** - Will your change break existing Taskfiles? It is
|
||||
much more likely that your change will merged if it backwards compatible. Is
|
||||
there an approach you can take that maintains this compatibility? If not,
|
||||
consider opening an issue first so that API changes can be discussed before
|
||||
you invest your time into a PR.
|
||||
- **Experiments** - If there is no way to make your change backward compatible
|
||||
then there is a procedure to introduce breaking changes into minor versions.
|
||||
We call these "[experiments][experiments]". If you're intending to work on an
|
||||
experiment, then please read the [experiments workflow][experiments-workflow]
|
||||
document carefully and submit a proposal first.
|
||||
|
||||
## 1. Setup
|
||||
|
||||
- **Go** - Task is written in [Go][go]. We always support the latest two major
|
||||
Go versions, so make sure your version is recent enough.
|
||||
- **Node.js** - [Node.js][nodejs] is used to host Task's documentation server
|
||||
and is required if you want to run this server locally. It is also required if
|
||||
you want to contribute to the Visual Studio Code extension.
|
||||
- **Yarn** - [Yarn][yarn] is the Node.js package manager used by Task.
|
||||
|
||||
## 2. Making changes
|
||||
|
||||
- **Code style** - Try to maintain the existing code style where possible. Go
|
||||
code should be formatted and linted by [`golangci-lint`][golangci-lint]. This
|
||||
wraps the [`gofumpt`][gofumpt] and [`gci`][gci] formatters and a number of
|
||||
linters. We recommend that you take a look at the [golangci-lint
|
||||
docs][golangci-lint-docs] for a guide on how to setup your editor to
|
||||
auto-format your code. Any Markdown or TypeScript files should be formatted
|
||||
and linted by [Prettier][prettier]. This style is enforced by our CI to ensure
|
||||
that we have a consistent style across the project. You can use the `task
|
||||
lint` command to lint the code locally and the `task lint:fix` command to try
|
||||
to automatically fix any issues that are found. You can also use the `task
|
||||
fmt` command to auto-format the files if your editor doesn't do it for you.
|
||||
- **Documentation** - Ensure that you add/update any relevant documentation. See
|
||||
the [updating documentation](#updating-documentation) section below.
|
||||
- **Tests** - Ensure that you add/update any relevant tests and that all tests
|
||||
are passing before submitting the PR. See the [writing tests](#writing-tests)
|
||||
section below.
|
||||
|
||||
### Running your changes
|
||||
|
||||
To run Task with working changes, you can use `go run ./cmd/task`. To run a
|
||||
development build of task against a test Taskfile in `testdata`, you can use
|
||||
`go run ./cmd/task --dir ./testdata/<my_test_dir> <task_name>`.
|
||||
|
||||
To run Task for Visual Studio Code, you can open the project in VSCode and hit
|
||||
F5 (or whatever you debug keybind is set to). This will open a new VSCode window
|
||||
with the extension running. Debugging this way is recommended as it will allow
|
||||
you to set breakpoints and step through the code. Otherwise, you can run
|
||||
`task package` which will generate a `.vsix` file that can be used to manually
|
||||
install the extension.
|
||||
|
||||
### Updating documentation
|
||||
|
||||
Task uses [Docusaurus][docusaurus] to host a documentation server. The code for
|
||||
this is located in the core Task repository. This can be setup and run locally
|
||||
by using `task website` (requires `nodejs` & `yarn`). All content is written in
|
||||
[MDX][mdx] (an extension of Markdown) and is located in the `website/docs`
|
||||
directory. All Markdown documents should have an 80 character line wrap limit
|
||||
(enforced by Prettier).
|
||||
|
||||
When making a change, consider whether a change to the [Usage Guide](/usage) is
|
||||
necessary. This document contains descriptions and examples of how to use Task
|
||||
features. If you're adding a new feature, try to find an appropriate place to
|
||||
add a new section. If you're updating an existing feature, ensure that the
|
||||
documentation and any examples are up-to-date. Ensure that any examples follow
|
||||
the [Taskfile Styleguide](/styleguide).
|
||||
|
||||
If you added a new command or flag, ensure that you add it to the [CLI
|
||||
Reference](/reference/cli). New fields also need to be added to the [Schema
|
||||
Reference](/reference/schema) and [JSON Schema][json-schema]. The descriptions
|
||||
for fields in the docs and the schema should match.
|
||||
|
||||
### Writing tests
|
||||
|
||||
A lot of Task's tests are held in the `task_test.go` file in the project root
|
||||
and this is where you'll most likely want to add new ones too. Most of these
|
||||
tests also have a subdirectory in the `testdata` directory where any
|
||||
Taskfiles/data required to run the tests are stored.
|
||||
|
||||
When making a changes, consider whether new tests are required. These tests
|
||||
should ensure that the functionality you are adding will continue to work in the
|
||||
future. Existing tests may also need updating if you have changed Task's
|
||||
behavior.
|
||||
|
||||
You may also consider adding unit tests for any new functions you have added.
|
||||
The unit tests should follow the Go convention of being location in a file named
|
||||
`*_test.go` in the same package as the code being tested.
|
||||
|
||||
## 3. Committing your code
|
||||
|
||||
Try to write meaningful commit messages and avoid having too many commits on the
|
||||
PR. Most PRs should likely have a single commit (although for bigger PRs it may
|
||||
be reasonable to split it in a few). Git squash and rebase is your friend!
|
||||
|
||||
If you're not sure how to format your commit message, check out [Conventional
|
||||
Commits][conventional-commits]. This style is not enforced, but it is a good way
|
||||
to make your commit messages more readable and consistent.
|
||||
|
||||
## 4. Submitting a PR
|
||||
|
||||
- **Describe your changes** - Ensure that you provide a comprehensive
|
||||
description of your changes.
|
||||
- **Issue/PR links** - Link any previous work such as related issues or PRs.
|
||||
Please describe how your changes differ to/extend this work.
|
||||
- **Examples** - Add any examples or screenshots that you think are useful to
|
||||
demonstrate the effect of your changes.
|
||||
- **Draft PRs** - If your changes are incomplete, but you would like to discuss
|
||||
them, open the PR as a draft and add a comment to start a discussion. Using
|
||||
comments rather than the PR description allows the description to be updated
|
||||
later while preserving any discussions.
|
||||
|
||||
## FAQ
|
||||
|
||||
> I want to contribute, where do I start?
|
||||
|
||||
Take a look at the list of [open issues for Task][task-open-issues] or [Task for
|
||||
Visual Studio Code][vscode-task-open-issues]. We have a [good first
|
||||
issue][good-first-issue] label for simpler issues that are ideal for first time
|
||||
contributions.
|
||||
|
||||
All kinds of contributions are welcome, whether its a typo fix or a shiny new
|
||||
feature. You can also contribute by upvoting/commenting on issues, helping to
|
||||
answer questions or contributing to other [community projects](/community).
|
||||
|
||||
> I'm stuck, where can I get help?
|
||||
|
||||
If you have questions, feel free to ask them in the `#help` forum channel on our
|
||||
[Discord server][discord-server] or open a [Discussion][discussion] on GitHub.
|
||||
|
||||
---
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[experiments]: /experiments
|
||||
[experiments-workflow]: /experiments#workflow
|
||||
[task]: https://github.com/go-task/task
|
||||
[vscode-task]: https://github.com/go-task/vscode-task
|
||||
[go]: https://go.dev
|
||||
[gofumpt]: https://github.com/mvdan/gofumpt
|
||||
[gci]: https://github.com/daixiang0/gci
|
||||
[golangci-lint]: https://golangci-lint.run
|
||||
[golangci-lint-docs]: https://golangci-lint.run/welcome/integrations/
|
||||
[prettier]: https://prettier.io
|
||||
[nodejs]: https://nodejs.org/en/
|
||||
[yarn]: https://yarnpkg.com/
|
||||
[docusaurus]: https://docusaurus.io
|
||||
[json-schema]: https://github.com/go-task/task/blob/main/website/static/schema.json
|
||||
[task-open-issues]: https://github.com/go-task/task/issues
|
||||
[vscode-task-open-issues]: https://github.com/go-task/vscode-task/issues
|
||||
[good-first-issue]: https://github.com/go-task/task/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22
|
||||
[discord-server]: https://discord.gg/6TY36E39UK
|
||||
[discussion]: https://github.com/go-task/task/discussions
|
||||
[conventional-commits]: https://www.conventionalcommits.org
|
||||
[mdx]: https://mdxjs.com/
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
slug: /deprecations/completion-scripts/
|
||||
---
|
||||
|
||||
# Completion Scripts
|
||||
|
||||
:::warning
|
||||
|
||||
This deprecation breaks the following functionality:
|
||||
|
||||
- Any direct references to the completion scripts in the Task git repository
|
||||
|
||||
:::
|
||||
|
||||
Direct use of the completion scripts in the `completion/*` directory of the
|
||||
[github.com/go-task/task][task] Git repository is deprecated. Any shell
|
||||
configuration that directly refers to these scripts will potentially break in
|
||||
the future as the scripts may be moved or deleted entirely. Any configuration
|
||||
should be updated to use the [new method for generating shell
|
||||
completions][completions] instead.
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[completions]: ../installation.mdx#setup-completions
|
||||
[task]: https://github.com/go-task/task
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
slug: /deprecations/
|
||||
sidebar_position: 8
|
||||
---
|
||||
|
||||
# Deprecations
|
||||
|
||||
As Task evolves, it occasionally outgrows some of its functionality. This can be
|
||||
because they are no longer useful, because another feature has replaced it or
|
||||
because of a change in the way that Task works internally.
|
||||
|
||||
When this happens, we mark the functionality as deprecated. This means that it
|
||||
will be removed in a future version of Task. This functionality will continue to
|
||||
work until that time, but we strongly recommend that you do not implement this
|
||||
functionality in new Taskfiles and make a plan to migrate away from it as soon
|
||||
as possible.
|
||||
|
||||
You can view a full list of active deprecations in the "Deprecations" section of
|
||||
the sidebar.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
# This is a template for an experiments documentation
|
||||
# Copy this page and fill in the details as necessary
|
||||
title: '--- Template ---'
|
||||
sidebar_position: -1 # Always push to the top
|
||||
draft: true # Hide in production
|
||||
---
|
||||
|
||||
# \{Name of Deprecated Feature\} (#\{Issue\})
|
||||
|
||||
:::warning
|
||||
|
||||
This deprecation breaks the following functionality:
|
||||
|
||||
- \{list any existing functionality that will be broken by this deprecation\}
|
||||
- \{if there are no breaking changes, remove this admonition\}
|
||||
|
||||
:::
|
||||
|
||||
\{Short description of the feature/behavior and why it is being deprecated\}
|
||||
|
||||
\{Short explanation of any replacement features/behaviors and how users should
|
||||
migrate to it\}
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
slug: /deprecations/version-2-schema/
|
||||
---
|
||||
|
||||
# Version 2 Schema (#1197)
|
||||
|
||||
:::warning
|
||||
|
||||
This deprecation breaks the following functionality:
|
||||
|
||||
- Any Taskfiles that use the version 2 schema
|
||||
- `Taskvar.yml` files
|
||||
|
||||
:::
|
||||
|
||||
The Taskfile version 2 schema was introduced in March 2018 and replaced by
|
||||
version 3 in August 2019. In May 2023 [we published a deprecation
|
||||
notice][deprecation-notice] for the version 2 schema on the basis that the vast
|
||||
majority of users had already upgraded to version 3 and removing support for
|
||||
version 2 would allow us to tidy up the codebase and focus on new functionality
|
||||
instead.
|
||||
|
||||
In December 2023, the final version of Task that supports the version 2 schema
|
||||
([v3.33.0][v3.33.0]) was published and all legacy code was removed from Task's
|
||||
main branch. To use a more recent version of Task, you will need to ensure that
|
||||
your Taskfile uses the version 3 schema instead. A list of changes between
|
||||
version 2 and version 3 are available in the [Task v3 Release Notes][v3.0.0].
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[v3.0.0]: https://github.com/go-task/task/releases/tag/v3.0.0
|
||||
[v3.33.0]: https://github.com/go-task/task/releases/tag/v3.33.0
|
||||
[deprecation-notice]: https://github.com/go-task/task/issues/1197
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,151 +0,0 @@
|
||||
---
|
||||
slug: /experiments/
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Experiments
|
||||
|
||||
:::caution
|
||||
|
||||
All experimental features are subject to breaking changes and/or removal _at any
|
||||
time_. We strongly recommend that you do not use these features in a production
|
||||
environment. They are intended for testing and feedback only.
|
||||
|
||||
:::
|
||||
|
||||
In order to allow Task to evolve quickly, we sometimes roll out breaking changes
|
||||
to minor versions behind experimental flags. This allows us to gather feedback
|
||||
on breaking changes before committing to a major release. This process can also
|
||||
be used to gather feedback on important non-breaking features before their
|
||||
design is completed. This document describes the
|
||||
[experiment workflow](#workflow) and how you can get involved.
|
||||
|
||||
You can view the full list of active experiments in the sidebar submenu to the
|
||||
left of the page and click on each one to find out more about it.
|
||||
|
||||
## Enabling Experiments
|
||||
|
||||
Task uses environment variables to detect whether or not an experiment is
|
||||
enabled. All of the experiment variables will begin with the same `TASK_X_`
|
||||
prefix followed by the name of the experiment. You can find the exact name for
|
||||
each experiment on their respective pages in the sidebar. If the variable is set
|
||||
`=1` then it will be enabled. Some experiments may have multiple proposals, in
|
||||
which case, you will need to set the variable equal to the number of the
|
||||
proposal that you want to enable (`=2`, `=3` etc).
|
||||
|
||||
There are three main ways to set the environment variables for an experiment.
|
||||
Which method you use depends on how you intend to use the experiment:
|
||||
|
||||
1. Prefixing your task commands with the relevant environment variable(s). For
|
||||
example, `TASK_X_{FEATURE}=1 task {my-task}`. This is intended for one-off
|
||||
invocations of Task to test out experimental features.
|
||||
2. Adding the relevant environment variable(s) in your "dotfiles" (e.g.
|
||||
`.bashrc`, `.zshrc` etc.). This will permanently enable experimental features
|
||||
for your personal environment.
|
||||
|
||||
```shell title="~/.bashrc"
|
||||
export TASK_X_FEATURE=1
|
||||
```
|
||||
|
||||
3. Creating a `.env` or a `.taskrc.yml` file in the same directory as
|
||||
your root Taskfile.\
|
||||
The `.env` file should contain the relevant environment
|
||||
variable(s), while the `.taskrc.yml` file should use a YAML format
|
||||
where each experiment is defined as a key with a corresponding value.
|
||||
|
||||
This allows you to enable an experimental feature at a project level. If you
|
||||
commit this file to source control, then other users of your project will
|
||||
also have these experiments enabled.
|
||||
|
||||
If both files are present, the values in the `.taskrc.yml` file
|
||||
will take precedence.
|
||||
|
||||
<Tabs values={[ {label: '.taskrc.yml', value: 'yaml'}, {label: '.env', value: 'env'}]}>
|
||||
<TabItem value="yaml">
|
||||
```yaml title=".taskrc.yml"
|
||||
experiments:
|
||||
FEATURE: 1
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="env">
|
||||
```shell title=".env"
|
||||
TASK_X_FEATURE=1
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Workflow
|
||||
|
||||
Experiments are a way for us to test out new features in Task before committing
|
||||
to them in a major release. Because this concept is built around the idea of
|
||||
feedback from our community, we have built a workflow for the process of
|
||||
introducing these changes. This ensures that experiments are given the attention
|
||||
and time that they need and that we are getting the best possible results out of
|
||||
them.
|
||||
|
||||
The sections below describe the various stages that an experiment must go
|
||||
through from its proposal all the way to being released in a major version of
|
||||
Task.
|
||||
|
||||
### 1. Proposal
|
||||
|
||||
All experimental features start with a proposal in the form of a GitHub issue.
|
||||
If the maintainers decide that an issue has enough support and is a breaking
|
||||
change or is complex/controversial enough to require user feedback, then the
|
||||
issue will be marked with the `status: proposal` label. At this point, the issue
|
||||
becomes a proposal and a period of consultation begins. During this period, we
|
||||
request that users provide feedback on the proposal and how it might effect
|
||||
their use of Task. It is up to the discretion of the maintainers to decide how
|
||||
long this period lasts.
|
||||
|
||||
### 2. Draft
|
||||
|
||||
Once a proposal's consultation ends, a contributor may pick up the work and
|
||||
begin the initial implementation. Once a PR is opened, the maintainers will
|
||||
ensure that it meets the requirements for an experimental feature (i.e. flags
|
||||
are in the right format etc) and merge the feature. Once this code is released,
|
||||
the status will be updated via the `status: draft` label. This indicates that an
|
||||
implementation is now available for use in a release and the experiment is open
|
||||
for feedback.
|
||||
|
||||
:::note
|
||||
|
||||
During the draft period, major changes to the implementation may be made based
|
||||
on the feedback received from users. There are _no stability guarantees_ and
|
||||
experimental features may be abandoned _at any time_.
|
||||
|
||||
:::
|
||||
|
||||
### 3. Candidate
|
||||
|
||||
Once an acceptable level of consensus has been reached by the community and
|
||||
feedback/changes are less frequent/significant, the status may be updated via
|
||||
the `status: candidate` label. This indicates that a proposal is _likely_ to
|
||||
accepted and will enter a period for final comments and minor changes.
|
||||
|
||||
### 4. Stable
|
||||
|
||||
Once a suitable amount of time has passed with no changes or feedback, an
|
||||
experiment will be given the `status: stable` label. At this point, the
|
||||
functionality will be treated like any other feature in Task and any changes
|
||||
_must_ be backward compatible. This allows users to migrate to the new
|
||||
functionality without having to worry about anything breaking in future
|
||||
releases. This provides the best experience for users migrating to a new major
|
||||
version.
|
||||
|
||||
### 5. Released
|
||||
|
||||
When making a new major release of Task, all experiments marked as `status:
|
||||
stable` will move to `status: released` and their behaviors will become the new
|
||||
default in Task. Experiments in an earlier stage (i.e. not stable) cannot be
|
||||
released and so will continue to be experiments in the new version.
|
||||
|
||||
### Abandoned / Superseded
|
||||
|
||||
If an experiment is unsuccessful at any point then it will be given the `status:
|
||||
abandoned` or `status: superseded` labels depending on which is more suitable.
|
||||
These experiments will be removed from Task.
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
slug: /experiments/gentle-force/
|
||||
---
|
||||
|
||||
# Gentle Force (#1200)
|
||||
|
||||
:::caution
|
||||
|
||||
All experimental features are subject to breaking changes and/or removal _at any
|
||||
time_. We strongly recommend that you do not use these features in a production
|
||||
environment. They are intended for testing and feedback only.
|
||||
|
||||
:::
|
||||
|
||||
:::warning
|
||||
|
||||
This experiment breaks the following functionality:
|
||||
|
||||
- The `--force` flag
|
||||
|
||||
:::
|
||||
|
||||
:::info
|
||||
|
||||
To enable this experiment, set the environment variable:
|
||||
`TASK_X_GENTLE_FORCE=1`. Check out [our guide to enabling experiments
|
||||
][enabling-experiments] for more information.
|
||||
|
||||
:::
|
||||
|
||||
The `--force` flag currently forces _all_ tasks to run regardless of the status
|
||||
checks. This can be useful, but we have found that most of the time users only
|
||||
expect the direct task they are calling to be forced and _not_ all of its
|
||||
dependant tasks.
|
||||
|
||||
This experiment changes the `--force` flag to only force the directly called
|
||||
task. All dependant tasks will have their statuses checked as normal and will
|
||||
only run if Task considers them to be out of date. A new `--force-all` flag will
|
||||
also be added to maintain the current behavior for users that need this
|
||||
functionality.
|
||||
|
||||
If you want to migrate, but continue to force all dependant tasks to run, you
|
||||
should replace all uses of the `--force` flag with `--force-all`. Alternatively,
|
||||
if you want to adopt the new behavior, you can continue to use the `--force`
|
||||
flag as you do now!
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[enabling-experiments]: ./experiments.mdx#enabling-experiments
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,290 +0,0 @@
|
||||
---
|
||||
slug: /experiments/remote-taskfiles/
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Remote Taskfiles (#1317)
|
||||
|
||||
:::caution
|
||||
|
||||
All experimental features are subject to breaking changes and/or removal _at any
|
||||
time_. We strongly recommend that you do not use these features in a production
|
||||
environment. They are intended for testing and feedback only.
|
||||
|
||||
:::
|
||||
|
||||
:::info
|
||||
|
||||
To enable this experiment, set the environment variable:
|
||||
`TASK_X_REMOTE_TASKFILES=1`. Check out [our guide to enabling experiments
|
||||
][enabling-experiments] for more information.
|
||||
|
||||
:::
|
||||
|
||||
:::danger
|
||||
Never run remote Taskfiles from sources that you do not trust.
|
||||
:::
|
||||
|
||||
This experiment allows you to use Taskfiles which are stored in remote
|
||||
locations. This applies to both the root Taskfile (aka. Entrypoint) and also
|
||||
when including Taskfiles.
|
||||
|
||||
Task uses "nodes" to reference remote Taskfiles. There are a few different types
|
||||
of node which you can use:
|
||||
|
||||
<Tabs groupId="method" queryString>
|
||||
<TabItem value="http" label="HTTP/HTTPS">
|
||||
|
||||
`https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml`
|
||||
|
||||
This is the most basic type of remote node and works by downloading the file
|
||||
from the specified URL. The file must be a valid Taskfile and can be of any
|
||||
name. If a file is not found at the specified URL, Task will append each of the
|
||||
[supported file names][supported-file-names] in turn until it finds a valid
|
||||
file. If it still does not find a valid Taskfile, an error is returned.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="git-http" label="Git over HTTP">
|
||||
|
||||
`https://github.com/go-task/task.git//website/static/Taskfile.yml?ref=main`
|
||||
|
||||
This type of node works by downloading the file from a Git repository over
|
||||
HTTP/HTTPS. The first part of the URL is the base URL of the Git repository.
|
||||
This is the same URL that you would use to clone the repo over HTTP.
|
||||
|
||||
- You can optionally add the path to the Taskfile in the repository by appending
|
||||
`//<path>` to the URL.
|
||||
- You can also optionally specify a branch or tag to use by appending
|
||||
`?ref=<ref>` to the end of the URL. If you omit a reference, the default branch
|
||||
will be used.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="git-ssh" label="Git over SSH">
|
||||
|
||||
`git@github.com/go-task/task.git//website/static/Taskfile.yml?ref=main`
|
||||
|
||||
This type of node works by downloading the file from a Git repository over SSH.
|
||||
The first part of the URL is the user and base URL of the Git repository. This
|
||||
is the same URL that you would use to clone the repo over SSH.
|
||||
|
||||
To use Git over SSH, you need to make sure that your SSH agent has your private
|
||||
SSH keys added so that they can be used during authentication.
|
||||
|
||||
- You can optionally add the path to the Taskfile in the repository by appending
|
||||
`//<path>` to the URL.
|
||||
- You can also optionally specify a branch or tag to use by appending
|
||||
`?ref=<ref>` to the end of the URL. If you omit a reference, the default branch
|
||||
will be used.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Task has an [example remote Taskfile][example-remote-taskfile] in our repository
|
||||
that you can use for testing and that we will use throughout this document:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
cmds:
|
||||
- task: hello
|
||||
|
||||
hello:
|
||||
cmds:
|
||||
- echo "Hello Task!"
|
||||
```
|
||||
|
||||
## Specifying a remote entrypoint
|
||||
|
||||
By default, Task will look for one of the [supported file
|
||||
names][supported-file-names] on your local filesystem. If you want to use a
|
||||
remote file instead, you can pass its URI into the `--taskfile`/`-t` flag just
|
||||
like you would to specify a different local file. For example:
|
||||
|
||||
<Tabs groupId="method" queryString>
|
||||
<TabItem value="http" label="HTTP/HTTPS">
|
||||
```shell
|
||||
$ task --taskfile https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml
|
||||
task: [hello] echo "Hello Task!"
|
||||
Hello Task!
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="git-http" label="Git over HTTP">
|
||||
```shell
|
||||
$ task --taskfile https://github.com/go-task/task.git//website/static/Taskfile.yml?ref=main
|
||||
task: [hello] echo "Hello Task!"
|
||||
Hello Task!
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="git-ssh" label="Git over SSH">
|
||||
```shell
|
||||
$ task --taskfile git@github.com/go-task/task.git//website/static/Taskfile.yml?ref=main
|
||||
task: [hello] echo "Hello Task!"
|
||||
Hello Task!
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Including remote Taskfiles
|
||||
|
||||
Including a remote file works exactly the same way that including a local file
|
||||
does. You just need to replace the local path with a remote URI. Any tasks in
|
||||
the remote Taskfile will be available to run from your main Taskfile.
|
||||
|
||||
<Tabs groupId="method" queryString>
|
||||
<TabItem value="http" label="HTTP/HTTPS">
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
includes:
|
||||
my-remote-namespace: https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="git-http" label="Git over HTTP">
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
includes:
|
||||
my-remote-namespace: https://github.com/go-task/task.git//website/static/Taskfile.yml?ref=main
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="git-ssh" label="Git over SSH">
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
includes:
|
||||
my-remote-namespace: git@github.com/go-task/task.git//website/static/Taskfile.yml?ref=main
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
```shell
|
||||
$ task my-remote-namespace:hello
|
||||
task: [hello] echo "Hello Task!"
|
||||
Hello Task!
|
||||
```
|
||||
|
||||
### Authenticating using environment variables
|
||||
|
||||
The Taskfile location is processed by the templating system, so you can
|
||||
reference environment variables in your URL if you need to add authentication.
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
includes:
|
||||
my-remote-namespace: https://{{.TOKEN}}@raw.githubusercontent.com/my-org/my-repo/main/Taskfile.yml
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### Automatic checksums
|
||||
|
||||
Running commands from sources that you do not control is always a potential
|
||||
security risk. For this reason, we have added some automatic checks when using
|
||||
remote Taskfiles:
|
||||
|
||||
1. When running a task from a remote Taskfile for the first time, Task will
|
||||
print a warning to the console asking you to check that you are sure that you
|
||||
trust the source of the Taskfile. If you do not accept the prompt, then Task
|
||||
will exit with code `104` (not trusted) and nothing will run. If you accept
|
||||
the prompt, the remote Taskfile will run and further calls to the remote
|
||||
Taskfile will not prompt you again.
|
||||
2. Whenever you run a remote Taskfile, Task will create and store a checksum of
|
||||
the file that you are running. If the checksum changes, then Task will print
|
||||
another warning to the console to inform you that the contents of the remote
|
||||
file has changed. If you do not accept the prompt, then Task will exit with
|
||||
code `104` (not trusted) and nothing will run. If you accept the prompt, the
|
||||
checksum will be updated and the remote Taskfile will run.
|
||||
|
||||
Sometimes you need to run Task in an environment that does not have an
|
||||
interactive terminal, so you are not able to accept a prompt. In these cases you
|
||||
are able to tell task to accept these prompts automatically by using the `--yes`
|
||||
flag. Before enabling this flag, you should:
|
||||
|
||||
1. Be sure that you trust the source and contents of the remote Taskfile.
|
||||
2. Consider using a pinned version of the remote Taskfile (e.g. A link
|
||||
containing a commit hash) to prevent Task from automatically accepting a
|
||||
prompt that says a remote Taskfile has changed.
|
||||
|
||||
### Manual checksum pinning
|
||||
|
||||
Alternatively, if you expect the contents of your remote files to be a constant
|
||||
value, you can pin the checksum of the included file instead:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
includes:
|
||||
included:
|
||||
taskfile: https://taskfile.dev
|
||||
checksum: c153e97e0b3a998a7ed2e61064c6ddaddd0de0c525feefd6bba8569827d8efe9
|
||||
```
|
||||
|
||||
This will disable the automatic checksum prompts discussed above. However, if
|
||||
the checksums do not match, Task will exit immediately with an error. When
|
||||
setting this up for the first time, you may not know the correct value of the
|
||||
checksum. There are a couple of ways you can obtain this:
|
||||
|
||||
1. Add the include normally without the `checksum` key. The first time you run
|
||||
the included Taskfile, a `.task/remote` temporary directory is created. Find
|
||||
the correct set of files for your included Taskfile and open the file that
|
||||
ends with `.checksum`. You can copy the contents of this file and paste it
|
||||
into the `checksum` key of your include. This method is safest as it allows
|
||||
you to inspect the downloaded Taskfile before you pin it.
|
||||
2. Alternatively, add the include with a temporary random value in the
|
||||
`checksum` key. When you try to run the Taskfile, you will get an error that
|
||||
will report the incorrect expected checksum and the actual checksum. You can
|
||||
copy the actual checksum and replace your temporary random value.
|
||||
|
||||
### TLS
|
||||
|
||||
Task currently supports both `http` and `https` URLs. However, the `http`
|
||||
requests will not execute by default unless you run the task with the
|
||||
`--insecure` flag. This is to protect you from accidentally running a remote
|
||||
Taskfile that is downloaded via an unencrypted connection. Sources that are not
|
||||
protected by TLS are vulnerable to [man-in-the-middle
|
||||
attacks][man-in-the-middle-attacks] and should be avoided unless you know what
|
||||
you are doing.
|
||||
|
||||
## Caching & Running Offline
|
||||
|
||||
Whenever you run a remote Taskfile, the latest copy will be downloaded from the
|
||||
internet and cached locally. This cached file will be used for all future
|
||||
invocations of the Taskfile until the cache expires. Once it expires, Task will
|
||||
download the latest copy of the file and update the cache. By default, the cache
|
||||
is set to expire immediately. This means that Task will always fetch the latest
|
||||
version. However, the cache expiry duration can be modified by setting the
|
||||
`--expiry` flag.
|
||||
|
||||
If for any reason you lose access to the internet or you are running Task in
|
||||
offline mode (via the `--offline` flag or `TASK_OFFLINE` environment variable),
|
||||
Task will run the any available cached files _even if they are expired_. This
|
||||
means that you should never be stuck without the ability to run your tasks as
|
||||
long as you have downloaded a remote Taskfile at least once.
|
||||
|
||||
By default, Task will timeout requests to download remote files after 10 seconds
|
||||
and look for a cached copy instead. This timeout can be configured by setting
|
||||
the `--timeout` flag and specifying a duration. For example, `--timeout 5s` will
|
||||
set the timeout to 5 seconds.
|
||||
|
||||
By default, the cache is stored in the Task temp directory, represented by the
|
||||
`TASK_TEMP_DIR` [environment variable](../reference/environment.mdx) You can
|
||||
override the location of the cache by setting the `TASK_REMOTE_DIR` environment
|
||||
variable. This way, you can share the cache between different projects.
|
||||
|
||||
You can force Task to ignore the cache and download the latest version
|
||||
by using the `--download` flag.
|
||||
|
||||
You can use the `--clear-cache` flag to clear all cached remote files.
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[enabling-experiments]: ./experiments.mdx#enabling-experiments
|
||||
[man-in-the-middle-attacks]: https://en.wikipedia.org/wiki/Man-in-the-middle_attack
|
||||
[supported-file-names]: https://taskfile.dev/usage/#supported-file-names
|
||||
[example-remote-taskfile]: https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
# This is a template for an experiments documentation
|
||||
# Copy this page and fill in the details as necessary
|
||||
title: '--- Template ---'
|
||||
sidebar_position: -1 # Always push to the top
|
||||
draft: true # Hide in production
|
||||
---
|
||||
|
||||
# \{Name of Experiment\} (#\{Issue\})
|
||||
|
||||
:::caution
|
||||
|
||||
All experimental features are subject to breaking changes and/or removal _at any
|
||||
time_. We strongly recommend that you do not use these features in a production
|
||||
environment. They are intended for testing and feedback only.
|
||||
|
||||
:::
|
||||
|
||||
:::warning
|
||||
|
||||
This experiment breaks the following functionality:
|
||||
|
||||
- \{list any existing functionality that will be broken by this experiment\}
|
||||
- \{if there are no breaking changes, remove this admonition\}
|
||||
|
||||
:::
|
||||
|
||||
:::info
|
||||
|
||||
To enable this experiment, set the environment variable: `TASK_X_{feature}=1`.
|
||||
Check out [our guide to enabling experiments ][enabling-experiments] for more
|
||||
information.
|
||||
|
||||
:::
|
||||
|
||||
\{Short description of the feature\}
|
||||
|
||||
\{Short explanation of how users should migrate to the new behavior\}
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[enabling-experiments]: ./experiments.mdx#enabling-experiments
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,120 +0,0 @@
|
||||
---
|
||||
slug: /faq/
|
||||
sidebar_position: 15
|
||||
---
|
||||
|
||||
# FAQ
|
||||
|
||||
This page contains a list of frequently asked questions about Task.
|
||||
|
||||
## When will \<feature\> be released? / ETAs
|
||||
|
||||
Task is _free_ and _open source_ project maintained by a small group of
|
||||
volunteers with full time jobs and lives outside of the project. Because of
|
||||
this, it is difficult to predict how much time we will be able to dedicate to
|
||||
the project in advance and we don't want to make any promises that we can't
|
||||
keep. For this reason, we are unable to provide ETAs for new features or
|
||||
releases. We make a "best effort" to provide regular releases and fix bugs in a
|
||||
timely fashion, but sometimes our personal lives must take priority.
|
||||
|
||||
ETAs are probably the number one question we (and maintainers of other open
|
||||
source projects) get asked. We understand that you are passionate about the
|
||||
project, but it can be overwhelming to be asked this question so often. Please
|
||||
be patient and avoid asking for ETAs.
|
||||
|
||||
The best way to speed things up is to contribute to the project yourself. We
|
||||
always appreciate new contributors. If you are interested in contributing, check
|
||||
out the [contributing guide](./contributing.mdx).
|
||||
|
||||
## Why won't my task update my shell environment?
|
||||
|
||||
This is a limitation of how shells work. Task runs as a subprocess of your
|
||||
current shell, so it can't change the environment of the shell that started it.
|
||||
This limitation is shared by other task runners and build tools too.
|
||||
|
||||
A common way to work around this is to create a task that will generate output
|
||||
that can be parsed by your shell. For example, to set an environment variable on
|
||||
your shell you can write a task like this:
|
||||
|
||||
```yaml
|
||||
my-shell-env:
|
||||
cmds:
|
||||
- echo "export FOO=foo"
|
||||
- echo "export BAR=bar"
|
||||
```
|
||||
|
||||
Now run `eval $(task my-shell-env)` and the variables `$FOO` and `$BAR` will be
|
||||
available in your shell.
|
||||
|
||||
## I can't reuse my shell in a task's commands
|
||||
|
||||
Task runs each command as a separate shell process, so something you do in one
|
||||
command won't effect any future commands. For example, this won't work:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
foo:
|
||||
cmds:
|
||||
- a=foo
|
||||
- echo $a
|
||||
# outputs ""
|
||||
```
|
||||
|
||||
To work around this you can either use a multiline command:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
foo:
|
||||
cmds:
|
||||
- |
|
||||
a=foo
|
||||
echo $a
|
||||
# outputs "foo"
|
||||
```
|
||||
|
||||
Or for more complex multi-line commands it is recommended to move your code into
|
||||
a separate file and call that instead:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
foo:
|
||||
cmds:
|
||||
- ./foo-printer.bash
|
||||
```
|
||||
|
||||
```shell
|
||||
#!/bin/bash
|
||||
a=foo
|
||||
echo $a
|
||||
```
|
||||
|
||||
## 'x' builtin command doesn't work on Windows
|
||||
|
||||
The default shell on Windows (`cmd` and `powershell`) do not have commands like
|
||||
`rm` and `cp` available as builtins. This means that these commands won't work.
|
||||
If you want to make your Taskfile fully cross-platform, you'll need to work
|
||||
around this limitation using one of the following methods:
|
||||
|
||||
- Use the `{{OS}}` function to run an OS-specific script.
|
||||
- Use something like `{{if eq OS "windows"}}powershell {{end}}<my_cmd>` to
|
||||
detect windows and run the command in Powershell directly.
|
||||
- Use a shell on Windows that supports these commands as builtins, such as [Git
|
||||
Bash][git-bash] or [WSL][wsl].
|
||||
|
||||
We want to make improvements to this part of Task and the issues below track
|
||||
this work. Constructive comments and contributions are very welcome!
|
||||
|
||||
- #197
|
||||
- [mvdan/sh#93](https://github.com/mvdan/sh/issues/93)
|
||||
- [mvdan/sh#97](https://github.com/mvdan/sh/issues/97)
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[git-bash]: https://gitforwindows.org/
|
||||
[wsl]: https://learn.microsoft.com/en-us/windows/wsl/install
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,354 +0,0 @@
|
||||
---
|
||||
slug: /installation/
|
||||
sidebar_position: 2
|
||||
toc_max_heading_level: 4
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Installation
|
||||
|
||||
Task offers many installation methods. Check out the available methods below.
|
||||
|
||||
:::info
|
||||
|
||||
Some of the methods below are marked as ![Community][community]. This means they
|
||||
are not maintained by the Task team and may not be up-to-date.
|
||||
|
||||
:::
|
||||
|
||||
## Package Managers
|
||||
|
||||
### [Homebrew][homebrew] ![][macos] ![][linux] \{#homebrew}
|
||||
|
||||
Task is available via our official Homebrew tap [[source](https://github.com/go-task/homebrew-tap/blob/main/Formula/go-task.rb)]:
|
||||
|
||||
```shell
|
||||
brew install go-task/tap/go-task
|
||||
```
|
||||
|
||||
Alternatively it can be installed from the official Homebrew
|
||||
repository [[package](https://formulae.brew.sh/formula/go-task)]
|
||||
[[source](https://github.com/Homebrew/homebrew-core/blob/master/Formula/g/go-task.rb)] by running:
|
||||
|
||||
```shell
|
||||
brew install go-task
|
||||
```
|
||||
|
||||
### [Macports][macports] ![][macos] ![][community] \{#macports}
|
||||
|
||||
Task repository is tracked by Macports [[package](https://ports.macports.org/port/go-task/details/)] [[source](https://github.com/macports/macports-ports/blob/master/devel/go-task/Portfile)]:
|
||||
|
||||
```shell
|
||||
port install go-task
|
||||
```
|
||||
|
||||
### [Snap][snapcraft] ![][macos] ![][linux] \{#snap}
|
||||
|
||||
Task is available on [Snapcraft][snapcraft] [[source](https://github.com/go-task/snap/blob/main/snap/snapcraft.yaml)], but keep in mind that your Linux
|
||||
distribution should allow classic confinement for Snaps to Task work correctly:
|
||||
|
||||
```shell
|
||||
sudo snap install task --classic
|
||||
```
|
||||
|
||||
### [npm][npm] ![][macos] ![][linux] ![][windows] \{#npm}
|
||||
|
||||
Npm can be used as cross-platform way to install Task globally or as a
|
||||
dependency of your project
|
||||
[[package](https://www.npmjs.com/package/@go-task/cli)] [[source](https://github.com/go-task/task/blob/main/package.json)]:
|
||||
|
||||
```shell
|
||||
npm install -g @go-task/cli
|
||||
```
|
||||
|
||||
### [pip][pip] ![][macos] ![][linux] ![][windows] ![][community] \{#pip}
|
||||
|
||||
Like npm, pip can be used as a cross-platform way to install Task
|
||||
[[package](https://pypi.org/project/go-task-bin)] [[source](https://github.com/Bing-su/pip-binary-factory/tree/main/task)]:
|
||||
|
||||
```shell
|
||||
pip install go-task-bin
|
||||
```
|
||||
|
||||
### [WinGet][winget] ![][windows] \{#winget}
|
||||
|
||||
Task is available via the [community repository](https://github.com/microsoft/winget-pkgs) [[source](https://github.com/microsoft/winget-pkgs/tree/master/manifests/t/Task/Task)]:
|
||||
|
||||
```shell
|
||||
winget install Task.Task
|
||||
```
|
||||
|
||||
### [Chocolatey][choco] ![][windows] ![][community] \{#chocolatey}
|
||||
|
||||
[[package](https://community.chocolatey.org/packages/go-task)] [[source](https://github.com/Starz0r/ChocolateyPackagingScripts/blob/master/src/go-task_gh_build.py)]
|
||||
|
||||
```shell
|
||||
choco install go-task
|
||||
```
|
||||
|
||||
### [Scoop][scoop] ![][windows] ![][community] \{#scoop}
|
||||
|
||||
[[source](https://github.com/ScoopInstaller/Main/blob/master/bucket/task.json)]
|
||||
|
||||
```shell
|
||||
scoop install task
|
||||
```
|
||||
|
||||
### Arch ([pacman][pacman]) ![][arch] ![][community] \{#arch}
|
||||
|
||||
[[package](https://archlinux.org/packages/extra/x86_64/go-task/)] [[source](https://gitlab.archlinux.org/archlinux/packaging/packages/go-task)]
|
||||
|
||||
```shell
|
||||
pacman -S go-task
|
||||
```
|
||||
|
||||
### Fedora ([dnf][dnf]) ![][fedora] ![][community] \{#fedora}
|
||||
|
||||
[[package](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/)] [[source](https://src.fedoraproject.org/rpms/golang-github-task)]
|
||||
|
||||
```shell
|
||||
dnf install go-task
|
||||
```
|
||||
|
||||
### FreeBSD ([Ports][freebsdports]) ![][freebsd] ![][community] \{#freebsd}
|
||||
|
||||
[[package](https://cgit.freebsd.org/ports/tree/devel/task)] [[source](https://cgit.freebsd.org/ports/tree/devel/task/Makefile)]
|
||||
|
||||
```shell
|
||||
pkg install task
|
||||
```
|
||||
|
||||
### NixOS ([nix][nix]) ![][nixos] ![][linux] ![][community] \{#nix}
|
||||
|
||||
[[source](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/go/go-task/package.nix)]
|
||||
|
||||
```shell
|
||||
nix-env -iA nixpkgs.go-task
|
||||
```
|
||||
|
||||
### [pacstall][pacstall] ![][debian] ![][ubuntu] ![][community] \{#pacstall}
|
||||
|
||||
[[package](https://pacstall.dev/packages/go-task-deb)] [[source](https://github.com/pacstall/pacstall-programs/blob/master/packages/go-task-deb/go-task-deb.pacscript)]
|
||||
|
||||
```shell
|
||||
pacstall -I go-task-deb
|
||||
```
|
||||
|
||||
### [pkgx][pkgx] ![][macos] ![][linux] ![][community] \{#pkgx}
|
||||
|
||||
[[package](https://pkgx.dev/pkgs/taskfile.dev)] [[source](https://github.com/pkgxdev/pantry/blob/main/projects/taskfile.dev/package.yml)]
|
||||
|
||||
```shell
|
||||
pkgx task
|
||||
```
|
||||
|
||||
or, if you have pkgx integration enabled:
|
||||
|
||||
```shell
|
||||
task
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
By default, it installs on the `./bin` directory relative to the working
|
||||
directory:
|
||||
|
||||
```shell
|
||||
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
|
||||
```
|
||||
|
||||
It is possible to override the installation directory with the `-b` parameter.
|
||||
On Linux, common choices are `~/.local/bin` and `~/bin` to install for the
|
||||
current user or `/usr/local/bin` to install for all users:
|
||||
|
||||
```shell
|
||||
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
|
||||
```
|
||||
|
||||
:::caution
|
||||
|
||||
On macOS and Windows, `~/.local/bin` and `~/bin` are not added to `$PATH` by
|
||||
default.
|
||||
|
||||
:::
|
||||
|
||||
By default, it installs the latest version available.
|
||||
You can also specify a tag (available in [releases](https://github.com/go-task/task/releases))
|
||||
to install a specific version:
|
||||
|
||||
```shell
|
||||
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d v3.36.0
|
||||
```
|
||||
|
||||
Parameters are order specific, to set both installation directory and version:
|
||||
```shell
|
||||
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin v3.42.1
|
||||
```
|
||||
|
||||
### 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@v2
|
||||
with:
|
||||
version: 3.x
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
This installation method is community owned.
|
||||
|
||||
## Build From Source
|
||||
|
||||
### Go Modules
|
||||
|
||||
Ensure that you have a supported version of [Go][go] properly installed and
|
||||
setup. You can find the minimum required version of Go in the
|
||||
[go.mod](https://github.com/go-task/task/blob/main/go.mod#L3) file.
|
||||
|
||||
You can then install the latest release globally by running:
|
||||
|
||||
```shell
|
||||
go install github.com/go-task/task/v3/cmd/task@latest
|
||||
```
|
||||
|
||||
Or you can install into another directory:
|
||||
|
||||
```shell
|
||||
env GOBIN=/bin go install github.com/go-task/task/v3/cmd/task@latest
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
For CI environments we recommend using the [install script](#install-script)
|
||||
instead, which is faster and more stable, since it'll just download the latest
|
||||
released binary.
|
||||
|
||||
:::
|
||||
|
||||
## Setup completions
|
||||
|
||||
Some installation methods will automatically install completions too, but if
|
||||
this isn't working for you or your chosen method doesn't include them, you can
|
||||
run `task --completion <shell>` to output a completion script for any supported
|
||||
shell. There are a couple of ways these completions can be added to your shell
|
||||
config:
|
||||
|
||||
### Option 1. Load the completions in your shell's startup config (Recommended)
|
||||
|
||||
This method loads the completion script from the currently installed version of
|
||||
task every time you create a new shell. This ensures that your completions are
|
||||
always up-to-date.
|
||||
|
||||
<Tabs values={[ {label: 'bash', value: '1'}, {label: 'zsh', value: '2'},
|
||||
{label: 'fish', value: '3'},
|
||||
{label: 'powershell', value: '4'}
|
||||
]}>
|
||||
|
||||
<TabItem value="1">
|
||||
```shell title="~/.bashrc"
|
||||
eval "$(task --completion bash)"
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="2">
|
||||
```shell title="~/.zshrc"
|
||||
eval "$(task --completion zsh)"
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="3">
|
||||
```shell title="~/.config/fish/config.fish"
|
||||
task --completion fish | source
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="4">
|
||||
```powershell title="$PROFILE\Microsoft.PowerShell_profile.ps1"
|
||||
Invoke-Expression (&task --completion powershell | Out-String)
|
||||
```
|
||||
</TabItem></Tabs>
|
||||
|
||||
### Option 2. Copy the script to your shell's completions directory
|
||||
|
||||
This method requires you to manually update the completions whenever Task is
|
||||
updated. However, it is useful if you want to modify the completions yourself.
|
||||
|
||||
<Tabs
|
||||
values={[
|
||||
{label: 'bash', value: '1'},
|
||||
{label: 'zsh', value: '2'},
|
||||
{label: 'fish', value: '3'}
|
||||
]}>
|
||||
|
||||
<TabItem value="1">
|
||||
```shell
|
||||
task --completion bash > /etc/bash_completion.d/task
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="2">
|
||||
```shell
|
||||
task --completion zsh > /usr/local/share/zsh/site-functions/_task
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="3">
|
||||
```shell
|
||||
task --completion fish > ~/.config/fish/completions/task.fish
|
||||
```
|
||||
</TabItem></Tabs>
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[homebrew]: https://brew.sh
|
||||
[macports]: https://macports.org
|
||||
[snapcraft]: https://snapcraft.io/task
|
||||
[winget]: https://github.com/microsoft/winget-cli
|
||||
[choco]: https://chocolatey.org
|
||||
[scoop]: https://scoop.sh
|
||||
[pacman]: https://wiki.archlinux.org/title/Pacman
|
||||
[dnf]: https://docs.fedoraproject.org/en-US/quick-docs/dnf
|
||||
[nix]: https://nixos.org
|
||||
[npm]: https://www.npmjs.com
|
||||
[pip]: https://pip.pypa.io
|
||||
[mise]: https://mise.jdx.dev
|
||||
[aqua]: https://aquaproj.github.io
|
||||
[pacstall]: https://github.com/pacstall/pacstall
|
||||
[pkgx]: https://pkgx.sh
|
||||
[freebsdports]: https://ports.freebsd.org/cgi/ports.cgi
|
||||
|
||||
[go]: https://golang.org
|
||||
[godownloader]: https://github.com/goreleaser/godownloader
|
||||
[releases]: https://github.com/go-task/task/releases
|
||||
[installscript]: https://github.com/go-task/task/blob/main/install-task.sh
|
||||
|
||||
[community]: https://img.shields.io/badge/Community%20Owned-orange
|
||||
[windows]: https://custom-icon-badges.demolab.com/badge/Windows-0078D6?logo=windows11&logoColor=white
|
||||
[macos]: https://img.shields.io/badge/MacOS-000000?logo=apple&logoColor=F0F0F0
|
||||
[linux]: https://img.shields.io/badge/Linux-FCC624?logo=linux&logoColor=black
|
||||
[arch]: https://img.shields.io/badge/Arch%20Linux-1793D1?logo=arch-linux&logoColor=fff
|
||||
[fedora]: https://img.shields.io/badge/Fedora-51A2DA?logo=fedora&logoColor=fff
|
||||
[nixos]: https://img.shields.io/badge/NixOS-5277C3?logo=nixos&logoColor=fff
|
||||
[debian]: https://img.shields.io/badge/Debian-A81D33?logo=debian&logoColor=fff
|
||||
[ubuntu]: https://img.shields.io/badge/Ubuntu-E95420?logo=ubuntu&logoColor=fff
|
||||
[freebsd]: https://img.shields.io/badge/FreeBSD-990000?logo=freebsd&logoColor=fff
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
slug: /
|
||||
sidebar_position: 1
|
||||
title: Home
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<img id="logo" src="/img/logo.svg" height="250px" width="250px" />
|
||||
</div>
|
||||
<br />
|
||||
|
||||
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.
|
||||
|
||||
## Features
|
||||
|
||||
- [Easy installation](/installation): 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#install-script) to install on your CI
|
||||
script and you're ready 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).
|
||||
|
||||
## Documentation
|
||||
|
||||
- If you're new to Task, we recommend taking a look at our [getting started
|
||||
guide][getting-started] for an quick introduction.
|
||||
- You can also browse our [usage documentation][usage] for more details on how
|
||||
all the features work.
|
||||
- Or use our quick reference documentation for the [Taskfile schema][schema] or
|
||||
[CLI][cli].
|
||||
|
||||
## Gold Sponsors
|
||||
|
||||
<table class="gold-sponsors">
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a target="_blank" href="https://devowl.io">
|
||||
<img src="/img/devowl.io.svg" height="100px" title="devowl.io" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[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
|
||||
[getting-started]: /getting-started/
|
||||
[usage]: /usage/
|
||||
[schema]: /reference/schema/
|
||||
[cli]: /reference/cli/
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,2 +0,0 @@
|
||||
position: 5
|
||||
label: Reference
|
||||
@@ -1,121 +0,0 @@
|
||||
---
|
||||
slug: /reference/cli
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# CLI Reference
|
||||
|
||||
Task CLI commands have the following syntax:
|
||||
|
||||
```shell
|
||||
task [--flags] [tasks...] [-- CLI_ARGS...]
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
If `--` is given, all remaining arguments will be assigned to a special
|
||||
`CLI_ARGS` variable
|
||||
|
||||
:::
|
||||
|
||||
## Flags
|
||||
|
||||
| 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 the directory in which Task will execute and look for a Taskfile. |
|
||||
| `-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. |
|
||||
| `-g` | `--global` | `bool` | `false` | Runs global Taskfile, from `$HOME/Taskfile.{yml,yaml}`. |
|
||||
| `-h` | `--help` | `bool` | `false` | Shows Task usage. |
|
||||
| `-i` | `--init` | `bool` | `false` | Creates a new Taskfile.yml in the current folder. |
|
||||
| `-I` | `--interval` | `string` | `5s` | Sets a different watch interval when using `--watch`, the default being 5 seconds. This string should be a valid [Go Duration](https://pkg.go.dev/time#ParseDuration). |
|
||||
| `-l` | `--list` | `bool` | `false` | Lists tasks with description of current Taskfile. |
|
||||
| `-a` | `--list-all` | `bool` | `false` | Lists tasks with or without a description. |
|
||||
| | `--sort` | `string` | `default` | Changes the order of the tasks when listed.<br />`default` - Alphanumeric with root tasks first<br />`alphanumeric` - Alphanumeric<br />`none` - No sorting (As they appear in the Taskfile) |
|
||||
| | `--json` | `bool` | `false` | See [JSON Output](#json-output) |
|
||||
| `-o` | `--output` | `string` | Default set in the Taskfile or `interleaved` | 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. |
|
||||
| | `--output-group-error-only` | `bool` | `false` | Swallow command output on zero exit code. |
|
||||
| `-p` | `--parallel` | `bool` | `false` | Executes tasks provided on command line in parallel. |
|
||||
| `-s` | `--silent` | `bool` | `false` | Disables echoing. |
|
||||
| `-y` | `--yes` | `bool` | `false` | Assume "yes" as answer to all prompts. |
|
||||
| | `--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 path to run.<br />Check the list of default filenames [here](../usage/#supported-file-names). |
|
||||
| `-v` | `--verbose` | `bool` | `false` | Enables verbose mode. |
|
||||
| | `--version` | `bool` | `false` | Show Task version. |
|
||||
| `-w` | `--watch` | `bool` | `false` | Enables watch of the given task.
|
||||
|
||||
## Exit Codes
|
||||
|
||||
Task will sometimes exit with specific exit codes. These codes are split into
|
||||
four groups with the following ranges:
|
||||
|
||||
- Success (0)
|
||||
- General errors (1-99)
|
||||
- Taskfile errors (100-199)
|
||||
- Task errors (200-255)
|
||||
|
||||
A full list of the exit codes and their descriptions can be found below:
|
||||
|
||||
| Code | Description |
|
||||
|------|---------------------------------------------------------------------|
|
||||
| 0 | Success |
|
||||
| 1 | An unknown error occurred |
|
||||
| 100 | No Taskfile was found |
|
||||
| 101 | A Taskfile already exists when trying to initialize one |
|
||||
| 102 | The Taskfile is invalid or cannot be parsed |
|
||||
| 103 | A remote Taskfile could not be downloaded |
|
||||
| 104 | A remote Taskfile was not trusted by the user |
|
||||
| 105 | A remote Taskfile was could not be fetched securely |
|
||||
| 106 | No cache was found for a remote Taskfile in offline mode |
|
||||
| 107 | No schema version was defined in the Taskfile |
|
||||
| 200 | The specified task could not be found |
|
||||
| 201 | An error occurred while executing a command inside of a task |
|
||||
| 202 | The user tried to invoke a task that is internal |
|
||||
| 203 | There a multiple tasks with the same name or alias |
|
||||
| 204 | A task was called too many times |
|
||||
| 205 | A task was cancelled by the user |
|
||||
| 206 | A task was not executed due to missing required variables |
|
||||
| 207 | A task was not executed due to a variable having an incorrect value |
|
||||
|
||||
These codes can also be found in the repository in
|
||||
[`errors/errors.go`](https://github.com/go-task/task/blob/main/errors/errors.go).
|
||||
|
||||
:::info
|
||||
|
||||
When Task is run with the `-x`/`--exit-code` flag, the exit code of any failed
|
||||
commands will be passed through to the user instead.
|
||||
|
||||
:::
|
||||
|
||||
## JSON Output
|
||||
|
||||
When using the `--json` flag in combination with either the `--list` or
|
||||
`--list-all` flags, the output will be a JSON object with the following
|
||||
structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"name": "",
|
||||
"task": "",
|
||||
"desc": "",
|
||||
"summary": "",
|
||||
"up_to_date": false,
|
||||
"location": {
|
||||
"line": 54,
|
||||
"column": 3,
|
||||
"taskfile": "/path/to/Taskfile.yml"
|
||||
}
|
||||
}
|
||||
// ...
|
||||
],
|
||||
"location": "/path/to/Taskfile.yml"
|
||||
}
|
||||
```
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
slug: /reference/environment
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# Environment Reference
|
||||
|
||||
Task allows you to configure some behavior using environment variables. This
|
||||
page lists all the environment variables that Task supports.
|
||||
|
||||
| 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_REMOTE_DIR` | `TASK_TEMP_DIR` | Location of the remote temp dir (used for caching). Can relative to the project like `tmp/task` or absolute like `/tmp/.task` or `~/.task`. |
|
||||
| `TASK_OFFLINE` | `false` | Set the `--offline` flag through the environment variable. Only for remote experiment. CLI flag `--offline` takes precedence over the env variable |
|
||||
| `FORCE_COLOR` | | Force color output usage. |
|
||||
|
||||
## Custom Colors
|
||||
|
||||
| ENV | Default | Description |
|
||||
|-----------------------------|---------|-------------------------|
|
||||
| `TASK_COLOR_RESET` | `0` | Color used for white. |
|
||||
| `TASK_COLOR_RED` | `31` | Color used for red. |
|
||||
| `TASK_COLOR_GREEN` | `32` | Color used for green. |
|
||||
| `TASK_COLOR_YELLOW` | `33` | Color used for yellow. |
|
||||
| `TASK_COLOR_BLUE` | `34` | Color used for blue. |
|
||||
| `TASK_COLOR_MAGENTA` | `35` | Color used for magenta. |
|
||||
| `TASK_COLOR_CYAN` | `36` | Color used for cyan. |
|
||||
| `TASK_COLOR_BRIGHT_RED` | `91` | Color used for red. |
|
||||
| `TASK_COLOR_BRIGHT_GREEN` | `92` | Color used for green. |
|
||||
| `TASK_COLOR_BRIGHT_YELLOW` | `93` | Color used for yellow. |
|
||||
| `TASK_COLOR_BRIGHT_BLUE` | `94` | Color used for blue. |
|
||||
| `TASK_COLOR_BRIGHT_MAGENTA` | `95` | Color used for magenta. |
|
||||
| `TASK_COLOR_BRIGHT_CYAN` | `96` | Color used for cyan. |
|
||||
|
||||
All color variables are [ANSI color codes][ansi]. You can specify multiple codes
|
||||
separated by a semicolon. For example: `31;1` will make the text bold and red.
|
||||
Task also supports 8-bit color (256 colors). You can specify these colors by
|
||||
using the sequence `38;2;R:G:B` for foreground colors and `48;2;R:G:B` for
|
||||
background colors where `R`, `G` and `B` should be replaced with values between
|
||||
0 and 255.
|
||||
|
||||
For convenience, we allow foreground colors to be specified using shorthand,
|
||||
comma-separated syntax: `R,G,B`. For example, `255,0,0` is equivalent to
|
||||
`38;2;255:0:0`.
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,167 +0,0 @@
|
||||
---
|
||||
slug: /reference/package
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Package API
|
||||
|
||||
:::warning
|
||||
|
||||
**_Task's package API is still experimental and subject to breaking changes._**
|
||||
|
||||
This means that unlike our CLI, we may make breaking changes to the package API
|
||||
in minor (or even patch) releases. We try to avoid this when possible, but it
|
||||
may be necessary in order to improve the overall design of the package API.
|
||||
|
||||
In the future we may stabilize the package API. However, this is not currently
|
||||
planned. For now, if you need to use Task as a Go package, we recommend pinning
|
||||
the version in your `go.mod` file. Where possible we will try to include a
|
||||
changelog entry for breaking changes to the package API.
|
||||
|
||||
:::
|
||||
|
||||
Task is primarily a CLI tool that is agnostic of any programming language.
|
||||
However, it is written in Go and therefore can also be used as a Go package too.
|
||||
This can be useful if you are already using Go in your project and you need to
|
||||
extend Task's functionality in some way. In this document, we describe the
|
||||
public API surface of Task and how to use it. This may also be useful if you
|
||||
want to contribute to Task or understand how it works in more detail.
|
||||
|
||||
## Key packages
|
||||
|
||||
The following packages make up the most important parts of Task's package API.
|
||||
Below we have listed what they are for and some of the key types available:
|
||||
|
||||
### [`github.com/go-task/task/v3`]
|
||||
|
||||
The core task package provides most of the main functionality for Task including
|
||||
fetching and executing tasks from a Taskfile. At this time, the vast majority of
|
||||
the this package's functionality is exposed via the [`task.Executor`] which
|
||||
allows the user to fetch and execute tasks from a Taskfile.
|
||||
|
||||
:::note
|
||||
This is the package which is most likely to be the subject of breaking changes
|
||||
as we refine the API.
|
||||
:::
|
||||
|
||||
### [`github.com/go-task/task/v3/taskfile`]
|
||||
|
||||
The `taskfile` package provides utilities for _reading_ Taskfiles from various
|
||||
sources. These sources can be local files, remote files, or even in-memory
|
||||
strings (via stdin).
|
||||
|
||||
- [`taskfile.Node`] - A reference to the location of a Taskfile. A `Node` is an
|
||||
interface that has several implementations:
|
||||
- [`taskfile.FileNode`] - Local files
|
||||
- [`taskfile.HTTPNode`] - Remote files via HTTP/HTTPS
|
||||
- [`taskfile.GitNode`] - Remote files via Git
|
||||
- [`taskfile.StdinNode`] - In-memory strings (via stdin)
|
||||
- [`taskfile.Reader`] - Accepts a `Node` and reads the Taskfile from it.
|
||||
- [`taskfile.Snippet`] - Mostly used for rendering Taskfile errors. A snippet
|
||||
stores a small part of a taskfile around a given line number and column. The
|
||||
output can be syntax highlighted for CLIs and include line/column indicators.
|
||||
|
||||
### [`github.com/go-task/task/v3/taskfile/ast`]
|
||||
|
||||
AST stands for ["Abstract Syntax Tree"][ast]. An AST allows us to easily
|
||||
represent the Taskfile syntax in Go. This package provides a way to parse
|
||||
Taskfile YAML into an AST and store them in memory.
|
||||
|
||||
- [`ast.TaskfileGraph`] - Represents a set of Taskfiles and their dependencies
|
||||
between one another.
|
||||
- [`ast.Taskfile`] - Represents a single Taskfile or a set of merged Taskfiles.
|
||||
The `Taskfile` type contains all of the subtypes for the Taskfile syntax, such
|
||||
as `tasks`, `includes`, `vars`, etc. These are not listed here for brevity.
|
||||
|
||||
### [`github.com/go-task/task/v3/errors`]
|
||||
|
||||
Contains all of the error types used in Task. All of these types implement the
|
||||
[`errors.TaskError`] interface which wraps Go's standard [`error`] interface.
|
||||
This allows you to call the `Code` method on the error to obtain the unique exit
|
||||
code for any error.
|
||||
|
||||
## Reading Taskfiles
|
||||
|
||||
Start by importing the `github.com/go-task/task/v3/taskfile` package. This
|
||||
provides all of the functions you need to read a Taskfile into memory:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/go-task/task/v3/taskfile"
|
||||
)
|
||||
```
|
||||
|
||||
Reading Taskfiles is done by using a [`taskfile.Reader`] and an implementation
|
||||
of [`taskfile.Node`]. In this example we will read a local file by using the
|
||||
[`taskfile.FileNode`] type. You can create this by calling the
|
||||
[`taskfile.NewFileNode`] function:
|
||||
|
||||
```go
|
||||
node := taskfile.NewFileNode("Taskfile.yml", "./path/to/dir")
|
||||
```
|
||||
|
||||
and then create a your reader by calling the [`taskfile.NewReader`] function and
|
||||
passing any functional options you want to use. For example, you could pass a
|
||||
debug function to the reader which will be called with debug messages:
|
||||
|
||||
```go
|
||||
reader := taskfile.NewReader(
|
||||
taskfile.WithDebugFunc(func(s string) {
|
||||
slog.Debug(s)
|
||||
}),
|
||||
)
|
||||
```
|
||||
|
||||
Now that everything is set up, you can read the Taskfile (and any included
|
||||
Taskfiles) by calling the `Read` method on the reader and pass the `Node` as an
|
||||
argument:
|
||||
|
||||
```go
|
||||
ctx := context.Background()
|
||||
tfg, err := reader.Read(ctx, node)
|
||||
// handle error
|
||||
```
|
||||
|
||||
This returns an instance of [`ast.TaskfileGraph`] which is a "Directed Acyclic
|
||||
Graph" (DAG) of all the parsed Taskfiles. We use this graph to store and resolve
|
||||
the `includes` directives in Taskfiles. However most of the time, you will want
|
||||
a merged Taskfile. To do this, simply call the `Merge` method on the Taskfile
|
||||
graph:
|
||||
|
||||
```go
|
||||
tf, err := tfg.Merge()
|
||||
// handle error
|
||||
```
|
||||
|
||||
This compiles the DAG into a single [`ast.Taskfile`] containing all the
|
||||
namespaces and tasks from all the Taskfiles we read.
|
||||
|
||||
:::note
|
||||
We plan to remove AST merging in the future as it is unnecessarily complex and
|
||||
causes lots of issues with scoping.
|
||||
:::
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[`github.com/go-task/task/v3`]: https://pkg.go.dev/github.com/go-task/task/v3
|
||||
[`github.com/go-task/task/v3/taskfile`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile
|
||||
[`github.com/go-task/task/v3/taskfile/ast`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile/ast
|
||||
[`github.com/go-task/task/v3/errors`]: https://pkg.go.dev/github.com/go-task/task/v3/errors
|
||||
|
||||
[`ast.TaskfileGraph`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile/ast#TaskfileGraph
|
||||
[`ast.Taskfile`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile/ast#Taskfile
|
||||
[`taskfile.Node`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Node
|
||||
[`taskfile.FileNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#FileNode
|
||||
[`taskfile.HTTPNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#HTTPNode
|
||||
[`taskfile.GitNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#GitNode
|
||||
[`taskfile.StdinNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#StdinNode
|
||||
[`taskfile.NewFileNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#NewFileNode
|
||||
[`taskfile.Reader`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader
|
||||
[`taskfile.NewReader`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#NewReader
|
||||
[`taskfile.Snippet`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Snippet
|
||||
[`task.Executor`]: https://pkg.go.dev/github.com/go-task/task/v3#Executor
|
||||
[`task.Formatter`]: https://pkg.go.dev/github.com/go-task/task/v3#Formatter
|
||||
[`errors.TaskError`]: https://pkg.go.dev/github.com/go-task/task/v3/errors#TaskError
|
||||
[`error`]: https://pkg.go.dev/builtin#error
|
||||
|
||||
[ast]: https://en.wikipedia.org/wiki/Abstract_syntax_tree
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,242 +0,0 @@
|
||||
---
|
||||
slug: /reference/schema
|
||||
sidebar_position: 3
|
||||
toc_min_heading_level: 2
|
||||
toc_max_heading_level: 5
|
||||
---
|
||||
|
||||
# Schema Reference
|
||||
|
||||
| Attribute | Type | Default | Description |
|
||||
|------------|------------------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `version` | `string` | | Version of the Taskfile. The current version is `3`. |
|
||||
| `output` | `string` | `interleaved` | Output mode. Available options: `interleaved`, `group` and `prefixed`. |
|
||||
| `method` | `string` | `checksum` | Default method in this Taskfile. Can be overridden in a task by task basis. Available options: `checksum`, `timestamp` and `none`. |
|
||||
| `includes` | [`map[string]Include`](#include) | | Additional Taskfiles to be included. |
|
||||
| `vars` | [`map[string]Variable`](#variable) | | A set of global variables. |
|
||||
| `env` | [`map[string]Variable`](#variable) | | A set of global environment variables. |
|
||||
| `tasks` | [`map[string]Task`](#task) | | A set of task definitions. |
|
||||
| `silent` | `bool` | `false` | Default 'silent' options for this Taskfile. If `false`, can be overridden with `true` in a task by task basis. |
|
||||
| `dotenv` | `[]string` | | A list of `.env` file paths to be parsed. |
|
||||
| `run` | `string` | `always` | Default 'run' option for this Taskfile. Available options: `always`, `once` and `when_changed`. |
|
||||
| `interval` | `string` | `5s` | Sets a different watch interval when using `--watch`, the default being 5 seconds. This string should be a valid [Go Duration](https://pkg.go.dev/time#ParseDuration). |
|
||||
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
|
||||
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
|
||||
|
||||
## Include
|
||||
|
||||
| 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. If a relative path, resolved relative to the directory containing the including Taskfile. |
|
||||
| `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. |
|
||||
| `flatten` | `bool` | `false` | If `true`, the tasks from the included Taskfile will be available in the including Taskfile without a namespace. If a task with the same name already exists in the including Taskfile, an error will be thrown. |
|
||||
| `internal` | `bool` | `false` | Stops any task in the included Taskfile from being callable on the command line. These commands will also be omitted from the output when used with `--list`. |
|
||||
| `aliases` | `[]string` | | Alternative names for the namespace of the included Taskfile. |
|
||||
| `vars` | `map[string]Variable` | | A set of variables to apply to the included Taskfile. |
|
||||
| `checksum` | `string` | | The checksum of the file you expect to include. If the checksum does not match, the file will not be included. |
|
||||
|
||||
:::info
|
||||
|
||||
Informing only a string like below is equivalent to setting that value to the
|
||||
`taskfile` attribute.
|
||||
|
||||
```yaml
|
||||
includes:
|
||||
foo: ./path
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 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"
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::info
|
||||
|
||||
In a variables map, variables defined later may reference variables defined
|
||||
earlier (declaration order is respected):
|
||||
|
||||
```yaml
|
||||
vars:
|
||||
FIRST_VAR: "hello"
|
||||
SECOND_VAR: "{{.FIRST_VAR}} world"
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Task
|
||||
|
||||
| Attribute | Type | Default | Description |
|
||||
| --------------- | ---------------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `cmds` | [`[]Command`](#command) | | A list of shell commands to be executed. |
|
||||
| `deps` | [`[]Dependency`](#dependency) | | A list of dependencies of this task. Tasks defined here will run in parallel before this task. |
|
||||
| `label` | `string` | | Overrides the name of the task in the output when a task is run. Supports variables. |
|
||||
| `desc` | `string` | | A short description of the task. This is displayed when calling `task --list`. |
|
||||
| `prompt` | `[]string` | | One or more prompts that will be presented before a task is run. Declining will cancel running the current and any subsequent tasks. |
|
||||
| `summary` | `string` | | A longer description of the task. This is displayed when calling `task --summary [task]`. |
|
||||
| `aliases` | `[]string` | | A list of alternative names by which the task can be called. |
|
||||
| `sources` | `[]string` | | A list of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs. |
|
||||
| `generates` | `[]string` | | A list of files meant to be generated by this task. Relevant for `timestamp` method. Can be file paths or star globs. |
|
||||
| `status` | `[]string` | | A list of commands to check if this task should run. The task is skipped otherwise. This overrides `method`, `sources` and `generates`. |
|
||||
| `preconditions` | [`[]Precondition`](#precondition) | | A list of commands to check if this task should run. If a condition is not met, the task will error. |
|
||||
| `requires` | [`Requires`](#requires) | | A list of required variables which should be set if this task is to run, if any variables listed are unset the task will error and not run. |
|
||||
| `dir` | `string` | | The directory in which this task should run. Defaults to the current working directory. |
|
||||
| `vars` | [`map[string]Variable`](#variable) | | A set of variables that can be used in the task. |
|
||||
| `env` | [`map[string]Variable`](#variable) | | A set of environment variables that will be made available to shell commands. |
|
||||
| `dotenv` | `[]string` | | A list of `.env` file paths to be parsed. |
|
||||
| `silent` | `bool` | `false` | Hides task name and command from output. The command's output will still be redirected to `STDOUT` and `STDERR`. When combined with the `--list` flag, task descriptions will be hidden. |
|
||||
| `interactive` | `bool` | `false` | Tells task that the command is interactive. |
|
||||
| `internal` | `bool` | `false` | Stops a task from being callable on the command line. It will also be omitted from the output when used with `--list`. |
|
||||
| `method` | `string` | `checksum` | Defines which method is used to check the task is up-to-date. `timestamp` will compare the timestamp of the sources and generates files. `checksum` will check the checksum (You probably want to ignore the .task folder in your .gitignore file). `none` skips any validation and always run the task. |
|
||||
| `prefix` | `string` | | Defines a string to prefix the output of tasks running in parallel. Only used when the output mode is `prefixed`. |
|
||||
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing commands. |
|
||||
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
|
||||
| `platforms` | `[]string` | All platforms | Specifies which platforms the task should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/master/src/internal/syslist/syslist.go). Task will be skipped otherwise. |
|
||||
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
|
||||
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
|
||||
|
||||
:::info
|
||||
|
||||
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"
|
||||
|
||||
baz:
|
||||
cmd: echo "baz"
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Command
|
||||
|
||||
| Attribute | Type | Default | Description |
|
||||
| -------------- | ---------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `cmd` | `string` | | The shell command to be executed. |
|
||||
| `task` | `string` | | Set this to trigger execution of another task instead of running a command. This cannot be set together with `cmd`. |
|
||||
| `for` | [`For`](#for) | | Runs the command once for each given value. |
|
||||
| `silent` | `bool` | `false` | Skips some output for this command. Note that STDOUT and STDERR of the commands will still be redirected. |
|
||||
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the referenced task. Only relevant when setting `task` instead of `cmd`. |
|
||||
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
|
||||
| `defer` | [`Defer`](#defer) | | Alternative to `cmd`, but schedules the command or a task to be executed at the end of this task instead of immediately. This cannot be used together with `cmd`. |
|
||||
| `platforms` | `[]string` | All platforms | Specifies which platforms the command should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/master/src/internal/syslist/syslist.go). Command will be skipped otherwise. |
|
||||
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
|
||||
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
|
||||
|
||||
:::info
|
||||
|
||||
If given as a a string, the value will be assigned to `cmd`:
|
||||
|
||||
```yaml
|
||||
tasks:
|
||||
foo:
|
||||
cmds:
|
||||
- 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. |
|
||||
| `silent` | `bool` | `false` | Hides task name and command from output. The command's output will still be redirected to `STDOUT` and `STDERR`. |
|
||||
|
||||
:::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]
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Defer
|
||||
|
||||
The `defer` parameter defines a shell command to run, or a task to trigger, at the end of the current task instead of immediately.
|
||||
If defined as a string this is a shell command, otherwise it is a map defining a task to call:
|
||||
|
||||
| Attribute | Type | Default | Description |
|
||||
| --------- | ---------------------------------- | ------- | ----------------------------------------------------------------- |
|
||||
| `task` | `string` | | The deferred task to trigger. |
|
||||
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the deferred task. |
|
||||
| `silent` | `bool` | `false` | Hides task name and command from output. The command's output will still be redirected to `STDOUT` and `STDERR`. |
|
||||
|
||||
### For
|
||||
|
||||
The `for` parameter can be defined as a string, a list of strings or a map. If
|
||||
it is defined as a string, you can give it any of the following values:
|
||||
|
||||
- `sources` - Will run the command for each source file defined on the task.
|
||||
(Glob patterns will be resolved, so `*.go` will run for every Go file that
|
||||
matches).
|
||||
- `generates` - Will run the command for each file defined in the task's generates
|
||||
list. (Glob patterns will be resolved, so `*.txt` will run for every text file
|
||||
that matches).
|
||||
|
||||
If it is defined as a list of strings, the command will be run for each value.
|
||||
|
||||
Finally, the `for` parameter can be defined as a map when you want to use a
|
||||
variable to define the values to loop over:
|
||||
|
||||
| Attribute | Type | Default | Description |
|
||||
| --------- | -------- | ---------------- | -------------------------------------------- |
|
||||
| `var` | `string` | | The name of the variable to use as an input. |
|
||||
| `split` | `string` | (any whitespace) | What string the variable should be split on. |
|
||||
| `as` | `string` | `ITEM` | The name of the iterator variable. |
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Requires
|
||||
|
||||
| Attribute | Type | Default | Description |
|
||||
| --------- | ---------- | ------- | -------------------------------------------------------------------------------------------------- |
|
||||
| `vars` | `[]string` | | List of variable or environment variable names that must be set if this task is to execute and run |
|
||||
@@ -1,426 +0,0 @@
|
||||
---
|
||||
slug: /reference/templating/
|
||||
sidebar_position: 4
|
||||
toc_min_heading_level: 2
|
||||
toc_max_heading_level: 5
|
||||
---
|
||||
|
||||
# Templating Reference
|
||||
|
||||
Task's templating engine uses Go's [text/template][text/template] package to
|
||||
interpolate values. For detailed information about the usage of Go's templating
|
||||
engine, we recommend reading [the official documentation][text/template].
|
||||
However, we will provide a basic overview of the main features here.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Most string values in Task (though, not all) can be templated. The templating
|
||||
engine uses double curly braces `{{` and `}}` to denote a template. Anything
|
||||
inside the curly braces will be executed as a Go template and the result will be
|
||||
inserted into the resulting string. For example:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
hello:
|
||||
vars:
|
||||
MESSAGE: 'Hello, World!'
|
||||
cmds:
|
||||
- 'echo {{.MESSAGE}}'
|
||||
```
|
||||
|
||||
In this example, we have a task called `hello` with a single variable, `MESSAGE`
|
||||
defined. When the command is run, the templating engine will evaluate the
|
||||
variable and replace `{{.MESSAGE}}` with the variable's contents. This task will
|
||||
output `Hello, World!` to the terminal. Note that when referring to a variable,
|
||||
you must use dot notation.
|
||||
|
||||
You are also able to do more complex things in templates, such as conditional
|
||||
logic:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
maybe-happy:
|
||||
vars:
|
||||
SMILE: ':\)'
|
||||
FROWN: ':\('
|
||||
HAPPY: true
|
||||
cmds:
|
||||
- 'echo {{if .HAPPY}}{{.SMILE}}{{else}}{{.FROWN}}{{end}}'
|
||||
```
|
||||
|
||||
```txt
|
||||
:)
|
||||
```
|
||||
|
||||
...calling functions and piping values:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
uniq:
|
||||
vars:
|
||||
NUMBERS: '0,1,1,1,2,2,3'
|
||||
cmds:
|
||||
- 'echo {{splitList "," .NUMBERS | uniq | join ", " }}!'
|
||||
```
|
||||
|
||||
```txt
|
||||
0, 1, 2, 3
|
||||
```
|
||||
|
||||
...looping over values with control flow operators:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
loop:
|
||||
vars:
|
||||
NUMBERS: [0, 1, 1, 1, 2, 2, 3]
|
||||
cmds:
|
||||
# Ranges over NUMBERS and prints the index and value of each number until it finds a number greater than 1
|
||||
- "{{range $index, $num := .NUMBERS}}{{if gt $num 1 }}{{break}}{{end}}echo {{$index}}: {{$num}}\n{{end}}"
|
||||
```
|
||||
|
||||
```txt
|
||||
0: 0
|
||||
1: 1
|
||||
2: 1
|
||||
3: 1
|
||||
```
|
||||
|
||||
## Special Variables
|
||||
|
||||
Task defines some special variables that are always available to the templating
|
||||
engine. If you define a variable with the same name as a special variable, the
|
||||
special variable will be overridden.
|
||||
|
||||
| Var | Description |
|
||||
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `CLI_ARGS` | Contain all extra arguments passed after `--` when calling Task through the CLI as a string. |
|
||||
| `CLI_ARGS_LIST` | Contain all extra arguments passed after `--` when calling Task through the CLI as a shell parsed list. |
|
||||
| `CLI_FORCE` | A boolean containing whether the `--force` or `--force-all` flags were set. |
|
||||
| `CLI_SILENT` | A boolean containing whether the `--silent` flag was set. |
|
||||
| `CLI_VERBOSE` | A boolean containing whether the `--verbose` flag was set. |
|
||||
| `CLI_OFFLINE` | A boolean containing whether the `--offline` flag was set. |
|
||||
| `TASK` | The name of the current task. |
|
||||
| `ALIAS` | The alias used for the current task, otherwise matches `TASK`. |
|
||||
| `TASK_EXE` | The Task executable name or path. |
|
||||
| `ROOT_TASKFILE` | The absolute path of the root Taskfile. |
|
||||
| `ROOT_DIR` | The absolute path of the root Taskfile directory. |
|
||||
| `TASKFILE` | The absolute path of the included Taskfile. |
|
||||
| `TASKFILE_DIR` | The absolute path of the included Taskfile directory. |
|
||||
| `TASK_DIR` | The absolute path of the directory where the task is executed. |
|
||||
| `USER_WORKING_DIR` | The absolute path of the directory `task` was called from. |
|
||||
| `CHECKSUM` | The checksum of the files listed in `sources`. Only available within the `status` prop and if method is set to `checksum`. |
|
||||
| `TIMESTAMP` | The date object of the greatest timestamp of the files listed in `sources`. Only available within the `status` prop and if method is set to `timestamp`. |
|
||||
| `TASK_VERSION` | The current version of task. |
|
||||
| `ITEM` | The value of the current iteration when using the `for` property. Can be changed to a different variable name using `as:`. |
|
||||
| `EXIT_CODE` | Available exclusively inside the `defer:` command. Contains the failed command exit code. Only set when non-zero. |
|
||||
|
||||
## Functions
|
||||
|
||||
Functions are provided at a few different levels in Task. Below, we list all the
|
||||
functions available for use in Task.
|
||||
|
||||
:::note
|
||||
|
||||
Functions marked with an asterisk (\*) also have `must` variants that will panic
|
||||
rather than erroring.
|
||||
|
||||
:::
|
||||
|
||||
### Built-in Functions
|
||||
|
||||
The first set of functions are [provided by Golang
|
||||
itself][go-template-functions]:
|
||||
|
||||
| Function | Description |
|
||||
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `and` | Returns the boolean AND of its arguments by returning the first empty argument or the last argument. That is, `and x y` behaves as `if x then y else x`. Evaluation proceeds through the arguments left to right and returns when the result is determined. |
|
||||
| `call` | Returns the result of calling the first argument, which must be a function, with the remaining arguments as parameters. Thus `call .X.Y 1 2` is, in Go notation, `dot.X.Y(1, 2)` where `Y` is a func-valued field, map entry, or the like. The first argument must be the result of an evaluation that yields a value of function type (as distinct from a predefined function such as print). The function must return either one or two result values, the second of which is of type error. If the arguments don't match the function or the returned error value is non-nil, execution stops. |
|
||||
| `html` | Returns the escaped HTML equivalent of the textual representation of its arguments. This function is unavailable in [html/template][html/template], with a few exceptions. |
|
||||
| `index` | Returns the result of indexing its first argument by the following arguments. Thus `index x 1 2 3` is, in Go syntax, `x[1][2][3]`. Each indexed item must be a map, slice, or array. |
|
||||
| `slice` | slice returns the result of slicing its first argument by the remaining arguments. Thus `slice x 1 2` is, in Go syntax, `x[1:2]`, while `slice x` is `x[:]`, `slice x 1` is `x[1:]`, and `slice x 1 2 3` is `x[1:2:3]`. The first argument must be a string, slice, or array. |
|
||||
| `js` | Returns the escaped JavaScript equivalent of the textual representation of its arguments. |
|
||||
| `len` | Returns the integer length of its argument. |
|
||||
| `not` | Returns the boolean negation of its single argument. |
|
||||
| `or` | Returns the boolean OR of its arguments by returning the first non-empty argument or the last argument, that is, `or x y` behaves as `if x then x else y`. Evaluation proceeds through the arguments left to right and returns when the result is determined. |
|
||||
| `print` | An alias for `fmt.Sprint`. |
|
||||
| `printf` | An alias for `fmt.Sprintf`. |
|
||||
| `println` | An alias for `fmt.Sprintln`. |
|
||||
| `urlquery` | Returns the escaped value of the textual representation of its arguments in a form suitable for embedding in a URL query. This function is unavailable in [html/template][html/template], with a few exceptions. |
|
||||
|
||||
### Slim-Sprig Functions
|
||||
|
||||
In addition to the built-in functions, Task also provides a set of functions
|
||||
imported via the [slim-sprig][slim-sprig] package. We only provide a very basic
|
||||
description here for completeness. For detailed usage, please refer to the
|
||||
[slim-sprig documentation][slim-sprig]:
|
||||
|
||||
#### [String Functions][string-functions]
|
||||
|
||||
| Function | Description |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `trim` | Removes space from either side of a string. |
|
||||
| `trimAll` | Removes given characters from the front or back of a string. |
|
||||
| `trimSuffix` | Trims just the suffix from a string. |
|
||||
| `trimPrefix` | Trims just the prefix from a string. |
|
||||
| `upper` | Converts the entire string to uppercase. |
|
||||
| `lower` | Converts the entire string to lowercase. |
|
||||
| `title` | Converts to title case. |
|
||||
| `repeat` | Repeats a string multiple times. |
|
||||
| `substr` | Gets a substring from a string. |
|
||||
| `trunc` | Truncates a string. |
|
||||
| `contains` | Tests to see if one string is contained inside of another. |
|
||||
| `hasPrefix` | Tests whether a string has a given prefix. |
|
||||
| `hasSuffix` | Tests whether a string has a given suffix. |
|
||||
| `quote` | Wraps a string in double quotes. |
|
||||
| `squote` | Wraps a string in single quotes. |
|
||||
| `cat` | Concatenates multiple strings together into one, separating them with spaces. |
|
||||
| `indent` | Indents every line in a given string to the specified indent width. |
|
||||
| `nindent` | Identical to `indent`, but prepends a new line to the beginning of the string. |
|
||||
| `replace` | Replaces a string. |
|
||||
| `plural` | Pluralizes a string. |
|
||||
| `regexMatch`\* | Returns true if the input string contains any match of the regular expression. |
|
||||
| `regexFindAll`\* | Returns a slice of all matches of the regular expression in the input string. |
|
||||
| `regexFind`\* | Returns the first (left most) match of the regular expression in the input string. |
|
||||
| `regexReplaceAll`\* | Returns a copy of the input string, replacing matches of the Regexp with the replacement string replacement. |
|
||||
| `regexReplaceAllLiteral`\* | Returns a copy of the input string, replacing matches of the Regexp with the replacement string replacement without expanding `$`. |
|
||||
| `regexSplit`\* | Slices the input string into substrings separated by the expression and returns a slice of the substrings between those expression matches. |
|
||||
| `regexQuoteMeta`\* | Returns a string that escapes all regular expression metacharacters inside the argument text. |
|
||||
|
||||
#### [String Slice Functions][string-list-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ----------- | ----------------------------------------------------------------------- |
|
||||
| `join` | Joins a list of strings into a single string, with the given separator. |
|
||||
| `splitList` | Splits a string into a list of strings. |
|
||||
| `split` | Splits a string into a map of strings where each key is an index. |
|
||||
| `splitn` | Identical to `split`, but stops splitting after `n` values. |
|
||||
| `sortAlpha` | Sorts a list of strings into alphabetical (lexicographical) order. |
|
||||
|
||||
#### [Integer Functions][math-functions]
|
||||
|
||||
| Function | Description |
|
||||
| --------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `add` | Sum a set of numbers. |
|
||||
| `add1` | Increments a number by 1. |
|
||||
| `sub` | Subtracts one number from another. |
|
||||
| `div` | Performs integer division. |
|
||||
| `mod` | Modulo. |
|
||||
| `mul` | Multiplies a set of numbers. |
|
||||
| `max` | Returns the largest of a set of integers. |
|
||||
| `min` | Returns the smallest of a set of integers. |
|
||||
| `floor` | Returns the greatest float value less than or equal to input value |
|
||||
| `ceil` | Returns the greatest float value greater than or equal to input value |
|
||||
| `round` | Returns a float value with the remainder rounded to the given number to digits after the decimal point. |
|
||||
| `randInt` | Returns a random integer value from min (inclusive) to max (exclusive). |
|
||||
|
||||
#### [Integer Slice Functions][integer-list-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ----------- | --------------------------------------------------------------------------- |
|
||||
| `until` | Builds a range of integers. |
|
||||
| `untilStep` | Builds a range of integers, but allows you to define a start, stop and step |
|
||||
| `seq` | Works like the bash `seq` command. |
|
||||
|
||||
#### [Date Functions][date-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ---------------- | ------------------------------------------------------------------------------ |
|
||||
| `now` | Gets the current date/time. |
|
||||
| `ago` | Returns the duration since the given date/time. |
|
||||
| `date` | Formats a date. |
|
||||
| `dateInZone` | Identical to `date`, but with the given timezone. |
|
||||
| `duration` | Formats the number of seconds into a string. |
|
||||
| `durationRound` | Identical to `duration`, but rounds the duration to the most significant unit. |
|
||||
| `unixEpoch` | Returns the seconds since the unix epoch for the given date/time. |
|
||||
| `dateModify`\* | Modifies a date using the given input string. |
|
||||
| `htmlDate` | Formats a date for inserting into an HTML date picker input field. |
|
||||
| `htmlDateInZone` | Identical to `htmlDate`, but with the given timezone. |
|
||||
| `toDate`\* | Converts a string to a date/time. |
|
||||
|
||||
#### [Default Functions][default-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ---------- | ------------------------------------------------------------------------ |
|
||||
| `default` | Uses a default value if the given value is considered "zero" or "empty". |
|
||||
| `empty` | Returns true if a value is considered "zero" or "empty". |
|
||||
| `coalesce` | Takes a list of values and returns the first non-empty one. |
|
||||
| `all` | Returns true if all values are non-empty. |
|
||||
| `any` | Returns true if any value is non-empty. |
|
||||
| `ternary` | The ternary function takes two values, and a test value. If the test value is true, the first value will be returned. If the test value is empty, the second value will be returned. |
|
||||
|
||||
#### [Encoding Functions][encoding-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ---------------- | ------------------------------------------------------------------ |
|
||||
| `fromJson`\* | Decodes a JSON string into an object. |
|
||||
| `toJson`\* | Encodes an object as a JSON string. |
|
||||
| `toPrettyJson`\* | Encodes an object as a JSON string with new lines and indentation. |
|
||||
| `toRawJson`\* | Encodes an object as a JSON string with HTML characters unescaped. |
|
||||
| `b64enc` | Encodes a string into base 64. |
|
||||
| `b64dec` | Decodes a string from base 64. |
|
||||
| `b32enc` | Encodes a string into base 32. |
|
||||
| `b32dec` | Decodes a string from base 32. |
|
||||
|
||||
:::note
|
||||
YAML encoding functions are [provided directly by Task](#task-functions).
|
||||
:::
|
||||
|
||||
#### [List Functions][list-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ----------- | ---------------------------------------------------------------- |
|
||||
| `list` | Creates a list from a set of values. |
|
||||
| `first`\* | Gets the first value in a list. |
|
||||
| `rest`\* | Gets all values except the first value in the list. |
|
||||
| `last`\* | Gets the last value in the list. |
|
||||
| `initial`\* | Get all values except the last value in the list. |
|
||||
| `append`\* | Adds a new value to the end of the list. |
|
||||
| `prepend`\* | Adds a new value to the start of the list. |
|
||||
| `concat` | Joins two or more lists together. |
|
||||
| `reverse`\* | Reverses the order of a list. |
|
||||
| `uniq`\* | Generate a list with all of the duplicates removed. |
|
||||
| `without`\* | Filters matching items out of a list. |
|
||||
| `has`\* | Tests to see if a list has a particular element. |
|
||||
| `compact`\* | Removes entries with empty values. |
|
||||
| `slice`\* | Returns a partial copy of a list given start and end parameters. |
|
||||
| `chunk` | Splits a list into chunks of given size. |
|
||||
|
||||
#### [Dictionary Functions][dictionary-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------ |
|
||||
| `dict` | Creates a dictionary from a set of key/value pairs. |
|
||||
| `get` | Gets the value from the dictionary with the given key. |
|
||||
| `set` | Adds a new key/value pair to a dictionary. |
|
||||
| `unset` | Deletes a key from a dictionary. |
|
||||
| `hasKey` | Returns true if a dictionary contains the given key. |
|
||||
| `pluck` | Gets a list of all of the matching values in a set of maps given a key. |
|
||||
| `dig` | Returns the value in a nested map given a path of keys. |
|
||||
| `merge`\* | Merges two or more dictionaries into one. |
|
||||
| `mergeOverwrite`\* | Identical to `merge`, but giving precedence from right to left. |
|
||||
| `keys` | Returns a list of all of the keys in a dictionary. |
|
||||
| `pick` | Creates a new dictionary containing only the given keys of an existing map. |
|
||||
| `omit` | Creates a new dictionary containing all the keys of an existing map except the ones given. |
|
||||
| `values` | Returns a list of all the values in a dictionary. |
|
||||
|
||||
#### [Type Conversion Functions][type-conversion-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ----------- | ------------------------------------------------------ |
|
||||
| `atoi` | Converts a string to an integer. |
|
||||
| `float64` | Converts to a float64. |
|
||||
| `int` | Converts to an int at the system's width. |
|
||||
| `int64` | Converts to an int64. |
|
||||
| `toDecimal` | Converts a unix octal to a int64. |
|
||||
| `toString` | Converts to a string. |
|
||||
| `toStrings` | Converts a list, slice, or array to a list of strings. |
|
||||
| `toStrings` | Produces a slice of strings from any list. |
|
||||
| `toDecimal` | Given a unix octal permission, produce a decimal. |
|
||||
|
||||
#### [Path and Filepath Functions][path-functions]
|
||||
|
||||
| Function | Description |
|
||||
| --------- | ----------------------------------------- |
|
||||
| `base` | Returns the last element of a path. |
|
||||
| `dir` | Returns the directory of a path. |
|
||||
| `clean` | Cleans up a path. |
|
||||
| `ext` | Returns the file extension of a path. |
|
||||
| `isAbs` | Checks if a path is absolute. |
|
||||
| `osBase` | Returns the last element of a filepath. |
|
||||
| `osDir` | Returns the directory of a filepath. |
|
||||
| `osClean` | Cleans up a filepath. |
|
||||
| `osExt` | Returns the file extension of a filepath. |
|
||||
| `osIsAbs` | Checks if a filepath is absolute. |
|
||||
|
||||
:::note
|
||||
More filepath encoding functions are [provided directly by Task](#task-functions).
|
||||
:::
|
||||
|
||||
#### [Flow Control Functions][flow-control-functions]
|
||||
|
||||
| Function | Description |
|
||||
| -------- | ----------------------------------------------------------------------------- |
|
||||
| `fail` | Unconditionally returns an empty string and an error with the specified text. |
|
||||
|
||||
#### [OS Functions][os-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ----------- | --------------------------------------------- |
|
||||
| `env` | Reads an environment variable. |
|
||||
| `expandenv` | Substitutes environment variables in a string |
|
||||
|
||||
#### [Reflection Functions][reflection-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ------------ | ------------------------------------------------------ |
|
||||
| `kindOf` | Returns the kind of a value. |
|
||||
| `kindIs` | Verifies that a value is a particular kind. |
|
||||
| `typeOf` | Returns the underlying type of a value. |
|
||||
| `typeIs` | Verifies that a value is of a particular type. |
|
||||
| `typeIsLike` | Identical to `typeIs`, but also dereferences pointers. |
|
||||
| `deepEqual` | Returns true if two values are "deeply equal". |
|
||||
|
||||
#### [Cryptographic and Security Functions][crypto-functions]
|
||||
|
||||
| Function | Description |
|
||||
| ------------ | -------------------------------------- |
|
||||
| `sha1sum` | Computes a string's SHA1 digest. |
|
||||
| `sha256sum` | Computes a string's SHA256 digest. |
|
||||
| `adler32sum` | Computes a string's Adler-32 checksum. |
|
||||
|
||||
### Task Functions
|
||||
|
||||
Lastly, Task itself provides a few functions:
|
||||
|
||||
| Function | Description |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `OS` | Returns the operating system. Possible values are `windows`, `linux`, `darwin` (macOS) and `freebsd`. |
|
||||
| `ARCH` | Returns the architecture Task was compiled to: `386`, `amd64`, `arm` or `s390x`. |
|
||||
| `numCPU` | Returns the number of logical CPU's usable by the current process. |
|
||||
| `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` | Opposite 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). |
|
||||
| `shellQuote` | (aliased to `q`): Quotes a string to make it safe for use in shell scripts. Task uses [this Go function](https://pkg.go.dev/mvdan.cc/sh/v3@v3.4.0/syntax#Quote) for this. The Bash dialect is assumed. |
|
||||
| `splitArgs` | Splits a string as if it were a command's arguments. Task uses [this Go function](https://pkg.go.dev/mvdan.cc/sh/v3@v3.4.0/shell#Fields). |
|
||||
| `joinPath` | Joins any number of arguments into a path. The same as Go's [filepath.Join](https://pkg.go.dev/path/filepath#Join). |
|
||||
| `relPath` | Converts an absolute path (second argument) into a relative path, based on a base path (first argument). The same as Go's [filepath.Rel](https://pkg.go.dev/path/filepath#Rel). |
|
||||
| `merge` | Creates a new map that is a copy of the first map with the keys of each subsequent map merged into it. If there is a duplicate key, the value of the last map with that key is used. |
|
||||
| `spew` | Returns the Go representation of a specific variable. Useful for debugging. Uses the [davecgh/go-spew](https://github.com/davecgh/go-spew) package. |
|
||||
| `fromYaml`\* | Decodes a YAML string into an object. |
|
||||
| `toYaml`\* | Encodes an object as a YAML string. |
|
||||
| `uuid` | Generates a new pseudo-random UUIDv4 string. |
|
||||
| `randIntN` | Generates a new pseudo-random, non-negative, 32bit integer in the half-open interval `[0,n)`. Generated numbers are not suitable for security-sensitive work. |
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[text/template]: https://pkg.go.dev/text/template
|
||||
[html/template]: https://pkg.go.dev/html/template
|
||||
[go-template-functions]: https://pkg.go.dev/text/template#hdr-Functions
|
||||
[slim-sprig]: https://go-task.github.io/slim-sprig/
|
||||
[os-functions]: https://go-task.github.io/slim-sprig/os.html
|
||||
[string-functions]: https://go-task.github.io/slim-sprig/strings.html
|
||||
[string-list-functions]: https://go-task.github.io/slim-sprig/string_slice.html
|
||||
[math-functions]: https://go-task.github.io/slim-sprig/math.html
|
||||
[integer-list-functions]: https://go-task.github.io/slim-sprig/integer_slice.html
|
||||
[date-functions]: https://go-task.github.io/slim-sprig/date.html
|
||||
[default-functions]: https://go-task.github.io/slim-sprig/defaults.html
|
||||
[encoding-functions]: https://go-task.github.io/slim-sprig/encoding.html
|
||||
[list-functions]: https://go-task.github.io/slim-sprig/lists.html
|
||||
[dictionary-functions]: https://go-task.github.io/slim-sprig/dicts.html
|
||||
[type-conversion-functions]: https://go-task.github.io/slim-sprig/conversion.html
|
||||
[path-functions]: https://go-task.github.io/slim-sprig/paths.html
|
||||
[flow-control-functions]: https://go-task.github.io/slim-sprig/flow_control.html
|
||||
[os-functions]: https://go-task.github.io/slim-sprig/os.html
|
||||
[reflection-functions]: https://go-task.github.io/slim-sprig/reflection.html
|
||||
[crypto-functions]: https://go-task.github.io/slim-sprig/crypto.html
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,227 +0,0 @@
|
||||
---
|
||||
slug: /styleguide/
|
||||
sidebar_position: 11
|
||||
---
|
||||
|
||||
# Style Guide
|
||||
|
||||
This is the official style guide for `Taskfile.yml` files. It provides basic
|
||||
instructions for keeping your Taskfiles clean and familiar to other users.
|
||||
|
||||
This guide contains general guidelines, but they do not necessarily need to be
|
||||
followed strictly. Feel free to disagree and do things differently if you need
|
||||
or want to. Any improvements to this guide are welcome! Please open an issue or
|
||||
create a pull request to contribute.
|
||||
|
||||
## Use the suggested ordering of the main sections
|
||||
|
||||
```yaml
|
||||
version:
|
||||
includes:
|
||||
# optional configurations (output, silent, method, run, etc.)
|
||||
vars:
|
||||
env: # followed or replaced by dotenv
|
||||
tasks:
|
||||
```
|
||||
|
||||
## Use two 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 the main sections with empty lines
|
||||
|
||||
```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:
|
||||
# ...
|
||||
```
|
||||
|
||||
## Separate tasks with empty lines
|
||||
|
||||
```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 only uppercase letters for 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}} .
|
||||
```
|
||||
|
||||
## Avoid using whitespace when templating variables
|
||||
|
||||
```yaml
|
||||
# bad
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
greet:
|
||||
cmds:
|
||||
- echo '{{ .MESSAGE }}'
|
||||
|
||||
|
||||
# good
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
greet:
|
||||
cmds:
|
||||
- echo '{{.MESSAGE}}'
|
||||
```
|
||||
|
||||
This convention is also commonly used in templates for the Go programming
|
||||
language.
|
||||
|
||||
## Use kebab case for task names
|
||||
|
||||
```yaml
|
||||
# bad
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
do_something_fancy:
|
||||
cmds:
|
||||
- echo 'Do something'
|
||||
|
||||
|
||||
# good
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
do-something-fancy:
|
||||
cmds:
|
||||
- echo 'Do something'
|
||||
```
|
||||
|
||||
## Use a colon to separate the task namespace and name
|
||||
|
||||
```yaml
|
||||
# good
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
docker:build:
|
||||
cmds:
|
||||
- docker ...
|
||||
|
||||
docker:run:
|
||||
cmds:
|
||||
- docker-compose ...
|
||||
```
|
||||
|
||||
This is also done automatically when using included Taskfiles.
|
||||
|
||||
## Prefer using external scripts instead of multi-line commands
|
||||
|
||||
```yaml
|
||||
# bad
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
build:
|
||||
cmds:
|
||||
- |
|
||||
for i in $(seq 1 10); do
|
||||
echo $i
|
||||
echo "some other complex logic"
|
||||
done'
|
||||
|
||||
# good
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
build:
|
||||
cmds:
|
||||
- ./scripts/my_complex_script.sh
|
||||
```
|
||||
@@ -1,72 +0,0 @@
|
||||
---
|
||||
slug: /taskfile-versions/
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Taskfile Versions
|
||||
|
||||
The Taskfile schema slowly changes as new features are added and old ones are
|
||||
removed. This document explains how to use a Taskfile's schema version to ensure
|
||||
that the users of your Taskfile are using the correct versions of Task.
|
||||
|
||||
## What the Taskfile version means
|
||||
|
||||
The schema version at the top of every Taskfile corresponds to a version of the
|
||||
Task CLI, and by extension, the features that are provided by that version. When
|
||||
creating a Taskfile, you should specify the _minimum_ version of Task that
|
||||
supports the features you require. If you try to run a Taskfile with a version
|
||||
of Task that does not meet this minimum required version, it will exit with an
|
||||
error. For example, given a Taskfile that starts with:
|
||||
|
||||
```yaml
|
||||
version: '3.2.1'
|
||||
```
|
||||
|
||||
When executed with Task `v3.2.0`, it will exit with an error. Running with
|
||||
version `v3.2.1` or higher will work as expected.
|
||||
|
||||
Task accepts any [SemVer][semver] compatible string including versions which
|
||||
omit the minor or patch numbers. For example, `3`, `3.0`, and `3.0.0` all mean
|
||||
the same thing and are all valid. Most Taskfiles only specify the major version
|
||||
number. However it can be useful to be more specific when you intend to share a
|
||||
Taskfile with others.
|
||||
|
||||
For example, the Taskfile below makes use of aliases:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
hello:
|
||||
aliases:
|
||||
- hi
|
||||
- hey
|
||||
cmds:
|
||||
- echo "Hello, world!"
|
||||
```
|
||||
|
||||
Aliases were introduced in Task `v3.17.0`, but the Taskfile only specifies `3`
|
||||
as the version. This means that a user who has `v3.16.0` or lower installed will
|
||||
get a potentially confusing error message when trying to run the Task as the
|
||||
Taskfile specifies that any version greater or equal to `v3.0.0` is fine.
|
||||
|
||||
Instead, we should start the file like this:
|
||||
|
||||
```yaml
|
||||
version: '3.17'
|
||||
```
|
||||
|
||||
Now when someone tries to run the Taskfile with an older version of Task, they
|
||||
will receive an error prompting them to upgrade their version of Task to
|
||||
`v3.17.0` or greater.
|
||||
|
||||
## Versions 1 & 2
|
||||
|
||||
Version 1 and 2 of Task are no longer officially supported and anyone still
|
||||
using them is strongly encouraged to upgrade to the latest version of Task.
|
||||
|
||||
While `version: 2` of Task did support schema versions, the behavior did not
|
||||
work in quite the same way and cannot be relied upon for the purposes discussed
|
||||
above.
|
||||
|
||||
[semver]: https://semver.org/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,249 +0,0 @@
|
||||
import type {Config} from '@docusaurus/types';
|
||||
import type * as Preset from '@docusaurus/preset-classic';
|
||||
import { EnumChangefreq } from 'sitemap';
|
||||
|
||||
import remarkGithub from 'remark-github';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
import { DISCORD_URL } from './constants';
|
||||
import { GITHUB_URL } from './constants';
|
||||
import { BLUESKY_URL } from './constants';
|
||||
import { MASTODON_URL } from './constants';
|
||||
import { TWITTER_URL } from './constants';
|
||||
import { STACK_OVERFLOW } from './constants';
|
||||
import { ANSWER_OVERFLOW } from './constants';
|
||||
|
||||
import lightCodeTheme from './src/themes/prismLight';
|
||||
import darkCodeTheme from './src/themes/prismDark';
|
||||
|
||||
const config: Config = {
|
||||
title: 'Task',
|
||||
tagline: 'A task runner / simpler Make alternative written in Go ',
|
||||
url: 'https://taskfile.dev',
|
||||
baseUrl: '/',
|
||||
onBrokenLinks: 'warn',
|
||||
onBrokenMarkdownLinks: 'warn',
|
||||
favicon: 'img/favicon.ico',
|
||||
|
||||
organizationName: 'go-task',
|
||||
projectName: 'task',
|
||||
deploymentBranch: 'gh-pages',
|
||||
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en'],
|
||||
localeConfigs: {
|
||||
en: {
|
||||
label: 'English',
|
||||
direction: 'ltr',
|
||||
htmlLang: 'en-US'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
presets: [
|
||||
[
|
||||
'classic',
|
||||
{
|
||||
docs: {
|
||||
routeBasePath: '/',
|
||||
sidebarPath: './sidebars.ts',
|
||||
remarkPlugins: [remarkGithub, remarkGfm],
|
||||
includeCurrentVersion: true,
|
||||
versions: {
|
||||
current: {
|
||||
label: `Next 🚧`,
|
||||
path: 'next',
|
||||
badge: false
|
||||
},
|
||||
latest: {
|
||||
label: 'Latest',
|
||||
badge: false
|
||||
}
|
||||
}
|
||||
},
|
||||
blog: {
|
||||
blogSidebarTitle: 'All posts',
|
||||
blogSidebarCount: 'ALL'
|
||||
},
|
||||
theme: {
|
||||
customCss: [
|
||||
'./src/css/custom.css',
|
||||
'./src/css/carbon.css',
|
||||
]
|
||||
},
|
||||
gtag: {
|
||||
trackingID: 'G-4RT25NXQ7N',
|
||||
anonymizeIP: true
|
||||
},
|
||||
sitemap: {
|
||||
changefreq: EnumChangefreq.WEEKLY,
|
||||
priority: 0.5,
|
||||
ignorePatterns: ['/tags/**']
|
||||
}
|
||||
} satisfies Preset.Options,
|
||||
]
|
||||
],
|
||||
|
||||
scripts: [
|
||||
{
|
||||
src: '/js/carbon.js',
|
||||
async: true
|
||||
}
|
||||
],
|
||||
|
||||
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: 'intro',
|
||||
position: 'left',
|
||||
label: 'Docs'
|
||||
},
|
||||
{
|
||||
to: 'blog',
|
||||
label: 'Blog',
|
||||
position: 'left'
|
||||
},
|
||||
{
|
||||
to: '/donate',
|
||||
position: 'left',
|
||||
label: 'Donate'
|
||||
},
|
||||
{
|
||||
type: 'docsVersionDropdown',
|
||||
position: 'right',
|
||||
dropdownActiveClassDisabled: true,
|
||||
},
|
||||
{
|
||||
href: GITHUB_URL,
|
||||
title: 'GitHub',
|
||||
position: 'right',
|
||||
className: "header-icon-link icon-github",
|
||||
},
|
||||
{
|
||||
href: DISCORD_URL,
|
||||
title: 'Discord',
|
||||
position: 'right',
|
||||
className: "header-icon-link icon-discord",
|
||||
},
|
||||
{
|
||||
href: TWITTER_URL,
|
||||
title: 'X (Twitter)',
|
||||
position: 'right',
|
||||
className: "header-icon-link icon-twitter",
|
||||
},
|
||||
{
|
||||
href: BLUESKY_URL,
|
||||
title: 'Bluesky',
|
||||
position: 'right',
|
||||
className: "header-icon-link icon-bluesky",
|
||||
},
|
||||
{
|
||||
href: MASTODON_URL,
|
||||
title: 'Mastodon',
|
||||
rel: 'me',
|
||||
position: 'right',
|
||||
className: "header-icon-link icon-mastodon",
|
||||
}
|
||||
]
|
||||
},
|
||||
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: 'Discord',
|
||||
href: DISCORD_URL
|
||||
},
|
||||
{
|
||||
label: 'Twitter',
|
||||
href: TWITTER_URL
|
||||
},
|
||||
{
|
||||
label: 'Bluesky',
|
||||
href: BLUESKY_URL,
|
||||
rel: 'me'
|
||||
},
|
||||
{
|
||||
label: 'Mastodon',
|
||||
href: MASTODON_URL,
|
||||
rel: 'me'
|
||||
},
|
||||
{
|
||||
label: 'Stack Overflow',
|
||||
href: STACK_OVERFLOW
|
||||
},
|
||||
{
|
||||
label: 'Answer Overflow',
|
||||
href: ANSWER_OVERFLOW
|
||||
},
|
||||
{
|
||||
label: 'OpenCollective',
|
||||
href: 'https://opencollective.com/task'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{
|
||||
html: '<a target="_blank" href="https://www.netlify.com"><img src="https://www.netlify.com/v3/img/components/netlify-color-accent.svg" alt="Deploys by Netlify" /></a>'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
prism: {
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
additionalLanguages: [
|
||||
"bash", // aka. shell
|
||||
"json",
|
||||
"powershell"
|
||||
]
|
||||
},
|
||||
// NOTE(@andreynering): Don't worry, these keys are meant to be public =)
|
||||
algolia: {
|
||||
appId: '7IZIJ13AI7',
|
||||
apiKey: '34b64ae4fc8d9da43d9a13d9710aaddc',
|
||||
indexName: 'taskfile'
|
||||
}
|
||||
} satisfies Preset.ThemeConfig,
|
||||
};
|
||||
|
||||
export default config;
|
||||
3
website/netlify.toml
Normal file
3
website/netlify.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[build]
|
||||
publish = ".vitepress/dist"
|
||||
command = "pnpm run build"
|
||||
@@ -1,54 +1,26 @@
|
||||
{
|
||||
"name": "taskfile-dev",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/go-task/task"
|
||||
},
|
||||
"name": "website2",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"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": "^3.5.2",
|
||||
"@docusaurus/preset-classic": "^3.5.2",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"prism-react-renderer": "^2.1.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-github": "^12.0.0"
|
||||
"dev": "vitepress dev",
|
||||
"build": "vitepress build",
|
||||
"preview": "vitepress preview",
|
||||
"lint": "prettier --write ."
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^3.5.2",
|
||||
"@docusaurus/tsconfig": "^3.5.2",
|
||||
"@docusaurus/types": "^3.5.2",
|
||||
"@types/react": "^19.0.0",
|
||||
"typescript": "^5.3.3"
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@types/node": "^24.1.0",
|
||||
"netlify-cli": "^23.1.1",
|
||||
"prettier": "^3.6.2",
|
||||
"vitepress": "^1.6.3",
|
||||
"vitepress-plugin-group-icons": "^1.6.1",
|
||||
"vitepress-plugin-tabs": "^0.7.1",
|
||||
"vue": "^3.5.18"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.5%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
"packageManager": "pnpm@10.15.1+sha512.34e538c329b5553014ca8e8f4535997f96180a1d0f614339357449935350d924e22f8614682191264ec33d1462ac21561aff97f6bb18065351c162c7e8f6de67"
|
||||
}
|
||||
|
||||
9420
website/pnpm-lock.yaml
generated
Normal file
9420
website/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,13 @@
|
||||
module.exports = {
|
||||
/**
|
||||
* @see https://prettier.io/docs/configuration
|
||||
* @type {import("prettier").Config}
|
||||
*/
|
||||
const config = {
|
||||
trailingComma: 'none',
|
||||
singleQuote: true,
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.md', '*.mdx'],
|
||||
files: ['*.md'],
|
||||
options: {
|
||||
printWidth: 80,
|
||||
proseWrap: 'always'
|
||||
@@ -11,3 +15,5 @@ module.exports = {
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { SidebarsConfig } from '@docusaurus/plugin-content-docs';
|
||||
|
||||
export default {
|
||||
taskSidebar: [
|
||||
{
|
||||
type: 'autogenerated',
|
||||
dirName: '.'
|
||||
},
|
||||
{
|
||||
type: 'html',
|
||||
value: '<div id="sidebar-ads"></div>'
|
||||
}
|
||||
],
|
||||
} satisfies SidebarsConfig;
|
||||
@@ -1,15 +1,13 @@
|
||||
---
|
||||
title: Any Variables
|
||||
description: Task variables are no longer limited to strings!
|
||||
slug: any-variables
|
||||
authors: [pd93]
|
||||
tags: [experiments, variables]
|
||||
image: https://i.imgur.com/mErPwqL.png
|
||||
hide_table_of_contents: false
|
||||
author: pd93
|
||||
date: 2024-05-09
|
||||
outline: deep
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
# Any Variables
|
||||
|
||||
<AuthorCard :author="$frontmatter.author" />
|
||||
|
||||
Task has always had variables, but even though you were able to define them
|
||||
using different YAML types, they would always be converted to strings by Task.
|
||||
@@ -18,8 +16,6 @@ simple problems. Starting from [v3.37.0][v3.37.0], this is no longer the case!
|
||||
Task now supports most variable types, including **booleans**, **integers**,
|
||||
**floats** and **arrays**!
|
||||
|
||||
{/* truncate */}
|
||||
|
||||
## What's the big deal?
|
||||
|
||||
These changes allow you to use variables in a much more natural way and opens up
|
||||
@@ -31,10 +27,9 @@ some of the examples below for some inspiration.
|
||||
No more comparing strings to "true" or "false". Now you can use actual boolean
|
||||
values in your templates:
|
||||
|
||||
<Tabs defaultValue="2">
|
||||
<TabItem value="1" label="Before">
|
||||
::: code-group
|
||||
|
||||
```yaml
|
||||
```yaml [Before]
|
||||
version: 3
|
||||
|
||||
tasks:
|
||||
@@ -45,10 +40,7 @@ tasks:
|
||||
- '{{if eq .BOOL "true"}}echo foo{{end}}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="2" label="After">
|
||||
|
||||
```yaml
|
||||
```yaml [After]
|
||||
version: 3
|
||||
|
||||
tasks:
|
||||
@@ -59,8 +51,7 @@ tasks:
|
||||
- '{{if .BOOL}}echo foo{{end}}' # <-- No need to compare to "true"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
:::
|
||||
|
||||
### Arithmetic
|
||||
|
||||
@@ -110,10 +101,9 @@ to specify the delimiter. However, we have now added support for looping over
|
||||
"collection-type" variables using the `for` keyword, so now you are able to loop
|
||||
over list variables directly:
|
||||
|
||||
<Tabs defaultValue="2">
|
||||
<TabItem value="1" label="Before">
|
||||
::: code-group
|
||||
|
||||
```yaml
|
||||
```yaml [Before]
|
||||
version: 3
|
||||
|
||||
tasks:
|
||||
@@ -127,10 +117,7 @@ tasks:
|
||||
cmd: echo {{.ITEM}}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="2" label="After">
|
||||
|
||||
```yaml
|
||||
```yaml [After]
|
||||
version: 3
|
||||
|
||||
tasks:
|
||||
@@ -143,8 +130,7 @@ tasks:
|
||||
cmd: echo {{.ITEM}}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
:::
|
||||
|
||||
## What about maps?
|
||||
|
||||
@@ -154,20 +140,9 @@ at once, we have released support for all other variable types and we will
|
||||
continue working on map support in the new "[Map Variables][map-variables]"
|
||||
experiment.
|
||||
|
||||
:::note
|
||||
|
||||
If you were previously using maps with the Any Variables experiment and wish to
|
||||
continue using them, you will need to enable the new [Map Variables
|
||||
experiment][map-variables] instead.
|
||||
|
||||
:::
|
||||
|
||||
We're looking for feedback on a couple of different proposals, so please give
|
||||
them a go and let us know what you think. :pray:
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[v3.37.0]: https://github.com/go-task/task/releases/tag/v3.37.0
|
||||
[slim-sprig-math]: https://go-task.github.io/slim-sprig/math.html
|
||||
[slim-sprig-list]: https://go-task.github.io/slim-sprig/lists.html
|
||||
[map-variables]: /experiments/map-variables
|
||||
{/* prettier-ignore-end */}
|
||||
31
website/src/blog/index.md
Normal file
31
website/src/blog/index.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: Blog
|
||||
description: Latest news and updates from the Task team
|
||||
---
|
||||
|
||||
<BlogPost
|
||||
title="Announcing Built-in Core Utilities for Windows"
|
||||
url="/blog/windows-core-utils"
|
||||
date="2025-09-15"
|
||||
author="andreynering"
|
||||
description="When I started Task back in 2017, one of my biggest goals was to build a task runner that would work well on all major platforms, including Windows. At the time, I was using Windows as my main platform, and it caught my attention how much of a pain it was to get a working version of Make on Windows, for example."
|
||||
:tags="['windows', 'core-utils']"
|
||||
/>
|
||||
|
||||
<BlogPost
|
||||
title="Any Variables"
|
||||
url="/blog/any-variables"
|
||||
date="2024-05-09"
|
||||
author="pd93"
|
||||
description="Task has always had variables, but even though you were able to define them using different YAML types, they would always be converted to strings by Task. This limited users to string manipulation and encouraged messy workarounds for simple problems. Starting from v3.37.0, this is no longer the case! Task now supports most variable types, including booleans, integers, floats and arrays!"
|
||||
:tags="['experiments', 'variables']"
|
||||
/>
|
||||
|
||||
<BlogPost
|
||||
title="Introducing Experiments"
|
||||
url="/blog/task-in-2023"
|
||||
date="2023-09-02"
|
||||
author="pd93"
|
||||
description="A look at where Task is, where it's going and how we're going to get there. Lately, Task has been growing extremely quickly and I've found myself thinking a lot about the future of the project and how we continue to evolve and grow. I'm not much of a writer, but I think one of the things we could do better is to communicate these kinds of thoughts to the community."
|
||||
:tags="['roadmap', 'experiments', 'community']"
|
||||
/>
|
||||
@@ -2,13 +2,15 @@
|
||||
title: Introducing Experiments
|
||||
description:
|
||||
A look at where task is, where it's going and how we're going to get there.
|
||||
slug: task-in-2023
|
||||
authors: [pd93]
|
||||
tags: [experiments, breaking-changes, roadmap, v4]
|
||||
image: https://i.imgur.com/mErPwqL.png
|
||||
hide_table_of_contents: false
|
||||
author: pd93
|
||||
date: 2024-05-09
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Introducing Experiments
|
||||
|
||||
<AuthorCard :author="$frontmatter.author" />
|
||||
|
||||
Lately, Task has been growing extremely quickly and I've found myself thinking a
|
||||
lot about the future of the project and how we continue to evolve and grow. I'm
|
||||
not much of a writer, but I think one of the things we could do better is to
|
||||
@@ -16,19 +18,17 @@ communicate these kinds of thoughts to the community. So, with that in mind,
|
||||
this is the first (hopefully of many) blog posts talking about Task and what
|
||||
we're up to.
|
||||
|
||||
{/* truncate */}
|
||||
|
||||
## :calendar: So, what have we been up to?
|
||||
|
||||
Over the past 12 months or so, [@andreynering] (Author and maintainer of the
|
||||
project) and I ([@pd93]) have been working in our spare time to maintain and
|
||||
Over the past 12 months or so, @andreynering (Author and maintainer of the
|
||||
project) and I (@pd93) have been working in our spare time to maintain and
|
||||
improve v3 of Task and we've made some amazing progress. Here are just some of
|
||||
the things we've released in that time:
|
||||
|
||||
- An official [extension for VS Code][vscode-task].
|
||||
- Internal Tasks ([#818](https://github.com/go-task/task/pull/818)).
|
||||
- Task aliases ([#879](https://github.com/go-task/task/pull/879)).
|
||||
- Looping over tasks ([#1220](https://github.com/go-task/task/pull/1200)).
|
||||
- Internal Tasks (#818).
|
||||
- Task aliases (#879).
|
||||
- Looping over tasks (#1220).
|
||||
- A series of refactors to the core codebase to make it more maintainable and
|
||||
extensible.
|
||||
- Loads of bug fixes and improvements.
|
||||
@@ -38,14 +38,13 @@ the things we've released in that time:
|
||||
- And much, much more! :sparkles:
|
||||
|
||||
We're also working on adding some really exciting and highly requested features
|
||||
to Task such as having the ability to run remote Taskfiles
|
||||
([#1317](https://github.com/go-task/task/issues/1317)).
|
||||
to Task such as having the ability to run remote Taskfiles (#1317).
|
||||
|
||||
None of this would have been possible without the [150 or so (and growing)
|
||||
contributors][contributors] to the project, numerous sponsors and a passionate
|
||||
community of users. Together we have more than doubled the number of GitHub
|
||||
stars to over 8400 :star: since the beginning of 2022 and this continues to
|
||||
accelerate. We can't thank you all enough for your help and support! :rocket:
|
||||
accelerate. We can't thank you all enough for your help and support! 🚀
|
||||
|
||||
[](https://star-history.com/#go-task/task&Date)
|
||||
|
||||
@@ -71,7 +70,7 @@ commitment to make. Smaller, more frequent major releases are also a significant
|
||||
inconvenience for users as they have to constantly keep up-to-date with our
|
||||
breaking changes. Fortunately, there is a better way.
|
||||
|
||||
## What's going to change? :monocle:
|
||||
## What's going to change? :monocle_face:
|
||||
|
||||
Going forwards, breaking changes will be allowed into _minor_ versions of Task
|
||||
as "experimental features". To access these features users will need opt-in by
|
||||
@@ -122,14 +121,11 @@ I plan to write more of these blog posts in the future on a variety of
|
||||
Task-related topics, so make sure to check in occasionally and see what we're up
|
||||
to!
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[vscode-task]: https://github.com/go-task/vscode-task
|
||||
[crowdin]: https://crowdin.com
|
||||
[contributors]: https://github.com/go-task/task/graphs/contributors
|
||||
[semver]: https://semver.org
|
||||
[breaking-change-proposal]: https://github.com/go-task/task/discussions/1191
|
||||
[@andreynering]: https://github.com/andreynering
|
||||
[@pd93]: https://github.com/pd93
|
||||
[experiments]: https://taskfile.dev/experiments
|
||||
[deprecations]: https://taskfile.dev/deprecations
|
||||
[deprecate-version-2-schema]: https://github.com/go-task/task/issues/1197
|
||||
@@ -139,4 +135,3 @@ to!
|
||||
[experiments-project]: https://github.com/orgs/go-task/projects/1
|
||||
[gentle-force-experiment]: https://github.com/go-task/task/issues/1200
|
||||
[remote-taskfiles-experiment]: https://github.com/go-task/task/issues/1317
|
||||
{/* prettier-ignore-end */}
|
||||
136
website/src/blog/windows-core-utils.md
Normal file
136
website/src/blog/windows-core-utils.md
Normal file
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: 'Announcing Built-in Core Utilities for Windows'
|
||||
description: The journey of enhancing Windows support in Task.
|
||||
author: andreynering
|
||||
date: 2025-09-15
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Announcing Built-in Core Utilities for Windows
|
||||
|
||||
<AuthorCard :author="$frontmatter.author" />
|
||||
|
||||
When I started Task back in 2017, one of my biggest goals was to build a task
|
||||
runner that would work well on all major platforms, including Windows. At the
|
||||
time, I was using Windows as my main platform, and it caught my attention how
|
||||
much of a pain it was to get a working version of Make on Windows, for example.
|
||||
|
||||
## The very beginning
|
||||
|
||||
The very first versions, which looked very prototyp-ish, already supported
|
||||
Windows, but it was falling back to Command Prompt (`cmd.exe`) to run commands
|
||||
if `bash` wasn't available in the system. That didn't mean you couldn't run Bash
|
||||
commands on Windows necessarily, because if you used Task inside Git Bash, it
|
||||
would expose `bash.exe` into your `$PATH`, which made possible for Task to use
|
||||
it. Outside of it, you would be out of luck, though, because running on Command
|
||||
Prompt meant that the commands wouldn't be really compatible.
|
||||
|
||||
## Adopting a shell interpreter
|
||||
|
||||
I didn't take too much time to discover that there was [a shell interpreter for
|
||||
Go that was very solid][mvdan], and I quickly adopted it to ensure we would be
|
||||
able to run commands with consistency across all platforms. It was fun because
|
||||
once adopted, I had the opportunity to [make some contributions to make it more
|
||||
stable][mvdan-prs], which I'm sure the author appreciated.
|
||||
|
||||
## The lack of core utilities
|
||||
|
||||
There was one important thing missing, though. If you needed to use any core
|
||||
utilities on Windows, like copying files with `cp`, moving with `mv`, creating a
|
||||
directory with `mkdir -p`, that likely would just fail :boom:. There were
|
||||
workarounds, of course. You could run `task` inside Git Bash which exposed core
|
||||
utils in `$PATH` for you, or you could install these core utils manually (there
|
||||
are a good number of alternative implementations available for download).
|
||||
|
||||
That was still far from ideal, though. One of my biggest goals with Task is that
|
||||
it should "just work", even on Windows. Requiring additional setup to make
|
||||
things work is exactly what I wanted to avoid.
|
||||
|
||||
## They finally arrive!
|
||||
|
||||
And here we are, in 2025, 8 years after the initial release. We might be late,
|
||||
but I'm happy nonetheless. From now on, the following core utilities will be
|
||||
available on Windows. This is the start. We want to add more with time.
|
||||
|
||||
- `base64`
|
||||
- `cat`
|
||||
- `chmod`
|
||||
- `cp`
|
||||
- `find`
|
||||
- `gzip`
|
||||
- `ls`
|
||||
- `mkdir`
|
||||
- `mktemp`
|
||||
- `mv`
|
||||
- `rm`
|
||||
- `shasum`
|
||||
- `tar`
|
||||
- `touch`
|
||||
- `xargs`
|
||||
|
||||
## How we made this possible
|
||||
|
||||
This was made possible via a collaboration with the maintainers of other Go
|
||||
projects.
|
||||
|
||||
### u-root/u-root
|
||||
|
||||
We are using the core utilities implementations in Go from the [u-root][u-root]
|
||||
project. It wasn't as simple as it sounds because they have originally
|
||||
implemented every core util as a standalone `main` package, which means we
|
||||
couldn't just import and use them as libraries. We had some discussion and we
|
||||
agreed on a common [interface][uroot-interface] and [base
|
||||
implementation][uroot-base]. Then, I refactored one-by-one of the core utils in
|
||||
the list above. This is the reason we don't have all of them: there are too
|
||||
many! But the good news is that we can refactor more with time and include them
|
||||
in Task.
|
||||
|
||||
### mvdan/sh
|
||||
|
||||
The other collaboration was with the maintainer of the shell interpreter. He
|
||||
agreed on having [an official middleware][middleware] to expose these core
|
||||
utilities. This means that other projects that use the shell interpreter can
|
||||
also benefit from this work, and as more utilities are included, those projects
|
||||
will benefit as well.
|
||||
|
||||
## Can I choose whether to use them or not?
|
||||
|
||||
Yes. We added a new environment variable called
|
||||
[`TASK_CORE_UTILS`][task-core-utils] to control if the Go implementations are
|
||||
used or not. By default, this is `true` on Windows and `false` on other
|
||||
platforms. You can override it like this:
|
||||
|
||||
```bash
|
||||
# Enable, even on non-Windows platforms
|
||||
env TASK_CORE_UTILS=1 task ...
|
||||
|
||||
# Disable, even on Windows
|
||||
env TASK_CORE_UTILS=0 task ...
|
||||
```
|
||||
|
||||
We'll consider making this enabled by default on all platforms in the future. In
|
||||
the meantime, we're still using the system core utils on non-Windows platforms
|
||||
to avoid regressions as the Go implementations may not be 100% compatible with
|
||||
the system ones.
|
||||
|
||||
## Feedback
|
||||
|
||||
If you have any feedback about this feature, join our [Discord server][discord]
|
||||
or [open an issue][gh-issue] on GitHub.
|
||||
|
||||
Also, if Task is useful for you or your company, consider [sponsoring the
|
||||
project][sponsor]!
|
||||
|
||||
[mvdan]: https://github.com/mvdan/sh
|
||||
[mvdan-prs]:
|
||||
https://github.com/mvdan/sh/pulls?q=is%3Apr+author%3Aandreynering+is%3Aclosed+sort%3Acreated-asc
|
||||
[u-root]: https://github.com/u-root/u-root
|
||||
[uroot-interface]:
|
||||
https://github.com/u-root/u-root/blob/main/pkg/core/command.go
|
||||
[uroot-base]: https://github.com/u-root/u-root/blob/main/pkg/core/base.go
|
||||
[middleware]:
|
||||
https://github.com/mvdan/sh/blob/master/moreinterp/coreutils/coreutils.go
|
||||
[task-core-utils]: /docs/reference/environment#task-core-utils
|
||||
[discord]: https://discord.com/invite/6TY36E39UK
|
||||
[gh-issue]: https://github.com/go-task/task/issues
|
||||
[sponsor]: /donate
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user