mirror of
https://github.com/go-task/task.git
synced 2026-05-18 21:26:37 +02:00
Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbde227167 | ||
|
|
fc06e92a87 | ||
|
|
a0cab3f5ec | ||
|
|
bb4c254211 | ||
|
|
57bf348829 | ||
|
|
092b9b6391 | ||
|
|
cd8c831204 | ||
|
|
0d03f4f266 | ||
|
|
b8bf298c84 | ||
|
|
9a91c4cb21 | ||
|
|
2921450bf7 | ||
|
|
dffa355cad | ||
|
|
48039be12c | ||
|
|
43cb64e6cc | ||
|
|
25a7b5936f | ||
|
|
4ae3071845 | ||
|
|
242523c797 | ||
|
|
0fdb5e8665 | ||
|
|
534dfa089c | ||
|
|
51a3bcaacd | ||
|
|
6289fcf34c | ||
|
|
2959737d7d | ||
|
|
a3047d3cd8 | ||
|
|
725600f220 | ||
|
|
fd83414074 | ||
|
|
6c645a33f7 | ||
|
|
9d969e5971 | ||
|
|
8b382a3bae | ||
|
|
a34892ad94 | ||
|
|
e55bb29554 | ||
|
|
1168ef32df | ||
|
|
245d7f747f | ||
|
|
b216ae885c | ||
|
|
61cb15ad01 | ||
|
|
04579c0c44 | ||
|
|
39462cbfde | ||
|
|
72dfec68b0 | ||
|
|
f89c12ddf0 | ||
|
|
c903d07332 | ||
|
|
138b9a5a4f | ||
|
|
1e2121a99f | ||
|
|
9495fb2b1c | ||
|
|
1fda55910e | ||
|
|
e6c808c02b | ||
|
|
0fc26a43a9 | ||
|
|
c0b4c19443 | ||
|
|
1a8df44e9e | ||
|
|
82ad1de8d0 | ||
|
|
d59c795502 | ||
|
|
504cb94e8b | ||
|
|
e7606635fe | ||
|
|
9a05ceaa80 | ||
|
|
083654d8c9 | ||
|
|
79c93fb42b | ||
|
|
64fc538a16 | ||
|
|
4da081e5c3 | ||
|
|
4bdfe5ce3b | ||
|
|
26ef693417 | ||
|
|
952f32d388 | ||
|
|
e72c35f79f | ||
|
|
72991d4f04 | ||
|
|
6f965e3043 | ||
|
|
1c6d686356 | ||
|
|
dac5aa1954 | ||
|
|
303bd6ccb2 | ||
|
|
f736cfaaf1 | ||
|
|
53f97889bc | ||
|
|
fe2da74ea3 | ||
|
|
64fb66895b | ||
|
|
d2bd834c81 | ||
|
|
8a43ca5d8f | ||
|
|
a10a9faabf | ||
|
|
3d3ed0e403 | ||
|
|
47dc87a2c9 | ||
|
|
3b0a746f85 | ||
|
|
281edfe5b3 | ||
|
|
7289ffce0b | ||
|
|
61e1af50ff | ||
|
|
715a143735 | ||
|
|
a0b1605634 | ||
|
|
69fc13bd13 | ||
|
|
b42a52ba77 |
@@ -8,6 +8,6 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = tab
|
||||
|
||||
[*.{md,mdx,yml,yaml,json,toml,htm,html,js,ts,css,svg,sh,bash,fish}]
|
||||
[*.{md,mdx,yml,yaml,json,toml,htm,html,js,ts,vue,css,svg,sh,bash,fish}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
41
.github/workflows/lint.yml
vendored
41
.github/workflows/lint.yml
vendored
@@ -13,14 +13,14 @@ jobs:
|
||||
name: Lint
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x, 1.24.x]
|
||||
go-version: [1.24.x, 1.25.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{matrix.go-version}}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
@@ -32,43 +32,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
python-version: 3.13
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: install check-jsonschema
|
||||
run: python -m pip install 'check-jsonschema==0.27.3'
|
||||
|
||||
- name: check-jsonschema (metaschema)
|
||||
run: check-jsonschema --check-metaschema website/static/schema.json
|
||||
check_doc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Get changed files in the docs folder
|
||||
id: changed-files-specific
|
||||
uses: tj-actions/changed-files@v46
|
||||
with:
|
||||
files: website/versioned_docs/**
|
||||
|
||||
- uses: actions/github-script@v7
|
||||
if: steps.changed-files-specific.outputs.any_changed == 'true'
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('website/versioned_docs has changed. Instead you need to update the docs in the website/docs folder.')
|
||||
check_schema:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Get changed files in the docs folder
|
||||
id: changed-files-specific
|
||||
uses: tj-actions/changed-files@v46
|
||||
with:
|
||||
files: |
|
||||
website/static/schema.json
|
||||
website/static/schema-taskrc.json
|
||||
- uses: actions/github-script@v7
|
||||
if: steps.changed-files-specific.outputs.any_changed == 'true'
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('schema.json or schema-taskrc.json has changed. Instead you need to update next-schema.json or next-schema-taskrc.json.')
|
||||
run: check-jsonschema --check-metaschema website/src/public/schema.json
|
||||
|
||||
30
.github/workflows/release-nightly.yml
vendored
Normal file
30
.github/workflows/release-nightly.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Release nightly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 0 0 * * *
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.25.x
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: latest
|
||||
args: release --clean --nightly -f .goreleaser-nightly.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
||||
CLOUDSMITH_TOKEN: ${{secrets.CLOUDSMITH_TOKEN}}
|
||||
27
.github/workflows/release.yml
vendored
27
.github/workflows/release.yml
vendored
@@ -10,21 +10,42 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.25.x
|
||||
|
||||
- name: npm-login
|
||||
run: |
|
||||
npm config set '//registry.npmjs.org/:_authToken'=${{ secrets.NPM_TOKEN }}
|
||||
- name: Install Task
|
||||
uses: go-task/setup-task@v1
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.x'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: latest
|
||||
args: release --clean
|
||||
args: release --clean --draft
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
||||
CLOUDSMITH_TOKEN: ${{secrets.CLOUDSMITH_TOKEN}}
|
||||
|
||||
- name: Deploy Website
|
||||
shell: bash
|
||||
run: |
|
||||
task website:deploy:prod
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Test
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.23.x, 1.24.x]
|
||||
go-version: [1.24.x, 1.25.x]
|
||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{matrix.platform}}
|
||||
steps:
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Download Go modules
|
||||
run: go mod download
|
||||
|
||||
15
.goreleaser-nightly.yml
Normal file
15
.goreleaser-nightly.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||
version: 2
|
||||
pro: true
|
||||
|
||||
release:
|
||||
name_template: 'v{{.Version}}'
|
||||
|
||||
nightly:
|
||||
publish_release: true
|
||||
keep_single_release: true
|
||||
version_template: "{{incminor .Version}}-nightly"
|
||||
|
||||
includes:
|
||||
- from_file:
|
||||
path: ./.goreleaser.yml
|
||||
@@ -30,13 +30,14 @@ builds:
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w # Don't set main.version.
|
||||
- "-s -w"
|
||||
- "{{if .IsNightly}}-X github.com/go-task/task/v3/internal/version.version={{.Version}}{{end}}"
|
||||
|
||||
gomod:
|
||||
proxy: true
|
||||
|
||||
archives:
|
||||
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
|
||||
- name_template: '{{.Binary}}_{{.Os}}_{{.Arch}}'
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
@@ -45,27 +46,30 @@ archives:
|
||||
- goos: windows
|
||||
formats: [zip]
|
||||
|
||||
release:
|
||||
draft: true
|
||||
git:
|
||||
ignore_tags:
|
||||
- "{{if not .IsNightly}}nightly{{end}}"
|
||||
|
||||
snapshot:
|
||||
version_template: "{{.Version}}"
|
||||
version_template: '{{.Version}}'
|
||||
|
||||
checksum:
|
||||
name_template: "task_checksums.txt"
|
||||
name_template: 'task_checksums.txt'
|
||||
|
||||
nfpms:
|
||||
- vendor: Task
|
||||
homepage: https://taskfile.dev
|
||||
maintainer: The Task authors <task@taskfile.dev>
|
||||
description: Simple task runner written in Go
|
||||
section: golang
|
||||
license: MIT
|
||||
conflicts:
|
||||
- taskwarrior
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
|
||||
- apk
|
||||
file_name_template: '{{.ProjectName}}_{{.Os}}_{{.Arch}}'
|
||||
contents:
|
||||
- src: completion/bash/task.bash
|
||||
dst: /etc/bash_completion.d/task
|
||||
@@ -83,8 +87,7 @@ brews:
|
||||
repository:
|
||||
owner: go-task
|
||||
name: homebrew-tap
|
||||
test:
|
||||
system "#{bin}/task", "--help"
|
||||
test: system "#{bin}/task", "--help"
|
||||
install: |-
|
||||
bin.install "task"
|
||||
bash_completion.install "completion/bash/task.bash" => "task"
|
||||
@@ -107,7 +110,7 @@ winget:
|
||||
commit_author:
|
||||
name: task-bot
|
||||
email: 106601941+task-bot@users.noreply.github.com
|
||||
commit_msg_template: "chore: bump {{.PackageIdentifier}} to {{.Tag}}"
|
||||
commit_msg_template: 'chore: release {{.PackageIdentifier}} {{.Tag}}'
|
||||
release_notes_url: https://github.com/go-task/task/releases/tag/{{.Tag}}
|
||||
tags:
|
||||
- build
|
||||
@@ -121,13 +124,49 @@ winget:
|
||||
- task-runner
|
||||
- taskfile
|
||||
- tool
|
||||
skip_upload: true
|
||||
repository:
|
||||
owner: microsoft
|
||||
owner: go-task
|
||||
name: winget-pkgs
|
||||
branch: 'chore/task-{{.Version}}'
|
||||
pull_request:
|
||||
enabled: true
|
||||
draft: false
|
||||
check_boxes: true
|
||||
base:
|
||||
owner: go-task
|
||||
owner: microsoft
|
||||
name: winget-pkgs
|
||||
branch: "bump-task-to-{{.Tag}}"
|
||||
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
|
||||
|
||||
64
CHANGELOG.md
64
CHANGELOG.md
@@ -1,5 +1,69 @@
|
||||
# Changelog
|
||||
|
||||
## v3.45.1 - 2025-09-15
|
||||
|
||||
- Task now includes built-in core utilities to greatly improve compatibility on
|
||||
Windows. This means that your commands that uses `cp`, `mv`, `mkdir` or any
|
||||
other common core utility will now work by default on Windows, without extra
|
||||
setup. This is something we wanted to address for many many years, and it's
|
||||
finally being shipped!
|
||||
[Read our blog post this the topic](https://taskfile.dev/blog/windows-core-utils).
|
||||
(#197, #2360 by @andreynering).
|
||||
- :sparkles: Built and deployed a [brand new website](https://taskfile.dev)
|
||||
using [VitePress](https://vitepress.dev) (#2359, #2369, #2371, #2375, #2378 by
|
||||
@vmaerten, @andreynering, @pd93).
|
||||
- Began releasing
|
||||
[nightly builds](https://github.com/go-task/task/releases/tag/nightly). This
|
||||
will allow people to test our changes before they are fully released and
|
||||
without having to install Go to build them (#2358 by @vmaerten).
|
||||
- Added support for global config files in `$XDG_CONFIG_HOME/task/taskrc.yml` or
|
||||
`$HOME/.taskrc.yml`. Check out our new
|
||||
[configuration guide](https://taskfile.dev/docs/reference/config) for more
|
||||
details (#2247, #2380, #2390, #2391 by @vmaerten, @pd93).
|
||||
- Added experiments to the taskrc schema to clarify the expected keys and values
|
||||
(#2235 by @vmaerten).
|
||||
- Added support for new properties in `.taskrc.yml`: insecure, verbose,
|
||||
concurrency, remote offline, remote timeout, and remote expiry. :warning:
|
||||
Note: setting offline via environment variable is no longer supported. (#2389
|
||||
by @vmaerten)
|
||||
- Added a `--nested` flag when outputting tasks using `--list --json`. This will
|
||||
output tasks in a nested structure when tasks are namespaced (#2415 by @pd93).
|
||||
- Enhanced support for tasks with wildcards: they are now logged correctly, and
|
||||
wildcard parameters are fully considered during fingerprinting (#1808, #1795
|
||||
by @vmaerten).
|
||||
- Fixed panic when a variable was declared as an empty hash (`{}`) (#2416, #2417
|
||||
by @trulede).
|
||||
|
||||
#### Package API
|
||||
|
||||
- Bumped the minimum version of Go to 1.24 (#2358 by @vmaerten).
|
||||
|
||||
#### Other news
|
||||
|
||||
We recently released our
|
||||
[official GitHub Action](https://github.com/go-task/setup-task). This is based
|
||||
on the fantastic work by the Arduino team who created and maintained the
|
||||
community version. Now that this is officially adopted, fixes/updates should be
|
||||
more timely. We have already merged a couple of longstanding PRs in our
|
||||
[first release](https://github.com/go-task/setup-task/releases/tag/v1.0.0) (by
|
||||
@pd93, @shrink, @trim21 and all the previous contributors to
|
||||
[arduino/setup-task](https://github.com/arduino/setup-task/)).
|
||||
|
||||
## v3.45.0 - 2025-09-15
|
||||
|
||||
Failed due to an issue with our release process.
|
||||
|
||||
## v3.44.1 - 2025-07-23
|
||||
|
||||
- Internal tasks will no longer be shown as suggestions since they cannot be
|
||||
called (#2309, #2323 by @maxmzkrcensys)
|
||||
- Fixed install script for some ARM platforms (#1516, #2291 by @trulede).
|
||||
- Fixed a regression where fingerprinting was not working correctly if the path
|
||||
to you Taskfile contained a space (#2321, #2322 by @pd93).
|
||||
- Reverted a breaking change to `randInt` (#2312, #2316 by @pd93).
|
||||
- Made new variables `TEST_NAME` and `TEST_DIR` available in fixture tests
|
||||
(#2265 by @pd93).
|
||||
|
||||
## v3.44.0 - 2025-06-08
|
||||
|
||||
- Added `uuid`, `randInt` and `randIntN` template functions (#1346, #2225 by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div align="center">
|
||||
<a href="https://taskfile.dev">
|
||||
<img src="website/static/img/logo.svg" width="200px" height="200px" />
|
||||
<img src="website/src/public/img/logo.svg" width="200px" height="200px" />
|
||||
</a>
|
||||
|
||||
<h1>Task</h1>
|
||||
@@ -19,7 +19,7 @@
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a target="_blank" href="https://devowl.io">
|
||||
<img src="/website/static/img/devowl.io.svg" height="100px" title="devowl.io" />
|
||||
<img src="https://devowl.io/wp-content/uploads/meta/favicon.webp" height="100px" title="devowl.io" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
32
Taskfile.yml
32
Taskfile.yml
@@ -8,6 +8,7 @@ includes:
|
||||
|
||||
vars:
|
||||
BIN: "{{.ROOT_DIR}}/bin"
|
||||
GOTESTSUM_FORMAT: '{{if .CI}}github-actions{{else}}pkgname{{end}}'
|
||||
|
||||
env:
|
||||
CGO_ENABLED: '0'
|
||||
@@ -53,9 +54,12 @@ tasks:
|
||||
generate:fixtures:
|
||||
desc: Runs tests and generates golden fixture files
|
||||
aliases: [gen:fixtures, g:fixtures]
|
||||
env:
|
||||
GOLDIE_UPDATE: 'true'
|
||||
GOLDIE_TEMPLATE: 'true'
|
||||
cmds:
|
||||
- find ./testdata -name '*.golden' -delete
|
||||
- go test -update ./...
|
||||
- go test ./...
|
||||
|
||||
install:mockery:
|
||||
desc: Installs mockgen; a tool to generate mock files
|
||||
@@ -87,6 +91,7 @@ tasks:
|
||||
sources:
|
||||
- './**/*.go'
|
||||
- .golangci.yml
|
||||
- go.mod
|
||||
cmds:
|
||||
- golangci-lint run
|
||||
|
||||
@@ -95,6 +100,7 @@ tasks:
|
||||
sources:
|
||||
- './**/*.go'
|
||||
- .golangci.yml
|
||||
- go.mod
|
||||
cmds:
|
||||
- golangci-lint run --fix
|
||||
|
||||
@@ -126,29 +132,37 @@ tasks:
|
||||
test:
|
||||
desc: Runs test suite
|
||||
aliases: [t]
|
||||
deps: [gotestsum:install]
|
||||
sources:
|
||||
- "**/*.go"
|
||||
- "testdata/**/*"
|
||||
cmds:
|
||||
- go test ./...
|
||||
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' ./...
|
||||
|
||||
test:watch:
|
||||
desc: Runs test suite with watch tests included
|
||||
deps: [sleepit:build]
|
||||
deps: [sleepit:build, gotestsum:install]
|
||||
cmds:
|
||||
- go test ./... -tags 'watch'
|
||||
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' ./... -tags 'watch'
|
||||
|
||||
test:all:
|
||||
desc: Runs test suite with signals and watch tests included
|
||||
deps: [sleepit:build]
|
||||
deps: [sleepit:build, gotestsum:install]
|
||||
cmds:
|
||||
- go test -tags 'signals watch' ./...
|
||||
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' -tags 'signals watch' ./...
|
||||
|
||||
goreleaser:test:
|
||||
desc: Tests release process without publishing
|
||||
cmds:
|
||||
- goreleaser --snapshot --clean
|
||||
|
||||
gotestsum:install:
|
||||
desc: Installs gotestsum
|
||||
status:
|
||||
- command -v gotestsum
|
||||
cmds:
|
||||
- go install gotest.tools/gotestsum@latest
|
||||
|
||||
goreleaser:install:
|
||||
desc: Installs goreleaser
|
||||
cmds:
|
||||
@@ -198,7 +212,6 @@ tasks:
|
||||
Please wait for the CI to finish and then do the following:
|
||||
|
||||
- Copy the changelog for v{{.VERSION}} to the GitHub release
|
||||
- Publish the package to NPM with `task npm:publish`
|
||||
- Update and push the snapcraft manifest in https://github.com/go-task/snap/blob/main/snap/snapcraft.yaml
|
||||
preconditions:
|
||||
- sh: test $(git rev-parse --abbrev-ref HEAD) = "main"
|
||||
@@ -217,8 +230,3 @@ tasks:
|
||||
- "git push origin tag v{{.VERSION}}"
|
||||
- cmd: printf "%s" '{{.COMPLETE_MESSAGE}}'
|
||||
silent: true
|
||||
|
||||
npm:publish:
|
||||
desc: Publish release to npm
|
||||
cmds:
|
||||
- npm publish --access=public
|
||||
|
||||
@@ -3,33 +3,23 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/otiai10/copy"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/go-task/task/v3/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
changelogSource = "CHANGELOG.md"
|
||||
changelogTarget = "website/docs/changelog.mdx"
|
||||
docsSource = "website/docs"
|
||||
docsTarget = "website/versioned_docs/version-latest"
|
||||
schemaSource = "website/static/next-schema.json"
|
||||
schemaTarget = "website/static/schema.json"
|
||||
schemaTaskrcSource = "website/static/next-schema-taskrc.json"
|
||||
schemaTaskrcTarget = "website/static/schema-taskrc.json"
|
||||
changelogSource = "CHANGELOG.md"
|
||||
changelogTarget = "website/src/docs/changelog.md"
|
||||
versionFile = "internal/version/version.txt"
|
||||
)
|
||||
|
||||
var (
|
||||
changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
|
||||
versionRegex = regexp.MustCompile(`(?m)^ "version": "\d+\.\d+\.\d+",$`)
|
||||
)
|
||||
var changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
|
||||
|
||||
// Flags
|
||||
var (
|
||||
@@ -53,7 +43,7 @@ func release() error {
|
||||
return errors.New("error: expected version number")
|
||||
}
|
||||
|
||||
version, err := getVersion()
|
||||
version, err := getVersion(versionFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -71,36 +61,18 @@ func release() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := setVersionFile("internal/version/version.txt", version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := setJSONVersion("package.json", version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := setJSONVersion("package-lock.json", version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := docs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := schema(); err != nil {
|
||||
if err := setVersionFile(versionFile, version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getVersion() (*semver.Version, error) {
|
||||
cmd := exec.Command("git", "describe", "--tags", "--abbrev=0")
|
||||
b, err := cmd.Output()
|
||||
func getVersion(filename string) (*semver.Version, error) {
|
||||
b, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return semver.NewVersion(strings.TrimSpace(string(b)))
|
||||
}
|
||||
|
||||
@@ -159,37 +131,3 @@ func changelog(version *semver.Version) error {
|
||||
func setVersionFile(fileName string, version *semver.Version) error {
|
||||
return os.WriteFile(fileName, []byte(version.String()+"\n"), 0o644)
|
||||
}
|
||||
|
||||
func setJSONVersion(fileName string, version *semver.Version) error {
|
||||
// Read the JSON file
|
||||
b, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Replace the version
|
||||
new := versionRegex.ReplaceAllString(string(b), fmt.Sprintf(` "version": "%s",`, version.String()))
|
||||
|
||||
// Write the JSON file
|
||||
return os.WriteFile(fileName, []byte(new), 0o644)
|
||||
}
|
||||
|
||||
func docs() error {
|
||||
if err := os.RemoveAll(docsTarget); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copy.Copy(docsSource, docsTarget); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func schema() error {
|
||||
if err := copy.Copy(schemaSource, schemaTarget); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copy.Copy(schemaTaskrcSource, schemaTaskrcTarget); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@ func run() error {
|
||||
flags.ListAll,
|
||||
flags.ListJson,
|
||||
flags.NoStatus,
|
||||
flags.Nested,
|
||||
)
|
||||
if listOptions.ShouldListTasks() {
|
||||
if flags.Silent {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -46,8 +47,9 @@ func (err *TaskRunError) Code() int {
|
||||
}
|
||||
|
||||
func (err *TaskRunError) TaskExitCode() int {
|
||||
if c, ok := interp.IsExitStatus(err.Err); ok {
|
||||
return int(c)
|
||||
var exit interp.ExitStatus
|
||||
if errors.As(err.Err, &exit) {
|
||||
return int(exit)
|
||||
}
|
||||
return err.Code()
|
||||
}
|
||||
@@ -160,7 +162,7 @@ func (v MissingVar) String() string {
|
||||
}
|
||||
|
||||
func (err *TaskMissingRequiredVarsError) Error() string {
|
||||
var vars []string
|
||||
vars := make([]string, 0, len(err.MissingVars))
|
||||
for _, v := range err.MissingVars {
|
||||
vars = append(vars, v.String())
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package task_test
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -50,7 +49,8 @@ func NewExecutorTest(t *testing.T, opts ...ExecutorTestOption) {
|
||||
task: "default",
|
||||
vars: map[string]any{},
|
||||
TaskTest: TaskTest{
|
||||
experiments: map[*experiments.Experiment]int{},
|
||||
experiments: map[*experiments.Experiment]int{},
|
||||
fixtureTemplateData: map[string]any{},
|
||||
},
|
||||
}
|
||||
// Apply the functional options
|
||||
@@ -188,7 +188,7 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
||||
}
|
||||
|
||||
// Run the task and check for errors
|
||||
ctx := context.Background()
|
||||
ctx := t.Context()
|
||||
if err := e.Run(ctx, call); tt.wantRunError {
|
||||
require.Error(t, err)
|
||||
tt.writeFixtureErrRun(t, g, err)
|
||||
@@ -232,7 +232,7 @@ func TestEmptyTaskfile(t *testing.T) {
|
||||
task.WithDir("testdata/empty_taskfile"),
|
||||
),
|
||||
WithSetupError(),
|
||||
WithPostProcessFn(PPRemoveAbsolutePaths),
|
||||
WithFixtureTemplating(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ func TestSpecialVars(t *testing.T) {
|
||||
task.WithVersionCheck(true),
|
||||
),
|
||||
WithTask(test),
|
||||
WithPostProcessFn(PPRemoveAbsolutePaths),
|
||||
WithFixtureTemplating(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -551,7 +551,7 @@ func TestStatus(t *testing.T) {
|
||||
task.WithVerbose(true),
|
||||
),
|
||||
WithTask("gen-silent-baz"),
|
||||
WithPostProcessFn(PPRemoveAbsolutePaths),
|
||||
WithFixtureTemplating(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -777,7 +777,7 @@ func TestForCmds(t *testing.T) {
|
||||
task.WithForce(true),
|
||||
),
|
||||
WithTask(test.name),
|
||||
WithPostProcessFn(PPRemoveAbsolutePaths),
|
||||
WithFixtureTemplating(),
|
||||
}
|
||||
if test.wantErr {
|
||||
opts = append(opts, WithRunError())
|
||||
@@ -822,7 +822,7 @@ func TestForDeps(t *testing.T) {
|
||||
task.WithOutputStyle(ast.Output{Name: "group"}),
|
||||
),
|
||||
WithTask(test.name),
|
||||
WithPostProcessFn(PPRemoveAbsolutePaths),
|
||||
WithFixtureTemplating(),
|
||||
WithPostProcessFn(PPSortedLines),
|
||||
}
|
||||
if test.wantErr {
|
||||
@@ -957,6 +957,15 @@ func TestFuzzyModel(t *testing.T) {
|
||||
),
|
||||
WithTask("install"),
|
||||
)
|
||||
|
||||
NewExecutorTest(t,
|
||||
WithName("intern"),
|
||||
WithExecutorOptions(
|
||||
task.WithDir("testdata/fuzzy"),
|
||||
),
|
||||
WithTask("intern"),
|
||||
WithRunError(),
|
||||
)
|
||||
}
|
||||
|
||||
func TestIncludeChecksum(t *testing.T) {
|
||||
@@ -975,6 +984,6 @@ func TestIncludeChecksum(t *testing.T) {
|
||||
task.WithDir("testdata/includes_checksum/incorrect"),
|
||||
),
|
||||
WithSetupError(),
|
||||
WithPostProcessFn(PPRemoveAbsolutePaths),
|
||||
WithFixtureTemplating(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/joho/godotenv"
|
||||
|
||||
"github.com/go-task/task/v3/taskrc"
|
||||
"github.com/go-task/task/v3/taskrc/ast"
|
||||
)
|
||||
|
||||
const envPrefix = "TASK_X_"
|
||||
@@ -31,16 +32,15 @@ var (
|
||||
var xList []Experiment
|
||||
|
||||
func Parse(dir string) {
|
||||
config, _ := taskrc.GetConfig(dir)
|
||||
|
||||
ParseWithConfig(dir, config)
|
||||
}
|
||||
|
||||
func ParseWithConfig(dir string, config *ast.TaskRC) {
|
||||
// Read any .env files
|
||||
readDotEnv(dir)
|
||||
|
||||
// Create a node for the Task config reader
|
||||
node, _ := taskrc.NewNode("", dir)
|
||||
|
||||
// Read the Task config file
|
||||
reader := taskrc.NewReader()
|
||||
config, _ := reader.Read(node)
|
||||
|
||||
// Initialize the experiments
|
||||
GentleForce = New("GENTLE_FORCE", config, 1)
|
||||
RemoteTaskfiles = New("REMOTE_TASKFILES", config, 1)
|
||||
|
||||
@@ -44,7 +44,8 @@ func NewFormatterTest(t *testing.T, opts ...FormatterTestOption) {
|
||||
task: "default",
|
||||
vars: map[string]any{},
|
||||
TaskTest: TaskTest{
|
||||
experiments: map[*experiments.Experiment]int{},
|
||||
experiments: map[*experiments.Experiment]int{},
|
||||
fixtureTemplateData: map[string]any{},
|
||||
},
|
||||
}
|
||||
// Apply the functional options
|
||||
@@ -222,8 +223,6 @@ func TestListDescInterpolation(t *testing.T) {
|
||||
func TestJsonListFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fp, err := filepath.Abs("testdata/json_list_format/Taskfile.yml")
|
||||
require.NoError(t, err)
|
||||
NewFormatterTest(t,
|
||||
WithExecutorOptions(
|
||||
task.WithDir("testdata/json_list_format"),
|
||||
@@ -231,10 +230,6 @@ func TestJsonListFormat(t *testing.T) {
|
||||
WithListOptions(task.ListOptions{
|
||||
FormatTaskListAsJSON: true,
|
||||
}),
|
||||
WithFixtureTemplateData(struct {
|
||||
TaskfileLocation string
|
||||
}{
|
||||
TaskfileLocation: fp,
|
||||
}),
|
||||
WithFixtureTemplating(),
|
||||
)
|
||||
}
|
||||
|
||||
33
go.mod
33
go.mod
@@ -1,11 +1,11 @@
|
||||
module github.com/go-task/task/v3
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/Ladicle/tabwriter v1.0.0
|
||||
github.com/Masterminds/semver/v3 v3.3.1
|
||||
github.com/alecthomas/chroma/v2 v2.18.0
|
||||
github.com/Masterminds/semver/v3 v3.4.0
|
||||
github.com/alecthomas/chroma/v2 v2.20.0
|
||||
github.com/chainguard-dev/git-urls v1.0.2
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/dominikbraun/graph v0.23.0
|
||||
@@ -13,23 +13,23 @@ require (
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/go-git/go-billy/v5 v5.6.2
|
||||
github.com/go-git/go-git/v5 v5.16.0
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0
|
||||
github.com/go-task/template v0.1.0
|
||||
github.com/go-task/template v0.2.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/otiai10/copy v1.14.1
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
||||
github.com/sajari/fuzzy v1.0.0
|
||||
github.com/sebdah/goldie/v2 v2.5.5
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/sebdah/goldie/v2 v2.7.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/zeebo/xxh3 v1.0.2
|
||||
golang.org/x/sync v0.14.0
|
||||
golang.org/x/term v0.32.0
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/term v0.35.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/sh/v3 v3.11.0
|
||||
mvdan.cc/sh/moreinterp v0.0.0-20250807215248-5a1a658912aa
|
||||
mvdan.cc/sh/v3 v3.12.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -39,23 +39,28 @@ require (
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/u-root/u-root v0.14.1-0.20250807200646-5e7721023dc7 // indirect
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
80
go.sum
80
go.sum
@@ -2,8 +2,8 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/Ladicle/tabwriter v1.0.0 h1:DZQqPvMumBDwVNElso13afjYLNp0Z7pHqHnu0r4t9Dg=
|
||||
github.com/Ladicle/tabwriter v1.0.0/go.mod h1:c4MdCjxQyTbGuQO/gvqJ+IA/89UEwrsD6hUCW98dyp4=
|
||||
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
|
||||
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
@@ -11,10 +11,10 @@ github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNx
|
||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4=
|
||||
github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
|
||||
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
||||
github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
@@ -34,6 +34,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
|
||||
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
|
||||
@@ -52,14 +54,14 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ=
|
||||
github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-task/template v0.1.0 h1:ym/r2G937RZA1bsgiWedNnY9e5kxDT+3YcoAnuIetTE=
|
||||
github.com/go-task/template v0.1.0/go.mod h1:RgwRaZK+kni/hJJ7/AaOE2lPQFPbAdji/DyhC6pxo4k=
|
||||
github.com/go-task/template v0.2.0 h1:xW7ek0o65FUSTbKcSNeg2Vyf/I7wYXFgLUznptvviBE=
|
||||
github.com/go-task/template v0.2.0/go.mod h1:dbdoUb6qKnHQi1y6o+IdIrs0J4o/SEhSTA6bbzZmdtc=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
@@ -74,8 +76,12 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
@@ -92,10 +98,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -109,24 +113,32 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY=
|
||||
github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo=
|
||||
github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY=
|
||||
github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||
github.com/sebdah/goldie/v2 v2.7.1 h1:PkBHymaYdtvEkZV7TmyqKxdmn5/Vcj+8TpATWZjnG5E=
|
||||
github.com/sebdah/goldie/v2 v2.7.1/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
||||
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/u-root/u-root v0.14.1-0.20250807200646-5e7721023dc7 h1:ax+jBy7xFhh+Ka0IGLmH5mft+YDuqvzEjSgWuAP0nsM=
|
||||
github.com/u-root/u-root v0.14.1-0.20250807200646-5e7721023dc7/go.mod h1:/0Qr7qJeDwWxoKku2xKQ4Szc+SwBE3g9VE8jNiamsmc=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
@@ -136,13 +148,15 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -152,11 +166,15 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
@@ -171,5 +189,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
mvdan.cc/sh/v3 v3.11.0 h1:q5h+XMDRfUGUedCqFFsjoFjrhwf2Mvtt1rkMvVz0blw=
|
||||
mvdan.cc/sh/v3 v3.11.0/go.mod h1:LRM+1NjoYCzuq/WZ6y44x14YNAI0NK7FLPeQSaFagGg=
|
||||
mvdan.cc/sh/moreinterp v0.0.0-20250807215248-5a1a658912aa h1:sRmA9AmA5+9CbK6a7N52q9W9jAeoBy1EJ7cncm+SLxw=
|
||||
mvdan.cc/sh/moreinterp v0.0.0-20250807215248-5a1a658912aa/go.mod h1:Of9PCedbLDYT8b3EyiYG64rNnx5nOp27OLCVdDrjJyo=
|
||||
mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
|
||||
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
|
||||
|
||||
65
help.go
65
help.go
@@ -24,15 +24,17 @@ type ListOptions struct {
|
||||
ListAllTasks bool
|
||||
FormatTaskListAsJSON bool
|
||||
NoStatus bool
|
||||
Nested bool
|
||||
}
|
||||
|
||||
// NewListOptions creates a new ListOptions instance
|
||||
func NewListOptions(list, listAll, listAsJson, noStatus bool) ListOptions {
|
||||
func NewListOptions(list, listAll, listAsJson, noStatus, nested bool) ListOptions {
|
||||
return ListOptions{
|
||||
ListOnlyTasksWithDescriptions: list,
|
||||
ListAllTasks: listAll,
|
||||
FormatTaskListAsJSON: listAsJson,
|
||||
NoStatus: noStatus,
|
||||
Nested: nested,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +65,7 @@ func (e *Executor) ListTasks(o ListOptions) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
if o.FormatTaskListAsJSON {
|
||||
output, err := e.ToEditorOutput(tasks, o.NoStatus)
|
||||
output, err := e.ToEditorOutput(tasks, o.NoStatus, o.Nested)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -135,33 +137,17 @@ func (e *Executor) ListTaskNames(allTasks bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool) (*editors.Taskfile, error) {
|
||||
o := &editors.Taskfile{
|
||||
Tasks: make([]editors.Task, len(tasks)),
|
||||
Location: e.Taskfile.Location,
|
||||
}
|
||||
func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool, nested bool) (*editors.Namespace, error) {
|
||||
var g errgroup.Group
|
||||
editorTasks := make([]editors.Task, len(tasks))
|
||||
|
||||
// Look over each task in parallel and turn it into an editor task
|
||||
for i := range tasks {
|
||||
aliases := []string{}
|
||||
if len(tasks[i].Aliases) > 0 {
|
||||
aliases = tasks[i].Aliases
|
||||
}
|
||||
g.Go(func() error {
|
||||
o.Tasks[i] = editors.Task{
|
||||
Name: tasks[i].Name(),
|
||||
Task: tasks[i].Task,
|
||||
Desc: tasks[i].Desc,
|
||||
Summary: tasks[i].Summary,
|
||||
Aliases: aliases,
|
||||
UpToDate: false,
|
||||
Location: &editors.Location{
|
||||
Line: tasks[i].Location.Line,
|
||||
Column: tasks[i].Location.Column,
|
||||
Taskfile: tasks[i].Location.Taskfile,
|
||||
},
|
||||
}
|
||||
editorTask := editors.NewTask(tasks[i])
|
||||
|
||||
if noStatus {
|
||||
editorTasks[i] = editorTask
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -180,10 +166,35 @@ func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool) (*editors.Ta
|
||||
return err
|
||||
}
|
||||
|
||||
o.Tasks[i].UpToDate = upToDate
|
||||
|
||||
editorTask.UpToDate = &upToDate
|
||||
editorTasks[i] = editorTask
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return o, g.Wait()
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the root namespace
|
||||
var tasksLen int
|
||||
if !nested {
|
||||
tasksLen = len(editorTasks)
|
||||
}
|
||||
rootNamespace := &editors.Namespace{
|
||||
Tasks: make([]editors.Task, tasksLen),
|
||||
Location: e.Taskfile.Location,
|
||||
}
|
||||
|
||||
// Recursively add namespaces to the root namespace or if nesting is
|
||||
// disabled add them all to the root namespace
|
||||
for i, task := range editorTasks {
|
||||
taskNamespacePath := strings.Split(task.Task, ast.NamespaceSeparator)
|
||||
if nested {
|
||||
rootNamespace.AddNamespace(taskNamespacePath, task)
|
||||
} else {
|
||||
rootNamespace.Tasks[i] = task
|
||||
}
|
||||
}
|
||||
|
||||
return rootNamespace, g.Wait()
|
||||
}
|
||||
|
||||
@@ -64,21 +64,15 @@ get_binaries() {
|
||||
case "$PLATFORM" in
|
||||
darwin/amd64) BINARIES="task" ;;
|
||||
darwin/arm64) BINARIES="task" ;;
|
||||
darwin/armv5) BINARIES="task" ;;
|
||||
darwin/armv6) BINARIES="task" ;;
|
||||
darwin/armv7) BINARIES="task" ;;
|
||||
darwin/arm) BINARIES="task" ;;
|
||||
linux/386) BINARIES="task" ;;
|
||||
linux/amd64) BINARIES="task" ;;
|
||||
linux/arm64) BINARIES="task" ;;
|
||||
linux/armv5) BINARIES="task" ;;
|
||||
linux/armv6) BINARIES="task" ;;
|
||||
linux/armv7) BINARIES="task" ;;
|
||||
linux/arm) BINARIES="task" ;;
|
||||
windows/386) BINARIES="task" ;;
|
||||
windows/amd64) BINARIES="task" ;;
|
||||
windows/arm64) BINARIES="task" ;;
|
||||
windows/armv5) BINARIES="task" ;;
|
||||
windows/armv6) BINARIES="task" ;;
|
||||
windows/armv7) BINARIES="task" ;;
|
||||
windows/arm) BINARIES="task" ;;
|
||||
*)
|
||||
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
|
||||
exit 1
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package editors
|
||||
|
||||
import (
|
||||
"github.com/go-task/task/v3/taskfile/ast"
|
||||
)
|
||||
|
||||
type (
|
||||
// Taskfile wraps task list output for use in editor integrations (e.g. VSCode, etc)
|
||||
Taskfile struct {
|
||||
Tasks []Task `json:"tasks"`
|
||||
Location string `json:"location"`
|
||||
// Namespace wraps task list output for use in editor integrations (e.g. VSCode, etc)
|
||||
Namespace struct {
|
||||
Tasks []Task `json:"tasks"`
|
||||
Namespaces map[string]*Namespace `json:"namespaces,omitempty"`
|
||||
Location string `json:"location,omitempty"`
|
||||
}
|
||||
// Task describes a single task
|
||||
Task struct {
|
||||
@@ -13,7 +18,7 @@ type (
|
||||
Desc string `json:"desc"`
|
||||
Summary string `json:"summary"`
|
||||
Aliases []string `json:"aliases"`
|
||||
UpToDate bool `json:"up_to_date"`
|
||||
UpToDate *bool `json:"up_to_date,omitempty"`
|
||||
Location *Location `json:"location"`
|
||||
}
|
||||
// Location describes a task's location in a taskfile
|
||||
@@ -23,3 +28,59 @@ type (
|
||||
Taskfile string `json:"taskfile"`
|
||||
}
|
||||
)
|
||||
|
||||
func NewTask(task *ast.Task) Task {
|
||||
aliases := []string{}
|
||||
if len(task.Aliases) > 0 {
|
||||
aliases = task.Aliases
|
||||
}
|
||||
return Task{
|
||||
Name: task.Name(),
|
||||
Task: task.Task,
|
||||
Desc: task.Desc,
|
||||
Summary: task.Summary,
|
||||
Aliases: aliases,
|
||||
Location: &Location{
|
||||
Line: task.Location.Line,
|
||||
Column: task.Location.Column,
|
||||
Taskfile: task.Location.Taskfile,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (parent *Namespace) AddNamespace(namespacePath []string, task Task) {
|
||||
if len(namespacePath) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// If there are no child namespaces, then we have found a task and we can
|
||||
// simply add it to the current namespace
|
||||
if len(namespacePath) == 1 {
|
||||
parent.Tasks = append(parent.Tasks, task)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the key of the current namespace in the path
|
||||
namespaceKey := namespacePath[0]
|
||||
|
||||
// Add the namespace to the parent namespaces map using the namespace key
|
||||
if parent.Namespaces == nil {
|
||||
parent.Namespaces = make(map[string]*Namespace, 0)
|
||||
}
|
||||
|
||||
// Search for the current namespace in the parent namespaces map
|
||||
// If it doesn't exist, create it
|
||||
namespace, ok := parent.Namespaces[namespaceKey]
|
||||
if !ok {
|
||||
namespace = &Namespace{}
|
||||
parent.Namespaces[namespaceKey] = namespace
|
||||
}
|
||||
|
||||
// Remove the current namespace key from the namespace path.
|
||||
childNamespacePath := namespacePath[1:]
|
||||
|
||||
// If there are no child namespaces in the task name, then we have found the
|
||||
// namespace of the task and we can add it to the current namespace.
|
||||
// Otherwise, we need to go deeper
|
||||
namespace.AddNamespace(childNamespacePath, task)
|
||||
}
|
||||
|
||||
20
internal/execext/coreutils.go
Normal file
20
internal/execext/coreutils.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package execext
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-task/task/v3/internal/env"
|
||||
)
|
||||
|
||||
var useGoCoreUtils bool
|
||||
|
||||
func init() {
|
||||
// If TASK_CORE_UTILS is set to either true or false, respect that.
|
||||
// By default, enable on Windows only.
|
||||
if v, err := strconv.ParseBool(env.GetTaskEnv("CORE_UTILS")); err == nil {
|
||||
useGoCoreUtils = v
|
||||
} else {
|
||||
useGoCoreUtils = runtime.GOOS == "windows"
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"mvdan.cc/sh/moreinterp/coreutils"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
@@ -59,7 +59,7 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
||||
r, err := interp.New(
|
||||
interp.Params(params...),
|
||||
interp.Env(expand.ListEnviron(environ...)),
|
||||
interp.ExecHandlers(execHandler),
|
||||
interp.ExecHandlers(execHandlers()...),
|
||||
interp.OpenHandler(openHandler),
|
||||
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
|
||||
dirOption(opts.Dir),
|
||||
@@ -90,6 +90,15 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
||||
return r.Run(ctx, p)
|
||||
}
|
||||
|
||||
func escape(s string) string {
|
||||
s = filepath.ToSlash(s)
|
||||
s = strings.ReplaceAll(s, " ", `\ `)
|
||||
s = strings.ReplaceAll(s, "&", `\&`)
|
||||
s = strings.ReplaceAll(s, "(", `\(`)
|
||||
s = strings.ReplaceAll(s, ")", `\)`)
|
||||
return s
|
||||
}
|
||||
|
||||
// ExpandLiteral is a wrapper around [expand.Literal]. It will escape the input
|
||||
// string, expand any shell symbols (such as '~') and resolve any environment
|
||||
// variables.
|
||||
@@ -115,6 +124,7 @@ func ExpandLiteral(s string) (string, error) {
|
||||
// variables. It also expands brace expressions ({a.b}) and globs (*/**) and
|
||||
// returns the results as a list of strings.
|
||||
func ExpandFields(s string) ([]string, error) {
|
||||
s = escape(s)
|
||||
p := syntax.NewParser()
|
||||
var words []*syntax.Word
|
||||
err := p.Words(strings.NewReader(s), func(w *syntax.Word) bool {
|
||||
@@ -133,8 +143,11 @@ func ExpandFields(s string) ([]string, error) {
|
||||
return expand.Fields(cfg, words...)
|
||||
}
|
||||
|
||||
func execHandler(next interp.ExecHandlerFunc) interp.ExecHandlerFunc {
|
||||
return interp.DefaultExecHandler(15 * time.Second)
|
||||
func execHandlers() (handlers []func(next interp.ExecHandlerFunc) interp.ExecHandlerFunc) {
|
||||
if useGoCoreUtils {
|
||||
handlers = append(handlers, coreutils.ExecHandler)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func openHandler(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package fingerprint
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -164,7 +163,7 @@ func TestIsTaskUpToDate(t *testing.T) {
|
||||
}
|
||||
|
||||
result, err := IsTaskUpToDate(
|
||||
context.Background(),
|
||||
t.Context(),
|
||||
tt.task,
|
||||
WithStatusChecker(mockStatusChecker),
|
||||
WithSourcesChecker(mockSourcesChecker),
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
@@ -13,9 +12,10 @@ import (
|
||||
"github.com/go-task/task/v3"
|
||||
"github.com/go-task/task/v3/errors"
|
||||
"github.com/go-task/task/v3/experiments"
|
||||
"github.com/go-task/task/v3/internal/env"
|
||||
"github.com/go-task/task/v3/internal/sort"
|
||||
"github.com/go-task/task/v3/taskfile/ast"
|
||||
"github.com/go-task/task/v3/taskrc"
|
||||
taskrcast "github.com/go-task/task/v3/taskrc/ast"
|
||||
)
|
||||
|
||||
const usage = `Usage: task [flags...] [task...]
|
||||
@@ -51,6 +51,7 @@ var (
|
||||
TaskSort string
|
||||
Status bool
|
||||
NoStatus bool
|
||||
Nested bool
|
||||
Insecure bool
|
||||
Force bool
|
||||
ForceAll bool
|
||||
@@ -95,7 +96,9 @@ func init() {
|
||||
|
||||
// Parse the experiments
|
||||
dir = cmp.Or(dir, filepath.Dir(entrypoint))
|
||||
experiments.Parse(dir)
|
||||
|
||||
config, _ := taskrc.GetConfig(dir)
|
||||
experiments.ParseWithConfig(dir, config)
|
||||
|
||||
// Parse the rest of the flags
|
||||
log.SetFlags(0)
|
||||
@@ -104,10 +107,7 @@ func init() {
|
||||
log.Print(usage)
|
||||
pflag.PrintDefaults()
|
||||
}
|
||||
offline, err := strconv.ParseBool(cmp.Or(env.GetTaskEnv("OFFLINE"), "false"))
|
||||
if err != nil {
|
||||
offline = false
|
||||
}
|
||||
|
||||
pflag.BoolVar(&Version, "version", false, "Show Task version.")
|
||||
pflag.BoolVarP(&Help, "help", "h", false, "Shows Task usage.")
|
||||
pflag.BoolVarP(&Init, "init", "i", false, "Creates a new Taskfile.yml in the current folder.")
|
||||
@@ -118,9 +118,10 @@ func init() {
|
||||
pflag.StringVar(&TaskSort, "sort", "", "Changes the order of the tasks when listed. [default|alphanumeric|none].")
|
||||
pflag.BoolVar(&Status, "status", false, "Exits with non-zero exit code if any of the given tasks is not up-to-date.")
|
||||
pflag.BoolVar(&NoStatus, "no-status", false, "Ignore status when listing tasks as JSON")
|
||||
pflag.BoolVar(&Insecure, "insecure", false, "Forces Task to download Taskfiles over insecure connections.")
|
||||
pflag.BoolVar(&Nested, "nested", false, "Nest namespaces when listing tasks as JSON")
|
||||
pflag.BoolVar(&Insecure, "insecure", getConfig(config, func() *bool { return config.Remote.Insecure }, false), "Forces Task to download Taskfiles over insecure connections.")
|
||||
pflag.BoolVarP(&Watch, "watch", "w", false, "Enables watch of the given task.")
|
||||
pflag.BoolVarP(&Verbose, "verbose", "v", false, "Enables verbose mode.")
|
||||
pflag.BoolVarP(&Verbose, "verbose", "v", getConfig(config, func() *bool { return config.Verbose }, false), "Enables verbose mode.")
|
||||
pflag.BoolVarP(&Silent, "silent", "s", false, "Disables echoing.")
|
||||
pflag.BoolVarP(&AssumeYes, "yes", "y", false, "Assume \"yes\" as answer to all prompts.")
|
||||
pflag.BoolVarP(&Parallel, "parallel", "p", false, "Executes tasks provided on command line in parallel.")
|
||||
@@ -134,7 +135,7 @@ func init() {
|
||||
pflag.StringVar(&Output.Group.End, "output-group-end", "", "Message template to print after a task's grouped output.")
|
||||
pflag.BoolVar(&Output.Group.ErrorOnly, "output-group-error-only", false, "Swallow output from successful tasks.")
|
||||
pflag.BoolVarP(&Color, "color", "c", true, "Colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable.")
|
||||
pflag.IntVarP(&Concurrency, "concurrency", "C", 0, "Limit number of tasks to run concurrently.")
|
||||
pflag.IntVarP(&Concurrency, "concurrency", "C", getConfig(config, func() *int { return config.Concurrency }, 0), "Limit number of tasks to run concurrently.")
|
||||
pflag.DurationVarP(&Interval, "interval", "I", 0, "Interval to watch for changes.")
|
||||
pflag.BoolVarP(&Global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.")
|
||||
pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.")
|
||||
@@ -150,12 +151,11 @@ func init() {
|
||||
// Remote Taskfiles experiment will adds the "download" and "offline" flags
|
||||
if experiments.RemoteTaskfiles.Enabled() {
|
||||
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
||||
pflag.BoolVar(&Offline, "offline", offline, "Forces Task to only use local or cached Taskfiles.")
|
||||
pflag.DurationVar(&Timeout, "timeout", time.Second*10, "Timeout for downloading remote Taskfiles.")
|
||||
pflag.BoolVar(&Offline, "offline", getConfig(config, func() *bool { return config.Remote.Offline }, false), "Forces Task to only use local or cached Taskfiles.")
|
||||
pflag.DurationVar(&Timeout, "timeout", getConfig(config, func() *time.Duration { return config.Remote.Timeout }, time.Second*10), "Timeout for downloading remote Taskfiles.")
|
||||
pflag.BoolVar(&ClearCache, "clear-cache", false, "Clear the remote cache.")
|
||||
pflag.DurationVar(&CacheExpiryDuration, "expiry", 0, "Expiry duration for cached remote Taskfiles.")
|
||||
pflag.DurationVar(&CacheExpiryDuration, "expiry", getConfig(config, func() *time.Duration { return config.Remote.Timeout }, 0), "Expiry duration for cached remote Taskfiles.")
|
||||
}
|
||||
|
||||
pflag.Parse()
|
||||
}
|
||||
|
||||
@@ -196,6 +196,10 @@ func Validate() error {
|
||||
return errors.New("task: --no-status only applies to --json with --list or --list-all")
|
||||
}
|
||||
|
||||
if Nested && !ListJson {
|
||||
return errors.New("task: --nested only applies to --json with --list or --list-all")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -251,3 +255,16 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
||||
task.WithVersionCheck(true),
|
||||
)
|
||||
}
|
||||
|
||||
// getConfig extracts a config value directly from a pointer field with a fallback default
|
||||
func getConfig[T any](config *taskrcast.TaskRC, fieldFunc func() *T, fallback T) T {
|
||||
if config == nil {
|
||||
return fallback
|
||||
}
|
||||
|
||||
field := fieldFunc()
|
||||
if field != nil {
|
||||
return *field
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
@@ -37,51 +37,87 @@ func DefaultDir(entrypoint, dir string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Search will look for files with the given possible filenames using the given
|
||||
// entrypoint and directory. If the entrypoint is set, it will check if the
|
||||
// ResolveDir returns an absolute path to the directory that the task should be
|
||||
// run in. If the entrypoint and dir are BOTH set, then the Taskfile will not
|
||||
// sit inside the directory specified by dir and we should ensure that the dir
|
||||
// is absolute. Otherwise, the dir will always be the parent directory of the
|
||||
// resolved entrypoint, so we should return that parent directory.
|
||||
func ResolveDir(entrypoint, resolvedEntrypoint, dir string) (string, error) {
|
||||
if entrypoint != "" && dir != "" {
|
||||
return filepath.Abs(dir)
|
||||
}
|
||||
return filepath.Dir(resolvedEntrypoint), nil
|
||||
}
|
||||
|
||||
// Search looks for files with the given possible filenames using the given
|
||||
// entrypoint and directory. If the entrypoint is set, it checks if the
|
||||
// entrypoint matches a file or if it matches a directory containing one of the
|
||||
// possible filenames. Otherwise, it will walk up the file tree starting at the
|
||||
// given directory and perform a search in each directory for the possible
|
||||
// possible filenames. Otherwise, it walks up the file tree starting at the
|
||||
// given directory and performs a search in each directory for the possible
|
||||
// filenames until it finds a match or reaches the root directory. If the
|
||||
// entrypoint and directory are both empty, it will default the directory to the
|
||||
// current working directory and perform a recursive search starting there. If a
|
||||
// match is found, the absolute path to the file will be returned with its
|
||||
// directory. If no match is found, an error will be returned.
|
||||
func Search(entrypoint, dir string, possibleFilenames []string) (string, string, error) {
|
||||
// entrypoint and directory are both empty, it defaults the directory to the
|
||||
// current working directory and performs a recursive search starting there. If
|
||||
// a match is found, the absolute path to the file is returned with its
|
||||
// directory. If no match is found, an error is returned.
|
||||
func Search(entrypoint, dir string, possibleFilenames []string) (string, error) {
|
||||
var err error
|
||||
if entrypoint != "" {
|
||||
entrypoint, err = SearchPath(entrypoint, possibleFilenames)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
if dir == "" {
|
||||
dir = filepath.Dir(entrypoint)
|
||||
} else {
|
||||
dir, err = filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
}
|
||||
return entrypoint, dir, nil
|
||||
return entrypoint, nil
|
||||
}
|
||||
if dir == "" {
|
||||
dir, err = os.Getwd()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
entrypoint, err = SearchPathRecursively(dir, possibleFilenames)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
dir = filepath.Dir(entrypoint)
|
||||
return entrypoint, dir, nil
|
||||
return entrypoint, nil
|
||||
}
|
||||
|
||||
// Search will check if a file at the given path exists or not. If it does, it
|
||||
// will return the path to it. If it does not, it will search for any files at
|
||||
// the given path with any of the given possible names. If any of these match a
|
||||
// file, the first matching path will be returned. If no files are found, an
|
||||
// SearchAll looks for files with the given possible filenames using the given
|
||||
// entrypoint and directory. If the entrypoint is set, it checks if the
|
||||
// entrypoint matches a file or if it matches a directory containing one of the
|
||||
// possible filenames and add it to a list of matches. It then walks up the file
|
||||
// tree starting at the given directory and performs a search in each directory
|
||||
// for the possible filenames until it finds a match or reaches the root
|
||||
// directory. If the entrypoint and directory are both empty, it defaults the
|
||||
// directory to the current working directory and performs a recursive search
|
||||
// starting there. If matches are found, the absolute path to each file is added
|
||||
// to the list and returned.
|
||||
func SearchAll(entrypoint, dir string, possibleFilenames []string) ([]string, error) {
|
||||
var err error
|
||||
var entrypoints []string
|
||||
if entrypoint != "" {
|
||||
entrypoint, err = SearchPath(entrypoint, possibleFilenames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entrypoints = append(entrypoints, entrypoint)
|
||||
}
|
||||
if dir == "" {
|
||||
dir, err = os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
paths, err := SearchNPathRecursively(dir, possibleFilenames, -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(entrypoints, paths...), nil
|
||||
}
|
||||
|
||||
// SearchPath will check if a file at the given path exists or not. If it does,
|
||||
// it will return the path to it. If it does not, it will search for any files
|
||||
// at the given path with any of the given possible names. If any of these match
|
||||
// a file, the first matching path will be returned. If no files are found, an
|
||||
// error will be returned.
|
||||
func SearchPath(path string, possibleFilenames []string) (string, error) {
|
||||
// Get file info about the path
|
||||
@@ -111,36 +147,56 @@ func SearchPath(path string, possibleFilenames []string) (string, error) {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
|
||||
// SearchRecursively will check if a file at the given path exists by calling
|
||||
// the exists function. If a file is not found, it will walk up the directory
|
||||
// tree calling the Search function until it finds a file or reaches the root
|
||||
// directory. On supported operating systems, it will also check if the user ID
|
||||
// of the directory changes and abort if it does.
|
||||
// SearchPathRecursively walks up the directory tree starting at the given
|
||||
// path, calling the Search function in each directory until it finds a matching
|
||||
// file or reaches the root directory. On supported operating systems, it will
|
||||
// also check if the user ID of the directory changes and abort if it does.
|
||||
func SearchPathRecursively(path string, possibleFilenames []string) (string, error) {
|
||||
owner, err := sysinfo.Owner(path)
|
||||
paths, err := SearchNPathRecursively(path, possibleFilenames, 1)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for {
|
||||
if len(paths) == 0 {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
return paths[0], nil
|
||||
}
|
||||
|
||||
// SearchNPathRecursively walks up the directory tree starting at the given
|
||||
// path, calling the Search function in each directory and adding each matching
|
||||
// file that it finds to a list until it reaches the root directory or the
|
||||
// length of the list exceeds n. On supported operating systems, it will also
|
||||
// check if the user ID of the directory changes and abort if it does.
|
||||
func SearchNPathRecursively(path string, possibleFilenames []string, n int) ([]string, error) {
|
||||
var paths []string
|
||||
|
||||
owner, err := sysinfo.Owner(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for n == -1 || len(paths) < n {
|
||||
fpath, err := SearchPath(path, possibleFilenames)
|
||||
if err == nil {
|
||||
return fpath, nil
|
||||
paths = append(paths, fpath)
|
||||
}
|
||||
|
||||
// Get the parent path/user id
|
||||
parentPath := filepath.Dir(path)
|
||||
parentOwner, err := sysinfo.Owner(parentPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Error if we reached the root directory and still haven't found a file
|
||||
// OR if the user id of the directory changes
|
||||
if path == parentPath || (parentOwner != owner) {
|
||||
return "", os.ErrNotExist
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
owner = parentOwner
|
||||
path = parentPath
|
||||
}
|
||||
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
@@ -71,35 +71,30 @@ func TestSearch(t *testing.T) {
|
||||
dir string
|
||||
possibleFilenames []string
|
||||
expectedEntrypoint string
|
||||
expectedDir string
|
||||
}{
|
||||
{
|
||||
name: "find foo.txt using relative entrypoint",
|
||||
entrypoint: "./testdata/foo.txt",
|
||||
possibleFilenames: []string{"foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using absolute entrypoint",
|
||||
entrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
possibleFilenames: []string{"foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using relative dir",
|
||||
dir: "./testdata",
|
||||
possibleFilenames: []string{"foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using absolute dir",
|
||||
dir: filepath.Join(wd, "testdata"),
|
||||
possibleFilenames: []string{"foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using relative dir and relative entrypoint",
|
||||
@@ -107,7 +102,6 @@ func TestSearch(t *testing.T) {
|
||||
dir: "./testdata/some/other/dir",
|
||||
possibleFilenames: []string{"foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata", "some", "other", "dir"),
|
||||
},
|
||||
{
|
||||
name: "find fs.go using no entrypoint or dir",
|
||||
@@ -115,7 +109,6 @@ func TestSearch(t *testing.T) {
|
||||
dir: "",
|
||||
possibleFilenames: []string{"fs.go"},
|
||||
expectedEntrypoint: filepath.Join(wd, "fs.go"),
|
||||
expectedDir: wd,
|
||||
},
|
||||
{
|
||||
name: "find ../../Taskfile.yml using no entrypoint or dir by walking",
|
||||
@@ -123,30 +116,109 @@ func TestSearch(t *testing.T) {
|
||||
dir: "",
|
||||
possibleFilenames: []string{"Taskfile.yml"},
|
||||
expectedEntrypoint: filepath.Join(wd, "..", "..", "Taskfile.yml"),
|
||||
expectedDir: filepath.Join(wd, "..", ".."),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt first if listed first in possible filenames",
|
||||
entrypoint: "./testdata",
|
||||
possibleFilenames: []string{"foo.txt", "bar.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find bar.txt first if listed first in possible filenames",
|
||||
entrypoint: "./testdata",
|
||||
possibleFilenames: []string{"bar.txt", "foo.txt"},
|
||||
expectedEntrypoint: filepath.Join(wd, "testdata", "bar.txt"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
entrypoint, err := Search(tt.entrypoint, tt.dir, tt.possibleFilenames)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expectedEntrypoint, entrypoint)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
entrypoint string
|
||||
resolvedEntrypoint string
|
||||
dir string
|
||||
expectedDir string
|
||||
}{
|
||||
{
|
||||
name: "find foo.txt using relative entrypoint",
|
||||
entrypoint: "./testdata/foo.txt",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using absolute entrypoint",
|
||||
entrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using relative dir",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
dir: "./testdata",
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using absolute dir",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
dir: filepath.Join(wd, "testdata"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt using relative dir and relative entrypoint",
|
||||
entrypoint: "./testdata/foo.txt",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
dir: "./testdata/some/other/dir",
|
||||
expectedDir: filepath.Join(wd, "testdata", "some", "other", "dir"),
|
||||
},
|
||||
{
|
||||
name: "find fs.go using no entrypoint or dir",
|
||||
entrypoint: "",
|
||||
resolvedEntrypoint: filepath.Join(wd, "fs.go"),
|
||||
dir: "",
|
||||
expectedDir: wd,
|
||||
},
|
||||
{
|
||||
name: "find ../../Taskfile.yml using no entrypoint or dir by walking",
|
||||
entrypoint: "",
|
||||
resolvedEntrypoint: filepath.Join(wd, "..", "..", "Taskfile.yml"),
|
||||
dir: "",
|
||||
expectedDir: filepath.Join(wd, "..", ".."),
|
||||
},
|
||||
{
|
||||
name: "find foo.txt first if listed first in possible filenames",
|
||||
entrypoint: "./testdata",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
{
|
||||
name: "find bar.txt first if listed first in possible filenames",
|
||||
entrypoint: "./testdata",
|
||||
resolvedEntrypoint: filepath.Join(wd, "testdata", "bar.txt"),
|
||||
expectedDir: filepath.Join(wd, "testdata"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
entrypoint, dir, err := Search(tt.entrypoint, tt.dir, tt.possibleFilenames)
|
||||
dir, err := ResolveDir(tt.entrypoint, tt.resolvedEntrypoint, tt.dir)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expectedEntrypoint, entrypoint)
|
||||
require.Equal(t, tt.expectedDir, dir)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ func init() {
|
||||
"toYaml": toYaml,
|
||||
"mustToYaml": mustToYaml,
|
||||
"uuid": uuid.New,
|
||||
"randInt": rand.Int,
|
||||
"randIntN": rand.IntN,
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.44.0
|
||||
3.45.1
|
||||
|
||||
32
package-lock.json
generated
32
package-lock.json
generated
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"name": "@go-task/cli",
|
||||
"version": "3.44.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@go-task/cli",
|
||||
"version": "3.26.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@go-task/go-npm": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@go-task/go-npm": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@go-task/go-npm/-/go-npm-0.2.0.tgz",
|
||||
"integrity": "sha512-vQbdtBvesHm8EUFHX8QKg4rbBodmu9VsAXH1ozpbiN5jdTMOYHTCMM31EurAYmY+rNNtxJQ4JGy6t383RPlqbw==",
|
||||
"bin": {
|
||||
"go-npm": "bin/index.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@go-task/go-npm": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@go-task/go-npm/-/go-npm-0.2.0.tgz",
|
||||
"integrity": "sha512-vQbdtBvesHm8EUFHX8QKg4rbBodmu9VsAXH1ozpbiN5jdTMOYHTCMM31EurAYmY+rNNtxJQ4JGy6t383RPlqbw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
34
package.json
34
package.json
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "@go-task/cli",
|
||||
"version": "3.44.0",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
3
setup.go
3
setup.go
@@ -104,6 +104,9 @@ func (e *Executor) setupFuzzyModel() {
|
||||
|
||||
var words []string
|
||||
for name, task := range e.Taskfile.Tasks.All(nil) {
|
||||
if task.Internal {
|
||||
continue
|
||||
}
|
||||
words = append(words, name)
|
||||
words = slices.Concat(words, task.Aliases)
|
||||
}
|
||||
|
||||
11
task.go
11
task.go
@@ -172,7 +172,6 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
||||
if t.Method != "" {
|
||||
method = t.Method
|
||||
}
|
||||
|
||||
upToDate, err := fingerprint.IsTaskUpToDate(ctx, t,
|
||||
fingerprint.WithMethod(method),
|
||||
fingerprint.WithTempDir(e.TempDir.Fingerprint),
|
||||
@@ -220,13 +219,13 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
||||
e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v\n", err2)
|
||||
}
|
||||
|
||||
exitCode, isExitError := interp.IsExitStatus(err)
|
||||
if isExitError {
|
||||
var exitCode interp.ExitStatus
|
||||
if errors.As(err, &exitCode) {
|
||||
if t.IgnoreError {
|
||||
e.Logger.VerboseErrf(logger.Yellow, "task: task error ignored: %v\n", err)
|
||||
continue
|
||||
}
|
||||
deferredExitCode = exitCode
|
||||
deferredExitCode = uint8(exitCode)
|
||||
}
|
||||
|
||||
if call.Indirect {
|
||||
@@ -356,7 +355,8 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *Call, i in
|
||||
if closeErr := closer(err); closeErr != nil {
|
||||
e.Logger.Errf(logger.Red, "task: unable to close writer: %v\n", closeErr)
|
||||
}
|
||||
if _, isExitError := interp.IsExitStatus(err); isExitError && cmd.IgnoreError {
|
||||
var exitCode interp.ExitStatus
|
||||
if errors.As(err, &exitCode) && cmd.IgnoreError {
|
||||
e.Logger.VerboseErrf(logger.Yellow, "task: [%s] command error ignored: %v\n", t.Name(), err)
|
||||
return nil
|
||||
}
|
||||
@@ -466,7 +466,6 @@ func (e *Executor) GetTask(call *Call) (*ast.Task, error) {
|
||||
DidYouMean: didYouMean,
|
||||
}
|
||||
}
|
||||
|
||||
return matchingTask, nil
|
||||
}
|
||||
|
||||
|
||||
242
task_test.go
242
task_test.go
@@ -2,10 +2,10 @@ package task_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"maps"
|
||||
rand "math/rand/v2"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -42,10 +42,11 @@ type (
|
||||
FormatterTestOption
|
||||
}
|
||||
TaskTest struct {
|
||||
name string
|
||||
experiments map[*experiments.Experiment]int
|
||||
postProcessFns []PostProcessFn
|
||||
fixtureTemplateData any
|
||||
name string
|
||||
experiments map[*experiments.Experiment]int
|
||||
postProcessFns []PostProcessFn
|
||||
fixtureTemplateData map[string]any
|
||||
fixtureTemplatingEnabled bool
|
||||
}
|
||||
)
|
||||
|
||||
@@ -80,8 +81,19 @@ func (tt *TaskTest) writeFixture(
|
||||
if goldenFileSuffix != "" {
|
||||
goldenFileName += "-" + goldenFileSuffix
|
||||
}
|
||||
if tt.fixtureTemplateData != nil {
|
||||
g.AssertWithTemplate(t, goldenFileName, tt.fixtureTemplateData, b)
|
||||
// Create a set of data to be made available to every test fixture
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
if tt.fixtureTemplatingEnabled {
|
||||
fixtureTemplateData := map[string]any{
|
||||
"TEST_NAME": t.Name(),
|
||||
"TEST_DIR": wd,
|
||||
}
|
||||
// If the test has additional template data, copy it into the map
|
||||
if tt.fixtureTemplateData != nil {
|
||||
maps.Copy(fixtureTemplateData, tt.fixtureTemplateData)
|
||||
}
|
||||
g.AssertWithTemplate(t, goldenFileName, fixtureTemplateData, b)
|
||||
} else {
|
||||
g.Assert(t, goldenFileName, b)
|
||||
}
|
||||
@@ -239,24 +251,44 @@ func (opt *setupErrorTestOption) applyToFormatterTest(t *FormatterTest) {
|
||||
t.wantSetupError = true
|
||||
}
|
||||
|
||||
// WithFixtureTemplateData sets up data defined in the golden file using golang
|
||||
// template. Useful if the golden file can change depending on the test.
|
||||
// Example template: {{ .Value }}
|
||||
// Example data definition: struct{ Value string }{Value: "value"}
|
||||
func WithFixtureTemplateData(data any) TestOption {
|
||||
return &fixtureTemplateDataTestOption{data: data}
|
||||
// WithFixtureTemplating enables templating for the golden fixture files with
|
||||
// the default set of data. This is useful if the golden file is dynamic in some
|
||||
// way (e.g. contains user-specific directories). To add more data, see
|
||||
// WithFixtureTemplateData.
|
||||
func WithFixtureTemplating() TestOption {
|
||||
return &fixtureTemplatingTestOption{}
|
||||
}
|
||||
|
||||
type fixtureTemplatingTestOption struct{}
|
||||
|
||||
func (opt *fixtureTemplatingTestOption) applyToExecutorTest(t *ExecutorTest) {
|
||||
t.fixtureTemplatingEnabled = true
|
||||
}
|
||||
|
||||
func (opt *fixtureTemplatingTestOption) applyToFormatterTest(t *FormatterTest) {
|
||||
t.fixtureTemplatingEnabled = true
|
||||
}
|
||||
|
||||
// WithFixtureTemplateData adds data to the golden fixture file templates. Keys
|
||||
// given here will override any existing values. This option will also enable
|
||||
// global templating, so you do not need to call WithFixtureTemplating as well.
|
||||
func WithFixtureTemplateData(key string, value any) TestOption {
|
||||
return &fixtureTemplateDataTestOption{key, value}
|
||||
}
|
||||
|
||||
type fixtureTemplateDataTestOption struct {
|
||||
data any
|
||||
k string
|
||||
v any
|
||||
}
|
||||
|
||||
func (opt *fixtureTemplateDataTestOption) applyToExecutorTest(t *ExecutorTest) {
|
||||
t.fixtureTemplateData = opt.data
|
||||
t.fixtureTemplatingEnabled = true
|
||||
t.fixtureTemplateData[opt.k] = opt.v
|
||||
}
|
||||
|
||||
func (opt *fixtureTemplateDataTestOption) applyToFormatterTest(t *FormatterTest) {
|
||||
t.fixtureTemplateData = opt.data
|
||||
t.fixtureTemplatingEnabled = true
|
||||
t.fixtureTemplateData[opt.k] = opt.v
|
||||
}
|
||||
|
||||
// Post-processing
|
||||
@@ -265,17 +297,6 @@ func (opt *fixtureTemplateDataTestOption) applyToFormatterTest(t *FormatterTest)
|
||||
// fixture before the file is written.
|
||||
type PostProcessFn func(*testing.T, []byte) []byte
|
||||
|
||||
// PPRemoveAbsolutePaths removes any absolute paths from the output of the task.
|
||||
// This is useful when the task output contains paths that are can be different
|
||||
// in different environments such as home directories. The function looks for
|
||||
// any paths that contain the current working directory and truncates them.
|
||||
func PPRemoveAbsolutePaths(t *testing.T, b []byte) []byte {
|
||||
t.Helper()
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
return bytes.ReplaceAll(b, []byte(wd), nil)
|
||||
}
|
||||
|
||||
// PPSortedLines sorts the lines of the output of the task. This is useful when
|
||||
// the order of the output is not important, but the output is expected to be
|
||||
// the same each time the task is run (e.g. when running tasks in parallel).
|
||||
@@ -334,7 +355,7 @@ func (fct fileContentTest) Run(t *testing.T) {
|
||||
)
|
||||
|
||||
require.NoError(t, e.Setup(), "e.Setup()")
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: fct.Target}), "e.Run(target)")
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: fct.Target}), "e.Run(target)")
|
||||
for name, expectContent := range fct.Files {
|
||||
t.Run(fct.name(name), func(t *testing.T) {
|
||||
path := filepathext.SmartJoin(e.Dir, name)
|
||||
@@ -385,7 +406,7 @@ func TestGenerates(t *testing.T) {
|
||||
fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask)
|
||||
|
||||
// Run task for the first time.
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: theTask}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: theTask}))
|
||||
|
||||
if _, err := os.Stat(srcFile); err != nil {
|
||||
t.Errorf("File should exist: %v", err)
|
||||
@@ -400,7 +421,7 @@ func TestGenerates(t *testing.T) {
|
||||
buff.Reset()
|
||||
|
||||
// Re-run task to ensure it's now found to be up-to-date.
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: theTask}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: theTask}))
|
||||
if buff.String() != upToDate {
|
||||
t.Errorf("Wrong output message: %s", buff.String())
|
||||
}
|
||||
@@ -416,6 +437,7 @@ func TestStatusChecksum(t *testing.T) { // nolint:paralleltest // cannot run in
|
||||
task string
|
||||
}{
|
||||
{[]string{"generated.txt", ".task/checksum/build"}, "build"},
|
||||
{[]string{"generated-wildcard.txt", ".task/checksum/build-wildcard"}, "build-wildcard"},
|
||||
{[]string{"generated.txt", ".task/checksum/build-with-status"}, "build-with-status"},
|
||||
}
|
||||
|
||||
@@ -441,7 +463,7 @@ func TestStatusChecksum(t *testing.T) { // nolint:paralleltest // cannot run in
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: test.task}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: test.task}))
|
||||
for _, f := range test.files {
|
||||
_, err := os.Stat(filepathext.SmartJoin(dir, f))
|
||||
require.NoError(t, err)
|
||||
@@ -454,7 +476,7 @@ func TestStatusChecksum(t *testing.T) { // nolint:paralleltest // cannot run in
|
||||
time := s.ModTime()
|
||||
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: test.task}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: test.task}))
|
||||
assert.Equal(t, `task: Task "`+test.task+`" is up to date`+"\n", buff.String())
|
||||
|
||||
s, err = os.Stat(filepathext.SmartJoin(tempDir.Fingerprint, "checksum/"+test.task))
|
||||
@@ -485,12 +507,12 @@ func TestStatusVariables(t *testing.T) {
|
||||
task.WithVerbose(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build-checksum"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-checksum"}))
|
||||
|
||||
assert.Contains(t, buff.String(), "3e464c4b03f4b65d740e1e130d4d108a")
|
||||
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build-ts"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-ts"}))
|
||||
|
||||
inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt"))
|
||||
require.NoError(t, err)
|
||||
@@ -521,12 +543,12 @@ func TestCmdsVariables(t *testing.T) {
|
||||
task.WithVerbose(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build-checksum"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-checksum"}))
|
||||
|
||||
assert.Contains(t, buff.String(), "3e464c4b03f4b65d740e1e130d4d108a")
|
||||
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build-ts"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-ts"}))
|
||||
inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt"))
|
||||
require.NoError(t, err)
|
||||
ts := fmt.Sprintf("%d", inf.ModTime().Unix())
|
||||
@@ -547,7 +569,7 @@ func TestCyclicDep(t *testing.T) {
|
||||
task.WithStderr(io.Discard),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
assert.IsType(t, &errors.TaskCalledTooManyTimesError{}, e.Run(context.Background(), &task.Call{Task: "task-1"}))
|
||||
assert.IsType(t, &errors.TaskCalledTooManyTimesError{}, e.Run(t.Context(), &task.Call{Task: "task-1"}))
|
||||
}
|
||||
|
||||
func TestTaskVersion(t *testing.T) {
|
||||
@@ -597,10 +619,10 @@ func TestTaskIgnoreErrors(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "task-should-pass"}))
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "task-should-fail"}))
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "cmd-should-pass"}))
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "cmd-should-fail"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task-should-pass"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "task-should-fail"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "cmd-should-pass"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "cmd-should-fail"}))
|
||||
}
|
||||
|
||||
func TestExpand(t *testing.T) {
|
||||
@@ -620,7 +642,7 @@ func TestExpand(t *testing.T) {
|
||||
task.WithStderr(&buff),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "pwd"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "pwd"}))
|
||||
assert.Equal(t, home, strings.TrimSpace(buff.String()))
|
||||
}
|
||||
|
||||
@@ -641,7 +663,7 @@ func TestDry(t *testing.T) {
|
||||
task.WithDry(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"}))
|
||||
|
||||
assert.Equal(t, "task: [build] touch file.txt", strings.TrimSpace(buff.String()))
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
@@ -670,13 +692,13 @@ func TestDryChecksum(t *testing.T) {
|
||||
task.WithDry(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
|
||||
_, err := os.Stat(checksumFile)
|
||||
require.Error(t, err, "checksum file should not exist")
|
||||
|
||||
e.Dry = false
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
_, err = os.Stat(checksumFile)
|
||||
require.NoError(t, err, "checksum file should exist")
|
||||
}
|
||||
@@ -818,7 +840,7 @@ func TestIncludesRemote(t *testing.T) {
|
||||
path := filepath.Join(dir, outputFile)
|
||||
require.NoError(t, os.RemoveAll(path))
|
||||
|
||||
require.NoError(t, e.executor.Run(context.Background(), taskCall))
|
||||
require.NoError(t, e.executor.Run(t.Context(), taskCall))
|
||||
|
||||
actualContent, err := os.ReadFile(path)
|
||||
require.NoError(t, err)
|
||||
@@ -1098,11 +1120,11 @@ func TestIncludesRelativePath(t *testing.T) {
|
||||
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "common:pwd"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "common:pwd"}))
|
||||
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
|
||||
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "included:common:pwd"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "included:common:pwd"}))
|
||||
assert.Contains(t, buff.String(), "testdata/includes_rel_path/common")
|
||||
}
|
||||
|
||||
@@ -1134,7 +1156,7 @@ func TestIncludesInternal(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
@@ -1181,7 +1203,7 @@ func TestIncludesFlatten(t *testing.T) {
|
||||
assert.EqualError(t, err, test.expectedOutput)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
_ = e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
_ = e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
assert.Equal(t, test.expectedOutput, buff.String())
|
||||
}
|
||||
})
|
||||
@@ -1213,7 +1235,7 @@ func TestIncludesInterpolation(t *testing.T) { // nolint:paralleltest // cannot
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
@@ -1236,20 +1258,20 @@ func TestIncludesWithExclude(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "included:bar"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "included:bar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "bar\n", buff.String())
|
||||
buff.Reset()
|
||||
|
||||
err = e.Run(context.Background(), &task.Call{Task: "included:foo"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "included:foo"})
|
||||
require.Error(t, err)
|
||||
buff.Reset()
|
||||
|
||||
err = e.Run(context.Background(), &task.Call{Task: "bar"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "bar"})
|
||||
require.Error(t, err)
|
||||
buff.Reset()
|
||||
|
||||
err = e.Run(context.Background(), &task.Call{Task: "foo"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "foo"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "foo\n", buff.String())
|
||||
}
|
||||
@@ -1279,7 +1301,7 @@ func TestIncludedTaskfileVarMerging(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, buff.String(), test.expectedOutput)
|
||||
})
|
||||
@@ -1314,7 +1336,7 @@ func TestInternalTask(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
if test.expectedErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
@@ -1399,7 +1421,7 @@ func TestSummary(t *testing.T) {
|
||||
task.WithSilent(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "task-with-summary"}, &task.Call{Task: "other-task-with-summary"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task-with-summary"}, &task.Call{Task: "other-task-with-summary"}))
|
||||
|
||||
data, err := os.ReadFile(filepathext.SmartJoin(dir, "task-with-summary.txt"))
|
||||
require.NoError(t, err)
|
||||
@@ -1425,7 +1447,7 @@ func TestWhenNoDirAttributeItRunsInSameDirAsTaskfile(t *testing.T) {
|
||||
)
|
||||
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "whereami"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "whereami"}))
|
||||
|
||||
// got should be the "dir" part of "testdata/dir"
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
@@ -1445,7 +1467,7 @@ func TestWhenDirAttributeAndDirExistsItRunsInThatDir(t *testing.T) {
|
||||
)
|
||||
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "whereami"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "whereami"}))
|
||||
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
assert.Equal(t, expected, got, "Mismatch in the working directory")
|
||||
@@ -1471,7 +1493,7 @@ func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) {
|
||||
t.Errorf("Directory should not exist: %v", err)
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: target}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: target}))
|
||||
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
assert.Equal(t, expected, got, "Mismatch in the working directory")
|
||||
@@ -1500,7 +1522,7 @@ func TestDynamicVariablesRunOnTheNewCreatedDir(t *testing.T) {
|
||||
t.Errorf("Directory should not exist: %v", err)
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: target}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: target}))
|
||||
|
||||
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
|
||||
assert.Equal(t, expected, got, "Mismatch in the working directory")
|
||||
@@ -1571,7 +1593,7 @@ func TestShortTaskNotation(t *testing.T) {
|
||||
task.WithSilent(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
assert.Equal(t, "string-slice-1\nstring-slice-2\nstring\n", buff.String())
|
||||
}
|
||||
|
||||
@@ -1769,7 +1791,7 @@ func TestExitImmediately(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
assert.Contains(t, buff.String(), `"this_should_fail": executable file not found in $PATH`)
|
||||
}
|
||||
|
||||
@@ -1789,6 +1811,22 @@ func TestRunOnlyRunsJobsHashOnce(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunOnlyRunsJobsHashOnceWithWildcard(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tt := fileContentTest{
|
||||
Dir: "testdata/run",
|
||||
Target: "deploy",
|
||||
Files: map[string]string{
|
||||
"wildcard.txt": "Deploy infra\nDeploy js\nDeploy go\n",
|
||||
},
|
||||
}
|
||||
t.Run("", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
tt.Run(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunOnceSharedDeps(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1802,7 +1840,7 @@ func TestRunOnceSharedDeps(t *testing.T) {
|
||||
task.WithForceAll(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build"}))
|
||||
|
||||
rx := regexp.MustCompile(`task: \[service-[a,b]:library:build\] echo "build library"`)
|
||||
matches := rx.FindAllStringSubmatch(buff.String(), -1)
|
||||
@@ -1834,10 +1872,10 @@ task-1 ran successfully
|
||||
task: [task-1] echo 'task-1 ran successfully'
|
||||
task-1 ran successfully
|
||||
`)
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "task-2"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "task-2"}))
|
||||
assert.Contains(t, buff.String(), expectedOutputOrder)
|
||||
buff.Reset()
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "parent"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "parent"}))
|
||||
assert.Contains(t, buff.String(), "child task deferred value-from-parent")
|
||||
}
|
||||
|
||||
@@ -1853,7 +1891,7 @@ func TestExitCodeZero(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "exit-zero"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "exit-zero"}))
|
||||
assert.Equal(t, "FOO=bar - DYNAMIC_FOO=bar - EXIT_CODE=", strings.TrimSpace(buff.String()))
|
||||
}
|
||||
|
||||
@@ -1869,7 +1907,7 @@ func TestExitCodeOne(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "exit-one"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "exit-one"}))
|
||||
assert.Equal(t, "FOO=bar - DYNAMIC_FOO=bar - EXIT_CODE=1", strings.TrimSpace(buff.String()))
|
||||
}
|
||||
|
||||
@@ -1898,7 +1936,7 @@ func TestIgnoreNilElements(t *testing.T) {
|
||||
task.WithSilent(true),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
assert.Equal(t, "string-slice-1\n", buff.String())
|
||||
})
|
||||
}
|
||||
@@ -1926,7 +1964,7 @@ task: [bye] echo 'Bye!'
|
||||
Bye!
|
||||
::endgroup::
|
||||
`)
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "bye"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "bye"}))
|
||||
t.Log(buff.String())
|
||||
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
|
||||
}
|
||||
@@ -1943,7 +1981,7 @@ func TestOutputGroupErrorOnlySwallowsOutputOnSuccess(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "passing"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "passing"}))
|
||||
t.Log(buff.String())
|
||||
assert.Empty(t, buff.String())
|
||||
}
|
||||
@@ -1960,7 +1998,7 @@ func TestOutputGroupErrorOnlyShowsOutputOnFailure(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: "failing"}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: "failing"}))
|
||||
t.Log(buff.String())
|
||||
assert.Contains(t, "failing-output", strings.TrimSpace(buff.String()))
|
||||
assert.NotContains(t, "passing", strings.TrimSpace(buff.String()))
|
||||
@@ -1992,7 +2030,7 @@ VAR_1 is included-default-var1
|
||||
task: [included3:task1] echo "VAR_2 is included-default-var2"
|
||||
VAR_2 is included-default-var2
|
||||
`)
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "task1"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task1"}))
|
||||
t.Log(buff.String())
|
||||
assert.Equal(t, strings.TrimSpace(buff.String()), expectedOutputOrder)
|
||||
}
|
||||
@@ -2030,7 +2068,7 @@ Hello foo
|
||||
task: [bar:lib:greet] echo 'Hello bar'
|
||||
Hello bar
|
||||
`)
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
t.Log(buff.String())
|
||||
assert.Equal(t, expectedOutputOrder, strings.TrimSpace(buff.String()))
|
||||
}
|
||||
@@ -2068,7 +2106,7 @@ func TestErrorCode(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
require.Error(t, err)
|
||||
taskRunErr, ok := err.(*errors.TaskRunError)
|
||||
assert.True(t, ok, "cannot cast returned error to *task.TaskRunError")
|
||||
@@ -2120,7 +2158,7 @@ func TestEvaluateSymlinksInPaths(t *testing.T) { // nolint:paralleltest // canno
|
||||
for _, test := range tests { // nolint:paralleltest // cannot run in parallel
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require.NoError(t, e.Setup())
|
||||
err := e.Run(context.Background(), &task.Call{Task: test.task})
|
||||
err := e.Run(t.Context(), &task.Call{Task: test.task})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expected, strings.TrimSpace(buff.String()))
|
||||
buff.Reset()
|
||||
@@ -2163,7 +2201,7 @@ func TestTaskfileWalk(t *testing.T) {
|
||||
task.WithStderr(&buff),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
assert.Equal(t, test.expected, buff.String())
|
||||
})
|
||||
}
|
||||
@@ -2181,7 +2219,7 @@ func TestUserWorkingDirectory(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "default"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"}))
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", wd), buff.String())
|
||||
}
|
||||
|
||||
@@ -2203,7 +2241,7 @@ func TestUserWorkingDirectoryWithIncluded(t *testing.T) {
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "included:echo"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "included:echo"}))
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", wd), buff.String())
|
||||
}
|
||||
|
||||
@@ -2217,7 +2255,7 @@ func TestPlatforms(t *testing.T) {
|
||||
task.WithStderr(&buff),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "build-" + runtime.GOOS}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "build-" + runtime.GOOS}))
|
||||
assert.Equal(t, fmt.Sprintf("task: [build-%s] echo 'Running task on %s'\nRunning task on %s\n", runtime.GOOS, runtime.GOOS, runtime.GOOS), buff.String())
|
||||
}
|
||||
|
||||
@@ -2232,7 +2270,7 @@ func TestPOSIXShellOptsGlobalLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "pipefail"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "pipefail"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||
}
|
||||
@@ -2248,7 +2286,7 @@ func TestPOSIXShellOptsTaskLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "pipefail"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "pipefail"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||
}
|
||||
@@ -2264,7 +2302,7 @@ func TestPOSIXShellOptsCommandLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "pipefail"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "pipefail"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "pipefail\ton\n", buff.String())
|
||||
}
|
||||
@@ -2280,7 +2318,7 @@ func TestBashShellOptsGlobalLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "globstar"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "globstar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||
}
|
||||
@@ -2296,7 +2334,7 @@ func TestBashShellOptsTaskLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "globstar"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "globstar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||
}
|
||||
@@ -2312,7 +2350,7 @@ func TestBashShellOptsCommandLevel(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "globstar"})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "globstar"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "globstar\ton\n", buff.String())
|
||||
}
|
||||
@@ -2332,7 +2370,7 @@ func TestSplitArgs(t *testing.T) {
|
||||
vars := ast.NewVars()
|
||||
vars.Set("CLI_ARGS", ast.Var{Value: "foo bar 'foo bar baz'"})
|
||||
|
||||
err := e.Run(context.Background(), &task.Call{Task: "default", Vars: vars})
|
||||
err := e.Run(t.Context(), &task.Call{Task: "default", Vars: vars})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "3\n", buff.String())
|
||||
}
|
||||
@@ -2373,14 +2411,14 @@ func TestSilence(t *testing.T) {
|
||||
|
||||
// Then test the two basic cases where the task is silent or not.
|
||||
// A silenced task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "silent"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "silent"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "siWhile running lent: Expected not see output, because the task is silent")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty (not silent) task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "chatty"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "chatty"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "chWhile running atty: Expected to see output, because the task is not silent")
|
||||
|
||||
@@ -2388,42 +2426,42 @@ func TestSilence(t *testing.T) {
|
||||
|
||||
// Then test invoking the two task from other tasks.
|
||||
// A silenced task that calls a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-silent-calls-chatty-non-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-silent-calls-chatty-non-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-silent-calls-chatty-non-silenced: Expected to see output. The task is silenced, but the called task is not. Silence does not propagate to called tasks.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A silent task that does a silent call to a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-silent-calls-chatty-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-silent-calls-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-silent-calls-chatty-silenced: Expected not to see output. The task calls chatty task, but the call is silenced.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that does a call to a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-chatty-calls-chatty-non-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-chatty-calls-chatty-non-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-chatty-calls-chatty-non-silenced: Expected to see output. Both caller and callee are chatty and not silenced.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that does a silenced call to a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-chatty-calls-chatty-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-chatty-calls-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-chatty-calls-chatty-silenced: Expected to see output. Call to a chatty task is silenced, but the parent task is not.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task with no cmd's of its own that does a silenced call to a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-no-cmds-calls-chatty-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-no-cmds-calls-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-no-cmds-calls-chatty-silenced: Expected not to see output. While the task itself is not silenced, it does not have any cmds and only does an invocation of a silenced task.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that does a silenced invocation of a task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-chatty-calls-silenced-cmd"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-chatty-calls-silenced-cmd"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-chatty-calls-silenced-cmd: Expected not to see output. While the task itself is not silenced, its call to the chatty task is silent.")
|
||||
|
||||
@@ -2431,21 +2469,21 @@ func TestSilence(t *testing.T) {
|
||||
|
||||
// Then test calls via dependencies.
|
||||
// A silent task that depends on a chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-is-silent-depends-on-chatty-non-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-is-silent-depends-on-chatty-non-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, buff.String(), "While running task-test-is-silent-depends-on-chatty-non-silenced: Expected to see output. The task is silent and depends on a chatty task. Dependencies does not inherit silence.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A silent task that depends on a silenced chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-is-silent-depends-on-chatty-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-is-silent-depends-on-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-is-silent-depends-on-chatty-silenced: Expected not to see output. The task is silent and has a silenced dependency on a chatty task.")
|
||||
|
||||
buff.Reset()
|
||||
|
||||
// A chatty task that, depends on a silenced chatty task.
|
||||
err = e.Run(context.Background(), &task.Call{Task: "task-test-is-chatty-depends-on-chatty-silenced"})
|
||||
err = e.Run(t.Context(), &task.Call{Task: "task-test-is-chatty-depends-on-chatty-silenced"})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, buff.String(), "While running task-test-is-chatty-depends-on-chatty-silenced: Expected not to see output. The task is chatty but does not have commands and has a silenced dependency on a chatty task.")
|
||||
|
||||
@@ -2497,7 +2535,7 @@ func TestForce(t *testing.T) {
|
||||
task.WithForceAll(tt.forceAll),
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: "task-with-dep"}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "task-with-dep"}))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2557,10 +2595,10 @@ func TestWildcard(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, e.Setup())
|
||||
if test.wantErr {
|
||||
require.Error(t, e.Run(context.Background(), &task.Call{Task: test.call}))
|
||||
require.Error(t, e.Run(t.Context(), &task.Call{Task: test.call}))
|
||||
return
|
||||
}
|
||||
require.NoError(t, e.Run(context.Background(), &task.Call{Task: test.call}))
|
||||
require.NoError(t, e.Run(t.Context(), &task.Call{Task: test.call}))
|
||||
assert.Equal(t, test.expectedOutput, buff.String())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -46,17 +46,22 @@ type Task struct {
|
||||
Namespace string
|
||||
IncludeVars *Vars
|
||||
IncludedTaskfileVars *Vars
|
||||
|
||||
FullName string
|
||||
}
|
||||
|
||||
func (t *Task) Name() string {
|
||||
if t.Label != "" {
|
||||
return t.Label
|
||||
}
|
||||
if t.FullName != "" {
|
||||
return t.FullName
|
||||
}
|
||||
return t.Task
|
||||
}
|
||||
|
||||
func (t *Task) LocalName() string {
|
||||
name := t.Task
|
||||
name := t.FullName
|
||||
name = strings.TrimPrefix(name, t.Namespace)
|
||||
name = strings.TrimPrefix(name, ":")
|
||||
return name
|
||||
@@ -220,6 +225,7 @@ func (t *Task) DeepCopy() *Task {
|
||||
Location: t.Location.DeepCopy(),
|
||||
Requires: t.Requires.DeepCopy(),
|
||||
Namespace: t.Namespace,
|
||||
FullName: t.FullName,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ type Var struct {
|
||||
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||
switch node.Kind {
|
||||
case yaml.MappingNode:
|
||||
key := node.Content[0].Value
|
||||
key := "<none>"
|
||||
if len(node.Content) > 0 {
|
||||
key = node.Content[0].Value
|
||||
}
|
||||
switch key {
|
||||
case "sh", "ref", "map":
|
||||
var m struct {
|
||||
|
||||
@@ -18,15 +18,21 @@ type FileNode struct {
|
||||
}
|
||||
|
||||
func NewFileNode(entrypoint, dir string, opts ...NodeOption) (*FileNode, error) {
|
||||
var err error
|
||||
base := NewBaseNode(dir, opts...)
|
||||
entrypoint, base.dir, err = fsext.Search(entrypoint, base.dir, defaultTaskfiles)
|
||||
// Find the entrypoint file
|
||||
resolvedEntrypoint, err := fsext.Search(entrypoint, dir, defaultTaskfiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Resolve the directory
|
||||
resolvedDir, err := fsext.ResolveDir(entrypoint, resolvedEntrypoint, dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &FileNode{
|
||||
baseNode: base,
|
||||
entrypoint: entrypoint,
|
||||
baseNode: NewBaseNode(resolvedDir, opts...),
|
||||
entrypoint: resolvedEntrypoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,48 @@
|
||||
package ast
|
||||
|
||||
import "github.com/Masterminds/semver/v3"
|
||||
import (
|
||||
"cmp"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
)
|
||||
|
||||
type TaskRC struct {
|
||||
Version *semver.Version `yaml:"version"`
|
||||
Verbose *bool `yaml:"verbose"`
|
||||
Concurrency *int `yaml:"concurrency"`
|
||||
Remote Remote `yaml:"remote"`
|
||||
Experiments map[string]int `yaml:"experiments"`
|
||||
}
|
||||
|
||||
type Remote struct {
|
||||
Insecure *bool `yaml:"insecure"`
|
||||
Offline *bool `yaml:"offline"`
|
||||
Timeout *time.Duration `yaml:"timeout"`
|
||||
CacheExpiry *time.Duration `yaml:"cache-expiry"`
|
||||
}
|
||||
|
||||
// Merge combines the current TaskRC with another TaskRC, prioritizing non-nil fields from the other TaskRC.
|
||||
func (t *TaskRC) Merge(other *TaskRC) {
|
||||
if other == nil {
|
||||
return
|
||||
}
|
||||
|
||||
t.Version = cmp.Or(other.Version, t.Version)
|
||||
|
||||
if t.Experiments == nil && other.Experiments != nil {
|
||||
t.Experiments = other.Experiments
|
||||
} else if t.Experiments != nil && other.Experiments != nil {
|
||||
maps.Copy(t.Experiments, other.Experiments)
|
||||
}
|
||||
|
||||
// Merge Remote fields
|
||||
t.Remote.Insecure = cmp.Or(other.Remote.Insecure, t.Remote.Insecure)
|
||||
t.Remote.Offline = cmp.Or(other.Remote.Offline, t.Remote.Offline)
|
||||
t.Remote.Timeout = cmp.Or(other.Remote.Timeout, t.Remote.Timeout)
|
||||
t.Remote.CacheExpiry = cmp.Or(other.Remote.CacheExpiry, t.Remote.CacheExpiry)
|
||||
|
||||
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
|
||||
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
package taskrc
|
||||
|
||||
import "github.com/go-task/task/v3/internal/fsext"
|
||||
import (
|
||||
"github.com/go-task/task/v3/internal/fsext"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
entrypoint string
|
||||
dir string
|
||||
}
|
||||
|
||||
func NewNode(
|
||||
entrypoint string,
|
||||
dir string,
|
||||
possibleFileNames []string,
|
||||
) (*Node, error) {
|
||||
dir = fsext.DefaultDir(entrypoint, dir)
|
||||
var err error
|
||||
entrypoint, dir, err = fsext.Search(entrypoint, dir, defaultTaskRCs)
|
||||
resolvedEntrypoint, err := fsext.SearchPath(dir, possibleFileNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Node{
|
||||
entrypoint: entrypoint,
|
||||
dir: dir,
|
||||
entrypoint: resolvedEntrypoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,89 @@
|
||||
package taskrc
|
||||
|
||||
var defaultTaskRCs = []string{
|
||||
".taskrc.yml",
|
||||
".taskrc.yaml",
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/go-task/task/v3/internal/fsext"
|
||||
"github.com/go-task/task/v3/taskrc/ast"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultXDGTaskRCs = []string{
|
||||
"taskrc.yml",
|
||||
"taskrc.yaml",
|
||||
}
|
||||
defaultTaskRCs = []string{
|
||||
".taskrc.yml",
|
||||
".taskrc.yaml",
|
||||
}
|
||||
)
|
||||
|
||||
// GetConfig loads and merges local and global Task configuration files
|
||||
func GetConfig(dir string) (*ast.TaskRC, error) {
|
||||
var config *ast.TaskRC
|
||||
reader := NewReader()
|
||||
|
||||
// Read the XDG config file
|
||||
if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
|
||||
xdgConfigNode, err := NewNode("", filepath.Join(xdgConfigHome, "task"), defaultXDGTaskRCs)
|
||||
if err == nil && xdgConfigNode != nil {
|
||||
xdgConfig, err := reader.Read(xdgConfigNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config = xdgConfig
|
||||
}
|
||||
}
|
||||
|
||||
// If the current path does not contain $HOME
|
||||
// If it does contain $HOME, then we will find this config later anyway
|
||||
home, err := os.UserHomeDir()
|
||||
if err == nil && !strings.Contains(home, dir) {
|
||||
homeNode, err := NewNode("", home, defaultTaskRCs)
|
||||
if err == nil && homeNode != nil {
|
||||
homeConfig, err := reader.Read(homeNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config == nil {
|
||||
config = homeConfig
|
||||
} else {
|
||||
config.Merge(homeConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find all the nodes from the given directory up to the users home directory
|
||||
entrypoints, err := fsext.SearchAll("", dir, defaultTaskRCs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Reverse the entrypoints since we want the child files to override parent ones
|
||||
slices.Reverse(entrypoints)
|
||||
|
||||
// Loop over the nodes, and merge them into the main config
|
||||
for _, entrypoint := range entrypoints {
|
||||
node, err := NewNode("", entrypoint, defaultTaskRCs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localConfig, err := reader.Read(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if localConfig == nil {
|
||||
continue
|
||||
}
|
||||
if config == nil {
|
||||
config = localConfig
|
||||
continue
|
||||
}
|
||||
config.Merge(localConfig)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
137
taskrc/taskrc_test.go
Normal file
137
taskrc/taskrc_test.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package taskrc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/go-task/task/v3/taskrc/ast"
|
||||
)
|
||||
|
||||
const (
|
||||
xdgConfigYAML = `
|
||||
experiments:
|
||||
FOO: 1
|
||||
BAR: 1
|
||||
BAZ: 1
|
||||
`
|
||||
|
||||
homeConfigYAML = `
|
||||
experiments:
|
||||
FOO: 2
|
||||
BAR: 2
|
||||
`
|
||||
|
||||
localConfigYAML = `
|
||||
experiments:
|
||||
FOO: 3
|
||||
`
|
||||
)
|
||||
|
||||
func setupDirs(t *testing.T) (string, string, string) {
|
||||
t.Helper()
|
||||
|
||||
xdgConfigDir := t.TempDir()
|
||||
xdgTaskConfigDir := filepath.Join(xdgConfigDir, "task")
|
||||
require.NoError(t, os.Mkdir(xdgTaskConfigDir, 0o755))
|
||||
|
||||
homeDir := t.TempDir()
|
||||
|
||||
localDir := filepath.Join(homeDir, "local")
|
||||
require.NoError(t, os.Mkdir(localDir, 0o755))
|
||||
|
||||
t.Setenv("XDG_CONFIG_HOME", xdgConfigDir)
|
||||
t.Setenv("HOME", homeDir)
|
||||
|
||||
return xdgTaskConfigDir, homeDir, localDir
|
||||
}
|
||||
|
||||
func writeFile(t *testing.T, dir, filename, content string) {
|
||||
t.Helper()
|
||||
err := os.WriteFile(filepath.Join(dir, filename), []byte(content), 0o644)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetConfig_NoConfigFiles(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||
_, _, localDir := setupDirs(t)
|
||||
|
||||
cfg, err := GetConfig(localDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, cfg)
|
||||
}
|
||||
|
||||
func TestGetConfig_OnlyXDG(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||
xdgDir, _, localDir := setupDirs(t)
|
||||
|
||||
writeFile(t, xdgDir, "taskrc.yml", xdgConfigYAML)
|
||||
|
||||
cfg, err := GetConfig(localDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &ast.TaskRC{
|
||||
Version: nil,
|
||||
Experiments: map[string]int{
|
||||
"FOO": 1,
|
||||
"BAR": 1,
|
||||
"BAZ": 1,
|
||||
},
|
||||
}, cfg)
|
||||
}
|
||||
|
||||
func TestGetConfig_OnlyHome(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||
_, homeDir, localDir := setupDirs(t)
|
||||
|
||||
writeFile(t, homeDir, ".taskrc.yml", homeConfigYAML)
|
||||
|
||||
cfg, err := GetConfig(localDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &ast.TaskRC{
|
||||
Version: nil,
|
||||
Experiments: map[string]int{
|
||||
"FOO": 2,
|
||||
"BAR": 2,
|
||||
},
|
||||
}, cfg)
|
||||
}
|
||||
|
||||
func TestGetConfig_OnlyLocal(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||
_, _, localDir := setupDirs(t)
|
||||
|
||||
writeFile(t, localDir, ".taskrc.yml", localConfigYAML)
|
||||
|
||||
cfg, err := GetConfig(localDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &ast.TaskRC{
|
||||
Version: nil,
|
||||
Experiments: map[string]int{
|
||||
"FOO": 3,
|
||||
},
|
||||
}, cfg)
|
||||
}
|
||||
|
||||
func TestGetConfig_All(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||
xdgConfigDir, homeDir, localDir := setupDirs(t)
|
||||
|
||||
// Write local config
|
||||
writeFile(t, localDir, ".taskrc.yml", localConfigYAML)
|
||||
|
||||
// Write home config
|
||||
writeFile(t, homeDir, ".taskrc.yml", homeConfigYAML)
|
||||
|
||||
// Write XDG config
|
||||
writeFile(t, xdgConfigDir, "taskrc.yml", xdgConfigYAML)
|
||||
|
||||
cfg, err := GetConfig(localDir)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, cfg)
|
||||
assert.Equal(t, &ast.TaskRC{
|
||||
Version: nil,
|
||||
Experiments: map[string]int{
|
||||
"FOO": 3,
|
||||
"BAR": 2,
|
||||
"BAZ": 1,
|
||||
},
|
||||
}, cfg)
|
||||
}
|
||||
8
testdata/checksum/Taskfile.yml
vendored
8
testdata/checksum/Taskfile.yml
vendored
@@ -12,6 +12,14 @@ tasks:
|
||||
generates:
|
||||
- ./generated.txt
|
||||
method: checksum
|
||||
build-*:
|
||||
cmds:
|
||||
- cp ./source.txt ./generated-{{index .MATCH 0}}.txt
|
||||
sources:
|
||||
- ./source.txt
|
||||
generates:
|
||||
- ./generated-{{index .MATCH 0}}.txt
|
||||
method: checksum
|
||||
|
||||
build-with-status:
|
||||
cmds:
|
||||
|
||||
1
testdata/checksum/generated-wildcard.txt
vendored
Normal file
1
testdata/checksum/generated-wildcard.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Hello, World!
|
||||
@@ -1 +1 @@
|
||||
task: Missing schema version in Taskfile "/testdata/empty_taskfile/Taskfile.yml"
|
||||
task: Missing schema version in Taskfile "{{.TEST_DIR}}/testdata/empty_taskfile/Taskfile.yml"
|
||||
@@ -1,2 +1,2 @@
|
||||
task: Failed to parse /testdata/for/cmds/Taskfile.yml:
|
||||
task: Failed to parse {{.TEST_DIR}}/testdata/for/cmds/Taskfile.yml:
|
||||
matrix reference ".NOT_A_LIST" must resolve to a list
|
||||
@@ -1,2 +1,2 @@
|
||||
matrix reference ".NOT_A_LIST" must resolve to a list
|
||||
task: Failed to parse /testdata/for/deps/Taskfile.yml:
|
||||
task: Failed to parse {{.TEST_DIR}}/testdata/for/deps/Taskfile.yml:
|
||||
|
||||
5
testdata/fuzzy/Taskfile.yml
vendored
5
testdata/fuzzy/Taskfile.yml
vendored
@@ -2,3 +2,8 @@ version: 3
|
||||
|
||||
tasks:
|
||||
install: echo 'install'
|
||||
|
||||
internal:
|
||||
internal: true
|
||||
cmds:
|
||||
- echo "internal"
|
||||
|
||||
1
testdata/fuzzy/testdata/TestFuzzyModel-intern-err-run.golden
vendored
Normal file
1
testdata/fuzzy/testdata/TestFuzzyModel-intern-err-run.golden
vendored
Normal file
@@ -0,0 +1 @@
|
||||
task: Task "intern" does not exist
|
||||
1
testdata/fuzzy/testdata/TestFuzzyModel-intern.golden
vendored
Normal file
1
testdata/fuzzy/testdata/TestFuzzyModel-intern.golden
vendored
Normal file
@@ -0,0 +1 @@
|
||||
task: No tasks with description available. Try --list-all to list all tasks
|
||||
@@ -1,3 +1,3 @@
|
||||
task: The checksum of the Taskfile at "/testdata/includes_checksum/included.yml" does not match!
|
||||
task: The checksum of the Taskfile at "{{.TEST_DIR}}/testdata/includes_checksum/included.yml" does not match!
|
||||
got: "c97f39eb96fe3fa5fe2a610d244b8449897b06f0c93821484af02e0999781bf5"
|
||||
want: "foo"
|
||||
@@ -10,9 +10,9 @@
|
||||
"location": {
|
||||
"line": 4,
|
||||
"column": 3,
|
||||
"taskfile": "{{ .TaskfileLocation }}"
|
||||
"taskfile": "{{.TEST_DIR}}/testdata/json_list_format/Taskfile.yml"
|
||||
}
|
||||
}
|
||||
],
|
||||
"location": "{{ .TaskfileLocation }}"
|
||||
"location": "{{.TEST_DIR}}/testdata/json_list_format/Taskfile.yml"
|
||||
}
|
||||
|
||||
11
testdata/run/Taskfile.yml
vendored
11
testdata/run/Taskfile.yml
vendored
@@ -22,3 +22,14 @@ tasks:
|
||||
run: once
|
||||
cmds:
|
||||
- echo starting {{.CONTENT}} >> hash.txt
|
||||
|
||||
deploy:
|
||||
cmds:
|
||||
- rm -rf wildcard.txt
|
||||
- task: deploy:infra
|
||||
- task: deploy:js
|
||||
- task: deploy:go
|
||||
|
||||
deploy:*:
|
||||
run: once
|
||||
cmd: echo "Deploy {{index .MATCH 0}}" >> wildcard.txt
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars
|
||||
{{.TEST_DIR}}/testdata/special_vars
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars/included
|
||||
{{.TEST_DIR}}/testdata/special_vars/included
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars/included/Taskfile.yml
|
||||
{{.TEST_DIR}}/testdata/special_vars/included/Taskfile.yml
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars
|
||||
{{.TEST_DIR}}/testdata/special_vars
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars/foo
|
||||
{{.TEST_DIR}}/testdata/special_vars/foo
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars
|
||||
{{.TEST_DIR}}/testdata/special_vars
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars/Taskfile.yml
|
||||
{{.TEST_DIR}}/testdata/special_vars/Taskfile.yml
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars
|
||||
{{.TEST_DIR}}/testdata/special_vars
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars/included
|
||||
{{.TEST_DIR}}/testdata/special_vars/included
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars/included/Taskfile.yml
|
||||
{{.TEST_DIR}}/testdata/special_vars/included/Taskfile.yml
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars
|
||||
{{.TEST_DIR}}/testdata/special_vars
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars/foo
|
||||
{{.TEST_DIR}}/testdata/special_vars/foo
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars
|
||||
{{.TEST_DIR}}/testdata/special_vars
|
||||
|
||||
@@ -1 +1 @@
|
||||
/testdata/special_vars/Taskfile.yml
|
||||
{{.TEST_DIR}}/testdata/special_vars/Taskfile.yml
|
||||
|
||||
@@ -44,9 +44,14 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fullName := origTask.Task
|
||||
if matches, exists := vars.Get("MATCH"); exists {
|
||||
for _, match := range matches.Value.([]string) {
|
||||
fullName = strings.Replace(fullName, "*", match, 1)
|
||||
}
|
||||
}
|
||||
|
||||
cache := &templater.Cache{Vars: vars}
|
||||
|
||||
new := ast.Task{
|
||||
Task: origTask.Task,
|
||||
Label: templater.Replace(origTask.Label, cache),
|
||||
@@ -76,6 +81,7 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err
|
||||
Requires: origTask.Requires,
|
||||
Watch: origTask.Watch,
|
||||
Namespace: origTask.Namespace,
|
||||
FullName: fullName,
|
||||
}
|
||||
new.Dir, err = execext.ExpandLiteral(new.Dir)
|
||||
if err != nil {
|
||||
|
||||
18
website/.gitignore
vendored
18
website/.gitignore
vendored
@@ -1,21 +1,9 @@
|
||||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
i18n
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.vitepress/cache
|
||||
.vitepress/dist
|
||||
.task/
|
||||
|
||||
1
website/.prettierignore
Normal file
1
website/.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
pnpm-lock.yaml
|
||||
5
website/.vitepress/components.d.ts
vendored
Normal file
5
website/.vitepress/components.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
97
website/.vitepress/components/AuthorCard.vue
Normal file
97
website/.vitepress/components/AuthorCard.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="author-compact" v-if="author">
|
||||
<img :src="author.avatar" :alt="author.name" class="author-avatar" />
|
||||
<div class="author-info">
|
||||
<div class="author-name-line">
|
||||
<span class="author-name">{{ author.name }}</span>
|
||||
|
||||
<div class="author-socials">
|
||||
<a
|
||||
v-for="{ link, icon } in author.links"
|
||||
:key="link"
|
||||
:href="link"
|
||||
target="_blank"
|
||||
class="social-link"
|
||||
>
|
||||
<span :class="`vpi-social-${icon}`"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<span class="author-bio">{{ author.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { team } from '../team.ts';
|
||||
import { computed } from 'vue';
|
||||
const props = defineProps({
|
||||
author: String
|
||||
});
|
||||
|
||||
const author = computed(() => {
|
||||
return team.find((m) => m.slug === props.author) || null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.author-compact {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.author-avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.author-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.1rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.author-name-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.author-name {
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.author-bio {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.author-socials {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.social-link {
|
||||
color: var(--vp-c-text-2);
|
||||
transition: color 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.social-link:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.author-compact {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
182
website/.vitepress/components/BlogPost.vue
Normal file
182
website/.vitepress/components/BlogPost.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<article class="blog-post">
|
||||
<div class="post-header">
|
||||
<h3 class="post-title">
|
||||
<a :href="url">{{ title }}</a>
|
||||
</h3>
|
||||
|
||||
<div class="post-meta">
|
||||
<time :datetime="date" class="post-date">
|
||||
{{ formatDate(date) }}
|
||||
</time>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="post-content">
|
||||
<div class="post-image" v-if="image">
|
||||
<img :src="image" :alt="title" />
|
||||
</div>
|
||||
|
||||
<div class="post-text">
|
||||
<AuthorCard :author="author" />
|
||||
|
||||
<p class="post-description">{{ description }}</p>
|
||||
|
||||
<div class="post-footer">
|
||||
<div class="post-tags" v-if="tags?.length">
|
||||
<strong>Tags:</strong>
|
||||
<code v-for="tag in tags" :key="tag" class="post-tag">{{
|
||||
tag
|
||||
}}</code>
|
||||
</div>
|
||||
|
||||
<a :href="url" class="read-more"> Read more → </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import AuthorCard from './AuthorCard.vue';
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
url: String,
|
||||
date: String,
|
||||
author: String,
|
||||
description: String,
|
||||
tags: Array,
|
||||
image: String
|
||||
});
|
||||
|
||||
function formatDate(date) {
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.blog-post {
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
padding-bottom: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.blog-post:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 1.8rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.post-title a {
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.post-title a:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.post-date {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.post-image {
|
||||
flex-shrink: 0;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.post-image img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
object-fit: cover;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.post-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.post-description {
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.6;
|
||||
margin: 1.5rem 0;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.post-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
margin-top: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.post-tags {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.post-tag {
|
||||
background: var(--vp-c-default-soft);
|
||||
color: var(--vp-c-text-2);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
margin-left: 0.5rem;
|
||||
font-family: var(--vp-font-family-mono);
|
||||
}
|
||||
|
||||
.read-more {
|
||||
color: var(--vp-c-brand-1);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--vp-c-brand-1);
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.read-more:hover {
|
||||
background: var(--vp-c-brand-1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.post-content {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.post-image {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.post-footer {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
20
website/.vitepress/components/HomePage.vue
Normal file
20
website/.vitepress/components/HomePage.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { VPHomeSponsors } from 'vitepress/theme';
|
||||
import { sponsors } from '../sponsors';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="content-container">
|
||||
<main class="main">
|
||||
<VPHomeSponsors
|
||||
v-if="sponsors"
|
||||
message="Task is free and open source, made possible by wonderful sponsors."
|
||||
:data="sponsors"
|
||||
/>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
245
website/.vitepress/components/VPTeamMembersItem.vue
Normal file
245
website/.vitepress/components/VPTeamMembersItem.vue
Normal file
@@ -0,0 +1,245 @@
|
||||
<script setup lang="ts">
|
||||
import type { DefaultTheme } from 'vitepress/theme';
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
|
||||
import VPSocialLinks from 'vitepress/dist/client/theme-default/components/VPSocialLinks.vue';
|
||||
|
||||
interface Props {
|
||||
size?: 'small' | 'medium';
|
||||
member: TeamMember;
|
||||
}
|
||||
|
||||
interface TeamMember extends DefaultTheme.TeamMember {
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 'medium'
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article class="VPTeamMembersItem" :class="[size]">
|
||||
<div class="profile">
|
||||
<figure class="avatar">
|
||||
<img class="avatar-img" :src="member.avatar" :alt="member.name" />
|
||||
</figure>
|
||||
<div class="data">
|
||||
<h1 class="name">
|
||||
<img :src="member.icon" alt="profile-icon" />
|
||||
{{ member.name }}
|
||||
</h1>
|
||||
<p v-if="member.title || member.org" class="affiliation">
|
||||
<span v-if="member.title" class="title">
|
||||
{{ member.title }}
|
||||
</span>
|
||||
<span v-if="member.title && member.org" class="at"> @ </span>
|
||||
<VPLink
|
||||
v-if="member.org"
|
||||
class="org"
|
||||
:class="{ link: member.orgLink }"
|
||||
:href="member.orgLink"
|
||||
no-icon
|
||||
>
|
||||
{{ member.org }}
|
||||
</VPLink>
|
||||
</p>
|
||||
<p v-if="member.desc" class="desc" v-html="member.desc" />
|
||||
<div v-if="member.links" class="links">
|
||||
<VPSocialLinks :links="member.links" :me="false" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="member.sponsor" class="sp">
|
||||
<VPLink class="sp-link" :href="member.sponsor" no-icon>
|
||||
<span class="vpi-heart sp-icon" /> {{ member.actionText || 'Sponsor' }}
|
||||
</VPLink>
|
||||
</div>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.VPTeamMembersItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .profile {
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .data {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .avatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .name {
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .affiliation {
|
||||
padding-top: 4px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .desc {
|
||||
padding-top: 12px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.small .links {
|
||||
margin: 0 -16px -20px;
|
||||
padding: 10px 0 0;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .profile {
|
||||
padding: 48px 32px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .data {
|
||||
padding-top: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .avatar {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .name {
|
||||
letter-spacing: 0.15px;
|
||||
line-height: 28px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .affiliation {
|
||||
padding-top: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .desc {
|
||||
padding-top: 16px;
|
||||
max-width: 288px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem.medium .links {
|
||||
margin: 0 -16px -12px;
|
||||
padding: 16px 12px 0;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem .profile .name {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.VPTeamMembersItem .profile .name img {
|
||||
display: inline-block;
|
||||
height: 22px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.profile {
|
||||
flex-grow: 1;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.data {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
margin: 0 auto;
|
||||
border-radius: 50%;
|
||||
box-shadow: var(--vp-shadow-3);
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.affiliation {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.org.link {
|
||||
color: var(--vp-c-text-2);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.org.link:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.desc :deep(a) {
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-brand-1);
|
||||
text-decoration-style: dotted;
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.sp-link {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-sponsor);
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
transition:
|
||||
color 0.25s,
|
||||
background-color 0.25s;
|
||||
}
|
||||
|
||||
.sp .sp-link.link:hover,
|
||||
.sp .sp-link.link:focus {
|
||||
outline: none;
|
||||
color: var(--vp-c-white);
|
||||
background-color: var(--vp-c-sponsor);
|
||||
}
|
||||
|
||||
.sp-icon {
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
7
website/.vitepress/components/Version.vue
Normal file
7
website/.vitepress/components/Version.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { VPBadge } from 'vitepress/theme';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPBadge type="info"> <slot />+ </VPBadge>
|
||||
</template>
|
||||
348
website/.vitepress/config.ts
Normal file
348
website/.vitepress/config.ts
Normal file
@@ -0,0 +1,348 @@
|
||||
import { defineConfig } from 'vitepress';
|
||||
import githubLinksPlugin from './plugins/github-links';
|
||||
import { readFileSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs';
|
||||
import {
|
||||
groupIconMdPlugin,
|
||||
groupIconVitePlugin,
|
||||
localIconLoader
|
||||
} from 'vitepress-plugin-group-icons';
|
||||
import { team } from './team.ts';
|
||||
import { taskDescription, taskName } from './meta.ts';
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
|
||||
const version = readFileSync(
|
||||
resolve(__dirname, '../../internal/version/version.txt'),
|
||||
'utf8'
|
||||
).trim();
|
||||
|
||||
const urlVersion =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? {
|
||||
current: 'https://taskfile.dev/',
|
||||
next: 'http://localhost:3002/'
|
||||
}
|
||||
: {
|
||||
current: 'https://taskfile.dev/',
|
||||
next: 'https://next.taskfile.dev/'
|
||||
};
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: taskName,
|
||||
description: taskDescription,
|
||||
lang: 'en-US',
|
||||
head: [
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/x-icon',
|
||||
href: '/img/favicon.icon',
|
||||
sizes: '48x48'
|
||||
}
|
||||
],
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'icon',
|
||||
sizes: 'any',
|
||||
type: 'image/svg+xml',
|
||||
href: '/img/logo.svg'
|
||||
}
|
||||
],
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'canonical',
|
||||
href: 'https://taskfile.dev/'
|
||||
}
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{ name: 'author', content: `${team.map((c) => c.name).join(', ')}` }
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
name: 'keywords',
|
||||
content:
|
||||
'task runner, build tool, taskfile, yaml build tool, go task runner, make alternative, cross-platform build tool, makefile alternative, automation tool, ci cd pipeline, developer productivity, build automation, command line tool, go binary, yaml configuration'
|
||||
}
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{
|
||||
async: '',
|
||||
src: 'https://www.googletagmanager.com/gtag/js?id=G-4RT25NXQ7N'
|
||||
}
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{},
|
||||
`window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag("js", new Date());
|
||||
gtag("config", "G-4RT25NXQ7N");`
|
||||
],
|
||||
[
|
||||
"script",
|
||||
{
|
||||
defer: "",
|
||||
src: "https://umami.taskfile.dev/script.js",
|
||||
"data-website-id": "084030b0-0e3f-4891-8d2a-0c12c40f5933"
|
||||
}
|
||||
]
|
||||
],
|
||||
srcDir: 'src',
|
||||
cleanUrls: true,
|
||||
markdown: {
|
||||
config: (md) => {
|
||||
md.use(githubLinksPlugin, {
|
||||
baseUrl: 'https://github.com',
|
||||
repo: 'go-task/task'
|
||||
});
|
||||
md.use(tabsMarkdownPlugin);
|
||||
md.use(groupIconMdPlugin);
|
||||
}
|
||||
},
|
||||
vite: {
|
||||
plugins: [
|
||||
groupIconVitePlugin({
|
||||
customIcon: {
|
||||
'.taskrc.yml': localIconLoader(
|
||||
import.meta.url,
|
||||
'./theme/icons/task.svg'
|
||||
),
|
||||
'Taskfile.yml': localIconLoader(
|
||||
import.meta.url,
|
||||
'./theme/icons/task.svg'
|
||||
)
|
||||
}
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: /^.*\/VPTeamMembersItem\.vue$/,
|
||||
replacement: fileURLToPath(
|
||||
new URL('./components/VPTeamMembersItem.vue', import.meta.url)
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
themeConfig: {
|
||||
logo: '/img/logo.svg',
|
||||
carbonAds: {
|
||||
code: 'CESI65QJ',
|
||||
placement: 'taskfiledev'
|
||||
},
|
||||
search: {
|
||||
provider: 'algolia',
|
||||
options: {
|
||||
appId: '7IZIJ13AI7',
|
||||
apiKey: '34b64ae4fc8d9da43d9a13d9710aaddc',
|
||||
indexName: 'taskfile'
|
||||
}
|
||||
},
|
||||
nav: [
|
||||
{ text: 'Home', link: '/' },
|
||||
{
|
||||
text: 'Docs',
|
||||
link: '/docs/guide',
|
||||
activeMatch: '^/docs'
|
||||
},
|
||||
{ text: 'Blog', link: '/blog', activeMatch: '^/blog' },
|
||||
{ text: 'Donate', link: '/donate' },
|
||||
{ text: 'Team', link: '/team' },
|
||||
{
|
||||
text: process.env.NODE_ENV === 'development' ? 'Next' : `v${version}`,
|
||||
items: [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: `v${version}`,
|
||||
link: urlVersion.current
|
||||
},
|
||||
{
|
||||
text: 'Next',
|
||||
link: urlVersion.next
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
sidebar: {
|
||||
'/blog/': [
|
||||
{
|
||||
text: '2025',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'Built-in Core Utilities',
|
||||
link: '/blog/windows-core-utils'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '2024',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'Any Variables',
|
||||
link: '/blog/any-variables'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '2023',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'Introducing Experiments',
|
||||
link: '/blog/task-in-2023'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'/': [
|
||||
{
|
||||
text: 'Installation',
|
||||
link: '/docs/installation'
|
||||
},
|
||||
{
|
||||
text: 'Getting Started',
|
||||
link: '/docs/getting-started'
|
||||
},
|
||||
{
|
||||
text: 'Guide',
|
||||
link: '/docs/guide'
|
||||
},
|
||||
{
|
||||
text: 'Reference',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: 'Taskfile Schema',
|
||||
link: '/docs/reference/schema'
|
||||
},
|
||||
{
|
||||
text: 'Environment',
|
||||
link: '/docs/reference/environment'
|
||||
},
|
||||
{
|
||||
text: 'Configuration',
|
||||
link: '/docs/reference/config'
|
||||
},
|
||||
{
|
||||
text: 'CLI',
|
||||
link: '/docs/reference/cli'
|
||||
},
|
||||
{
|
||||
text: 'Templating',
|
||||
link: '/docs/reference/templating'
|
||||
},
|
||||
{
|
||||
text: 'Package API',
|
||||
link: '/docs/reference/package'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Experiments',
|
||||
collapsed: true,
|
||||
link: '/docs/experiments/',
|
||||
items: [
|
||||
{
|
||||
text: 'Env Precedence (#1038)',
|
||||
link: '/docs/experiments/env-precedence'
|
||||
},
|
||||
{
|
||||
text: 'Gentle Force (#1200)',
|
||||
link: '/docs/experiments/gentle-force'
|
||||
},
|
||||
{
|
||||
text: 'Remote Taskfiles (#1317)',
|
||||
link: '/docs/experiments/remote-taskfiles'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Deprecations',
|
||||
collapsed: true,
|
||||
link: '/docs/deprecations/',
|
||||
items: [
|
||||
{
|
||||
text: 'Completion Scripts',
|
||||
link: '/docs/deprecations/completion-scripts'
|
||||
},
|
||||
{
|
||||
text: 'Template Functions',
|
||||
link: '/docs/deprecations/template-functions'
|
||||
},
|
||||
{
|
||||
text: 'Version 2 Schema (#1197)',
|
||||
link: '/docs/deprecations/version-2-schema'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Taskfile Versions',
|
||||
link: '/docs/taskfile-versions'
|
||||
},
|
||||
{
|
||||
text: 'Integrations',
|
||||
link: '/docs/integrations'
|
||||
},
|
||||
{
|
||||
text: 'Community',
|
||||
link: '/docs/community'
|
||||
},
|
||||
{
|
||||
text: 'Style Guide',
|
||||
link: '/docs/styleguide'
|
||||
},
|
||||
{
|
||||
text: 'Contributing',
|
||||
link: '/docs/contributing'
|
||||
},
|
||||
{
|
||||
text: 'Releasing',
|
||||
link: '/docs/releasing'
|
||||
},
|
||||
{
|
||||
text: 'Changelog',
|
||||
link: '/docs/changelog'
|
||||
},
|
||||
{
|
||||
text: 'FAQ',
|
||||
link: '/docs/faq'
|
||||
}
|
||||
],
|
||||
// Hacky to disable sidebar for these pages
|
||||
'/donate': [],
|
||||
'/team': []
|
||||
},
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/go-task/task' },
|
||||
{ icon: 'discord', link: 'https://discord.gg/6TY36E39UK' },
|
||||
{ icon: 'x', link: 'https://twitter.com/taskfiledev' },
|
||||
{ icon: 'bluesky', link: 'https://bsky.app/profile/taskfile.dev' },
|
||||
{ icon: 'mastodon', link: 'https://fosstodon.org/@task' }
|
||||
],
|
||||
|
||||
footer: {
|
||||
message:
|
||||
'Built with <a target="_blank" href="https://www.netlify.com">Netlify</a>'
|
||||
}
|
||||
},
|
||||
sitemap: {
|
||||
hostname: 'https://taskfile.dev'
|
||||
}
|
||||
});
|
||||
5
website/.vitepress/meta.ts
Normal file
5
website/.vitepress/meta.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const taskName = 'Task';
|
||||
export const taskDescription =
|
||||
'A fast, cross-platform build tool inspired by Make, designed for modern workflows.';
|
||||
|
||||
export const ogUrl = 'https://taskfile.dev/';
|
||||
63
website/.vitepress/plugins/github-links.ts
Normal file
63
website/.vitepress/plugins/github-links.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import type MarkdownIt from 'markdown-it';
|
||||
|
||||
interface PluginOptions {
|
||||
repo: string;
|
||||
}
|
||||
|
||||
function githubLinksPlugin(
|
||||
md: MarkdownIt,
|
||||
options: PluginOptions = {} as PluginOptions
|
||||
): void {
|
||||
const baseUrl = 'https://github.com';
|
||||
const { repo } = options;
|
||||
|
||||
md.core.ruler.after('inline', 'github-links', (state): void => {
|
||||
const tokens = state.tokens;
|
||||
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
if (tokens[i].type === 'inline' && tokens[i].children) {
|
||||
const inlineTokens = tokens[i].children!;
|
||||
|
||||
for (let j = 0; j < inlineTokens.length; j++) {
|
||||
if (inlineTokens[j].type === 'text') {
|
||||
let text: string = inlineTokens[j].content!;
|
||||
|
||||
const protectedRefs: string[] = [];
|
||||
let protectIndex: number = 0;
|
||||
|
||||
text = text.replace(
|
||||
/[\w.-]+\/[\w.-]+#(\d+)/g,
|
||||
(match: string): string => {
|
||||
const placeholder: string = `__PROTECTED_${protectIndex}__`;
|
||||
protectedRefs[protectIndex] = match;
|
||||
protectIndex++;
|
||||
return placeholder;
|
||||
}
|
||||
);
|
||||
|
||||
text = text.replace(
|
||||
/#(\d+)/g,
|
||||
`<a href="${baseUrl}/${repo}/issues/$1" target="_blank" class="github-pr-link">#$1</a>`
|
||||
);
|
||||
|
||||
text = text.replace(
|
||||
/@([a-zA-Z0-9_-]+)(?![\w@.])/g,
|
||||
`<a href="${baseUrl}/$1" target="_blank" class="github-user-mention">@$1</a>`
|
||||
);
|
||||
|
||||
protectedRefs.forEach((ref: string, index: number): void => {
|
||||
text = text.replace(`__PROTECTED_${index}__`, ref);
|
||||
});
|
||||
|
||||
if (text !== inlineTokens[j].content) {
|
||||
inlineTokens[j].content = text;
|
||||
inlineTokens[j].type = 'html_inline';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default githubLinksPlugin;
|
||||
13
website/.vitepress/sponsors.ts
Normal file
13
website/.vitepress/sponsors.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export const sponsors = [
|
||||
{
|
||||
tier: 'Gold Sponsors',
|
||||
size: 'big',
|
||||
items: [
|
||||
{
|
||||
name: 'devowl',
|
||||
url: 'https://devowl.io/',
|
||||
img: '/img/devowl.io.svg'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
45
website/.vitepress/team.ts
Normal file
45
website/.vitepress/team.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
export const team = [
|
||||
{
|
||||
slug: 'andreynering',
|
||||
avatar: 'https://www.github.com/andreynering.png',
|
||||
name: 'Andrey Nering',
|
||||
icon: '/img/flag-brazil.svg',
|
||||
title: 'Creator & Maintainer',
|
||||
sponsor: 'https://github.com/sponsors/andreynering',
|
||||
links: [
|
||||
{ icon: 'github', link: 'https://github.com/andreynering' },
|
||||
{ icon: 'discord', link: 'https://discord.com/users/310141681926275082' },
|
||||
{ icon: 'x', link: 'https://x.com/andreynering' },
|
||||
{
|
||||
icon: 'bluesky',
|
||||
link: 'https://bsky.app/profile/andreynering.bsky.social'
|
||||
},
|
||||
{ icon: 'mastodon', link: 'https://mastodon.social/@andreynering' }
|
||||
]
|
||||
},
|
||||
{
|
||||
slug: 'pd93',
|
||||
avatar: 'https://www.github.com/pd93.png',
|
||||
name: 'Pete Davison',
|
||||
icon: '/img/flag-wales.svg',
|
||||
title: 'Maintainer',
|
||||
sponsor: 'https://github.com/sponsors/pd93',
|
||||
links: [
|
||||
{ icon: 'github', link: 'https://github.com/pd93' },
|
||||
{ icon: 'bluesky', link: 'https://bsky.app/profile/pd93.uk' }
|
||||
]
|
||||
},
|
||||
{
|
||||
slug: 'vmaerten',
|
||||
avatar: 'https://www.github.com/vmaerten.png',
|
||||
name: 'Valentin Maerten',
|
||||
icon: '/img/flag-france.svg',
|
||||
title: 'Maintainer',
|
||||
sponsor: 'https://github.com/sponsors/vmaerten',
|
||||
links: [
|
||||
{ icon: 'github', link: 'https://github.com/vmaerten' },
|
||||
{ icon: 'x', link: 'https://x.com/v_maerten' },
|
||||
{ icon: 'bluesky', link: 'https://bsky.app/profile/vmaerten.bsky.social' }
|
||||
]
|
||||
}
|
||||
];
|
||||
147
website/.vitepress/theme/custom.css
Normal file
147
website/.vitepress/theme/custom.css
Normal file
@@ -0,0 +1,147 @@
|
||||
:root {
|
||||
--ifm-color-primary: #43aba2;
|
||||
--vp-home-hero-name-color: var(--ifm-color-primary);
|
||||
--vp-c-brand-1: var(--ifm-color-primary);
|
||||
--vp-c-brand-2: var(--ifm-color-primary);
|
||||
--vp-c-brand-3: var(--ifm-color-primary);
|
||||
|
||||
--vp-icon-info: #3b82f6;
|
||||
--vp-icon-tip: #10b981;
|
||||
--vp-icon-warning: #f59e0b;
|
||||
--vp-icon-danger: #ef4444;
|
||||
--vp-icon-details: #6b7280;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--vp-icon-info: #93c5fd;
|
||||
--vp-icon-tip: #34d399;
|
||||
--vp-icon-warning: #fbbf24;
|
||||
--vp-icon-danger: #f87171;
|
||||
--vp-icon-details: #9ca3af;
|
||||
}
|
||||
|
||||
img[src*='shields.io'] {
|
||||
display: inline;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
img[src*='custom-icon-badges.demolab.com'] {
|
||||
display: inline;
|
||||
height: 1em;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.github-user-mention {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.vp-doc .blog-post:first-of-type {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.blog-post {
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.blog-post:nth-of-type(1) {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
.blog-post:nth-of-type(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
.blog-post:nth-of-type(3) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.custom-block .custom-block-title::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
flex-shrink: 0;
|
||||
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
-webkit-mask-position: center;
|
||||
-webkit-mask-size: contain;
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: contain;
|
||||
}
|
||||
|
||||
.custom-block.info .custom-block-title::before {
|
||||
background-color: var(--vp-icon-info);
|
||||
-webkit-mask-image: url('./icons/info.svg');
|
||||
mask-image: url('./icons/info.svg');
|
||||
}
|
||||
|
||||
.custom-block.tip .custom-block-title::before {
|
||||
background-color: var(--vp-icon-tip);
|
||||
-webkit-mask-image: url('./icons/tip.svg');
|
||||
mask-image: url('./icons/tip.svg');
|
||||
}
|
||||
|
||||
.custom-block.warning .custom-block-title::before {
|
||||
background-color: var(--vp-icon-warning);
|
||||
-webkit-mask-image: url('./icons/warning.svg');
|
||||
mask-image: url('./icons/warning.svg');
|
||||
}
|
||||
|
||||
.custom-block.danger .custom-block-title::before {
|
||||
background-color: var(--vp-icon-danger);
|
||||
-webkit-mask-image: url('./icons/danger.svg');
|
||||
mask-image: url('./icons/danger.svg');
|
||||
}
|
||||
|
||||
.custom-block.details[open] summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.custom-block .custom-block-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@supports not (mask-image: none) {
|
||||
.custom-block .custom-block-title::before,
|
||||
.custom-block.details summary::before {
|
||||
font-size: 18px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
background: none !important;
|
||||
-webkit-mask: none !important;
|
||||
mask: none !important;
|
||||
}
|
||||
|
||||
.custom-block.info .custom-block-title::before {
|
||||
content: 'ℹ️';
|
||||
}
|
||||
|
||||
.custom-block.tip .custom-block-title::before {
|
||||
content: '💡';
|
||||
}
|
||||
|
||||
.custom-block.warning .custom-block-title::before {
|
||||
content: '⚠️';
|
||||
}
|
||||
|
||||
.custom-block.danger .custom-block-title::before {
|
||||
content: '🔥';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.VPTeamPage > .VPTeamPageTitle {
|
||||
padding-top: 0
|
||||
}
|
||||
3
website/.vitepress/theme/icons/danger.svg
Normal file
3
website/.vitepress/theme/icons/danger.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M7.998 14.5c2.832 0 5-1.98 5-4.5 0-1.463-.68-2.19-1.879-3.383l-.036-.037c-1.013-1.008-2.3-2.29-2.834-4.434-.322.256-.63.579-.864.953-.432.696-.621 1.58-.046 2.73.473.947.67 2.284-.278 3.232-.61.61-1.545.84-2.403.633a2.788 2.788 0 0 1-1.436-.874A3.21 3.21 0 0 0 3 10c0 2.53 2.164 4.5 4.998 4.5zM9.533.753C9.496.34 9.16.009 8.77.146 7.035.75 4.34 3.187 5.997 6.5c.344.689.285 1.218.003 1.5-.419.419-1.54.487-2.04-.832-.173-.454-.659-.762-1.035-.454C2.036 7.44 1.5 8.702 1.5 10c0 3.512 2.998 6 6.498 6s6.5-2.5 6.5-6c0-2.137-1.128-3.26-2.312-4.438-1.19-1.184-2.436-2.425-2.653-4.81z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 681 B |
3
website/.vitepress/theme/icons/info.svg
Normal file
3
website/.vitepress/theme/icons/info.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 350 B |
|
Before Width: | Height: | Size: 435 B After Width: | Height: | Size: 435 B |
3
website/.vitepress/theme/icons/tip.svg
Normal file
3
website/.vitepress/theme/icons/tip.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 796 B |
3
website/.vitepress/theme/icons/warning.svg
Normal file
3
website/.vitepress/theme/icons/warning.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 405 B |
24
website/.vitepress/theme/index.ts
Normal file
24
website/.vitepress/theme/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import DefaultTheme from 'vitepress/theme';
|
||||
import type { Theme } from 'vitepress';
|
||||
import './custom.css';
|
||||
import HomePage from '../components/HomePage.vue';
|
||||
import AuthorCard from '../components/AuthorCard.vue';
|
||||
import BlogPost from '../components/BlogPost.vue';
|
||||
import Version from '../components/Version.vue';
|
||||
import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client';
|
||||
import { h } from 'vue';
|
||||
import 'virtual:group-icons.css';
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
Layout() {
|
||||
return h(DefaultTheme.Layout, null, {
|
||||
'home-features-after': () => h(HomePage)
|
||||
});
|
||||
},
|
||||
enhanceApp({ app }) {
|
||||
app.component('AuthorCard', AuthorCard);
|
||||
app.component('BlogPost', BlogPost);
|
||||
app.component('Version', Version);
|
||||
enhanceAppWithTabs(app);
|
||||
}
|
||||
} satisfies Theme;
|
||||
@@ -1,29 +1,35 @@
|
||||
version: "3"
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
yarn:install:
|
||||
desc: Setup Docusaurus locally
|
||||
install:
|
||||
desc: Setup VitePress locally
|
||||
cmds:
|
||||
- yarn install
|
||||
- pnpm install
|
||||
sources:
|
||||
- package.json
|
||||
- yarn.lock
|
||||
- pnpm-lock.yaml
|
||||
|
||||
default:
|
||||
desc: Start website
|
||||
deps: [yarn:install]
|
||||
deps: [install]
|
||||
aliases: [s, start]
|
||||
vars:
|
||||
HOST: '{{default "0.0.0.0" .HOST}}'
|
||||
PORT: '{{default "3001" .PORT}}'
|
||||
cmds:
|
||||
- npx docusaurus start --no-open --host={{.HOST}} --port={{.PORT}}
|
||||
- pnpm dev --host={{.HOST}} --port={{.PORT}}
|
||||
|
||||
lint:
|
||||
desc: Lint website
|
||||
deps: [install]
|
||||
cmds:
|
||||
- pnpm lint
|
||||
|
||||
build:
|
||||
desc: Build website
|
||||
deps: [yarn:install]
|
||||
deps: [install]
|
||||
cmds:
|
||||
- npx docusaurus build
|
||||
- pnpm build
|
||||
|
||||
preview:
|
||||
desc: Preview Website
|
||||
@@ -33,20 +39,19 @@ tasks:
|
||||
HOST: '{{default "localhost" .HOST}}'
|
||||
PORT: '{{default "3001" .PORT}}'
|
||||
cmds:
|
||||
- npx docusaurus serve --no-open --host={{.HOST}} --port={{.PORT}}
|
||||
- pnpm preview --host={{.HOST}} --port={{.PORT}}
|
||||
|
||||
clean:
|
||||
desc: Clean temp directories
|
||||
cmds:
|
||||
- rm -rf ./build
|
||||
- rm -rf ./vitepress/dist
|
||||
|
||||
deploy:
|
||||
desc: Build and deploy Docusaurus
|
||||
summary: Requires GIT_USER and GIT_PASS envs to be previous set
|
||||
deploy:next:
|
||||
desc: Build and deploy next.taskfile.dev
|
||||
cmds:
|
||||
- npx docusaurus deploy
|
||||
- pnpm netlify deploy --prod --site=4e13dfcf-fc0d-4bec-ad60-b918a8dc3942
|
||||
|
||||
upgrade:
|
||||
desc: Upgrade Docusaurus
|
||||
deploy:prod:
|
||||
desc: Build and deploy taskfile.dev
|
||||
cmds:
|
||||
- yarn upgrade @docusaurus/core@latest @docusaurus/preset-classic@latest @docusaurus/module-type-aliases@latest @docusaurus/tsconfig@latest @docusaurus/types@latest
|
||||
- pnpm netlify deploy --prod --site=e625bc6a-1cd3-465d-ad30-7bbddaeb4f31
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export default {
|
||||
presets: ['@docusaurus/core/lib/babel/preset'],
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
andreynering:
|
||||
name: Andrey Nering
|
||||
title: Maintainer of Task
|
||||
url: https://github.com/andreynering
|
||||
image_url: https://github.com/andreynering.png
|
||||
pd93:
|
||||
name: Pete Davison
|
||||
title: Maintainer of Task
|
||||
url: https://github.com/pd93
|
||||
image_url: https://github.com/pd93.png
|
||||
@@ -1,7 +0,0 @@
|
||||
export const GITHUB_URL = 'https://github.com/go-task/task';
|
||||
export const TWITTER_URL = 'https://twitter.com/taskfiledev';
|
||||
export const BLUESKY_URL = 'https://bsky.app/profile/taskfile.dev';
|
||||
export const MASTODON_URL = 'https://fosstodon.org/@task';
|
||||
export const DISCORD_URL = 'https://discord.gg/6TY36E39UK';
|
||||
export const STACK_OVERFLOW = 'https://stackoverflow.com/questions/tagged/taskfile';
|
||||
export const ANSWER_OVERFLOW = 'https://www.answeroverflow.com/c/974121106208354339';
|
||||
@@ -1,177 +0,0 @@
|
||||
---
|
||||
slug: /contributing/
|
||||
sidebar_position: 12
|
||||
---
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions to Task are very welcome, but we ask that you read this document
|
||||
before submitting a PR.
|
||||
|
||||
:::note
|
||||
|
||||
This document applies to the core [Task][task] repository _and_ [Task for Visual
|
||||
Studio Code][vscode-task].
|
||||
|
||||
:::
|
||||
|
||||
## Before you start
|
||||
|
||||
- **Check existing work** - Is there an existing PR? Are there issues discussing
|
||||
the feature/change you want to make? Please make sure you consider/address
|
||||
these discussions in your work.
|
||||
- **Backwards compatibility** - Will your change break existing Taskfiles? It is
|
||||
much more likely that your change will merged if it backwards compatible. Is
|
||||
there an approach you can take that maintains this compatibility? If not,
|
||||
consider opening an issue first so that API changes can be discussed before
|
||||
you invest your time into a PR.
|
||||
- **Experiments** - If there is no way to make your change backward compatible
|
||||
then there is a procedure to introduce breaking changes into minor versions.
|
||||
We call these "[experiments][experiments]". If you're intending to work on an
|
||||
experiment, then please read the [experiments workflow][experiments-workflow]
|
||||
document carefully and submit a proposal first.
|
||||
|
||||
## 1. Setup
|
||||
|
||||
- **Go** - Task is written in [Go][go]. We always support the latest two major
|
||||
Go versions, so make sure your version is recent enough.
|
||||
- **Node.js** - [Node.js][nodejs] is used to host Task's documentation server
|
||||
and is required if you want to run this server locally. It is also required if
|
||||
you want to contribute to the Visual Studio Code extension.
|
||||
- **Yarn** - [Yarn][yarn] is the Node.js package manager used by Task.
|
||||
|
||||
## 2. Making changes
|
||||
|
||||
- **Code style** - Try to maintain the existing code style where possible. Go
|
||||
code should be formatted and linted by [`golangci-lint`][golangci-lint]. This
|
||||
wraps the [`gofumpt`][gofumpt] and [`gci`][gci] formatters and a number of
|
||||
linters. We recommend that you take a look at the [golangci-lint
|
||||
docs][golangci-lint-docs] for a guide on how to setup your editor to
|
||||
auto-format your code. Any Markdown or TypeScript files should be formatted
|
||||
and linted by [Prettier][prettier]. This style is enforced by our CI to ensure
|
||||
that we have a consistent style across the project. You can use the `task
|
||||
lint` command to lint the code locally and the `task lint:fix` command to try
|
||||
to automatically fix any issues that are found. You can also use the `task
|
||||
fmt` command to auto-format the files if your editor doesn't do it for you.
|
||||
- **Documentation** - Ensure that you add/update any relevant documentation. See
|
||||
the [updating documentation](#updating-documentation) section below.
|
||||
- **Tests** - Ensure that you add/update any relevant tests and that all tests
|
||||
are passing before submitting the PR. See the [writing tests](#writing-tests)
|
||||
section below.
|
||||
|
||||
### Running your changes
|
||||
|
||||
To run Task with working changes, you can use `go run ./cmd/task`. To run a
|
||||
development build of task against a test Taskfile in `testdata`, you can use
|
||||
`go run ./cmd/task --dir ./testdata/<my_test_dir> <task_name>`.
|
||||
|
||||
To run Task for Visual Studio Code, you can open the project in VSCode and hit
|
||||
F5 (or whatever you debug keybind is set to). This will open a new VSCode window
|
||||
with the extension running. Debugging this way is recommended as it will allow
|
||||
you to set breakpoints and step through the code. Otherwise, you can run
|
||||
`task package` which will generate a `.vsix` file that can be used to manually
|
||||
install the extension.
|
||||
|
||||
### Updating documentation
|
||||
|
||||
Task uses [Docusaurus][docusaurus] to host a documentation server. The code for
|
||||
this is located in the core Task repository. This can be setup and run locally
|
||||
by using `task website` (requires `nodejs` & `yarn`). All content is written in
|
||||
[MDX][mdx] (an extension of Markdown) and is located in the `website/docs`
|
||||
directory. All Markdown documents should have an 80 character line wrap limit
|
||||
(enforced by Prettier).
|
||||
|
||||
When making a change, consider whether a change to the [Usage Guide](/usage) is
|
||||
necessary. This document contains descriptions and examples of how to use Task
|
||||
features. If you're adding a new feature, try to find an appropriate place to
|
||||
add a new section. If you're updating an existing feature, ensure that the
|
||||
documentation and any examples are up-to-date. Ensure that any examples follow
|
||||
the [Taskfile Styleguide](/styleguide).
|
||||
|
||||
If you added a new command or flag, ensure that you add it to the [CLI
|
||||
Reference](/reference/cli). New fields also need to be added to the [Schema
|
||||
Reference](/reference/schema) and [JSON Schema][json-schema]. The descriptions
|
||||
for fields in the docs and the schema should match.
|
||||
|
||||
### Writing tests
|
||||
|
||||
A lot of Task's tests are held in the `task_test.go` file in the project root
|
||||
and this is where you'll most likely want to add new ones too. Most of these
|
||||
tests also have a subdirectory in the `testdata` directory where any
|
||||
Taskfiles/data required to run the tests are stored.
|
||||
|
||||
When making a changes, consider whether new tests are required. These tests
|
||||
should ensure that the functionality you are adding will continue to work in the
|
||||
future. Existing tests may also need updating if you have changed Task's
|
||||
behavior.
|
||||
|
||||
You may also consider adding unit tests for any new functions you have added.
|
||||
The unit tests should follow the Go convention of being location in a file named
|
||||
`*_test.go` in the same package as the code being tested.
|
||||
|
||||
## 3. Committing your code
|
||||
|
||||
Try to write meaningful commit messages and avoid having too many commits on the
|
||||
PR. Most PRs should likely have a single commit (although for bigger PRs it may
|
||||
be reasonable to split it in a few). Git squash and rebase is your friend!
|
||||
|
||||
If you're not sure how to format your commit message, check out [Conventional
|
||||
Commits][conventional-commits]. This style is not enforced, but it is a good way
|
||||
to make your commit messages more readable and consistent.
|
||||
|
||||
## 4. Submitting a PR
|
||||
|
||||
- **Describe your changes** - Ensure that you provide a comprehensive
|
||||
description of your changes.
|
||||
- **Issue/PR links** - Link any previous work such as related issues or PRs.
|
||||
Please describe how your changes differ to/extend this work.
|
||||
- **Examples** - Add any examples or screenshots that you think are useful to
|
||||
demonstrate the effect of your changes.
|
||||
- **Draft PRs** - If your changes are incomplete, but you would like to discuss
|
||||
them, open the PR as a draft and add a comment to start a discussion. Using
|
||||
comments rather than the PR description allows the description to be updated
|
||||
later while preserving any discussions.
|
||||
|
||||
## FAQ
|
||||
|
||||
> I want to contribute, where do I start?
|
||||
|
||||
Take a look at the list of [open issues for Task][task-open-issues] or [Task for
|
||||
Visual Studio Code][vscode-task-open-issues]. We have a [good first
|
||||
issue][good-first-issue] label for simpler issues that are ideal for first time
|
||||
contributions.
|
||||
|
||||
All kinds of contributions are welcome, whether its a typo fix or a shiny new
|
||||
feature. You can also contribute by upvoting/commenting on issues, helping to
|
||||
answer questions or contributing to other [community projects](/community).
|
||||
|
||||
> I'm stuck, where can I get help?
|
||||
|
||||
If you have questions, feel free to ask them in the `#help` forum channel on our
|
||||
[Discord server][discord-server] or open a [Discussion][discussion] on GitHub.
|
||||
|
||||
---
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[experiments]: /experiments
|
||||
[experiments-workflow]: /experiments#workflow
|
||||
[task]: https://github.com/go-task/task
|
||||
[vscode-task]: https://github.com/go-task/vscode-task
|
||||
[go]: https://go.dev
|
||||
[gofumpt]: https://github.com/mvdan/gofumpt
|
||||
[gci]: https://github.com/daixiang0/gci
|
||||
[golangci-lint]: https://golangci-lint.run
|
||||
[golangci-lint-docs]: https://golangci-lint.run/welcome/integrations/
|
||||
[prettier]: https://prettier.io
|
||||
[nodejs]: https://nodejs.org/en/
|
||||
[yarn]: https://yarnpkg.com/
|
||||
[docusaurus]: https://docusaurus.io
|
||||
[json-schema]: https://github.com/go-task/task/blob/main/website/static/schema.json
|
||||
[task-open-issues]: https://github.com/go-task/task/issues
|
||||
[vscode-task-open-issues]: https://github.com/go-task/vscode-task/issues
|
||||
[good-first-issue]: https://github.com/go-task/task/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22
|
||||
[discord-server]: https://discord.gg/6TY36E39UK
|
||||
[discussion]: https://github.com/go-task/task/discussions
|
||||
[conventional-commits]: https://www.conventionalcommits.org
|
||||
[mdx]: https://mdxjs.com/
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
slug: /deprecations/completion-scripts/
|
||||
---
|
||||
|
||||
# Completion Scripts
|
||||
|
||||
:::warning
|
||||
|
||||
This deprecation breaks the following functionality:
|
||||
|
||||
- Any direct references to the completion scripts in the Task git repository
|
||||
|
||||
:::
|
||||
|
||||
Direct use of the completion scripts in the `completion/*` directory of the
|
||||
[github.com/go-task/task][task] Git repository is deprecated. Any shell
|
||||
configuration that directly refers to these scripts will potentially break in
|
||||
the future as the scripts may be moved or deleted entirely. Any configuration
|
||||
should be updated to use the [new method for generating shell
|
||||
completions][completions] instead.
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[completions]: ../installation.mdx#setup-completions
|
||||
[task]: https://github.com/go-task/task
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
slug: /deprecations/
|
||||
sidebar_position: 8
|
||||
---
|
||||
|
||||
# Deprecations
|
||||
|
||||
As Task evolves, it occasionally outgrows some of its functionality. This can be
|
||||
because they are no longer useful, because another feature has replaced it or
|
||||
because of a change in the way that Task works internally.
|
||||
|
||||
When this happens, we mark the functionality as deprecated. This means that it
|
||||
will be removed in a future version of Task. This functionality will continue to
|
||||
work until that time, but we strongly recommend that you do not implement this
|
||||
functionality in new Taskfiles and make a plan to migrate away from it as soon
|
||||
as possible.
|
||||
|
||||
You can view a full list of active deprecations in the "Deprecations" section of
|
||||
the sidebar.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
# This is a template for an experiments documentation
|
||||
# Copy this page and fill in the details as necessary
|
||||
title: '--- Template ---'
|
||||
sidebar_position: -1 # Always push to the top
|
||||
draft: true # Hide in production
|
||||
---
|
||||
|
||||
# \{Name of Deprecated Feature\} (#\{Issue\})
|
||||
|
||||
:::warning
|
||||
|
||||
This deprecation breaks the following functionality:
|
||||
|
||||
- \{list any existing functionality that will be broken by this deprecation\}
|
||||
- \{if there are no breaking changes, remove this admonition\}
|
||||
|
||||
:::
|
||||
|
||||
\{Short description of the feature/behavior and why it is being deprecated\}
|
||||
|
||||
\{Short explanation of any replacement features/behaviors and how users should
|
||||
migrate to it\}
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
slug: /deprecations/template-functions/
|
||||
---
|
||||
|
||||
# Template Functions
|
||||
|
||||
:::warning
|
||||
|
||||
This deprecation breaks the following functionality:
|
||||
|
||||
- A small set of templating functions
|
||||
|
||||
:::
|
||||
|
||||
The following templating functions are deprecated. Any replacement functions are
|
||||
listed besides the function being removed.
|
||||
|
||||
| Deprecated function | Replaced by |
|
||||
| ------------------- | ----------- |
|
||||
| `IsSH` | - |
|
||||
| `FromSlash` | `fromSlash` |
|
||||
| `ToSlash` | `toSlash` |
|
||||
| `ExeExt` | `exeExt` |
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
slug: /deprecations/version-2-schema/
|
||||
---
|
||||
|
||||
# Version 2 Schema (#1197)
|
||||
|
||||
:::warning
|
||||
|
||||
This deprecation breaks the following functionality:
|
||||
|
||||
- Any Taskfiles that use the version 2 schema
|
||||
- `Taskvar.yml` files
|
||||
|
||||
:::
|
||||
|
||||
The Taskfile version 2 schema was introduced in March 2018 and replaced by
|
||||
version 3 in August 2019. In May 2023 [we published a deprecation
|
||||
notice][deprecation-notice] for the version 2 schema on the basis that the vast
|
||||
majority of users had already upgraded to version 3 and removing support for
|
||||
version 2 would allow us to tidy up the codebase and focus on new functionality
|
||||
instead.
|
||||
|
||||
In December 2023, the final version of Task that supports the version 2 schema
|
||||
([v3.33.0][v3.33.0]) was published and all legacy code was removed from Task's
|
||||
main branch. To use a more recent version of Task, you will need to ensure that
|
||||
your Taskfile uses the version 3 schema instead. A list of changes between
|
||||
version 2 and version 3 are available in the [Task v3 Release Notes][v3.0.0].
|
||||
|
||||
{/* prettier-ignore-start */}
|
||||
[v3.0.0]: https://github.com/go-task/task/releases/tag/v3.0.0
|
||||
[v3.33.0]: https://github.com/go-task/task/releases/tag/v3.33.0
|
||||
[deprecation-notice]: https://github.com/go-task/task/issues/1197
|
||||
{/* prettier-ignore-end */}
|
||||
@@ -1,74 +0,0 @@
|
||||
---
|
||||
slug: '/experiments/env-precedence'
|
||||
---
|
||||
|
||||
# Env Precedence (#1038)
|
||||
|
||||
:::caution
|
||||
|
||||
All experimental features are subject to breaking changes and/or removal _at any
|
||||
time_. We strongly recommend that you do not use these features in a production
|
||||
environment. They are intended for testing and feedback only.
|
||||
|
||||
:::
|
||||
|
||||
:::warning
|
||||
|
||||
This experiment breaks the following functionality:
|
||||
|
||||
- environment variable will take precedence over OS environment variables
|
||||
|
||||
:::
|
||||
|
||||
:::info
|
||||
|
||||
To enable this experiment, set the environment variable:
|
||||
`TASK_X_ENV_PRECEDENCE=1`. Check out [our guide to enabling
|
||||
experiments][enabling-experiments] for more information.
|
||||
|
||||
:::
|
||||
|
||||
Before this experiment, the OS variable took precedence over the task
|
||||
environment variable. This experiment changes the precedence to make the task
|
||||
environment variable take precedence over the OS variable.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```yml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
env:
|
||||
KEY: 'other'
|
||||
cmds:
|
||||
- echo "$KEY"
|
||||
```
|
||||
Running `KEY=some task` before this experiment, the output would be `some`, but
|
||||
after this experiment, the output would be `other`.
|
||||
|
||||
If you still want to get the OS variable, you can use the template function env
|
||||
like follow : `{{env "OS_VAR"}}`.
|
||||
|
||||
```yml
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
env:
|
||||
KEY: 'other'
|
||||
cmds:
|
||||
- echo "$KEY"
|
||||
- echo {{env "KEY"}}
|
||||
```
|
||||
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
|
||||
default template function:
|
||||
```yml
|
||||
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