mirror of
https://github.com/go-task/task.git
synced 2026-06-16 20:31:39 +00:00
Compare commits
5 Commits
v3.45.2
...
split-exec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5128a98ee2 | ||
|
|
8353dffc7a | ||
|
|
6e80b401e6 | ||
|
|
1402e2baaf | ||
|
|
4b99f60039 |
@@ -8,6 +8,6 @@ charset = utf-8
|
|||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
||||||
[*.{md,mdx,yml,yaml,json,toml,htm,html,js,ts,vue,css,svg,sh,bash,fish}]
|
[*.{md,mdx,yml,yaml,json,toml,htm,html,js,ts,css,svg,sh,bash,fish}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|||||||
41
.github/workflows/lint.yml
vendored
41
.github/workflows/lint.yml
vendored
@@ -13,14 +13,14 @@ jobs:
|
|||||||
name: Lint
|
name: Lint
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.24.x, 1.25.x]
|
go-version: [1.23.x, 1.24.x]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{matrix.go-version}}
|
go-version: ${{matrix.go-version}}
|
||||||
|
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v8
|
||||||
@@ -32,12 +32,43 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: 3.13
|
python-version: 3.12
|
||||||
|
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: install check-jsonschema
|
- name: install check-jsonschema
|
||||||
run: python -m pip install 'check-jsonschema==0.27.3'
|
run: python -m pip install 'check-jsonschema==0.27.3'
|
||||||
|
|
||||||
- name: check-jsonschema (metaschema)
|
- name: check-jsonschema (metaschema)
|
||||||
run: check-jsonschema --check-metaschema website/src/public/schema.json
|
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.')
|
||||||
|
|||||||
7
.github/workflows/release-nightly.yml
vendored
7
.github/workflows/release-nightly.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Release nightly
|
name: Realease nightly
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -9,14 +9,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.25.x
|
go-version: 1.24.x
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v6
|
uses: goreleaser/goreleaser-action@v6
|
||||||
@@ -27,4 +27,3 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
||||||
CLOUDSMITH_TOKEN: ${{secrets.CLOUDSMITH_TOKEN}}
|
|
||||||
|
|||||||
27
.github/workflows/release.yml
vendored
27
.github/workflows/release.yml
vendored
@@ -10,31 +10,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.25.x
|
go-version: 1.24.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
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '22.x'
|
|
||||||
cache: 'pnpm'
|
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v6
|
uses: goreleaser/goreleaser-action@v6
|
||||||
@@ -45,9 +28,3 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
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
|
name: Test
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.24.x, 1.25.x]
|
go-version: [1.23.x, 1.24.x]
|
||||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
runs-on: ${{matrix.platform}}
|
runs-on: ${{matrix.platform}}
|
||||||
steps:
|
steps:
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Download Go modules
|
- name: Download Go modules
|
||||||
run: go mod download
|
run: go mod download
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ nfpms:
|
|||||||
formats:
|
formats:
|
||||||
- deb
|
- deb
|
||||||
- rpm
|
- rpm
|
||||||
- apk
|
|
||||||
file_name_template: '{{.ProjectName}}_{{.Os}}_{{.Arch}}'
|
file_name_template: '{{.ProjectName}}_{{.Os}}_{{.Arch}}'
|
||||||
contents:
|
contents:
|
||||||
- src: completion/bash/task.bash
|
- src: completion/bash/task.bash
|
||||||
@@ -136,37 +135,3 @@ winget:
|
|||||||
owner: microsoft
|
owner: microsoft
|
||||||
name: winget-pkgs
|
name: winget-pkgs
|
||||||
branch: master
|
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,58 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## v3.45.2 - 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-v3.45.1 - 2025-09-15
|
|
||||||
|
|
||||||
Failed due to an issue with our release process.
|
|
||||||
|
|
||||||
## v3.44.1 - 2025-07-23
|
## v3.44.1 - 2025-07-23
|
||||||
|
|
||||||
- Internal tasks will no longer be shown as suggestions since they cannot be
|
- Internal tasks will no longer be shown as suggestions since they cannot be
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://taskfile.dev">
|
<a href="https://taskfile.dev">
|
||||||
<img src="website/src/public/img/logo.svg" width="200px" height="200px" />
|
<img src="website/static/img/logo.svg" width="200px" height="200px" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<h1>Task</h1>
|
<h1>Task</h1>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td align="center" valign="middle">
|
<td align="center" valign="middle">
|
||||||
<a target="_blank" href="https://devowl.io">
|
<a target="_blank" href="https://devowl.io">
|
||||||
<img src="https://devowl.io/wp-content/uploads/meta/favicon.webp" height="100px" title="devowl.io" />
|
<img src="/website/static/img/devowl.io.svg" height="100px" title="devowl.io" />
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
25
Taskfile.yml
25
Taskfile.yml
@@ -8,7 +8,6 @@ includes:
|
|||||||
|
|
||||||
vars:
|
vars:
|
||||||
BIN: "{{.ROOT_DIR}}/bin"
|
BIN: "{{.ROOT_DIR}}/bin"
|
||||||
GOTESTSUM_FORMAT: '{{if .CI}}github-actions{{else}}pkgname{{end}}'
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: '0'
|
CGO_ENABLED: '0'
|
||||||
@@ -132,37 +131,29 @@ tasks:
|
|||||||
test:
|
test:
|
||||||
desc: Runs test suite
|
desc: Runs test suite
|
||||||
aliases: [t]
|
aliases: [t]
|
||||||
deps: [gotestsum:install]
|
|
||||||
sources:
|
sources:
|
||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
- "testdata/**/*"
|
- "testdata/**/*"
|
||||||
cmds:
|
cmds:
|
||||||
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' ./...
|
- go test ./...
|
||||||
|
|
||||||
test:watch:
|
test:watch:
|
||||||
desc: Runs test suite with watch tests included
|
desc: Runs test suite with watch tests included
|
||||||
deps: [sleepit:build, gotestsum:install]
|
deps: [sleepit:build]
|
||||||
cmds:
|
cmds:
|
||||||
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' ./... -tags 'watch'
|
- go test ./... -tags 'watch'
|
||||||
|
|
||||||
test:all:
|
test:all:
|
||||||
desc: Runs test suite with signals and watch tests included
|
desc: Runs test suite with signals and watch tests included
|
||||||
deps: [sleepit:build, gotestsum:install]
|
deps: [sleepit:build]
|
||||||
cmds:
|
cmds:
|
||||||
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' -tags 'signals watch' ./...
|
- go test -tags 'signals watch' ./...
|
||||||
|
|
||||||
goreleaser:test:
|
goreleaser:test:
|
||||||
desc: Tests release process without publishing
|
desc: Tests release process without publishing
|
||||||
cmds:
|
cmds:
|
||||||
- goreleaser --snapshot --clean
|
- goreleaser --snapshot --clean
|
||||||
|
|
||||||
gotestsum:install:
|
|
||||||
desc: Installs gotestsum
|
|
||||||
status:
|
|
||||||
- command -v gotestsum
|
|
||||||
cmds:
|
|
||||||
- go install gotest.tools/gotestsum@latest
|
|
||||||
|
|
||||||
goreleaser:install:
|
goreleaser:install:
|
||||||
desc: Installs goreleaser
|
desc: Installs goreleaser
|
||||||
cmds:
|
cmds:
|
||||||
@@ -212,6 +203,7 @@ tasks:
|
|||||||
Please wait for the CI to finish and then do the following:
|
Please wait for the CI to finish and then do the following:
|
||||||
|
|
||||||
- Copy the changelog for v{{.VERSION}} to the GitHub release
|
- 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
|
- Update and push the snapcraft manifest in https://github.com/go-task/snap/blob/main/snap/snapcraft.yaml
|
||||||
preconditions:
|
preconditions:
|
||||||
- sh: test $(git rev-parse --abbrev-ref HEAD) = "main"
|
- sh: test $(git rev-parse --abbrev-ref HEAD) = "main"
|
||||||
@@ -230,3 +222,8 @@ tasks:
|
|||||||
- "git push origin tag v{{.VERSION}}"
|
- "git push origin tag v{{.VERSION}}"
|
||||||
- cmd: printf "%s" '{{.COMPLETE_MESSAGE}}'
|
- cmd: printf "%s" '{{.COMPLETE_MESSAGE}}'
|
||||||
silent: true
|
silent: true
|
||||||
|
|
||||||
|
npm:publish:
|
||||||
|
desc: Publish release to npm
|
||||||
|
cmds:
|
||||||
|
- npm publish --access=public
|
||||||
|
|||||||
@@ -3,23 +3,33 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/otiai10/copy"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
changelogSource = "CHANGELOG.md"
|
changelogSource = "CHANGELOG.md"
|
||||||
changelogTarget = "website/src/docs/changelog.md"
|
changelogTarget = "website/docs/changelog.mdx"
|
||||||
versionFile = "internal/version/version.txt"
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
var changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
|
var (
|
||||||
|
changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
|
||||||
|
versionRegex = regexp.MustCompile(`(?m)^ "version": "\d+\.\d+\.\d+",$`)
|
||||||
|
)
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
var (
|
var (
|
||||||
@@ -43,7 +53,7 @@ func release() error {
|
|||||||
return errors.New("error: expected version number")
|
return errors.New("error: expected version number")
|
||||||
}
|
}
|
||||||
|
|
||||||
version, err := getVersion(versionFile)
|
version, err := getVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -61,18 +71,36 @@ func release() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setVersionFile(versionFile, version); err != nil {
|
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 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVersion(filename string) (*semver.Version, error) {
|
func getVersion() (*semver.Version, error) {
|
||||||
b, err := os.ReadFile(filename)
|
cmd := exec.Command("git", "describe", "--tags", "--abbrev=0")
|
||||||
|
b, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return semver.NewVersion(strings.TrimSpace(string(b)))
|
return semver.NewVersion(strings.TrimSpace(string(b)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,3 +159,37 @@ func changelog(version *semver.Version) error {
|
|||||||
func setVersionFile(fileName string, version *semver.Version) error {
|
func setVersionFile(fileName string, version *semver.Version) error {
|
||||||
return os.WriteFile(fileName, []byte(version.String()+"\n"), 0o644)
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/go-task/task/v3/internal/flags"
|
"github.com/go-task/task/v3/internal/flags"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/version"
|
"github.com/go-task/task/v3/internal/version"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -110,16 +111,63 @@ func run() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
e := task.NewExecutor(
|
if err := experiments.Validate(); err != nil {
|
||||||
flags.WithFlags(),
|
log.Warnf("%s\n", err.Error())
|
||||||
task.WithVersionCheck(true),
|
}
|
||||||
|
|
||||||
|
// Create a new root node for the given entrypoint
|
||||||
|
node, err := taskfile.NewRootNode(
|
||||||
|
flags.Entrypoint,
|
||||||
|
flags.Dir,
|
||||||
|
flags.Insecure,
|
||||||
)
|
)
|
||||||
if err := e.Setup(); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tempDir, err := task.NewTempDir(node.Dir())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := taskfile.NewReader(
|
||||||
|
flags.WithFlags(),
|
||||||
|
taskfile.WithTempDir(tempDir.Remote),
|
||||||
|
taskfile.WithDebugFunc(func(s string) {
|
||||||
|
log.VerboseOutf(logger.Magenta, s)
|
||||||
|
}),
|
||||||
|
taskfile.WithPromptFunc(func(s string) error {
|
||||||
|
return log.Prompt(logger.Yellow, s, "n", "y", "yes")
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx, cf := context.WithTimeout(context.Background(), flags.Timeout)
|
||||||
|
defer cf()
|
||||||
|
graph, err := reader.Read(ctx, node)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
|
return &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: flags.Timeout}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
executor, err := task.NewExecutor(graph,
|
||||||
|
flags.WithFlags(),
|
||||||
|
task.WithDir(node.Dir()),
|
||||||
|
task.WithTempDir(tempDir),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the download flag is specified, we should stop execution as soon as
|
||||||
|
// taskfile is downloaded
|
||||||
|
if flags.Download {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if flags.ClearCache {
|
if flags.ClearCache {
|
||||||
cachePath := filepath.Join(e.TempDir.Remote, "remote")
|
cachePath := filepath.Join(executor.TempDir.Remote, "remote")
|
||||||
return os.RemoveAll(cachePath)
|
return os.RemoveAll(cachePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,13 +176,12 @@ func run() error {
|
|||||||
flags.ListAll,
|
flags.ListAll,
|
||||||
flags.ListJson,
|
flags.ListJson,
|
||||||
flags.NoStatus,
|
flags.NoStatus,
|
||||||
flags.Nested,
|
|
||||||
)
|
)
|
||||||
if listOptions.ShouldListTasks() {
|
if listOptions.ShouldListTasks() {
|
||||||
if flags.Silent {
|
if flags.Silent {
|
||||||
return e.ListTaskNames(flags.ListAll)
|
return executor.ListTaskNames(flags.ListAll)
|
||||||
}
|
}
|
||||||
foundTasks, err := e.ListTasks(listOptions)
|
foundTasks, err := executor.ListTasks(listOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -166,17 +213,17 @@ func run() error {
|
|||||||
globals.Set("CLI_SILENT", ast.Var{Value: flags.Silent})
|
globals.Set("CLI_SILENT", ast.Var{Value: flags.Silent})
|
||||||
globals.Set("CLI_VERBOSE", ast.Var{Value: flags.Verbose})
|
globals.Set("CLI_VERBOSE", ast.Var{Value: flags.Verbose})
|
||||||
globals.Set("CLI_OFFLINE", ast.Var{Value: flags.Offline})
|
globals.Set("CLI_OFFLINE", ast.Var{Value: flags.Offline})
|
||||||
e.Taskfile.Vars.Merge(globals, nil)
|
executor.Taskfile.Vars.Merge(globals, nil)
|
||||||
|
|
||||||
if !flags.Watch {
|
if !flags.Watch {
|
||||||
e.InterceptInterruptSignals()
|
executor.InterceptInterruptSignals()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx = context.Background()
|
||||||
|
|
||||||
if flags.Status {
|
if flags.Status {
|
||||||
return e.Status(ctx, calls...)
|
return executor.Status(ctx, calls...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.Run(ctx, calls...)
|
return executor.Run(ctx, calls...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ func (v MissingVar) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (err *TaskMissingRequiredVarsError) Error() string {
|
func (err *TaskMissingRequiredVarsError) Error() string {
|
||||||
vars := make([]string, 0, len(err.MissingVars))
|
var vars []string
|
||||||
for _, v := range err.MissingVars {
|
for _, v := range err.MissingVars {
|
||||||
vars = append(vars, v.String())
|
vars = append(vars, v.String())
|
||||||
}
|
}
|
||||||
|
|||||||
150
executor.go
150
executor.go
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -26,27 +27,21 @@ type (
|
|||||||
// within them.
|
// within them.
|
||||||
Executor struct {
|
Executor struct {
|
||||||
// Flags
|
// Flags
|
||||||
Dir string
|
Dir string
|
||||||
Entrypoint string
|
TempDir *TempDir
|
||||||
TempDir TempDir
|
Force bool
|
||||||
Force bool
|
ForceAll bool
|
||||||
ForceAll bool
|
Watch bool
|
||||||
Insecure bool
|
Verbose bool
|
||||||
Download bool
|
Silent bool
|
||||||
Offline bool
|
AssumeYes bool
|
||||||
Timeout time.Duration
|
AssumeTerm bool // Used for testing
|
||||||
CacheExpiryDuration time.Duration
|
Dry bool
|
||||||
Watch bool
|
Summary bool
|
||||||
Verbose bool
|
Parallel bool
|
||||||
Silent bool
|
Color bool
|
||||||
AssumeYes bool
|
Concurrency int
|
||||||
AssumeTerm bool // Used for testing
|
Interval time.Duration
|
||||||
Dry bool
|
|
||||||
Summary bool
|
|
||||||
Parallel bool
|
|
||||||
Color bool
|
|
||||||
Concurrency int
|
|
||||||
Interval time.Duration
|
|
||||||
|
|
||||||
// I/O
|
// I/O
|
||||||
Stdin io.Reader
|
Stdin io.Reader
|
||||||
@@ -72,17 +67,17 @@ type (
|
|||||||
executionHashesMutex sync.Mutex
|
executionHashesMutex sync.Mutex
|
||||||
watchedDirs *xsync.MapOf[string, bool]
|
watchedDirs *xsync.MapOf[string, bool]
|
||||||
}
|
}
|
||||||
TempDir struct {
|
|
||||||
Remote string
|
|
||||||
Fingerprint string
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewExecutor creates a new [Executor] and applies the given functional options
|
// NewExecutor creates a new [Executor] and applies the given functional options
|
||||||
// to it.
|
// to it.
|
||||||
func NewExecutor(opts ...ExecutorOption) *Executor {
|
func NewExecutor(graph *ast.TaskfileGraph, opts ...ExecutorOption) (*Executor, error) {
|
||||||
|
tf, err := graph.Merge()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
e := &Executor{
|
e := &Executor{
|
||||||
Timeout: time.Second * 10,
|
Taskfile: tf,
|
||||||
Stdin: os.Stdin,
|
Stdin: os.Stdin,
|
||||||
Stdout: os.Stdout,
|
Stdout: os.Stdout,
|
||||||
Stderr: os.Stderr,
|
Stderr: os.Stderr,
|
||||||
@@ -100,7 +95,10 @@ func NewExecutor(opts ...ExecutorOption) *Executor {
|
|||||||
executionHashesMutex: sync.Mutex{},
|
executionHashesMutex: sync.Mutex{},
|
||||||
}
|
}
|
||||||
e.Options(opts...)
|
e.Options(opts...)
|
||||||
return e
|
if err := e.setup(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options loops through the given [ExecutorOption] functions and applies them
|
// Options loops through the given [ExecutorOption] functions and applies them
|
||||||
@@ -122,33 +120,23 @@ type dirOption struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *dirOption) ApplyToExecutor(e *Executor) {
|
func (o *dirOption) ApplyToExecutor(e *Executor) {
|
||||||
e.Dir = o.dir
|
absDir, err := filepath.Abs(o.dir)
|
||||||
}
|
if err != nil {
|
||||||
|
e.Dir = o.dir
|
||||||
// WithEntrypoint sets the entrypoint (main Taskfile) of the [Executor]. By
|
return
|
||||||
// default, Task will search for one of the default Taskfiles in the given
|
}
|
||||||
// directory.
|
e.Dir = absDir
|
||||||
func WithEntrypoint(entrypoint string) ExecutorOption {
|
|
||||||
return &entrypointOption{entrypoint}
|
|
||||||
}
|
|
||||||
|
|
||||||
type entrypointOption struct {
|
|
||||||
entrypoint string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *entrypointOption) ApplyToExecutor(e *Executor) {
|
|
||||||
e.Entrypoint = o.entrypoint
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTempDir sets the temporary directory that will be used by [Executor] for
|
// WithTempDir sets the temporary directory that will be used by [Executor] for
|
||||||
// storing temporary files like checksums and cached remote files. By default,
|
// storing temporary files like checksums and cached remote files. By default,
|
||||||
// the temporary directory is set to the user's temporary directory.
|
// the temporary directory is set to the user's temporary directory.
|
||||||
func WithTempDir(tempDir TempDir) ExecutorOption {
|
func WithTempDir(tempDir *TempDir) ExecutorOption {
|
||||||
return &tempDirOption{tempDir}
|
return &tempDirOption{tempDir}
|
||||||
}
|
}
|
||||||
|
|
||||||
type tempDirOption struct {
|
type tempDirOption struct {
|
||||||
tempDir TempDir
|
tempDir *TempDir
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *tempDirOption) ApplyToExecutor(e *Executor) {
|
func (o *tempDirOption) ApplyToExecutor(e *Executor) {
|
||||||
@@ -183,76 +171,6 @@ func (o *forceAllOption) ApplyToExecutor(e *Executor) {
|
|||||||
e.ForceAll = o.forceAll
|
e.ForceAll = o.forceAll
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithInsecure allows the [Executor] to make insecure connections when reading
|
|
||||||
// remote taskfiles. By default, insecure connections are rejected.
|
|
||||||
func WithInsecure(insecure bool) ExecutorOption {
|
|
||||||
return &insecureOption{insecure}
|
|
||||||
}
|
|
||||||
|
|
||||||
type insecureOption struct {
|
|
||||||
insecure bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *insecureOption) ApplyToExecutor(e *Executor) {
|
|
||||||
e.Insecure = o.insecure
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithDownload forces the [Executor] to download a fresh copy of the taskfile
|
|
||||||
// from the remote source.
|
|
||||||
func WithDownload(download bool) ExecutorOption {
|
|
||||||
return &downloadOption{download}
|
|
||||||
}
|
|
||||||
|
|
||||||
type downloadOption struct {
|
|
||||||
download bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *downloadOption) ApplyToExecutor(e *Executor) {
|
|
||||||
e.Download = o.download
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithOffline stops the [Executor] from being able to make network connections.
|
|
||||||
// It will still be able to read local files and cached copies of remote files.
|
|
||||||
func WithOffline(offline bool) ExecutorOption {
|
|
||||||
return &offlineOption{offline}
|
|
||||||
}
|
|
||||||
|
|
||||||
type offlineOption struct {
|
|
||||||
offline bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *offlineOption) ApplyToExecutor(e *Executor) {
|
|
||||||
e.Offline = o.offline
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTimeout sets the [Executor]'s timeout for fetching remote taskfiles. By
|
|
||||||
// default, the timeout is set to 10 seconds.
|
|
||||||
func WithTimeout(timeout time.Duration) ExecutorOption {
|
|
||||||
return &timeoutOption{timeout}
|
|
||||||
}
|
|
||||||
|
|
||||||
type timeoutOption struct {
|
|
||||||
timeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *timeoutOption) ApplyToExecutor(e *Executor) {
|
|
||||||
e.Timeout = o.timeout
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithCacheExpiryDuration sets the duration after which the cache is considered
|
|
||||||
// expired. By default, the cache is considered expired after 24 hours.
|
|
||||||
func WithCacheExpiryDuration(duration time.Duration) ExecutorOption {
|
|
||||||
return &cacheExpiryDurationOption{duration: duration}
|
|
||||||
}
|
|
||||||
|
|
||||||
type cacheExpiryDurationOption struct {
|
|
||||||
duration time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *cacheExpiryDurationOption) ApplyToExecutor(r *Executor) {
|
|
||||||
r.CacheExpiryDuration = o.duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithWatch tells the [Executor] to keep running in the background and watch
|
// WithWatch tells the [Executor] to keep running in the background and watch
|
||||||
// for changes to the fingerprint of the tasks that are run. When changes are
|
// for changes to the fingerprint of the tasks that are run. When changes are
|
||||||
// detected, a new task run is triggered.
|
// detected, a new task run is triggered.
|
||||||
|
|||||||
@@ -4,18 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/sajari/fuzzy"
|
"github.com/sajari/fuzzy"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/env"
|
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/output"
|
"github.com/go-task/task/v3/internal/output"
|
||||||
"github.com/go-task/task/v3/internal/version"
|
"github.com/go-task/task/v3/internal/version"
|
||||||
@@ -23,18 +18,8 @@ import (
|
|||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *Executor) Setup() error {
|
func (e *Executor) setup() error {
|
||||||
e.setupLogger()
|
e.setupLogger()
|
||||||
node, err := e.getRootNode()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := e.setupTempDir(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := e.readTaskfile(node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.setupFuzzyModel()
|
e.setupFuzzyModel()
|
||||||
e.setupStdFiles()
|
e.setupStdFiles()
|
||||||
if err := e.setupOutput(); err != nil {
|
if err := e.setupOutput(); err != nil {
|
||||||
@@ -54,46 +39,6 @@ func (e *Executor) Setup() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) getRootNode() (taskfile.Node, error) {
|
|
||||||
node, err := taskfile.NewRootNode(e.Entrypoint, e.Dir, e.Insecure, e.Timeout)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
e.Dir = node.Dir()
|
|
||||||
return node, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Executor) readTaskfile(node taskfile.Node) error {
|
|
||||||
ctx, cf := context.WithTimeout(context.Background(), e.Timeout)
|
|
||||||
defer cf()
|
|
||||||
debugFunc := func(s string) {
|
|
||||||
e.Logger.VerboseOutf(logger.Magenta, s)
|
|
||||||
}
|
|
||||||
promptFunc := func(s string) error {
|
|
||||||
return e.Logger.Prompt(logger.Yellow, s, "n", "y", "yes")
|
|
||||||
}
|
|
||||||
reader := taskfile.NewReader(
|
|
||||||
taskfile.WithInsecure(e.Insecure),
|
|
||||||
taskfile.WithDownload(e.Download),
|
|
||||||
taskfile.WithOffline(e.Offline),
|
|
||||||
taskfile.WithTempDir(e.TempDir.Remote),
|
|
||||||
taskfile.WithCacheExpiryDuration(e.CacheExpiryDuration),
|
|
||||||
taskfile.WithDebugFunc(debugFunc),
|
|
||||||
taskfile.WithPromptFunc(promptFunc),
|
|
||||||
)
|
|
||||||
graph, err := reader.Read(ctx, node)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, context.DeadlineExceeded) {
|
|
||||||
return &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: e.Timeout}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if e.Taskfile, err = graph.Merge(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Executor) setupFuzzyModel() {
|
func (e *Executor) setupFuzzyModel() {
|
||||||
if e.Taskfile == nil {
|
if e.Taskfile == nil {
|
||||||
return
|
return
|
||||||
@@ -115,52 +60,6 @@ func (e *Executor) setupFuzzyModel() {
|
|||||||
e.fuzzyModel = model
|
e.fuzzyModel = model
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) setupTempDir() error {
|
|
||||||
if e.TempDir != (TempDir{}) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tempDir := env.GetTaskEnv("TEMP_DIR")
|
|
||||||
if tempDir == "" {
|
|
||||||
e.TempDir = TempDir{
|
|
||||||
Remote: filepathext.SmartJoin(e.Dir, ".task"),
|
|
||||||
Fingerprint: filepathext.SmartJoin(e.Dir, ".task"),
|
|
||||||
}
|
|
||||||
} else if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") {
|
|
||||||
tempDir, err := execext.ExpandLiteral(tempDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
projectDir, _ := filepath.Abs(e.Dir)
|
|
||||||
projectName := filepath.Base(projectDir)
|
|
||||||
e.TempDir = TempDir{
|
|
||||||
Remote: tempDir,
|
|
||||||
Fingerprint: filepathext.SmartJoin(tempDir, projectName),
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
e.TempDir = TempDir{
|
|
||||||
Remote: filepathext.SmartJoin(e.Dir, tempDir),
|
|
||||||
Fingerprint: filepathext.SmartJoin(e.Dir, tempDir),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteDir := env.GetTaskEnv("REMOTE_DIR")
|
|
||||||
if remoteDir != "" {
|
|
||||||
if filepath.IsAbs(remoteDir) || strings.HasPrefix(remoteDir, "~") {
|
|
||||||
remoteTempDir, err := execext.ExpandLiteral(remoteDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.TempDir.Remote = remoteTempDir
|
|
||||||
} else {
|
|
||||||
e.TempDir.Remote = filepathext.SmartJoin(e.Dir, ".task")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Executor) setupStdFiles() {
|
func (e *Executor) setupStdFiles() {
|
||||||
if e.Stdin == nil {
|
if e.Stdin == nil {
|
||||||
e.Stdin = os.Stdin
|
e.Stdin = os.Stdin
|
||||||
@@ -206,7 +105,7 @@ func (e *Executor) setupCompiler() error {
|
|||||||
|
|
||||||
e.Compiler = &Compiler{
|
e.Compiler = &Compiler{
|
||||||
Dir: e.Dir,
|
Dir: e.Dir,
|
||||||
Entrypoint: e.Entrypoint,
|
Entrypoint: e.Taskfile.Location,
|
||||||
UserWorkingDir: e.UserWorkingDir,
|
UserWorkingDir: e.UserWorkingDir,
|
||||||
TaskfileEnv: e.Taskfile.Env,
|
TaskfileEnv: e.Taskfile.Env,
|
||||||
TaskfileVars: e.Taskfile.Vars,
|
TaskfileVars: e.Taskfile.Vars,
|
||||||
314
executor_test.go
314
executor_test.go
@@ -3,9 +3,11 @@ package task_test
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmp"
|
"cmp"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sebdah/goldie/v2"
|
"github.com/sebdah/goldie/v2"
|
||||||
@@ -14,6 +16,7 @@ import (
|
|||||||
"github.com/go-task/task/v3"
|
"github.com/go-task/task/v3"
|
||||||
"github.com/go-task/task/v3/experiments"
|
"github.com/go-task/task/v3/experiments"
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,7 +36,12 @@ type (
|
|||||||
task string
|
task string
|
||||||
vars map[string]any
|
vars map[string]any
|
||||||
input string
|
input string
|
||||||
|
nodeDir string
|
||||||
|
nodeEntrypoint string
|
||||||
|
nodeInsecure bool
|
||||||
|
readerOpts []taskfile.ReaderOption
|
||||||
executorOpts []task.ExecutorOption
|
executorOpts []task.ExecutorOption
|
||||||
|
wantReaderError bool
|
||||||
wantSetupError bool
|
wantSetupError bool
|
||||||
wantRunError bool
|
wantRunError bool
|
||||||
wantStatusError bool
|
wantStatusError bool
|
||||||
@@ -46,8 +54,9 @@ type (
|
|||||||
func NewExecutorTest(t *testing.T, opts ...ExecutorTestOption) {
|
func NewExecutorTest(t *testing.T, opts ...ExecutorTestOption) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tt := &ExecutorTest{
|
tt := &ExecutorTest{
|
||||||
task: "default",
|
task: "default",
|
||||||
vars: map[string]any{},
|
vars: map[string]any{},
|
||||||
|
nodeDir: ".",
|
||||||
TaskTest: TaskTest{
|
TaskTest: TaskTest{
|
||||||
experiments: map[*experiments.Experiment]int{},
|
experiments: map[*experiments.Experiment]int{},
|
||||||
fixtureTemplateData: map[string]any{},
|
fixtureTemplateData: map[string]any{},
|
||||||
@@ -144,11 +153,52 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
f := func(t *testing.T) {
|
f := func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := append(
|
// Create a new root node for the given entrypoint
|
||||||
|
node, err := taskfile.NewRootNode(
|
||||||
|
tt.nodeEntrypoint,
|
||||||
|
tt.nodeDir,
|
||||||
|
tt.nodeInsecure,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create a golden fixture file for the output
|
||||||
|
g := goldie.New(t,
|
||||||
|
goldie.WithFixtureDir(filepath.Join(node.Dir(), "testdata")),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set up a temporary directory for the taskfile reader and task executor
|
||||||
|
tempDir, err := task.NewTempDir(node.Dir())
|
||||||
|
require.NoError(t, err)
|
||||||
|
tt.readerOpts = append(tt.readerOpts, taskfile.WithTempDir(tempDir.Remote))
|
||||||
|
|
||||||
|
// Set up the taskfile reader
|
||||||
|
reader := taskfile.NewReader(tt.readerOpts...)
|
||||||
|
graph, err := reader.Read(ctx, node)
|
||||||
|
if tt.wantReaderError {
|
||||||
|
require.Error(t, err)
|
||||||
|
tt.writeFixtureErrReader(t, g, err)
|
||||||
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
executorOpts := slices.Concat(
|
||||||
|
// Apply the node directory and temp directory to the executor options
|
||||||
|
// by default, but allow them to by overridden by the test options
|
||||||
|
[]task.ExecutorOption{
|
||||||
|
task.WithDir(node.Dir()),
|
||||||
|
task.WithTempDir(tempDir),
|
||||||
|
},
|
||||||
|
// Apply the executor options from the test
|
||||||
tt.executorOpts,
|
tt.executorOpts,
|
||||||
task.WithStdout(&buf),
|
// Force the input/output streams to be set to the test buffer
|
||||||
task.WithStderr(&buf),
|
[]task.ExecutorOption{
|
||||||
|
task.WithStdout(&buf),
|
||||||
|
task.WithStderr(&buf),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// If the test has input, create a reader for it and add it to the
|
// If the test has input, create a reader for it and add it to the
|
||||||
@@ -156,19 +206,12 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
if tt.input != "" {
|
if tt.input != "" {
|
||||||
var reader bytes.Buffer
|
var reader bytes.Buffer
|
||||||
reader.WriteString(tt.input)
|
reader.WriteString(tt.input)
|
||||||
opts = append(opts, task.WithStdin(&reader))
|
executorOpts = append(executorOpts, task.WithStdin(&reader))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the task executor
|
// Set up the task executor
|
||||||
e := task.NewExecutor(opts...)
|
executor, err := task.NewExecutor(graph, executorOpts...)
|
||||||
|
if tt.wantSetupError {
|
||||||
// Create a golden fixture file for the output
|
|
||||||
g := goldie.New(t,
|
|
||||||
goldie.WithFixtureDir(filepath.Join(e.Dir, "testdata")),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Call setup and check for errors
|
|
||||||
if err := e.Setup(); tt.wantSetupError {
|
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrSetup(t, g, err)
|
tt.writeFixtureErrSetup(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
@@ -188,8 +231,7 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run the task and check for errors
|
// Run the task and check for errors
|
||||||
ctx := t.Context()
|
if err := executor.Run(ctx, call); tt.wantRunError {
|
||||||
if err := e.Run(ctx, call); tt.wantRunError {
|
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrRun(t, g, err)
|
tt.writeFixtureErrRun(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
@@ -200,7 +242,7 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
|
|
||||||
// If the status flag is set, run the status check
|
// If the status flag is set, run the status check
|
||||||
if tt.wantStatusError {
|
if tt.wantStatusError {
|
||||||
if err := e.Status(ctx, call); err != nil {
|
if err := executor.Status(ctx, call); err != nil {
|
||||||
tt.writeFixtureStatus(t, g, err.Error())
|
tt.writeFixtureStatus(t, g, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,19 +261,16 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
func TestEmptyTask(t *testing.T) {
|
func TestEmptyTask(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/empty_task"),
|
||||||
task.WithDir("testdata/empty_task"),
|
WithExecutorOptions(),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyTaskfile(t *testing.T) {
|
func TestEmptyTaskfile(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/empty_taskfile"),
|
||||||
task.WithDir("testdata/empty_taskfile"),
|
WithReaderError(),
|
||||||
),
|
|
||||||
WithSetupError(),
|
|
||||||
WithFixtureTemplating(),
|
WithFixtureTemplating(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -240,15 +279,15 @@ func TestEnv(t *testing.T) {
|
|||||||
t.Setenv("QUX", "from_os")
|
t.Setenv("QUX", "from_os")
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("env precedence disabled"),
|
WithName("env precedence disabled"),
|
||||||
|
WithNodeDir("testdata/env"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/env"),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("env precedence enabled"),
|
WithName("env precedence enabled"),
|
||||||
|
WithNodeDir("testdata/env"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/env"),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithExperiment(&experiments.EnvPrecedence, 1),
|
WithExperiment(&experiments.EnvPrecedence, 1),
|
||||||
@@ -258,8 +297,8 @@ func TestEnv(t *testing.T) {
|
|||||||
func TestVars(t *testing.T) {
|
func TestVars(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
|
WithNodeDir("testdata/vars"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/vars"),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -269,25 +308,19 @@ func TestRequires(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var missing"),
|
WithName("required var missing"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/requires"),
|
||||||
task.WithDir("testdata/requires"),
|
|
||||||
),
|
|
||||||
WithTask("missing-var"),
|
WithTask("missing-var"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var ok"),
|
WithName("required var ok"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/requires"),
|
||||||
task.WithDir("testdata/requires"),
|
|
||||||
),
|
|
||||||
WithTask("missing-var"),
|
WithTask("missing-var"),
|
||||||
WithVar("FOO", "bar"),
|
WithVar("FOO", "bar"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("fails validation"),
|
WithName("fails validation"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/requires"),
|
||||||
task.WithDir("testdata/requires"),
|
|
||||||
),
|
|
||||||
WithTask("validation-var"),
|
WithTask("validation-var"),
|
||||||
WithVar("ENV", "dev"),
|
WithVar("ENV", "dev"),
|
||||||
WithVar("FOO", "bar"),
|
WithVar("FOO", "bar"),
|
||||||
@@ -295,48 +328,37 @@ func TestRequires(t *testing.T) {
|
|||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("passes validation"),
|
WithName("passes validation"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/requires"),
|
||||||
task.WithDir("testdata/requires"),
|
|
||||||
),
|
|
||||||
WithTask("validation-var"),
|
WithTask("validation-var"),
|
||||||
WithVar("FOO", "one"),
|
WithVar("FOO", "one"),
|
||||||
WithVar("ENV", "dev"),
|
WithVar("ENV", "dev"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var missing + fails validation"),
|
WithName("required var missing + fails validation"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/requires"),
|
||||||
task.WithDir("testdata/requires"),
|
|
||||||
),
|
|
||||||
WithTask("validation-var"),
|
WithTask("validation-var"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var missing + fails validation"),
|
WithName("required var missing + fails validation"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/requires"),
|
||||||
task.WithDir("testdata/requires"),
|
|
||||||
),
|
|
||||||
WithTask("validation-var-dynamic"),
|
WithTask("validation-var-dynamic"),
|
||||||
WithVar("FOO", "one"),
|
WithVar("FOO", "one"),
|
||||||
WithVar("ENV", "dev"),
|
WithVar("ENV", "dev"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("require before compile"),
|
WithName("require before compile"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/requires"),
|
||||||
task.WithDir("testdata/requires"),
|
|
||||||
),
|
|
||||||
WithTask("require-before-compile"),
|
WithTask("require-before-compile"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("var defined in task"),
|
WithName("var defined in task"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/requires"),
|
||||||
task.WithDir("testdata/requires"),
|
|
||||||
),
|
|
||||||
WithTask("var-defined-in-task"),
|
WithTask("var-defined-in-task"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: mock fs
|
|
||||||
func TestSpecialVars(t *testing.T) {
|
func TestSpecialVars(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -357,12 +379,13 @@ func TestSpecialVars(t *testing.T) {
|
|||||||
"included:print-taskfile-dir",
|
"included:print-taskfile-dir",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, dir := range []string{dir, subdir} {
|
for _, executorDir := range []string{dir, subdir} {
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
name := fmt.Sprintf("%s-%s", executorDir, test)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName(fmt.Sprintf("%s-%s", dir, test)),
|
WithName(name),
|
||||||
|
WithNodeDir(executorDir),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir(dir),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithVersionCheck(true),
|
task.WithVersionCheck(true),
|
||||||
),
|
),
|
||||||
@@ -376,8 +399,8 @@ func TestSpecialVars(t *testing.T) {
|
|||||||
func TestConcurrency(t *testing.T) {
|
func TestConcurrency(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
|
WithNodeDir("testdata/concurrency"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/concurrency"),
|
|
||||||
task.WithConcurrency(1),
|
task.WithConcurrency(1),
|
||||||
),
|
),
|
||||||
WithPostProcessFn(PPSortedLines),
|
WithPostProcessFn(PPSortedLines),
|
||||||
@@ -387,8 +410,8 @@ func TestConcurrency(t *testing.T) {
|
|||||||
func TestParams(t *testing.T) {
|
func TestParams(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
|
WithNodeDir("testdata/params"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/params"),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithPostProcessFn(PPSortedLines),
|
WithPostProcessFn(PPSortedLines),
|
||||||
@@ -398,15 +421,14 @@ func TestParams(t *testing.T) {
|
|||||||
func TestDeps(t *testing.T) {
|
func TestDeps(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
|
WithNodeDir("testdata/deps"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/deps"),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithPostProcessFn(PPSortedLines),
|
WithPostProcessFn(PPSortedLines),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: mock fs
|
|
||||||
func TestStatus(t *testing.T) {
|
func TestStatus(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -429,8 +451,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// gen-foo creates foo.txt, and will always fail it's status check.
|
// gen-foo creates foo.txt, and will always fail it's status check.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-foo 1 silent"),
|
WithName("run gen-foo 1 silent"),
|
||||||
|
WithNodeDir(dir),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir(dir),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-foo"),
|
WithTask("gen-foo"),
|
||||||
@@ -441,8 +463,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// only exists after the first run.
|
// only exists after the first run.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 1 silent"),
|
WithName("run gen-bar 1 silent"),
|
||||||
|
WithNodeDir(dir),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir(dir),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -451,8 +473,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// if e.Verbose is set to true.
|
// if e.Verbose is set to true.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz silent"),
|
WithName("run gen-baz silent"),
|
||||||
|
WithNodeDir(dir),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir(dir),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
@@ -467,8 +489,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// Run gen-bar a second time to produce a checksum file that matches bar.txt
|
// Run gen-bar a second time to produce a checksum file that matches bar.txt
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 2 silent"),
|
WithName("run gen-bar 2 silent"),
|
||||||
|
WithNodeDir(dir),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir(dir),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -476,8 +498,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// Run gen-bar a third time, to make sure we've triggered the status check.
|
// Run gen-bar a third time, to make sure we've triggered the status check.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 3 silent"),
|
WithName("run gen-bar 3 silent"),
|
||||||
|
WithNodeDir(dir),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir(dir),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -489,8 +511,8 @@ func TestStatus(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 4 silent"),
|
WithName("run gen-bar 4 silent"),
|
||||||
|
WithNodeDir(dir),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir(dir),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -498,56 +520,44 @@ func TestStatus(t *testing.T) {
|
|||||||
// all: not up-to-date
|
// all: not up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-foo 2"),
|
WithName("run gen-foo 2"),
|
||||||
WithExecutorOptions(
|
WithNodeDir(dir),
|
||||||
task.WithDir(dir),
|
|
||||||
),
|
|
||||||
WithTask("gen-foo"),
|
WithTask("gen-foo"),
|
||||||
)
|
)
|
||||||
// status: not up-to-date
|
// status: not up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-foo 3"),
|
WithName("run gen-foo 3"),
|
||||||
WithExecutorOptions(
|
WithNodeDir(dir),
|
||||||
task.WithDir(dir),
|
|
||||||
),
|
|
||||||
WithTask("gen-foo"),
|
WithTask("gen-foo"),
|
||||||
)
|
)
|
||||||
// sources: not up-to-date
|
// sources: not up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 5"),
|
WithName("run gen-bar 5"),
|
||||||
WithExecutorOptions(
|
WithNodeDir(dir),
|
||||||
task.WithDir(dir),
|
|
||||||
),
|
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
)
|
)
|
||||||
// all: up-to-date
|
// all: up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 6"),
|
WithName("run gen-bar 6"),
|
||||||
WithExecutorOptions(
|
WithNodeDir(dir),
|
||||||
task.WithDir(dir),
|
|
||||||
),
|
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
)
|
)
|
||||||
// sources: not up-to-date, no output produced.
|
// sources: not up-to-date, no output produced.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz 2"),
|
WithName("run gen-baz 2"),
|
||||||
WithExecutorOptions(
|
WithNodeDir(dir),
|
||||||
task.WithDir(dir),
|
|
||||||
),
|
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
)
|
)
|
||||||
// up-to-date, no output produced
|
// up-to-date, no output produced
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz 3"),
|
WithName("run gen-baz 3"),
|
||||||
WithExecutorOptions(
|
WithNodeDir(dir),
|
||||||
task.WithDir(dir),
|
|
||||||
),
|
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
)
|
)
|
||||||
// up-to-date, output produced due to Verbose mode.
|
// up-to-date, output produced due to Verbose mode.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz 4 verbose"),
|
WithName("run gen-baz 4 verbose"),
|
||||||
|
WithNodeDir(dir),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir(dir),
|
|
||||||
task.WithVerbose(true),
|
task.WithVerbose(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
@@ -560,32 +570,24 @@ func TestPrecondition(t *testing.T) {
|
|||||||
const dir = "testdata/precondition"
|
const dir = "testdata/precondition"
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("a precondition has been met"),
|
WithName("a precondition has been met"),
|
||||||
WithExecutorOptions(
|
WithNodeDir(dir),
|
||||||
task.WithDir(dir),
|
|
||||||
),
|
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("a precondition was not met"),
|
WithName("a precondition was not met"),
|
||||||
WithExecutorOptions(
|
WithNodeDir(dir),
|
||||||
task.WithDir(dir),
|
|
||||||
),
|
|
||||||
WithTask("impossible"),
|
WithTask("impossible"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("precondition in dependency fails the task"),
|
WithName("precondition in dependency fails the task"),
|
||||||
WithExecutorOptions(
|
WithNodeDir(dir),
|
||||||
task.WithDir(dir),
|
|
||||||
),
|
|
||||||
WithTask("depends_on_impossible"),
|
WithTask("depends_on_impossible"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("precondition in cmd fails the task"),
|
WithName("precondition in cmd fails the task"),
|
||||||
WithExecutorOptions(
|
WithNodeDir(dir),
|
||||||
task.WithDir(dir),
|
|
||||||
),
|
|
||||||
WithTask("executes_failing_task_as_cmd"),
|
WithTask("executes_failing_task_as_cmd"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
@@ -596,25 +598,21 @@ func TestAlias(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("alias"),
|
WithName("alias"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/alias"),
|
||||||
task.WithDir("testdata/alias"),
|
|
||||||
),
|
|
||||||
WithTask("f"),
|
WithTask("f"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("duplicate alias"),
|
WithName("duplicate alias"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/alias"),
|
||||||
task.WithDir("testdata/alias"),
|
|
||||||
),
|
|
||||||
WithTask("x"),
|
WithTask("x"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("alias summary"),
|
WithName("alias summary"),
|
||||||
|
WithNodeDir("testdata/alias"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/alias"),
|
|
||||||
task.WithSummary(true),
|
task.WithSummary(true),
|
||||||
),
|
),
|
||||||
WithTask("f"),
|
WithTask("f"),
|
||||||
@@ -626,16 +624,14 @@ func TestLabel(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("up to date"),
|
WithName("up to date"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/label_uptodate"),
|
||||||
task.WithDir("testdata/label_uptodate"),
|
|
||||||
),
|
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("summary"),
|
WithName("summary"),
|
||||||
|
WithNodeDir("testdata/label_summary"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/label_summary"),
|
|
||||||
task.WithSummary(true),
|
task.WithSummary(true),
|
||||||
),
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
@@ -643,26 +639,20 @@ func TestLabel(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("status"),
|
WithName("status"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/label_status"),
|
||||||
task.WithDir("testdata/label_status"),
|
|
||||||
),
|
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
WithStatusError(),
|
WithStatusError(),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("var"),
|
WithName("var"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/label_var"),
|
||||||
task.WithDir("testdata/label_var"),
|
|
||||||
),
|
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("label in summary"),
|
WithName("label in summary"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/label_summary"),
|
||||||
task.WithDir("testdata/label_summary"),
|
|
||||||
),
|
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -689,8 +679,8 @@ func TestPromptInSummary(t *testing.T) {
|
|||||||
|
|
||||||
opts := []ExecutorTestOption{
|
opts := []ExecutorTestOption{
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
|
WithNodeDir("testdata/prompt"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/prompt"),
|
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
),
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
@@ -708,8 +698,8 @@ func TestPromptWithIndirectTask(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
|
WithNodeDir("testdata/prompt"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/prompt"),
|
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
),
|
),
|
||||||
WithTask("bar"),
|
WithTask("bar"),
|
||||||
@@ -722,8 +712,8 @@ func TestPromptAssumeYes(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("--yes flag should skip prompt"),
|
WithName("--yes flag should skip prompt"),
|
||||||
|
WithNodeDir("testdata/prompt"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/prompt"),
|
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
task.WithAssumeYes(true),
|
task.WithAssumeYes(true),
|
||||||
),
|
),
|
||||||
@@ -733,8 +723,8 @@ func TestPromptAssumeYes(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("task should raise errors.TaskCancelledError"),
|
WithName("task should raise errors.TaskCancelledError"),
|
||||||
|
WithNodeDir("testdata/prompt"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/prompt"),
|
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
),
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
@@ -771,8 +761,8 @@ func TestForCmds(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
opts := []ExecutorTestOption{
|
opts := []ExecutorTestOption{
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
|
WithNodeDir("testdata/for/cmds"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/for/cmds"),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
),
|
),
|
||||||
@@ -814,8 +804,8 @@ func TestForDeps(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
opts := []ExecutorTestOption{
|
opts := []ExecutorTestOption{
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
|
WithNodeDir("testdata/for/deps"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/for/deps"),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
// Force output of each dep to be grouped together to prevent interleaving
|
// Force output of each dep to be grouped together to prevent interleaving
|
||||||
@@ -860,8 +850,8 @@ func TestReference(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
|
WithNodeDir("testdata/var_references"),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir("testdata/var_references"),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
),
|
),
|
||||||
@@ -928,8 +918,8 @@ func TestVarInheritance(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
|
WithNodeDir(fmt.Sprintf("testdata/var_inheritance/v3/%s", test.name)),
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
task.WithDir(fmt.Sprintf("testdata/var_inheritance/v3/%s", test.name)),
|
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
),
|
),
|
||||||
@@ -943,26 +933,20 @@ func TestFuzzyModel(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("fuzzy"),
|
WithName("fuzzy"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/fuzzy"),
|
||||||
task.WithDir("testdata/fuzzy"),
|
|
||||||
),
|
|
||||||
WithTask("instal"),
|
WithTask("instal"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("not-fuzzy"),
|
WithName("not-fuzzy"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/fuzzy"),
|
||||||
task.WithDir("testdata/fuzzy"),
|
|
||||||
),
|
|
||||||
WithTask("install"),
|
WithTask("install"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("intern"),
|
WithName("intern"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/fuzzy"),
|
||||||
task.WithDir("testdata/fuzzy"),
|
|
||||||
),
|
|
||||||
WithTask("intern"),
|
WithTask("intern"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
@@ -973,17 +957,65 @@ func TestIncludeChecksum(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("correct"),
|
WithName("correct"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/includes_checksum/correct"),
|
||||||
task.WithDir("testdata/includes_checksum/correct"),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("incorrect"),
|
WithName("incorrect"),
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/includes_checksum/incorrect"),
|
||||||
task.WithDir("testdata/includes_checksum/incorrect"),
|
WithReaderError(),
|
||||||
),
|
|
||||||
WithSetupError(),
|
|
||||||
WithFixtureTemplating(),
|
WithFixtureTemplating(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWildcard(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
call string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basic wildcard",
|
||||||
|
call: "wildcard-foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "double wildcard",
|
||||||
|
call: "foo-wildcard-bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "store wildcard",
|
||||||
|
call: "start-foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "matches exactly",
|
||||||
|
call: "matches-exactly-*",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no matches",
|
||||||
|
call: "no-match",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple matches",
|
||||||
|
call: "wildcard-foo-bar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
opts := []ExecutorTestOption{
|
||||||
|
WithName(test.name),
|
||||||
|
WithNodeDir("testdata/wildcards"),
|
||||||
|
WithExecutorOptions(
|
||||||
|
task.WithSilent(true),
|
||||||
|
task.WithForce(true),
|
||||||
|
),
|
||||||
|
WithTask(test.call),
|
||||||
|
}
|
||||||
|
if test.wantErr {
|
||||||
|
opts = append(opts, WithRunError())
|
||||||
|
}
|
||||||
|
NewExecutorTest(t, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/taskrc"
|
"github.com/go-task/task/v3/taskrc"
|
||||||
"github.com/go-task/task/v3/taskrc/ast"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const envPrefix = "TASK_X_"
|
const envPrefix = "TASK_X_"
|
||||||
@@ -32,15 +31,16 @@ var (
|
|||||||
var xList []Experiment
|
var xList []Experiment
|
||||||
|
|
||||||
func Parse(dir string) {
|
func Parse(dir string) {
|
||||||
config, _ := taskrc.GetConfig(dir)
|
|
||||||
|
|
||||||
ParseWithConfig(dir, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseWithConfig(dir string, config *ast.TaskRC) {
|
|
||||||
// Read any .env files
|
// Read any .env files
|
||||||
readDotEnv(dir)
|
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
|
// Initialize the experiments
|
||||||
GentleForce = New("GENTLE_FORCE", config, 1)
|
GentleForce = New("GENTLE_FORCE", config, 1)
|
||||||
RemoteTaskfiles = New("REMOTE_TASKFILES", config, 1)
|
RemoteTaskfiles = New("REMOTE_TASKFILES", config, 1)
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package task_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sebdah/goldie/v2"
|
"github.com/sebdah/goldie/v2"
|
||||||
@@ -10,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/go-task/task/v3"
|
"github.com/go-task/task/v3"
|
||||||
"github.com/go-task/task/v3/experiments"
|
"github.com/go-task/task/v3/experiments"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,12 +29,17 @@ type (
|
|||||||
// running `task gen:fixtures`.
|
// running `task gen:fixtures`.
|
||||||
FormatterTest struct {
|
FormatterTest struct {
|
||||||
TaskTest
|
TaskTest
|
||||||
task string
|
task string
|
||||||
vars map[string]any
|
vars map[string]any
|
||||||
executorOpts []task.ExecutorOption
|
nodeDir string
|
||||||
listOptions task.ListOptions
|
nodeEntrypoint string
|
||||||
wantSetupError bool
|
nodeInsecure bool
|
||||||
wantListError bool
|
readerOpts []taskfile.ReaderOption
|
||||||
|
executorOpts []task.ExecutorOption
|
||||||
|
listOptions task.ListOptions
|
||||||
|
wantReaderError bool
|
||||||
|
wantSetupError bool
|
||||||
|
wantListError bool
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,8 +49,9 @@ type (
|
|||||||
func NewFormatterTest(t *testing.T, opts ...FormatterTestOption) {
|
func NewFormatterTest(t *testing.T, opts ...FormatterTestOption) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tt := &FormatterTest{
|
tt := &FormatterTest{
|
||||||
task: "default",
|
task: "default",
|
||||||
vars: map[string]any{},
|
vars: map[string]any{},
|
||||||
|
nodeDir: ".",
|
||||||
TaskTest: TaskTest{
|
TaskTest: TaskTest{
|
||||||
experiments: map[*experiments.Experiment]int{},
|
experiments: map[*experiments.Experiment]int{},
|
||||||
fixtureTemplateData: map[string]any{},
|
fixtureTemplateData: map[string]any{},
|
||||||
@@ -114,23 +123,57 @@ func (tt *FormatterTest) run(t *testing.T) {
|
|||||||
f := func(t *testing.T) {
|
f := func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
opts := append(
|
// Create a new root node for the given entrypoint
|
||||||
tt.executorOpts,
|
node, err := taskfile.NewRootNode(
|
||||||
task.WithStdout(&buf),
|
tt.nodeEntrypoint,
|
||||||
task.WithStderr(&buf),
|
tt.nodeDir,
|
||||||
|
tt.nodeInsecure,
|
||||||
)
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
// Set up the task executor
|
|
||||||
e := task.NewExecutor(opts...)
|
|
||||||
|
|
||||||
// Create a golden fixture file for the output
|
// Create a golden fixture file for the output
|
||||||
g := goldie.New(t,
|
g := goldie.New(t,
|
||||||
goldie.WithFixtureDir(filepath.Join(e.Dir, "testdata")),
|
goldie.WithFixtureDir(filepath.Join(node.Dir(), "testdata")),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Call setup and check for errors
|
// Set up a temporary directory for the taskfile reader and task executor
|
||||||
if err := e.Setup(); tt.wantSetupError {
|
tempDir, err := task.NewTempDir(node.Dir())
|
||||||
|
require.NoError(t, err)
|
||||||
|
tt.readerOpts = append(tt.readerOpts, taskfile.WithTempDir(tempDir.Remote))
|
||||||
|
|
||||||
|
// Set up the taskfile reader
|
||||||
|
reader := taskfile.NewReader(tt.readerOpts...)
|
||||||
|
graph, err := reader.Read(ctx, node)
|
||||||
|
if tt.wantReaderError {
|
||||||
|
require.Error(t, err)
|
||||||
|
tt.writeFixtureErrReader(t, g, err)
|
||||||
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
executorOpts := slices.Concat(
|
||||||
|
// Apply the node directory and temp directory to the executor options
|
||||||
|
// by default, but allow them to by overridden by the test options
|
||||||
|
[]task.ExecutorOption{
|
||||||
|
task.WithDir(node.Dir()),
|
||||||
|
task.WithTempDir(tempDir),
|
||||||
|
},
|
||||||
|
// Apply the executor options from the test
|
||||||
|
tt.executorOpts,
|
||||||
|
// Force the input/output streams to be set to the test buffer
|
||||||
|
[]task.ExecutorOption{
|
||||||
|
task.WithStdout(&buf),
|
||||||
|
task.WithStderr(&buf),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set up the task executor
|
||||||
|
executor, err := task.NewExecutor(graph, executorOpts...)
|
||||||
|
if tt.wantSetupError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrSetup(t, g, err)
|
tt.writeFixtureErrSetup(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
@@ -146,7 +189,7 @@ func (tt *FormatterTest) run(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run the formatter and check for errors
|
// Run the formatter and check for errors
|
||||||
if _, err := e.ListTasks(tt.listOptions); tt.wantListError {
|
if _, err := executor.ListTasks(tt.listOptions); tt.wantListError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrList(t, g, err)
|
tt.writeFixtureErrList(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
@@ -170,9 +213,7 @@ func TestNoLabelInList(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/label_list"),
|
||||||
task.WithDir("testdata/label_list"),
|
|
||||||
),
|
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: true,
|
ListOnlyTasksWithDescriptions: true,
|
||||||
}),
|
}),
|
||||||
@@ -184,9 +225,7 @@ func TestListAllShowsNoDesc(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/list_mixed_desc"),
|
||||||
task.WithDir("testdata/list_mixed_desc"),
|
|
||||||
),
|
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListAllTasks: true,
|
ListAllTasks: true,
|
||||||
}),
|
}),
|
||||||
@@ -198,9 +237,7 @@ func TestListCanListDescOnly(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/list_mixed_desc"),
|
||||||
task.WithDir("testdata/list_mixed_desc"),
|
|
||||||
),
|
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: true,
|
ListOnlyTasksWithDescriptions: true,
|
||||||
}),
|
}),
|
||||||
@@ -211,9 +248,7 @@ func TestListDescInterpolation(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/list_desc_interpolation"),
|
||||||
task.WithDir("testdata/list_desc_interpolation"),
|
|
||||||
),
|
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: true,
|
ListOnlyTasksWithDescriptions: true,
|
||||||
}),
|
}),
|
||||||
@@ -224,9 +259,7 @@ func TestJsonListFormat(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithExecutorOptions(
|
WithNodeDir("testdata/json_list_format"),
|
||||||
task.WithDir("testdata/json_list_format"),
|
|
||||||
),
|
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
FormatTaskListAsJSON: true,
|
FormatTaskListAsJSON: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
21
go.mod
21
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/go-task/task/v3
|
module github.com/go-task/task/v3
|
||||||
|
|
||||||
go 1.24.0
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Ladicle/tabwriter v1.0.0
|
github.com/Ladicle/tabwriter v1.0.0
|
||||||
@@ -19,16 +19,16 @@ require (
|
|||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
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/puzpuzpuz/xsync/v3 v3.5.1
|
||||||
github.com/sajari/fuzzy v1.0.0
|
github.com/sajari/fuzzy v1.0.0
|
||||||
github.com/sebdah/goldie/v2 v2.7.1
|
github.com/sebdah/goldie/v2 v2.7.1
|
||||||
github.com/spf13/pflag v1.0.10
|
github.com/spf13/pflag v1.0.7
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/zeebo/xxh3 v1.0.2
|
github.com/zeebo/xxh3 v1.0.2
|
||||||
golang.org/x/sync v0.17.0
|
golang.org/x/sync v0.16.0
|
||||||
golang.org/x/term v0.35.0
|
golang.org/x/term v0.33.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
mvdan.cc/sh/moreinterp v0.0.0-20250807215248-5a1a658912aa
|
|
||||||
mvdan.cc/sh/v3 v3.12.0
|
mvdan.cc/sh/v3 v3.12.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,28 +39,23 @@ require (
|
|||||||
github.com/cloudflare/circl v1.6.1 // indirect
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||||
github.com/dlclark/regexp2 v1.11.5 // 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/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // 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/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // 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/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-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
github.com/otiai10/mint v1.6.3 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // 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
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
golang.org/x/crypto v0.37.0 // indirect
|
||||||
golang.org/x/net v0.39.0 // indirect
|
golang.org/x/net v0.39.0 // indirect
|
||||||
golang.org/x/sys v0.36.0 // indirect
|
golang.org/x/sys v0.34.0 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
50
go.sum
50
go.sum
@@ -11,10 +11,12 @@ 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/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 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
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 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw=
|
||||||
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
|
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
|
||||||
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||||
github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
github.com/alecthomas/repr v0.4.0/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 h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
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=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
@@ -34,8 +36,6 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
|
|||||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
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 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
|
||||||
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
|
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 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||||
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
|
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
|
||||||
@@ -76,12 +76,8 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
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 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
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 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
@@ -98,8 +94,10 @@ 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/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 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
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/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -123,22 +121,14 @@ github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnB
|
|||||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
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 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
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.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 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
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 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||||
@@ -148,15 +138,13 @@ 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.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 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
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 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
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 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
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-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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -166,15 +154,11 @@ 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.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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.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.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||||
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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||||
@@ -189,7 +173,5 @@ 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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
|
||||||
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
|
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
|
||||||
|
|||||||
65
help.go
65
help.go
@@ -24,17 +24,15 @@ type ListOptions struct {
|
|||||||
ListAllTasks bool
|
ListAllTasks bool
|
||||||
FormatTaskListAsJSON bool
|
FormatTaskListAsJSON bool
|
||||||
NoStatus bool
|
NoStatus bool
|
||||||
Nested bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewListOptions creates a new ListOptions instance
|
// NewListOptions creates a new ListOptions instance
|
||||||
func NewListOptions(list, listAll, listAsJson, noStatus, nested bool) ListOptions {
|
func NewListOptions(list, listAll, listAsJson, noStatus bool) ListOptions {
|
||||||
return ListOptions{
|
return ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: list,
|
ListOnlyTasksWithDescriptions: list,
|
||||||
ListAllTasks: listAll,
|
ListAllTasks: listAll,
|
||||||
FormatTaskListAsJSON: listAsJson,
|
FormatTaskListAsJSON: listAsJson,
|
||||||
NoStatus: noStatus,
|
NoStatus: noStatus,
|
||||||
Nested: nested,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +63,7 @@ func (e *Executor) ListTasks(o ListOptions) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if o.FormatTaskListAsJSON {
|
if o.FormatTaskListAsJSON {
|
||||||
output, err := e.ToEditorOutput(tasks, o.NoStatus, o.Nested)
|
output, err := e.ToEditorOutput(tasks, o.NoStatus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -137,17 +135,33 @@ func (e *Executor) ListTaskNames(allTasks bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool, nested bool) (*editors.Namespace, error) {
|
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,
|
||||||
|
}
|
||||||
var g errgroup.Group
|
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 {
|
for i := range tasks {
|
||||||
|
aliases := []string{}
|
||||||
|
if len(tasks[i].Aliases) > 0 {
|
||||||
|
aliases = tasks[i].Aliases
|
||||||
|
}
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
editorTask := editors.NewTask(tasks[i])
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
if noStatus {
|
if noStatus {
|
||||||
editorTasks[i] = editorTask
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,35 +180,10 @@ func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool, nested bool)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
editorTask.UpToDate = &upToDate
|
o.Tasks[i].UpToDate = upToDate
|
||||||
editorTasks[i] = editorTask
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if err := g.Wait(); err != nil {
|
return o, g.Wait()
|
||||||
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,15 +1,10 @@
|
|||||||
package editors
|
package editors
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Namespace wraps task list output for use in editor integrations (e.g. VSCode, etc)
|
// Taskfile wraps task list output for use in editor integrations (e.g. VSCode, etc)
|
||||||
Namespace struct {
|
Taskfile struct {
|
||||||
Tasks []Task `json:"tasks"`
|
Tasks []Task `json:"tasks"`
|
||||||
Namespaces map[string]*Namespace `json:"namespaces,omitempty"`
|
Location string `json:"location"`
|
||||||
Location string `json:"location,omitempty"`
|
|
||||||
}
|
}
|
||||||
// Task describes a single task
|
// Task describes a single task
|
||||||
Task struct {
|
Task struct {
|
||||||
@@ -18,7 +13,7 @@ type (
|
|||||||
Desc string `json:"desc"`
|
Desc string `json:"desc"`
|
||||||
Summary string `json:"summary"`
|
Summary string `json:"summary"`
|
||||||
Aliases []string `json:"aliases"`
|
Aliases []string `json:"aliases"`
|
||||||
UpToDate *bool `json:"up_to_date,omitempty"`
|
UpToDate bool `json:"up_to_date"`
|
||||||
Location *Location `json:"location"`
|
Location *Location `json:"location"`
|
||||||
}
|
}
|
||||||
// Location describes a task's location in a taskfile
|
// Location describes a task's location in a taskfile
|
||||||
@@ -28,59 +23,3 @@ type (
|
|||||||
Taskfile string `json:"taskfile"`
|
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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
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"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"mvdan.cc/sh/moreinterp/coreutils"
|
|
||||||
"mvdan.cc/sh/v3/expand"
|
"mvdan.cc/sh/v3/expand"
|
||||||
"mvdan.cc/sh/v3/interp"
|
"mvdan.cc/sh/v3/interp"
|
||||||
"mvdan.cc/sh/v3/syntax"
|
"mvdan.cc/sh/v3/syntax"
|
||||||
@@ -59,7 +59,7 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
|||||||
r, err := interp.New(
|
r, err := interp.New(
|
||||||
interp.Params(params...),
|
interp.Params(params...),
|
||||||
interp.Env(expand.ListEnviron(environ...)),
|
interp.Env(expand.ListEnviron(environ...)),
|
||||||
interp.ExecHandlers(execHandlers()...),
|
interp.ExecHandlers(execHandler),
|
||||||
interp.OpenHandler(openHandler),
|
interp.OpenHandler(openHandler),
|
||||||
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
|
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
|
||||||
dirOption(opts.Dir),
|
dirOption(opts.Dir),
|
||||||
@@ -143,11 +143,8 @@ func ExpandFields(s string) ([]string, error) {
|
|||||||
return expand.Fields(cfg, words...)
|
return expand.Fields(cfg, words...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func execHandlers() (handlers []func(next interp.ExecHandlerFunc) interp.ExecHandlerFunc) {
|
func execHandler(next interp.ExecHandlerFunc) interp.ExecHandlerFunc {
|
||||||
if useGoCoreUtils {
|
return interp.DefaultExecHandler(15 * time.Second)
|
||||||
handlers = append(handlers, coreutils.ExecHandler)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func openHandler(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
func openHandler(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package fingerprint
|
package fingerprint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -163,7 +164,7 @@ func TestIsTaskUpToDate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result, err := IsTaskUpToDate(
|
result, err := IsTaskUpToDate(
|
||||||
t.Context(),
|
context.Background(),
|
||||||
tt.task,
|
tt.task,
|
||||||
WithStatusChecker(mockStatusChecker),
|
WithStatusChecker(mockStatusChecker),
|
||||||
WithSourcesChecker(mockSourcesChecker),
|
WithSourcesChecker(mockSourcesChecker),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
@@ -12,10 +13,10 @@ import (
|
|||||||
"github.com/go-task/task/v3"
|
"github.com/go-task/task/v3"
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/experiments"
|
"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/internal/sort"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"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...]
|
const usage = `Usage: task [flags...] [task...]
|
||||||
@@ -51,7 +52,6 @@ var (
|
|||||||
TaskSort string
|
TaskSort string
|
||||||
Status bool
|
Status bool
|
||||||
NoStatus bool
|
NoStatus bool
|
||||||
Nested bool
|
|
||||||
Insecure bool
|
Insecure bool
|
||||||
Force bool
|
Force bool
|
||||||
ForceAll bool
|
ForceAll bool
|
||||||
@@ -96,9 +96,7 @@ func init() {
|
|||||||
|
|
||||||
// Parse the experiments
|
// Parse the experiments
|
||||||
dir = cmp.Or(dir, filepath.Dir(entrypoint))
|
dir = cmp.Or(dir, filepath.Dir(entrypoint))
|
||||||
|
experiments.Parse(dir)
|
||||||
config, _ := taskrc.GetConfig(dir)
|
|
||||||
experiments.ParseWithConfig(dir, config)
|
|
||||||
|
|
||||||
// Parse the rest of the flags
|
// Parse the rest of the flags
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
@@ -107,7 +105,10 @@ func init() {
|
|||||||
log.Print(usage)
|
log.Print(usage)
|
||||||
pflag.PrintDefaults()
|
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.BoolVar(&Version, "version", false, "Show Task version.")
|
||||||
pflag.BoolVarP(&Help, "help", "h", false, "Shows Task usage.")
|
pflag.BoolVarP(&Help, "help", "h", false, "Shows Task usage.")
|
||||||
pflag.BoolVarP(&Init, "init", "i", false, "Creates a new Taskfile.yml in the current folder.")
|
pflag.BoolVarP(&Init, "init", "i", false, "Creates a new Taskfile.yml in the current folder.")
|
||||||
@@ -118,10 +119,9 @@ func init() {
|
|||||||
pflag.StringVar(&TaskSort, "sort", "", "Changes the order of the tasks when listed. [default|alphanumeric|none].")
|
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(&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(&NoStatus, "no-status", false, "Ignore status when listing tasks as JSON")
|
||||||
pflag.BoolVar(&Nested, "nested", false, "Nest namespaces when listing tasks as JSON")
|
pflag.BoolVar(&Insecure, "insecure", false, "Forces Task to download Taskfiles over insecure connections.")
|
||||||
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(&Watch, "watch", "w", false, "Enables watch of the given task.")
|
||||||
pflag.BoolVarP(&Verbose, "verbose", "v", getConfig(config, func() *bool { return config.Verbose }, false), "Enables verbose mode.")
|
pflag.BoolVarP(&Verbose, "verbose", "v", false, "Enables verbose mode.")
|
||||||
pflag.BoolVarP(&Silent, "silent", "s", false, "Disables echoing.")
|
pflag.BoolVarP(&Silent, "silent", "s", false, "Disables echoing.")
|
||||||
pflag.BoolVarP(&AssumeYes, "yes", "y", false, "Assume \"yes\" as answer to all prompts.")
|
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.")
|
pflag.BoolVarP(&Parallel, "parallel", "p", false, "Executes tasks provided on command line in parallel.")
|
||||||
@@ -135,7 +135,7 @@ func init() {
|
|||||||
pflag.StringVar(&Output.Group.End, "output-group-end", "", "Message template to print after a task's grouped output.")
|
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.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.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", getConfig(config, func() *int { return config.Concurrency }, 0), "Limit number of tasks to run concurrently.")
|
pflag.IntVarP(&Concurrency, "concurrency", "C", 0, "Limit number of tasks to run concurrently.")
|
||||||
pflag.DurationVarP(&Interval, "interval", "I", 0, "Interval to watch for changes.")
|
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.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.")
|
pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.")
|
||||||
@@ -151,11 +151,12 @@ func init() {
|
|||||||
// Remote Taskfiles experiment will adds the "download" and "offline" flags
|
// Remote Taskfiles experiment will adds the "download" and "offline" flags
|
||||||
if experiments.RemoteTaskfiles.Enabled() {
|
if experiments.RemoteTaskfiles.Enabled() {
|
||||||
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
||||||
pflag.BoolVar(&Offline, "offline", getConfig(config, func() *bool { return config.Remote.Offline }, false), "Forces Task to only use local or cached Taskfiles.")
|
pflag.BoolVar(&Offline, "offline", offline, "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.DurationVar(&Timeout, "timeout", time.Second*10, "Timeout for downloading remote Taskfiles.")
|
||||||
pflag.BoolVar(&ClearCache, "clear-cache", false, "Clear the remote cache.")
|
pflag.BoolVar(&ClearCache, "clear-cache", false, "Clear the remote cache.")
|
||||||
pflag.DurationVar(&CacheExpiryDuration, "expiry", getConfig(config, func() *time.Duration { return config.Remote.Timeout }, 0), "Expiry duration for cached remote Taskfiles.")
|
pflag.DurationVar(&CacheExpiryDuration, "expiry", 0, "Expiry duration for cached remote Taskfiles.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,16 +197,12 @@ func Validate() error {
|
|||||||
return errors.New("task: --no-status only applies to --json with --list or --list-all")
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFlags is a special internal functional option that is used to pass flags
|
// WithFlags is a special internal functional option that is used to pass flags
|
||||||
// from the CLI into any constructor that accepts functional options.
|
// from the CLI into any constructor that accepts functional options.
|
||||||
func WithFlags() task.ExecutorOption {
|
func WithFlags() *flagsOption {
|
||||||
return &flagsOption{}
|
return &flagsOption{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,14 +229,8 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
|
|
||||||
e.Options(
|
e.Options(
|
||||||
task.WithDir(dir),
|
task.WithDir(dir),
|
||||||
task.WithEntrypoint(Entrypoint),
|
|
||||||
task.WithForce(Force),
|
task.WithForce(Force),
|
||||||
task.WithForceAll(ForceAll),
|
task.WithForceAll(ForceAll),
|
||||||
task.WithInsecure(Insecure),
|
|
||||||
task.WithDownload(Download),
|
|
||||||
task.WithOffline(Offline),
|
|
||||||
task.WithTimeout(Timeout),
|
|
||||||
task.WithCacheExpiryDuration(CacheExpiryDuration),
|
|
||||||
task.WithWatch(Watch),
|
task.WithWatch(Watch),
|
||||||
task.WithVerbose(Verbose),
|
task.WithVerbose(Verbose),
|
||||||
task.WithSilent(Silent),
|
task.WithSilent(Silent),
|
||||||
@@ -256,15 +247,11 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getConfig extracts a config value directly from a pointer field with a fallback default
|
func (o *flagsOption) ApplyToReader(r *taskfile.Reader) {
|
||||||
func getConfig[T any](config *taskrcast.TaskRC, fieldFunc func() *T, fallback T) T {
|
r.Options(
|
||||||
if config == nil {
|
taskfile.WithInsecure(Insecure),
|
||||||
return fallback
|
taskfile.WithDownload(Download),
|
||||||
}
|
taskfile.WithOffline(Offline),
|
||||||
|
taskfile.WithCacheExpiryDuration(CacheExpiryDuration),
|
||||||
field := fieldFunc()
|
)
|
||||||
if field != nil {
|
|
||||||
return *field
|
|
||||||
}
|
|
||||||
return fallback
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,87 +37,51 @@ func DefaultDir(entrypoint, dir string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveDir returns an absolute path to the directory that the task should be
|
// Search will look for files with the given possible filenames using the given
|
||||||
// run in. If the entrypoint and dir are BOTH set, then the Taskfile will not
|
// entrypoint and directory. If the entrypoint is set, it will check if the
|
||||||
// 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
|
// entrypoint matches a file or if it matches a directory containing one of the
|
||||||
// possible filenames. Otherwise, it walks up the file tree starting at the
|
// possible filenames. Otherwise, it will walk up the file tree starting at the
|
||||||
// given directory and performs a search in each directory for the possible
|
// given directory and perform a search in each directory for the possible
|
||||||
// filenames until it finds a match or reaches the root directory. If the
|
// 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
|
// entrypoint and directory are both empty, it will default the directory to the
|
||||||
// current working directory and performs a recursive search starting there. If
|
// current working directory and perform a recursive search starting there. If a
|
||||||
// a match is found, the absolute path to the file is returned with its
|
// match is found, the absolute path to the file will be returned with its
|
||||||
// directory. If no match is found, an error is returned.
|
// directory. If no match is found, an error will be returned.
|
||||||
func Search(entrypoint, dir string, possibleFilenames []string) (string, error) {
|
func Search(entrypoint, dir string, possibleFilenames []string) (string, string, error) {
|
||||||
var err error
|
var err error
|
||||||
if entrypoint != "" {
|
if entrypoint != "" {
|
||||||
entrypoint, err = SearchPath(entrypoint, possibleFilenames)
|
entrypoint, err = SearchPath(entrypoint, possibleFilenames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
return entrypoint, nil
|
if dir == "" {
|
||||||
|
dir = filepath.Dir(entrypoint)
|
||||||
|
} else {
|
||||||
|
dir, err = filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entrypoint, dir, nil
|
||||||
}
|
}
|
||||||
if dir == "" {
|
if dir == "" {
|
||||||
dir, err = os.Getwd()
|
dir, err = os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entrypoint, err = SearchPathRecursively(dir, possibleFilenames)
|
entrypoint, err = SearchPathRecursively(dir, possibleFilenames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
return entrypoint, nil
|
dir = filepath.Dir(entrypoint)
|
||||||
|
return entrypoint, dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchAll looks for files with the given possible filenames using the given
|
// Search will check if a file at the given path exists or not. If it does, it
|
||||||
// entrypoint and directory. If the entrypoint is set, it checks if the
|
// will return the path to it. If it does not, it will search for any files at
|
||||||
// entrypoint matches a file or if it matches a directory containing one of the
|
// the given path with any of the given possible names. If any of these match a
|
||||||
// possible filenames and add it to a list of matches. It then walks up the file
|
// file, the first matching path will be returned. If no files are found, an
|
||||||
// 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.
|
// error will be returned.
|
||||||
func SearchPath(path string, possibleFilenames []string) (string, error) {
|
func SearchPath(path string, possibleFilenames []string) (string, error) {
|
||||||
// Get file info about the path
|
// Get file info about the path
|
||||||
@@ -147,56 +111,36 @@ func SearchPath(path string, possibleFilenames []string) (string, error) {
|
|||||||
return "", os.ErrNotExist
|
return "", os.ErrNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchPathRecursively walks up the directory tree starting at the given
|
// SearchRecursively will check if a file at the given path exists by calling
|
||||||
// path, calling the Search function in each directory until it finds a matching
|
// the exists function. If a file is not found, it will walk up the directory
|
||||||
// file or reaches the root directory. On supported operating systems, it will
|
// tree calling the Search function until it finds a file or reaches the root
|
||||||
// also check if the user ID of the directory changes and abort if it does.
|
// 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) {
|
func SearchPathRecursively(path string, possibleFilenames []string) (string, error) {
|
||||||
paths, err := SearchNPathRecursively(path, possibleFilenames, 1)
|
owner, err := sysinfo.Owner(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if len(paths) == 0 {
|
for {
|
||||||
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)
|
fpath, err := SearchPath(path, possibleFilenames)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
paths = append(paths, fpath)
|
return fpath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the parent path/user id
|
// Get the parent path/user id
|
||||||
parentPath := filepath.Dir(path)
|
parentPath := filepath.Dir(path)
|
||||||
parentOwner, err := sysinfo.Owner(parentPath)
|
parentOwner, err := sysinfo.Owner(parentPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error if we reached the root directory and still haven't found a file
|
// Error if we reached the root directory and still haven't found a file
|
||||||
// OR if the user id of the directory changes
|
// OR if the user id of the directory changes
|
||||||
if path == parentPath || (parentOwner != owner) {
|
if path == parentPath || (parentOwner != owner) {
|
||||||
return paths, nil
|
return "", os.ErrNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
owner = parentOwner
|
owner = parentOwner
|
||||||
path = parentPath
|
path = parentPath
|
||||||
}
|
}
|
||||||
|
|
||||||
return paths, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,30 +71,35 @@ func TestSearch(t *testing.T) {
|
|||||||
dir string
|
dir string
|
||||||
possibleFilenames []string
|
possibleFilenames []string
|
||||||
expectedEntrypoint string
|
expectedEntrypoint string
|
||||||
|
expectedDir string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "find foo.txt using relative entrypoint",
|
name: "find foo.txt using relative entrypoint",
|
||||||
entrypoint: "./testdata/foo.txt",
|
entrypoint: "./testdata/foo.txt",
|
||||||
possibleFilenames: []string{"foo.txt"},
|
possibleFilenames: []string{"foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
|
expectedDir: filepath.Join(wd, "testdata"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find foo.txt using absolute entrypoint",
|
name: "find foo.txt using absolute entrypoint",
|
||||||
entrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
entrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
possibleFilenames: []string{"foo.txt"},
|
possibleFilenames: []string{"foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
|
expectedDir: filepath.Join(wd, "testdata"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find foo.txt using relative dir",
|
name: "find foo.txt using relative dir",
|
||||||
dir: "./testdata",
|
dir: "./testdata",
|
||||||
possibleFilenames: []string{"foo.txt"},
|
possibleFilenames: []string{"foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
|
expectedDir: filepath.Join(wd, "testdata"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find foo.txt using absolute dir",
|
name: "find foo.txt using absolute dir",
|
||||||
dir: filepath.Join(wd, "testdata"),
|
dir: filepath.Join(wd, "testdata"),
|
||||||
possibleFilenames: []string{"foo.txt"},
|
possibleFilenames: []string{"foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
|
expectedDir: filepath.Join(wd, "testdata"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find foo.txt using relative dir and relative entrypoint",
|
name: "find foo.txt using relative dir and relative entrypoint",
|
||||||
@@ -102,6 +107,7 @@ func TestSearch(t *testing.T) {
|
|||||||
dir: "./testdata/some/other/dir",
|
dir: "./testdata/some/other/dir",
|
||||||
possibleFilenames: []string{"foo.txt"},
|
possibleFilenames: []string{"foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "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",
|
name: "find fs.go using no entrypoint or dir",
|
||||||
@@ -109,6 +115,7 @@ func TestSearch(t *testing.T) {
|
|||||||
dir: "",
|
dir: "",
|
||||||
possibleFilenames: []string{"fs.go"},
|
possibleFilenames: []string{"fs.go"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "fs.go"),
|
expectedEntrypoint: filepath.Join(wd, "fs.go"),
|
||||||
|
expectedDir: wd,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find ../../Taskfile.yml using no entrypoint or dir by walking",
|
name: "find ../../Taskfile.yml using no entrypoint or dir by walking",
|
||||||
@@ -116,109 +123,30 @@ func TestSearch(t *testing.T) {
|
|||||||
dir: "",
|
dir: "",
|
||||||
possibleFilenames: []string{"Taskfile.yml"},
|
possibleFilenames: []string{"Taskfile.yml"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "..", "..", "Taskfile.yml"),
|
expectedEntrypoint: filepath.Join(wd, "..", "..", "Taskfile.yml"),
|
||||||
|
expectedDir: filepath.Join(wd, "..", ".."),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find foo.txt first if listed first in possible filenames",
|
name: "find foo.txt first if listed first in possible filenames",
|
||||||
entrypoint: "./testdata",
|
entrypoint: "./testdata",
|
||||||
possibleFilenames: []string{"foo.txt", "bar.txt"},
|
possibleFilenames: []string{"foo.txt", "bar.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
|
expectedDir: filepath.Join(wd, "testdata"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find bar.txt first if listed first in possible filenames",
|
name: "find bar.txt first if listed first in possible filenames",
|
||||||
entrypoint: "./testdata",
|
entrypoint: "./testdata",
|
||||||
possibleFilenames: []string{"bar.txt", "foo.txt"},
|
possibleFilenames: []string{"bar.txt", "foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "bar.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "bar.txt"),
|
||||||
|
expectedDir: filepath.Join(wd, "testdata"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
entrypoint, err := Search(tt.entrypoint, tt.dir, tt.possibleFilenames)
|
entrypoint, dir, err := Search(tt.entrypoint, tt.dir, tt.possibleFilenames)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, tt.expectedEntrypoint, entrypoint)
|
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()
|
|
||||||
dir, err := ResolveDir(tt.entrypoint, tt.resolvedEntrypoint, tt.dir)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, tt.expectedDir, dir)
|
require.Equal(t, tt.expectedDir, dir)
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
3.45.2
|
3.44.1
|
||||||
|
|||||||
32
package-lock.json
generated
Normal file
32
package-lock.json
generated
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"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
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"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,6 +172,7 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
|||||||
if t.Method != "" {
|
if t.Method != "" {
|
||||||
method = t.Method
|
method = t.Method
|
||||||
}
|
}
|
||||||
|
|
||||||
upToDate, err := fingerprint.IsTaskUpToDate(ctx, t,
|
upToDate, err := fingerprint.IsTaskUpToDate(ctx, t,
|
||||||
fingerprint.WithMethod(method),
|
fingerprint.WithMethod(method),
|
||||||
fingerprint.WithTempDir(e.TempDir.Fingerprint),
|
fingerprint.WithTempDir(e.TempDir.Fingerprint),
|
||||||
@@ -466,6 +467,7 @@ func (e *Executor) GetTask(call *Call) (*ast.Task, error) {
|
|||||||
DidYouMean: didYouMean,
|
DidYouMean: didYouMean,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchingTask, nil
|
return matchingTask, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4521
task_test.go
4521
task_test.go
File diff suppressed because it is too large
Load Diff
@@ -46,22 +46,17 @@ type Task struct {
|
|||||||
Namespace string
|
Namespace string
|
||||||
IncludeVars *Vars
|
IncludeVars *Vars
|
||||||
IncludedTaskfileVars *Vars
|
IncludedTaskfileVars *Vars
|
||||||
|
|
||||||
FullName string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) Name() string {
|
func (t *Task) Name() string {
|
||||||
if t.Label != "" {
|
if t.Label != "" {
|
||||||
return t.Label
|
return t.Label
|
||||||
}
|
}
|
||||||
if t.FullName != "" {
|
|
||||||
return t.FullName
|
|
||||||
}
|
|
||||||
return t.Task
|
return t.Task
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) LocalName() string {
|
func (t *Task) LocalName() string {
|
||||||
name := t.FullName
|
name := t.Task
|
||||||
name = strings.TrimPrefix(name, t.Namespace)
|
name = strings.TrimPrefix(name, t.Namespace)
|
||||||
name = strings.TrimPrefix(name, ":")
|
name = strings.TrimPrefix(name, ":")
|
||||||
return name
|
return name
|
||||||
@@ -225,7 +220,6 @@ func (t *Task) DeepCopy() *Task {
|
|||||||
Location: t.Location.DeepCopy(),
|
Location: t.Location.DeepCopy(),
|
||||||
Requires: t.Requires.DeepCopy(),
|
Requires: t.Requires.DeepCopy(),
|
||||||
Namespace: t.Namespace,
|
Namespace: t.Namespace,
|
||||||
FullName: t.FullName,
|
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ type Var struct {
|
|||||||
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||||
switch node.Kind {
|
switch node.Kind {
|
||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
key := "<none>"
|
key := node.Content[0].Value
|
||||||
if len(node.Content) > 0 {
|
|
||||||
key = node.Content[0].Value
|
|
||||||
}
|
|
||||||
switch key {
|
switch key {
|
||||||
case "sh", "ref", "map":
|
case "sh", "ref", "map":
|
||||||
var m struct {
|
var m struct {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package taskfile
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
giturls "github.com/chainguard-dev/git-urls"
|
giturls "github.com/chainguard-dev/git-urls"
|
||||||
|
|
||||||
@@ -33,7 +32,6 @@ func NewRootNode(
|
|||||||
entrypoint string,
|
entrypoint string,
|
||||||
dir string,
|
dir string,
|
||||||
insecure bool,
|
insecure bool,
|
||||||
timeout time.Duration,
|
|
||||||
) (Node, error) {
|
) (Node, error) {
|
||||||
dir = fsext.DefaultDir(entrypoint, dir)
|
dir = fsext.DefaultDir(entrypoint, dir)
|
||||||
// If the entrypoint is "-", we read from stdin
|
// If the entrypoint is "-", we read from stdin
|
||||||
|
|||||||
@@ -18,21 +18,15 @@ type FileNode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewFileNode(entrypoint, dir string, opts ...NodeOption) (*FileNode, error) {
|
func NewFileNode(entrypoint, dir string, opts ...NodeOption) (*FileNode, error) {
|
||||||
// Find the entrypoint file
|
var err error
|
||||||
resolvedEntrypoint, err := fsext.Search(entrypoint, dir, defaultTaskfiles)
|
base := NewBaseNode(dir, opts...)
|
||||||
|
entrypoint, base.dir, err = fsext.Search(entrypoint, base.dir, defaultTaskfiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the directory
|
|
||||||
resolvedDir, err := fsext.ResolveDir(entrypoint, resolvedEntrypoint, dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &FileNode{
|
return &FileNode{
|
||||||
baseNode: NewBaseNode(resolvedDir, opts...),
|
baseNode: base,
|
||||||
entrypoint: resolvedEntrypoint,
|
entrypoint: entrypoint,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +1,8 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import "github.com/Masterminds/semver/v3"
|
||||||
"cmp"
|
|
||||||
"maps"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TaskRC struct {
|
type TaskRC struct {
|
||||||
Version *semver.Version `yaml:"version"`
|
Version *semver.Version `yaml:"version"`
|
||||||
Verbose *bool `yaml:"verbose"`
|
|
||||||
Concurrency *int `yaml:"concurrency"`
|
|
||||||
Remote Remote `yaml:"remote"`
|
|
||||||
Experiments map[string]int `yaml:"experiments"`
|
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
|
package taskrc
|
||||||
|
|
||||||
import (
|
import "github.com/go-task/task/v3/internal/fsext"
|
||||||
"github.com/go-task/task/v3/internal/fsext"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
entrypoint string
|
entrypoint string
|
||||||
|
dir string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNode(
|
func NewNode(
|
||||||
entrypoint string,
|
entrypoint string,
|
||||||
dir string,
|
dir string,
|
||||||
possibleFileNames []string,
|
|
||||||
) (*Node, error) {
|
) (*Node, error) {
|
||||||
dir = fsext.DefaultDir(entrypoint, dir)
|
dir = fsext.DefaultDir(entrypoint, dir)
|
||||||
resolvedEntrypoint, err := fsext.SearchPath(dir, possibleFileNames)
|
var err error
|
||||||
|
entrypoint, dir, err = fsext.Search(entrypoint, dir, defaultTaskRCs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Node{
|
return &Node{
|
||||||
entrypoint: resolvedEntrypoint,
|
entrypoint: entrypoint,
|
||||||
|
dir: dir,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,89 +1,6 @@
|
|||||||
package taskrc
|
package taskrc
|
||||||
|
|
||||||
import (
|
var defaultTaskRCs = []string{
|
||||||
"os"
|
".taskrc.yml",
|
||||||
"path/filepath"
|
".taskrc.yaml",
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
78
temp_dir.go
Normal file
78
temp_dir.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/env"
|
||||||
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TempDir struct {
|
||||||
|
Remote string
|
||||||
|
Fingerprint string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTempDir(dir string) (*TempDir, error) {
|
||||||
|
tempDir, err := setupTempDirFingerprint(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setupTempDirRemote(dir, tempDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTempDirFingerprint(dir string) (*TempDir, error) {
|
||||||
|
tempDir := env.GetTaskEnv("TEMP_DIR")
|
||||||
|
|
||||||
|
if tempDir == "" {
|
||||||
|
return &TempDir{
|
||||||
|
Remote: filepathext.SmartJoin(dir, ".task"),
|
||||||
|
Fingerprint: filepathext.SmartJoin(dir, ".task"),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") {
|
||||||
|
tempDir, err := execext.ExpandLiteral(tempDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
projectDir, _ := filepath.Abs(dir)
|
||||||
|
projectName := filepath.Base(projectDir)
|
||||||
|
return &TempDir{
|
||||||
|
Remote: tempDir,
|
||||||
|
Fingerprint: filepathext.SmartJoin(tempDir, projectName),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TempDir{
|
||||||
|
Remote: filepathext.SmartJoin(dir, tempDir),
|
||||||
|
Fingerprint: filepathext.SmartJoin(dir, tempDir),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTempDirRemote(dir string, tempDir *TempDir) error {
|
||||||
|
remoteDir := env.GetTaskEnv("REMOTE_DIR")
|
||||||
|
|
||||||
|
if remoteDir == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.IsAbs(remoteDir) || strings.HasPrefix(remoteDir, "~") {
|
||||||
|
remoteTempDir, err := execext.ExpandLiteral(remoteDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tempDir.Remote = remoteTempDir
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tempDir.Remote = filepathext.SmartJoin(dir, ".task")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
8
testdata/checksum/Taskfile.yml
vendored
8
testdata/checksum/Taskfile.yml
vendored
@@ -12,14 +12,6 @@ tasks:
|
|||||||
generates:
|
generates:
|
||||||
- ./generated.txt
|
- ./generated.txt
|
||||||
method: checksum
|
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:
|
build-with-status:
|
||||||
cmds:
|
cmds:
|
||||||
|
|||||||
1
testdata/checksum/generated-wildcard.txt
vendored
1
testdata/checksum/generated-wildcard.txt
vendored
@@ -1 +0,0 @@
|
|||||||
Hello, World!
|
|
||||||
11
testdata/run/Taskfile.yml
vendored
11
testdata/run/Taskfile.yml
vendored
@@ -22,14 +22,3 @@ tasks:
|
|||||||
run: once
|
run: once
|
||||||
cmds:
|
cmds:
|
||||||
- echo starting {{.CONTENT}} >> hash.txt
|
- 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
|
|
||||||
|
|||||||
1
testdata/wildcards/testdata/TestWildcard-basic_wildcard.golden
vendored
Normal file
1
testdata/wildcards/testdata/TestWildcard-basic_wildcard.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello foo
|
||||||
1
testdata/wildcards/testdata/TestWildcard-double_wildcard.golden
vendored
Normal file
1
testdata/wildcards/testdata/TestWildcard-double_wildcard.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello foo bar
|
||||||
1
testdata/wildcards/testdata/TestWildcard-matches_exactly.golden
vendored
Normal file
1
testdata/wildcards/testdata/TestWildcard-matches_exactly.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
I don't consume matches: []
|
||||||
1
testdata/wildcards/testdata/TestWildcard-multiple_matches.golden
vendored
Normal file
1
testdata/wildcards/testdata/TestWildcard-multiple_matches.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello foo-bar
|
||||||
1
testdata/wildcards/testdata/TestWildcard-no_matches-err-run.golden
vendored
Normal file
1
testdata/wildcards/testdata/TestWildcard-no_matches-err-run.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
task: Task "no-match" does not exist
|
||||||
1
testdata/wildcards/testdata/TestWildcard-no_matches.golden
vendored
Normal file
1
testdata/wildcards/testdata/TestWildcard-no_matches.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
task: No tasks with description available. Try --list-all to list all tasks
|
||||||
1
testdata/wildcards/testdata/TestWildcard-store_wildcard.golden
vendored
Normal file
1
testdata/wildcards/testdata/TestWildcard-store_wildcard.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Starting foo
|
||||||
@@ -44,14 +44,9 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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}
|
cache := &templater.Cache{Vars: vars}
|
||||||
|
|
||||||
new := ast.Task{
|
new := ast.Task{
|
||||||
Task: origTask.Task,
|
Task: origTask.Task,
|
||||||
Label: templater.Replace(origTask.Label, cache),
|
Label: templater.Replace(origTask.Label, cache),
|
||||||
@@ -81,7 +76,6 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err
|
|||||||
Requires: origTask.Requires,
|
Requires: origTask.Requires,
|
||||||
Watch: origTask.Watch,
|
Watch: origTask.Watch,
|
||||||
Namespace: origTask.Namespace,
|
Namespace: origTask.Namespace,
|
||||||
FullName: fullName,
|
|
||||||
}
|
}
|
||||||
new.Dir, err = execext.ExpandLiteral(new.Dir)
|
new.Dir, err = execext.ExpandLiteral(new.Dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
18
website/.gitignore
vendored
18
website/.gitignore
vendored
@@ -1,9 +1,21 @@
|
|||||||
# Dependencies
|
# Dependencies
|
||||||
/node_modules
|
/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*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
.vitepress/cache
|
|
||||||
.vitepress/dist
|
|
||||||
.task/
|
|
||||||
|
|||||||
0
website/.nojekyll
Normal file
0
website/.nojekyll
Normal file
@@ -1 +0,0 @@
|
|||||||
pnpm-lock.yaml
|
|
||||||
5
website/.vitepress/components.d.ts
vendored
5
website/.vitepress/components.d.ts
vendored
@@ -1,5 +0,0 @@
|
|||||||
declare module '*.vue' {
|
|
||||||
import type { DefineComponent } from 'vue';
|
|
||||||
const component: DefineComponent<{}, {}, any>;
|
|
||||||
export default component;
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,245 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { VPBadge } from 'vitepress/theme';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<VPBadge type="info"> <slot />+ </VPBadge>
|
|
||||||
</template>
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
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'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
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/';
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
export const sponsors = [
|
|
||||||
{
|
|
||||||
tier: 'Gold Sponsors',
|
|
||||||
size: 'big',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'devowl',
|
|
||||||
url: 'https://devowl.io/',
|
|
||||||
img: '/img/devowl.io.svg'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
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' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
: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
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 681 B |
@@ -1,3 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 350 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 375 375"><path fill="#29beb0" d="M 187.570312 190.933594 L 187.570312 375 L 30.070312 279.535156 L 30.070312 95.464844 Z"/><path fill="#69d2c8" d="M 187.570312 190.933594 L 187.570312 375 L 345.070312 279.535156 L 345.070312 95.464844 Z"/><path fill="#94dfd8" d="M 187.570312 190.933594 L 30.070312 95.464844 L 187.570312 0 L 345.070312 95.464844 Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 435 B |
@@ -1,3 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 796 B |
@@ -1,3 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 405 B |
@@ -1,24 +0,0 @@
|
|||||||
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,35 +1,29 @@
|
|||||||
version: '3'
|
version: "3"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
install:
|
yarn:install:
|
||||||
desc: Setup VitePress locally
|
desc: Setup Docusaurus locally
|
||||||
cmds:
|
cmds:
|
||||||
- pnpm install
|
- yarn install
|
||||||
sources:
|
sources:
|
||||||
- package.json
|
- package.json
|
||||||
- pnpm-lock.yaml
|
- yarn.lock
|
||||||
|
|
||||||
default:
|
default:
|
||||||
desc: Start website
|
desc: Start website
|
||||||
deps: [install]
|
deps: [yarn:install]
|
||||||
aliases: [s, start]
|
aliases: [s, start]
|
||||||
vars:
|
vars:
|
||||||
HOST: '{{default "0.0.0.0" .HOST}}'
|
HOST: '{{default "0.0.0.0" .HOST}}'
|
||||||
PORT: '{{default "3001" .PORT}}'
|
PORT: '{{default "3001" .PORT}}'
|
||||||
cmds:
|
cmds:
|
||||||
- pnpm dev --host={{.HOST}} --port={{.PORT}}
|
- npx docusaurus start --no-open --host={{.HOST}} --port={{.PORT}}
|
||||||
|
|
||||||
lint:
|
|
||||||
desc: Lint website
|
|
||||||
deps: [install]
|
|
||||||
cmds:
|
|
||||||
- pnpm lint
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
desc: Build website
|
desc: Build website
|
||||||
deps: [install]
|
deps: [yarn:install]
|
||||||
cmds:
|
cmds:
|
||||||
- pnpm build
|
- npx docusaurus build
|
||||||
|
|
||||||
preview:
|
preview:
|
||||||
desc: Preview Website
|
desc: Preview Website
|
||||||
@@ -39,19 +33,20 @@ tasks:
|
|||||||
HOST: '{{default "localhost" .HOST}}'
|
HOST: '{{default "localhost" .HOST}}'
|
||||||
PORT: '{{default "3001" .PORT}}'
|
PORT: '{{default "3001" .PORT}}'
|
||||||
cmds:
|
cmds:
|
||||||
- pnpm preview --host={{.HOST}} --port={{.PORT}}
|
- npx docusaurus serve --no-open --host={{.HOST}} --port={{.PORT}}
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
desc: Clean temp directories
|
desc: Clean temp directories
|
||||||
cmds:
|
cmds:
|
||||||
- rm -rf ./vitepress/dist
|
- rm -rf ./build
|
||||||
|
|
||||||
deploy:next:
|
deploy:
|
||||||
desc: Build and deploy next.taskfile.dev
|
desc: Build and deploy Docusaurus
|
||||||
|
summary: Requires GIT_USER and GIT_PASS envs to be previous set
|
||||||
cmds:
|
cmds:
|
||||||
- pnpm netlify deploy --prod --site=4e13dfcf-fc0d-4bec-ad60-b918a8dc3942
|
- npx docusaurus deploy
|
||||||
|
|
||||||
deploy:prod:
|
upgrade:
|
||||||
desc: Build and deploy taskfile.dev
|
desc: Upgrade Docusaurus
|
||||||
cmds:
|
cmds:
|
||||||
- pnpm netlify deploy --prod --site=e625bc6a-1cd3-465d-ad30-7bbddaeb4f31
|
- yarn upgrade @docusaurus/core@latest @docusaurus/preset-classic@latest @docusaurus/module-type-aliases@latest @docusaurus/tsconfig@latest @docusaurus/types@latest
|
||||||
|
|||||||
3
website/babel.config.ts
Normal file
3
website/babel.config.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
presets: ['@docusaurus/core/lib/babel/preset'],
|
||||||
|
};
|
||||||
@@ -2,15 +2,13 @@
|
|||||||
title: Introducing Experiments
|
title: Introducing Experiments
|
||||||
description:
|
description:
|
||||||
A look at where task is, where it's going and how we're going to get there.
|
A look at where task is, where it's going and how we're going to get there.
|
||||||
author: pd93
|
slug: task-in-2023
|
||||||
date: 2024-05-09
|
authors: [pd93]
|
||||||
outline: deep
|
tags: [experiments, breaking-changes, roadmap, v4]
|
||||||
|
image: https://i.imgur.com/mErPwqL.png
|
||||||
|
hide_table_of_contents: false
|
||||||
---
|
---
|
||||||
|
|
||||||
# Introducing Experiments
|
|
||||||
|
|
||||||
<AuthorCard :author="$frontmatter.author" />
|
|
||||||
|
|
||||||
Lately, Task has been growing extremely quickly and I've found myself thinking a
|
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
|
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
|
not much of a writer, but I think one of the things we could do better is to
|
||||||
@@ -18,17 +16,19 @@ 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
|
this is the first (hopefully of many) blog posts talking about Task and what
|
||||||
we're up to.
|
we're up to.
|
||||||
|
|
||||||
|
{/* truncate */}
|
||||||
|
|
||||||
## :calendar: So, what have we been up to?
|
## :calendar: So, what have we been up to?
|
||||||
|
|
||||||
Over the past 12 months or so, @andreynering (Author and maintainer of the
|
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
|
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
|
improve v3 of Task and we've made some amazing progress. Here are just some of
|
||||||
the things we've released in that time:
|
the things we've released in that time:
|
||||||
|
|
||||||
- An official [extension for VS Code][vscode-task].
|
- An official [extension for VS Code][vscode-task].
|
||||||
- Internal Tasks (#818).
|
- Internal Tasks ([#818](https://github.com/go-task/task/pull/818)).
|
||||||
- Task aliases (#879).
|
- Task aliases ([#879](https://github.com/go-task/task/pull/879)).
|
||||||
- Looping over tasks (#1220).
|
- Looping over tasks ([#1220](https://github.com/go-task/task/pull/1200)).
|
||||||
- A series of refactors to the core codebase to make it more maintainable and
|
- A series of refactors to the core codebase to make it more maintainable and
|
||||||
extensible.
|
extensible.
|
||||||
- Loads of bug fixes and improvements.
|
- Loads of bug fixes and improvements.
|
||||||
@@ -38,13 +38,14 @@ the things we've released in that time:
|
|||||||
- And much, much more! :sparkles:
|
- And much, much more! :sparkles:
|
||||||
|
|
||||||
We're also working on adding some really exciting and highly requested features
|
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).
|
to Task such as having the ability to run remote Taskfiles
|
||||||
|
([#1317](https://github.com/go-task/task/issues/1317)).
|
||||||
|
|
||||||
None of this would have been possible without the [150 or so (and growing)
|
None of this would have been possible without the [150 or so (and growing)
|
||||||
contributors][contributors] to the project, numerous sponsors and a passionate
|
contributors][contributors] to the project, numerous sponsors and a passionate
|
||||||
community of users. Together we have more than doubled the number of GitHub
|
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
|
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! 🚀
|
accelerate. We can't thank you all enough for your help and support! :rocket:
|
||||||
|
|
||||||
[](https://star-history.com/#go-task/task&Date)
|
[](https://star-history.com/#go-task/task&Date)
|
||||||
|
|
||||||
@@ -70,7 +71,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
|
inconvenience for users as they have to constantly keep up-to-date with our
|
||||||
breaking changes. Fortunately, there is a better way.
|
breaking changes. Fortunately, there is a better way.
|
||||||
|
|
||||||
## What's going to change? :monocle_face:
|
## What's going to change? :monocle:
|
||||||
|
|
||||||
Going forwards, breaking changes will be allowed into _minor_ versions of Task
|
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
|
as "experimental features". To access these features users will need opt-in by
|
||||||
@@ -121,11 +122,14 @@ 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
|
Task-related topics, so make sure to check in occasionally and see what we're up
|
||||||
to!
|
to!
|
||||||
|
|
||||||
|
{/* prettier-ignore-start */}
|
||||||
[vscode-task]: https://github.com/go-task/vscode-task
|
[vscode-task]: https://github.com/go-task/vscode-task
|
||||||
[crowdin]: https://crowdin.com
|
[crowdin]: https://crowdin.com
|
||||||
[contributors]: https://github.com/go-task/task/graphs/contributors
|
[contributors]: https://github.com/go-task/task/graphs/contributors
|
||||||
[semver]: https://semver.org
|
[semver]: https://semver.org
|
||||||
[breaking-change-proposal]: https://github.com/go-task/task/discussions/1191
|
[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
|
[experiments]: https://taskfile.dev/experiments
|
||||||
[deprecations]: https://taskfile.dev/deprecations
|
[deprecations]: https://taskfile.dev/deprecations
|
||||||
[deprecate-version-2-schema]: https://github.com/go-task/task/issues/1197
|
[deprecate-version-2-schema]: https://github.com/go-task/task/issues/1197
|
||||||
@@ -135,3 +139,4 @@ to!
|
|||||||
[experiments-project]: https://github.com/orgs/go-task/projects/1
|
[experiments-project]: https://github.com/orgs/go-task/projects/1
|
||||||
[gentle-force-experiment]: https://github.com/go-task/task/issues/1200
|
[gentle-force-experiment]: https://github.com/go-task/task/issues/1200
|
||||||
[remote-taskfiles-experiment]: https://github.com/go-task/task/issues/1317
|
[remote-taskfiles-experiment]: https://github.com/go-task/task/issues/1317
|
||||||
|
{/* prettier-ignore-end */}
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
---
|
---
|
||||||
title: Any Variables
|
title: Any Variables
|
||||||
author: pd93
|
description: Task variables are no longer limited to strings!
|
||||||
date: 2024-05-09
|
slug: any-variables
|
||||||
outline: deep
|
authors: [pd93]
|
||||||
|
tags: [experiments, variables]
|
||||||
|
image: https://i.imgur.com/mErPwqL.png
|
||||||
|
hide_table_of_contents: false
|
||||||
---
|
---
|
||||||
|
|
||||||
# Any Variables
|
import Tabs from '@theme/Tabs';
|
||||||
|
import TabItem from '@theme/TabItem';
|
||||||
<AuthorCard :author="$frontmatter.author" />
|
|
||||||
|
|
||||||
Task has always had variables, but even though you were able to define them
|
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.
|
using different YAML types, they would always be converted to strings by Task.
|
||||||
@@ -16,6 +18,8 @@ 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**,
|
Task now supports most variable types, including **booleans**, **integers**,
|
||||||
**floats** and **arrays**!
|
**floats** and **arrays**!
|
||||||
|
|
||||||
|
{/* truncate */}
|
||||||
|
|
||||||
## What's the big deal?
|
## What's the big deal?
|
||||||
|
|
||||||
These changes allow you to use variables in a much more natural way and opens up
|
These changes allow you to use variables in a much more natural way and opens up
|
||||||
@@ -27,9 +31,10 @@ some of the examples below for some inspiration.
|
|||||||
No more comparing strings to "true" or "false". Now you can use actual boolean
|
No more comparing strings to "true" or "false". Now you can use actual boolean
|
||||||
values in your templates:
|
values in your templates:
|
||||||
|
|
||||||
::: code-group
|
<Tabs defaultValue="2">
|
||||||
|
<TabItem value="1" label="Before">
|
||||||
|
|
||||||
```yaml [Before]
|
```yaml
|
||||||
version: 3
|
version: 3
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
@@ -40,7 +45,10 @@ tasks:
|
|||||||
- '{{if eq .BOOL "true"}}echo foo{{end}}'
|
- '{{if eq .BOOL "true"}}echo foo{{end}}'
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml [After]
|
</TabItem>
|
||||||
|
<TabItem value="2" label="After">
|
||||||
|
|
||||||
|
```yaml
|
||||||
version: 3
|
version: 3
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
@@ -51,7 +59,8 @@ tasks:
|
|||||||
- '{{if .BOOL}}echo foo{{end}}' # <-- No need to compare to "true"
|
- '{{if .BOOL}}echo foo{{end}}' # <-- No need to compare to "true"
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
### Arithmetic
|
### Arithmetic
|
||||||
|
|
||||||
@@ -101,9 +110,10 @@ 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
|
"collection-type" variables using the `for` keyword, so now you are able to loop
|
||||||
over list variables directly:
|
over list variables directly:
|
||||||
|
|
||||||
::: code-group
|
<Tabs defaultValue="2">
|
||||||
|
<TabItem value="1" label="Before">
|
||||||
|
|
||||||
```yaml [Before]
|
```yaml
|
||||||
version: 3
|
version: 3
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
@@ -117,7 +127,10 @@ tasks:
|
|||||||
cmd: echo {{.ITEM}}
|
cmd: echo {{.ITEM}}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml [After]
|
</TabItem>
|
||||||
|
<TabItem value="2" label="After">
|
||||||
|
|
||||||
|
```yaml
|
||||||
version: 3
|
version: 3
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
@@ -130,7 +143,8 @@ tasks:
|
|||||||
cmd: echo {{.ITEM}}
|
cmd: echo {{.ITEM}}
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
## What about maps?
|
## What about maps?
|
||||||
|
|
||||||
@@ -140,9 +154,20 @@ 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]"
|
continue working on map support in the new "[Map Variables][map-variables]"
|
||||||
experiment.
|
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
|
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:
|
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
|
[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-math]: https://go-task.github.io/slim-sprig/math.html
|
||||||
[slim-sprig-list]: https://go-task.github.io/slim-sprig/lists.html
|
[slim-sprig-list]: https://go-task.github.io/slim-sprig/lists.html
|
||||||
|
[map-variables]: /experiments/map-variables
|
||||||
|
{/* prettier-ignore-end */}
|
||||||
10
website/blog/authors.yml
Normal file
10
website/blog/authors.yml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
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
|
||||||
7
website/constants.ts
Normal file
7
website/constants.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
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,63 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: Changelog
|
slug: /changelog/
|
||||||
outline: deep
|
sidebar_position: 14
|
||||||
---
|
---
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## v3.45.2 - 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-v3.45.1 - 2025-09-15
|
|
||||||
|
|
||||||
Failed due to an issue with our release process.
|
|
||||||
|
|
||||||
## v3.44.1 - 2025-07-23
|
## v3.44.1 - 2025-07-23
|
||||||
|
|
||||||
- Internal tasks will no longer be shown as suggestions since they cannot be
|
- Internal tasks will no longer be shown as suggestions since they cannot be
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Community
|
slug: /community/
|
||||||
description:
|
sidebar_position: 10
|
||||||
Task community contributions, installation methods, and integrations
|
|
||||||
maintained by third parties
|
|
||||||
outline: deep
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Community
|
# Community
|
||||||
@@ -16,12 +13,13 @@ thankful for everyone that helps me to improve the overall experience.
|
|||||||
|
|
||||||
Many of our integrations are contributed and maintained by the community. You
|
Many of our integrations are contributed and maintained by the community. You
|
||||||
can view the full list of community integrations
|
can view the full list of community integrations
|
||||||
[here](./integrations.md#community-integrations).
|
[here](/integrations#community-integrations).
|
||||||
|
|
||||||
## Installation methods
|
## Installation methods
|
||||||
|
|
||||||
Some installation methods are maintained by third party:
|
Some installation methods are maintained by third party:
|
||||||
|
|
||||||
|
- [GitHub Actions](https://github.com/arduino/setup-task) by @arduino
|
||||||
- [AUR](https://aur.archlinux.org/packages/go-task-bin) by @carlsmedstad
|
- [AUR](https://aur.archlinux.org/packages/go-task-bin) by @carlsmedstad
|
||||||
- [Scoop](https://github.com/ScoopInstaller/Main/blob/master/bucket/task.json)
|
- [Scoop](https://github.com/ScoopInstaller/Main/blob/master/bucket/task.json)
|
||||||
- [Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/)
|
- [Fedora](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/)
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Contributing
|
slug: /contributing/
|
||||||
description:
|
sidebar_position: 12
|
||||||
Comprehensive guide for contributing to the Task project, including setup,
|
|
||||||
development, testing, and submitting PRs
|
|
||||||
outline: deep
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
@@ -11,7 +8,7 @@ outline: deep
|
|||||||
Contributions to Task are very welcome, but we ask that you read this document
|
Contributions to Task are very welcome, but we ask that you read this document
|
||||||
before submitting a PR.
|
before submitting a PR.
|
||||||
|
|
||||||
::: info
|
:::note
|
||||||
|
|
||||||
This document applies to the core [Task][task] repository _and_ [Task for Visual
|
This document applies to the core [Task][task] repository _and_ [Task for Visual
|
||||||
Studio Code][vscode-task].
|
Studio Code][vscode-task].
|
||||||
@@ -30,10 +27,9 @@ Studio Code][vscode-task].
|
|||||||
you invest your time into a PR.
|
you invest your time into a PR.
|
||||||
- **Experiments** - If there is no way to make your change backward compatible
|
- **Experiments** - If there is no way to make your change backward compatible
|
||||||
then there is a procedure to introduce breaking changes into minor versions.
|
then there is a procedure to introduce breaking changes into minor versions.
|
||||||
We call these "[experiments](./experiments/index.md)". If you're intending to work on
|
We call these "[experiments][experiments]". If you're intending to work on an
|
||||||
an experiment, then please read the
|
experiment, then please read the [experiments workflow][experiments-workflow]
|
||||||
[experiments workflow](./experiments/index.md#workflow) document carefully and submit a
|
document carefully and submit a proposal first.
|
||||||
proposal first.
|
|
||||||
|
|
||||||
## 1. Setup
|
## 1. Setup
|
||||||
|
|
||||||
@@ -42,7 +38,7 @@ Studio Code][vscode-task].
|
|||||||
- **Node.js** - [Node.js][nodejs] is used to host Task's documentation server
|
- **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
|
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.
|
you want to contribute to the Visual Studio Code extension.
|
||||||
- **Pnpm** - [Pnpm][pnpm] is the Node.js package manager used by Task.
|
- **Yarn** - [Yarn][yarn] is the Node.js package manager used by Task.
|
||||||
|
|
||||||
## 2. Making changes
|
## 2. Making changes
|
||||||
|
|
||||||
@@ -53,11 +49,10 @@ Studio Code][vscode-task].
|
|||||||
docs][golangci-lint-docs] for a guide on how to setup your editor to
|
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
|
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
|
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
|
that we have a consistent style across the project. You can use the `task
|
||||||
`task lint` command to lint the code locally and the `task lint:fix` command
|
lint` command to lint the code locally and the `task lint:fix` command to try
|
||||||
to try to automatically fix any issues that are found. You can also use the
|
to automatically fix any issues that are found. You can also use the `task
|
||||||
`task fmt` command to auto-format the files if your editor doesn't do it for
|
fmt` command to auto-format the files if your editor doesn't do it for you.
|
||||||
you.
|
|
||||||
- **Documentation** - Ensure that you add/update any relevant documentation. See
|
- **Documentation** - Ensure that you add/update any relevant documentation. See
|
||||||
the [updating documentation](#updating-documentation) section below.
|
the [updating documentation](#updating-documentation) section below.
|
||||||
- **Tests** - Ensure that you add/update any relevant tests and that all tests
|
- **Tests** - Ensure that you add/update any relevant tests and that all tests
|
||||||
@@ -79,23 +74,24 @@ install the extension.
|
|||||||
|
|
||||||
### Updating documentation
|
### Updating documentation
|
||||||
|
|
||||||
Task uses [Vitepress][vitepress] to host a documentation server. The code for
|
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
|
this is located in the core Task repository. This can be setup and run locally
|
||||||
by using `task website` (requires `nodejs` & `pnpm`). All content is written in
|
by using `task website` (requires `nodejs` & `yarn`). All content is written in
|
||||||
Markdown and is located in the `website/src` directory. All Markdown documents
|
[MDX][mdx] (an extension of Markdown) and is located in the `website/docs`
|
||||||
should have an 80 character line wrap limit (enforced by Prettier).
|
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](/docs/guide) is
|
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
|
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
|
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
|
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
|
documentation and any examples are up-to-date. Ensure that any examples follow
|
||||||
the [Taskfile Styleguide](./styleguide.md).
|
the [Taskfile Styleguide](/styleguide).
|
||||||
|
|
||||||
If you added a new command or flag, ensure that you add it to the
|
If you added a new command or flag, ensure that you add it to the [CLI
|
||||||
[CLI Reference](./reference/cli.md). New fields also need to be added to the
|
Reference](/reference/cli). New fields also need to be added to the [Schema
|
||||||
[Schema Reference](./reference/schema.md) and [JSON Schema][json-schema]. The
|
Reference](/reference/schema) and [JSON Schema][json-schema]. The descriptions
|
||||||
descriptions for fields in the docs and the schema should match.
|
for fields in the docs and the schema should match.
|
||||||
|
|
||||||
### Writing tests
|
### Writing tests
|
||||||
|
|
||||||
@@ -147,7 +143,7 @@ contributions.
|
|||||||
|
|
||||||
All kinds of contributions are welcome, whether its a typo fix or a shiny new
|
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
|
feature. You can also contribute by upvoting/commenting on issues, helping to
|
||||||
answer questions or contributing to other [community projects](./community.md).
|
answer questions or contributing to other [community projects](/community).
|
||||||
|
|
||||||
> I'm stuck, where can I get help?
|
> I'm stuck, where can I get help?
|
||||||
|
|
||||||
@@ -156,6 +152,9 @@ If you have questions, feel free to ask them in the `#help` forum channel on our
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{/* prettier-ignore-start */}
|
||||||
|
[experiments]: /experiments
|
||||||
|
[experiments-workflow]: /experiments#workflow
|
||||||
[task]: https://github.com/go-task/task
|
[task]: https://github.com/go-task/task
|
||||||
[vscode-task]: https://github.com/go-task/vscode-task
|
[vscode-task]: https://github.com/go-task/vscode-task
|
||||||
[go]: https://go.dev
|
[go]: https://go.dev
|
||||||
@@ -165,15 +164,14 @@ If you have questions, feel free to ask them in the `#help` forum channel on our
|
|||||||
[golangci-lint-docs]: https://golangci-lint.run/welcome/integrations/
|
[golangci-lint-docs]: https://golangci-lint.run/welcome/integrations/
|
||||||
[prettier]: https://prettier.io
|
[prettier]: https://prettier.io
|
||||||
[nodejs]: https://nodejs.org/en/
|
[nodejs]: https://nodejs.org/en/
|
||||||
[pnpm]: https://pnpm.io/
|
[yarn]: https://yarnpkg.com/
|
||||||
[vitepress]: https://vitepress.dev
|
[docusaurus]: https://docusaurus.io
|
||||||
[json-schema]:
|
[json-schema]: https://github.com/go-task/task/blob/main/website/static/schema.json
|
||||||
https://github.com/go-task/task/blob/main/website/static/schema.json
|
|
||||||
[task-open-issues]: https://github.com/go-task/task/issues
|
[task-open-issues]: https://github.com/go-task/task/issues
|
||||||
[vscode-task-open-issues]: https://github.com/go-task/vscode-task/issues
|
[vscode-task-open-issues]: https://github.com/go-task/vscode-task/issues
|
||||||
[good-first-issue]:
|
[good-first-issue]: https://github.com/go-task/task/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22
|
||||||
https://github.com/go-task/task/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22
|
|
||||||
[discord-server]: https://discord.gg/6TY36E39UK
|
[discord-server]: https://discord.gg/6TY36E39UK
|
||||||
[discussion]: https://github.com/go-task/task/discussions
|
[discussion]: https://github.com/go-task/task/discussions
|
||||||
[conventional-commits]: https://www.conventionalcommits.org
|
[conventional-commits]: https://www.conventionalcommits.org
|
||||||
[mdx]: https://mdxjs.com/
|
[mdx]: https://mdxjs.com/
|
||||||
|
{/* prettier-ignore-end */}
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: 'Completion Scripts'
|
slug: /deprecations/completion-scripts/
|
||||||
description: Deprecation of direct completion scripts in Task’s Git directory
|
|
||||||
outline: deep
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Completion Scripts
|
# Completion Scripts
|
||||||
|
|
||||||
::: danger
|
:::warning
|
||||||
|
|
||||||
This deprecation breaks the following functionality:
|
This deprecation breaks the following functionality:
|
||||||
|
|
||||||
@@ -21,5 +19,7 @@ the future as the scripts may be moved or deleted entirely. Any configuration
|
|||||||
should be updated to use the [new method for generating shell
|
should be updated to use the [new method for generating shell
|
||||||
completions][completions] instead.
|
completions][completions] instead.
|
||||||
|
|
||||||
[completions]: /docs/installation#setup-completions
|
{/* prettier-ignore-start */}
|
||||||
|
[completions]: ../installation.mdx#setup-completions
|
||||||
[task]: https://github.com/go-task/task
|
[task]: https://github.com/go-task/task
|
||||||
|
{/* prettier-ignore-end */}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Deprecations
|
slug: /deprecations/
|
||||||
description:
|
sidebar_position: 8
|
||||||
Guide to deprecated features in Task and how to migrate to the new
|
|
||||||
alternatives
|
|
||||||
outline: deep
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Deprecations
|
# Deprecations
|
||||||
23
website/docs/deprecations/template.mdx
Normal file
23
website/docs/deprecations/template.mdx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
# 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,14 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: 'Template Functions'
|
slug: /deprecations/template-functions/
|
||||||
description:
|
|
||||||
Deprecation of some templating functions in Task, with guidance on their
|
|
||||||
replacements.
|
|
||||||
outline: deep
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Template Functions
|
# Template Functions
|
||||||
|
|
||||||
::: danger
|
:::warning
|
||||||
|
|
||||||
This deprecation breaks the following functionality:
|
This deprecation breaks the following functionality:
|
||||||
|
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: 'Version 2 Schema (#1197)'
|
slug: /deprecations/version-2-schema/
|
||||||
description: Deprecation of Taskfile schema version 2 and migration to version 3
|
|
||||||
outline: deep
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Version 2 Schema (#1197)
|
# Version 2 Schema (#1197)
|
||||||
|
|
||||||
::: danger
|
:::warning
|
||||||
|
|
||||||
This deprecation breaks the following functionality:
|
This deprecation breaks the following functionality:
|
||||||
|
|
||||||
@@ -28,6 +26,8 @@ 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
|
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].
|
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.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
|
[v3.33.0]: https://github.com/go-task/task/releases/tag/v3.33.0
|
||||||
[deprecation-notice]: https://github.com/go-task/task/issues/1197
|
[deprecation-notice]: https://github.com/go-task/task/issues/1197
|
||||||
|
{/* prettier-ignore-end */}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: 'Env Precedence (#1038)'
|
slug: '/experiments/env-precedence'
|
||||||
description:
|
|
||||||
Experiment to change the precedence of environment variables in Task
|
|
||||||
outline: deep
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Env Precedence (#1038)
|
# Env Precedence (#1038)
|
||||||
|
|
||||||
::: warning
|
:::caution
|
||||||
|
|
||||||
All experimental features are subject to breaking changes and/or removal _at any
|
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
|
time_. We strongly recommend that you do not use these features in a production
|
||||||
@@ -15,7 +12,7 @@ environment. They are intended for testing and feedback only.
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: danger
|
:::warning
|
||||||
|
|
||||||
This experiment breaks the following functionality:
|
This experiment breaks the following functionality:
|
||||||
|
|
||||||
@@ -23,12 +20,11 @@ This experiment breaks the following functionality:
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: info
|
:::info
|
||||||
|
|
||||||
To enable this experiment, set the environment variable:
|
To enable this experiment, set the environment variable:
|
||||||
`TASK_X_ENV_PRECEDENCE=1`. Check out
|
`TASK_X_ENV_PRECEDENCE=1`. Check out [our guide to enabling
|
||||||
[our guide to enabling experiments](./index.md#enabling-experiments) for more
|
experiments][enabling-experiments] for more information.
|
||||||
information.
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@@ -48,12 +44,11 @@ tasks:
|
|||||||
cmds:
|
cmds:
|
||||||
- echo "$KEY"
|
- echo "$KEY"
|
||||||
```
|
```
|
||||||
|
|
||||||
Running `KEY=some task` before this experiment, the output would be `some`, but
|
Running `KEY=some task` before this experiment, the output would be `some`, but
|
||||||
after this experiment, the output would be `other`.
|
after this experiment, the output would be `other`.
|
||||||
|
|
||||||
If you still want to get the OS variable, you can use the template function env
|
If you still want to get the OS variable, you can use the template function env
|
||||||
like follow : <span v-pre>`{{env "OS_VAR"}}`</span>.
|
like follow : `{{env "OS_VAR"}}`.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: '3'
|
version: '3'
|
||||||
@@ -66,12 +61,14 @@ tasks:
|
|||||||
- echo "$KEY"
|
- echo "$KEY"
|
||||||
- echo {{env "KEY"}}
|
- echo {{env "KEY"}}
|
||||||
```
|
```
|
||||||
|
|
||||||
Running `KEY=some task`, the output would be `other` and `some`.
|
Running `KEY=some task`, the output would be `other` and `some`.
|
||||||
|
|
||||||
Like other variables/envs, you can also fall back to a given value using the
|
Like other variables/envs, you can also fall back to a given value using the
|
||||||
default template function:
|
default template function:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
MY_ENV: '{{.MY_ENV | default "fallback"}}'
|
MY_ENV: '{{.MY_ENV | default "fallback"}}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
{/* prettier-ignore-start */}
|
||||||
|
[enabling-experiments]: ./experiments.mdx#enabling-experiments
|
||||||
|
{/* prettier-ignore-end */}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user